一个例子看LLM的工具调用流程

前面在我们使用langchain等框架做多工具调用的时候,我们不清楚具体的交互流程。LLM的推理过程是怎么样的,是一次做完规划还是逐个调用工具。

我们今天直接使用openai的接口,用原始的写法来看它具体执行的过程是怎样的。

代码如下:

import os
import json
from openai import OpenAI
from dotenv import load_dotenv

# 加载 .env 文件中的环境变量 (如果存在)
# 建议将 API Key 存储在 .env 文件中,例如 OPENAI_API_KEY=your_key_here
load_dotenv() 

# --- 配置 ---
# 确保设置了 OPENAI_API_KEY 环境变量
# 或者取消下面的注释并直接设置密钥(安全性较低)
# os.environ["OPENAI_API_KEY"] = "sk-..." 

api_key = os.getenv("OPENAI_API_KEY")
base_url = "https://dashscope.aliyuncs.com/compatible-mode/v1/"
model_name = "qwen-plus"
print(f"api_key: {api_key}")
print(f"base_url: {base_url}")
print(f"model_name: {model_name}")

# --- 工具定义 (本地函数) ---
def get_current_weather(location, unit="celsius"):
    """获取给定地点的当前天气"""
    print(f"--- 正在调用工具: get_current_weather(location='{location}', unit='{unit}') ---")
    # 在实际应用中,这里会调用真实的天气 API
    # 为了演示,我们返回模拟数据
    if "beijing" in location.lower() or "北京" in location:
        weather_info = {
            "location": location,
            "temperature": "15",
            "unit": unit,
            "forecast": "晴朗",
        }
    elif "nanjing" in location.lower() or "南京" in location:
        weather_info = {
            "location": location,
            "temperature": "18",
            "unit": unit,
            "forecast": "多云",
        }
    else:
         weather_info = {
            "location": location,
            "temperature": "未知",
            "unit": unit,
            "forecast": "未知",
        }
    return json.dumps(weather_info, ensure_ascii=False) # ensure_ascii=False 以正确处理中文

def get_stock_price(symbol):
    """获取给定股票代码的当前价格"""
    print(f"--- 正在调用工具: get_stock_price(symbol='{symbol}') ---")
    # 在实际应用中,这里会调用真实的股票 API
    # 为了演示,我们返回模拟数据
    if symbol.upper() == "AAPL":
        stock_info = {"symbol": symbol, "price": "170.50", "currency": "USD"}
    elif symbol.upper() == "MSFT":
         stock_info = {"symbol": symbol, "price": "420.72", "currency": "USD"}
    else:
        stock_info = {"symbol": symbol, "price": "未知", "currency": "未知"}
    return json.dumps(stock_info)

# --- 主逻辑 ---
def run_conversation(user_query: str, model: str):
    """运行包含工具调用的对话流程 (顺序执行)"""
    try:
        client = OpenAI(api_key=api_key, base_url=base_url)
        # 确保 API Key 已设置
        if not client.api_key:
             print("错误:OPENAI_API_KEY 环境变量未设置。")
             return
    except Exception as e:
        print(f"初始化 OpenAI 客户端时出错: {e}")
        print("请确保已安装 'openai' 库并且 OPENAI_API_KEY 已正确设置。")
        return

    messages = [{"role": "user", "content": user_query}]
    
    # 定义 OpenAI API 需要的工具格式 (只需定义一次)
    tools = [
        {
            "type": "function",
            "function": {
                "name": "get_current_weather",
                "description": "获取给定位置的当前天气信息",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "location": {
                            "type": "string",
                            "description": "城市和省/州,例如 San Francisco, CA 或 Beijing",
                        },
                        "unit": {"type": "string", "enum": ["celsius", "fahrenheit"], "description": "温度单位"},
                    },
                    "required": ["location"],
                },
            },
        },
        {
            "type": "function",
            "function": {
                "name": "get_stock_price",
                "description": "获取指定股票代码的当前价格",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "symbol": {
                            "type": "string",
                            "description": "股票代码,例如 AAPL, MSFT",
                        }
                    },
                    "required": ["symbol"],
                },
            },
        }
    ]

    # 将本地函数映射到其名称,方便查找
    available_functions = {
        "get_current_weather": get_current_weather,
        "get_stock_price": get_stock_price,
    }

    print(f"\n{'='*10} 对话开始 (顺序工具调用) {'='*10}")
    print(f"初始用户请求: {user_query}")
    
    iteration_count = 0 # 防止无限循环
    max_iterations = 5  # 设置最大循环次数

    while iteration_count < max_iterations:
        iteration_count += 1
        print(f"\n--- 第 {iteration_count} 轮 API 调用 ---")
        print(f">>> 步骤 {iteration_count}.1: 向 OpenAI 模型发送当前对话历史和工具")
        print(f"    原始发送消息: messages={messages}, tools={tools}")
        
        try:
            response = client.chat.completions.create(
                model=model,
                messages=messages,
                tools=tools,
                tool_choice="auto", 
            )
            response_message = response.choices[0].message
            tool_calls = response_message.tool_calls
            finish_reason = response.choices[0].finish_reason

            print(f"\n<<< 步骤 {iteration_count}.1 响应: 收到模型的决策")
            print(f"    原始响应: {response}") # 打印完整响应对象
            print(f"    响应消息: {response_message}")
            print(f"    结束原因: {finish_reason}")

        except Exception as e:
            print(f"\n!!! 调用 OpenAI API 时出错: {e}")
            break # 出错时退出循环

        # 将模型的回复添加到消息历史中 (无论是否包含 tool_calls)
        messages.append(response_message)

        # --- 步骤 {iteration_count}.2: 检查模型是否请求调用工具 ---
        if tool_calls:
            print(f"\n>>> 步骤 {iteration_count}.2: 模型请求调用 {len(tool_calls)} 个工具。将处理第一个。")
            
            # --- 步骤 {iteration_count}.3: 顺序执行第一个工具调用 ---
            tool_call = tool_calls[0] # 只处理第一个工具调用
            function_name = tool_call.function.name
            function_to_call = available_functions.get(function_name)
            
            tool_output_content = "" # 初始化工具输出内容

            print(f"\n--- 开始执行工具: {function_name} (ID: {tool_call.id}) ---")
            
            if not function_to_call:
                 print(f"!!! 错误: 模型请求了未知的函数 '{function_name}'")
                 tool_output_content = json.dumps({"error": f"函数 {function_name} 未找到"})
            else:
                try:
                    function_args = json.loads(tool_call.function.arguments)
                    print(f"--- 准备调用函数: {function_name} 参数: {function_args}")
                    tool_output_content = function_to_call(**function_args)
                    print(f"--- 函数 {function_name} 执行完毕。结果: {tool_output_content}")
                
                except json.JSONDecodeError:
                    error_msg = f"无法解析函数 {function_name} 的参数。原始参数: '{tool_call.function.arguments}'"
                    print(f"!!! 错误: {error_msg}")
                    tool_output_content = json.dumps({"error": error_msg})
                except Exception as e:
                    error_msg = f"执行函数 {function_name} 时出错: {e}"
                    print(f"!!! {error_msg}")
                    tool_output_content = json.dumps({"error": str(e)})
            
            print("--- 工具执行结束 ---")

            # 将工具执行结果添加到消息历史中,准备下一次调用
            messages.append(
                {
                    "tool_call_id": tool_call.id,
                    "role": "tool",
                    "name": function_name,
                    "content": tool_output_content,
                }
            )
            print(f">>> 步骤 {iteration_count}.4: 已将工具 '{function_name}' 的结果添加到消息历史中,准备下一轮。")
            # 继续循环,将带有工具结果的消息发送给模型

        else:
            # --- 如果模型没有请求调用工具,说明得到了最终回复 ---
            print(f"\n>>> 步骤 {iteration_count}.2: 模型没有请求调用工具 (结束原因: {finish_reason})。流程结束。")
            print("\n=== 模型最终回复 ===")
            if response_message.content:
                print(response_message.content)
            else:
                print("(模型没有返回文本内容,可能是因为 finish_reason 不是 'stop')")
            break # 退出循环

    if iteration_count >= max_iterations:
         print(f"\n!!! 警告: 已达到最大循环次数 ({max_iterations}),强制结束对话。")

    print(f"\n{'='*10} 对话结束 {'='*10}")


# --- 示例用法 ---
if __name__ == "__main__":
    # 示例 1: 需要调用多个工具
    query1 = "请问北京今天天气怎么样?还有微软(MSFT)的股价是多少?南京天气如何?"
    run_conversation(query1, model_name)

测试场景一:

问题:请问北京今天天气怎么样?还有微软(MSFT)的股价是多少?南京天气如何?
模型:qwen-plus

========== 对话开始 (顺序工具调用) ==========
初始用户请求: 请问北京今天天气怎么样?还有微软(MSFT)的股价是多少?南京天气如何?

--- 第 1 轮 API 调用 ---
>>> 步骤 1.1: 向 OpenAI 模型发送当前对话历史和工具
    原始发送消息: messages=[{'role': 'user', 'content': '请问北京今天天气怎么样?还有微软(MSFT)的股价是多少?南京天气如何?'}], tools=[{'type': 'function', 'function': {'name': 'get_current_weather', 'description': '获取给定位置的当前天气信息', 'parameters': {'type': 'object', 'properties': {'location': {'type': 'string', 'description': '城市和省/州,例如 San Francisco, CA 或 Beijing'}, 'unit': {'type': 'string', 'enum': ['celsius', 'fahrenheit'], 'description': '温度单位'}}, 'required': ['location']}}}, {'type': 'function', 'function': {'name': 'get_stock_price', 'description': '获取 指定股票代码的当前价格', 'parameters': {'type': 'object', 'properties': {'symbol': {'type': 'string', 'description': '股票代码,例如 AAPL, MSFT'}}, 'required': ['symbol']}}}]

<<< 步骤 1.1 响应: 收到模型的决策
    原始响应: ChatCompletion(id='chatcmpl-1d154842-4f25-9ae6-8521-d8bb0faa4e50', choices=[Choice(finish_reason='tool_calls', index=0, logprobs=None, message=ChatCompletionMessage(content='', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_8c892135f8ff4b9a97faf2', function=Function(arguments='{"location": "北京", "unit": "celsius"}', name='get_current_weather'), type='function', index=0)]))], created=1746515789, model='qwen-plus', object='chat.completion', service_tier=None, system_fingerprint=None, usage=CompletionUsage(completion_tokens=25, prompt_tokens=300, total_tokens=325, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetails(audio_tokens=None, cached_tokens=0)))
    响应消息: ChatCompletionMessage(content='', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_8c892135f8ff4b9a97faf2', function=Function(arguments='{"location": "北京", "unit": "celsius"}', name='get_current_weather'), type='function', index=0)])
    结束原因: tool_calls

>>> 步骤 1.2: 模型请求调用 1 个工具。将处理第一个。

--- 开始执行工具: get_current_weather (ID: call_8c892135f8ff4b9a97faf2) ---
--- 准备调用函数: get_current_weather 参数: {'location': '北京', 'unit': 'celsius'}
--- 正在调用工具: get_current_weather(location='北京', unit='celsius') ---
--- 函数 get_current_weather 执行完毕。结果: {"location": "北京", "temperature": "15", "unit": "celsius", "forecast": "晴朗"}
--- 工具执行结束 ---
>>> 步骤 1.4: 已将工具 'get_current_weather' 的结果添加到消息历史中,准备下一轮。

--- 第 2 轮 API 调用 ---
>>> 步骤 2.1: 向 OpenAI 模型发送当前对话历史和工具
    原始发送消息: messages=[{'role': 'user', 'content': '请问北京今天天气怎么样?还有微软(MSFT)的股价是多少?南京天气如何?'}, ChatCompletionMessage(content='', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_8c892135f8ff4b9a97faf2', function=Function(arguments='{"location": "北京", "unit": "celsius"}', name='get_current_weather'), type='function', index=0)]), {'tool_call_id': 'call_8c892135f8ff4b9a97faf2', 'role': 'tool', 'name': 'get_current_weather', 'content': '{"location": "北京", "temperature": "15", "unit": "celsius", "forecast": "晴朗"}'}], tools=[{'type': 'function', 'function': {'name': 'get_current_weather', 'description': '获取给定位置的当前天气信息', 'parameters': {'type': 'object', 'properties': {'location': {'type': 'string', 'description': '城市和省/州,例如 San Francisco, CA 或 Beijing'}, 'unit': {'type': 'string', 'enum': ['celsius', 'fahrenheit'], 'description': '温度单位'}}, 'required': ['location']}}}, {'type': 'function', 'function': {'name': 'get_stock_price', 'description': '获取指定股票代码的当前价格', 'parameters': {'type': 'object', 'properties': {'symbol': {'type': 'string', 'description': '股票代码,例如 AAPL, MSFT'}}, 'required': ['symbol']}}}]

<<< 步骤 2.1 响应: 收到模型的决策
    原始响应: ChatCompletion(id='chatcmpl-61e19306-02aa-9a7f-a60a-00d54f149002', choices=[Choice(finish_reason='tool_calls', index=0, logprobs=None, message=ChatCompletionMessage(content='', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_f928cc129fbe41068e83f9', function=Function(arguments='{"symbol": "MSFT"}', name='get_stock_price'), type='function', index=0)]))], created=1746515790, model='qwen-plus', object='chat.completion', service_tier=None, system_fingerprint=None, usage=CompletionUsage(completion_tokens=21, prompt_tokens=359, total_tokens=380, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetails(audio_tokens=None, cached_tokens=0)))
    响应消息: ChatCompletionMessage(content='', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_f928cc129fbe41068e83f9', function=Function(arguments='{"symbol": "MSFT"}', name='get_stock_price'), type='function', index=0)])
    结束原因: tool_calls

>>> 步骤 2.2: 模型请求调用 1 个工具。将处理第一个。

--- 开始执行工具: get_stock_price (ID: call_f928cc129fbe41068e83f9) ---
--- 准备调用函数: get_stock_price 参数: {'symbol': 'MSFT'}
--- 正在调用工具: get_stock_price(symbol='MSFT') ---
--- 函数 get_stock_price 执行完毕。结果: {"symbol": "MSFT", "price": "420.72", "currency": "USD"}
--- 工具执行结束 ---
>>> 步骤 2.4: 已将工具 'get_stock_price' 的结果添加到消息历史中,准备下一轮。

--- 第 3 轮 API 调用 ---
>>> 步骤 3.1: 向 OpenAI 模型发送当前对话历史和工具
    原始发送消息: messages=[{'role': 'user', 'content': '请问北京今天天气怎么样?还有微软(MSFT)的股价是多少?南京天气如何?'}, ChatCompletionMessage(content='', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_8c892135f8ff4b9a97faf2', function=Function(arguments='{"location": "北京", "unit": "celsius"}', name='get_current_weather'), type='function', index=0)]), {'tool_call_id': 'call_8c892135f8ff4b9a97faf2', 'role': 'tool', 'name': 'get_current_weather', 'content': '{"location": "北京", "temperature": "15", "unit": "celsius", "forecast": "晴朗"}'}, ChatCompletionMessage(content='', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_f928cc129fbe41068e83f9', function=Function(arguments='{"symbol": "MSFT"}', name='get_stock_price'), type='function', index=0)]), {'tool_call_id': 'call_f928cc129fbe41068e83f9', 'role': 'tool', 'name': 'get_stock_price', 'content': '{"symbol": "MSFT", "price": "420.72", "currency": "USD"}'}], tools=[{'type': 'function', 'function': {'name': 'get_current_weather', 'description': '获取给定位置的当前天气信息', 'parameters': {'type': 'object', 'properties': {'location': {'type': 'string', 'description': '城市和省/州,例如 San Francisco, CA 或 Beijing'}, 'unit': {'type': 'string', 'enum': ['celsius', 'fahrenheit'], 'description': '温度单位'}}, 'required': ['location']}}}, {'type': 'function', 'function': {'name': 'get_stock_price', 'description': '获取指定股票代码的当前价格', 'parameters': {'type': 'object', 'properties': {'symbol': {'type': 'string', 'description': '股票代码,例如 AAPL, MSFT'}}, 'required': ['symbol']}}}]

<<< 步骤 3.1 响应: 收到模型的决策
    原始响应: ChatCompletion(id='chatcmpl-315fd813-5616-9cab-a9be-dab35992fb70', choices=[Choice(finish_reason='tool_calls', index=0, logprobs=None, message=ChatCompletionMessage(content='', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_6ad4a93a60e041ecbe4d1b', function=Function(arguments='{"location": "南京", "unit": "celsius"}', name='get_current_weather'), type='function', index=0)]))], created=1746515792, model='qwen-plus', object='chat.completion', service_tier=None, system_fingerprint=None, usage=CompletionUsage(completion_tokens=27, prompt_tokens=411, total_tokens=438, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetails(audio_tokens=None, cached_tokens=0)))
    响应消息: ChatCompletionMessage(content='', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_6ad4a93a60e041ecbe4d1b', function=Function(arguments='{"location": "南京", "unit": "celsius"}', name='get_current_weather'), type='function', index=0)])
    结束原因: tool_calls

>>> 步骤 3.2: 模型请求调用 1 个工具。将处理第一个。

--- 开始执行工具: get_current_weather (ID: call_6ad4a93a60e041ecbe4d1b) ---
--- 准备调用函数: get_current_weather 参数: {'location': '南京', 'unit': 'celsius'}
--- 正在调用工具: get_current_weather(location='南京', unit='celsius') ---
--- 函数 get_current_weather 执行完毕。结果: {"location": "南京", "temperature": "18", "unit": "celsius", "forecast": "多云"}
--- 工具执行结束 ---
>>> 步骤 3.4: 已将工具 'get_current_weather' 的结果添加到消息历史中,准备下一轮。

--- 第 4 轮 API 调用 ---
>>> 步骤 4.1: 向 OpenAI 模型发送当前对话历史和工具
    原始发送消息: messages=[{'role': 'user', 'content': '请问北京今天天气怎么样?还有微软(MSFT)的股价是多少?南京天气如何?'}, ChatCompletionMessage(content='', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_8c892135f8ff4b9a97faf2', function=Function(arguments='{"location": "北京", "unit": "celsius"}', name='get_current_weather'), type='function', index=0)]), {'tool_call_id': 'call_8c892135f8ff4b9a97faf2', 'role': 'tool', 'name': 'get_current_weather', 'content': '{"location": "北京", "temperature": "15", "unit": "celsius", "forecast": "晴朗"}'}, ChatCompletionMessage(content='', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_f928cc129fbe41068e83f9', function=Function(arguments='{"symbol": "MSFT"}', name='get_stock_price'), type='function', index=0)]), {'tool_call_id': 'call_f928cc129fbe41068e83f9', 'role': 'tool', 'name': 'get_stock_price', 'content': '{"symbol": "MSFT", "price": "420.72", "currency": "USD"}'}, ChatCompletionMessage(content='', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_6ad4a93a60e041ecbe4d1b', function=Function(arguments='{"location": "南京", "unit": "celsius"}', name='get_current_weather'), type='function', index=0)]), {'tool_call_id': 'call_6ad4a93a60e041ecbe4d1b', 'role': 'tool', 'name': 'get_current_weather', 'content': '{"location": "南京", "temperature": "18", "unit": "celsius", "forecast": "多云"}'}], tools=[{'type': 'function', 'function': {'name': 'get_current_weather', 'description': '获取给定位置的当前天气信息', 'parameters': {'type': 'object', 'properties': {'location': {'type': 'string', 'description': '城市和省/州,例如 San Francisco, CA 或 Beijing'}, 'unit': {'type': 'string', 'enum': ['celsius', 'fahrenheit'], 'description': '温度单位'}}, 'required': ['location']}}}, {'type': 'function', 'function': {'name': 'get_stock_price', 'description': '获取指定股票代码的当前价格', 'parameters': {'type': 'object', 'properties': {'symbol': {'type': 'string', 'description': '股票代码,例如 AAPL, MSFT'}}, 'required': ['symbol']}}}]

<<< 步骤 4.1 响应: 收到模型的决策
    原始响应: ChatCompletion(id='chatcmpl-192fb2e9-43cd-9468-a086-ba430c9e9b54', choices=[Choice(finish_reason='stop', index=0, logprobs=None, message=ChatCompletionMessage(content='今天北京的天气是15摄氏度,晴朗。微软(MSFT)的当前股价为420.72美元。南京今天的天气是18摄氏度,多云。', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=None))], created=1746515794, model='qwen-plus', object='chat.completion', service_tier=None, system_fingerprint=None, usage=CompletionUsage(completion_tokens=46, prompt_tokens=472, total_tokens=518, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetails(audio_tokens=None, cached_tokens=256)))
    响应消息: ChatCompletionMessage(content='今天北京的天气是15摄氏度,晴朗。微软(MSFT)的当前股价为420.72美元。南京今天的天气是18摄氏度,多云。', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=None)
    结束原因: stop

>>> 步骤 4.2: 模型没有请求调用工具 (结束原因: stop)。流程结束。

=== 模型最终回复 ===
今天北京的天气是15摄氏度,晴朗。微软(MSFT)的当前股价为420.72美元。南京今天的天气是18摄氏度,多云。

========== 对话结束 ==========

我们可以看到,LLM调用工具的模式是每次只调用一个工具,增加了交互次数,agent一共需要与LLM交互4次才得到最终答案。

测试场景二:

问题:请问北京今天天气怎么样?还有微软(MSFT)的股价是多少?南京天气如何?
模型:deepseek-v3

base_url: https://api.deepseek.com
model_name: deepseek-chat

========== 对话开始 (顺序工具调用) ==========
初始用户请求: 请问北京今天天气怎么样?还有微软(MSFT)的股价是多少?南京天气如何?

--- 第 1 轮 API 调用 ---
>>> 步骤 1.1: 向 OpenAI 模型发送当前对话历史和工具
    原始发送消息: messages=[{'role': 'user', 'content': '请问北京今天天气怎么样?还有微软(MSFT)的股价是多少?南京天气如何?'}], tools=[{'type': 'function', 'function': {'name': 'get_current_weather', 'description': '获取给定位置的当前天气信息', 'parameters': {'type': 'object', 'properties': {'location': {'type': 'string', 'description': '城市和省/州,例如 San Francisco, CA 或 Beijing'}, 'unit': {'type': 'string', 'enum': ['celsius', 'fahrenheit'], 'description': '温度单位'}}, 'required': ['location']}}}, {'type': 'function', 'function': {'name': 'get_stock_price', 'description': '获取 指定股票代码的当前价格', 'parameters': {'type': 'object', 'properties': {'symbol': {'type': 'string', 'description': '股票代码,例如 AAPL, MSFT'}}, 'required': ['symbol']}}}]

<<< 步骤 1.1 响应: 收到模型的决策
    原始响应: ChatCompletion(id='5cc9cb74-a325-4cde-891f-d090a7af6cc8', choices=[Choice(finish_reason='tool_calls', index=0, logprobs=None, message=ChatCompletionMessage(content='', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_0_f4fab653-d221-48d5-81d7-1ccd207e6e5d', function=Function(arguments='{"location": "Beijing"}', name='get_current_weather'), type='function', index=0), ChatCompletionMessageToolCall(id='call_1_988d3173-a2e1-4ad0-a951-3b4b7282f23d', function=Function(arguments='{"symbol": "MSFT"}', name='get_stock_price'), type='function', index=1), ChatCompletionMessageToolCall(id='call_2_9f1b3e7f-9b6a-447d-90a5-bf86804c1f38', function=Function(arguments='{"location": "Nanjing"}', name='get_current_weather'), type='function', index=2)]))], created=1746517433, model='deepseek-chat', object='chat.completion', service_tier=None, system_fingerprint='fp_8802369eaa_prod0425fp8', usage=CompletionUsage(completion_tokens=64, prompt_tokens=280, total_tokens=344, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetails(audio_tokens=None, cached_tokens=0), prompt_cache_hit_tokens=0, prompt_cache_miss_tokens=280))
    响应消息: ChatCompletionMessage(content='', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_0_f4fab653-d221-48d5-81d7-1ccd207e6e5d', function=Function(arguments='{"location": "Beijing"}', name='get_current_weather'), type='function', index=0), ChatCompletionMessageToolCall(id='call_1_988d3173-a2e1-4ad0-a951-3b4b7282f23d', function=Function(arguments='{"symbol": "MSFT"}', name='get_stock_price'), type='function', index=1), ChatCompletionMessageToolCall(id='call_2_9f1b3e7f-9b6a-447d-90a5-bf86804c1f38', function=Function(arguments='{"location": "Nanjing"}', name='get_current_weather'), type='function', index=2)])
    结束原因: tool_calls

>>> 步骤 1.2: 模型请求调用 3 个工具。将处理第一个。

--- 开始执行工具: get_current_weather (ID: call_0_f4fab653-d221-48d5-81d7-1ccd207e6e5d) ---
--- 准备调用函数: get_current_weather 参数: {'location': 'Beijing'}
--- 正在调用工具: get_current_weather(location='Beijing', unit='celsius') ---
--- 函数 get_current_weather 执行完毕。结果: {"location": "Beijing", "temperature": "15", "unit": "celsius", "forecast": "晴朗"}
--- 工具执行结束 ---
>>> 步骤 1.4: 已将工具 'get_current_weather' 的结果添加到消息历史中,准备下一轮。

--- 开始执行工具: get_stock_price (ID: call_1_988d3173-a2e1-4ad0-a951-3b4b7282f23d) ---
--- 准备调用函数: get_stock_price 参数: {'symbol': 'MSFT'}
--- 正在调用工具: get_stock_price(symbol='MSFT') ---
--- 函数 get_stock_price 执行完毕。结果: {"symbol": "MSFT", "price": "420.72", "currency": "USD"}
--- 工具执行结束 ---
>>> 步骤 1.4: 已将工具 'get_stock_price' 的结果添加到消息历史中,准备下一轮。

--- 开始执行工具: get_current_weather (ID: call_2_9f1b3e7f-9b6a-447d-90a5-bf86804c1f38) ---
--- 准备调用函数: get_current_weather 参数: {'location': 'Nanjing'}
--- 正在调用工具: get_current_weather(location='Nanjing', unit='celsius') ---
--- 函数 get_current_weather 执行完毕。结果: {"location": "Nanjing", "temperature": "18", "unit": "celsius", "forecast": "多云"}
--- 工具执行结束 ---
>>> 步骤 1.4: 已将工具 'get_current_weather' 的结果添加到消息历史中,准备下一轮。

--- 第 2 轮 API 调用 ---
>>> 步骤 2.1: 向 OpenAI 模型发送当前对话历史和工具
    原始发送消息: messages=[{'role': 'user', 'content': '请问北京今天天气怎么样?还有微软(MSFT)的股价是多少?南京天气如何?'}, ChatCompletionMessage>>> 步骤 2.1: 向 OpenAI 模型发送当前对话历史和工具
    原始发送消息: messages=[{'role': 'user', 'content': '请问北京今天天气怎么样?还有微软(MSFT)的股价是多少?南京天气如何?'}, ChatCompletionMessage    原始发送消息: messages=[{'role': 'user', 'content': '请问北京今天天气怎么样?还有微软(MSFT)的股价是多少?南京天气如何?'}, ChatCompletionMessage(content='', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_0_f4fab653-d221-48d5-81d7-1ccd207e6e5d', function=Function(arguments='{"location": "Beijing"}', name='get_current_weather'), type='function', index=0), ChatCompletionMessageToolCall(id='call_1_988d3173-a2e1-4ad0-a951-3b4b7282f23d', function=Function(arguments='{"symbol": "MSFT"}', name='get_stock_price'), type='function', index=1), ChatCompletionMessageToolCall(id='call_2_9f1b3e7f-9b6a-447d-90a5-bf86804c1f38', function=Function(arguments='{"location": "Nanjing"}', name='get_current_weather'), type='function', index=2)]), {'tool_call_id': 'call_0_f4fab653-d221-48d5-81d7-1ccd207e6e5d', 'role': 'tool', 'name': 'get_current_weather', 'content': '{"location": "Beijing", "temperature": "15", "unit": "celsius", "forecast": "晴朗"}'}, {'tool_call_id': 'call_1_988d3173-a2e1-4ad0-a951-3b4b7282f23d', 'role': 'tool', 'name': 'get_stock_price', 'content': '{"symbol": "MSFT", "price": "420.72", "currency": "USD"}'}, {'tool_call_id': 'call_2_9f1b3e7f-9b6a-447d-90a5-bf86804c1f38', 'role': 'tool', 'name': 'get_current_weather', 'content': '{"location": "Nanjing", "temperature": "18", "unit": "celsius", "forecast": "多云"}'}], tools=[{'type': 'function', 'function': {'name': 'get_current_weather', 'description': '获取给定位置的当前天气信息', 'parameters': {'type': 'object', 'properties': {'location': {'type': 'string', 'description': '城市和省/州,例如 San Francisco, CA 或 Beijing'}, 'unit': {'type': 'string', 'enum': ['celsius', 'fahrenheit'], 'description': '温度单位'}}, 'required': ['location']}}}, {'type': 'function', 'function': {'name': 'get_stock_price', 'description': '获取指定股票代码的当前价格', 'parameters': {'type': 'object', 'properties': {'symbol': {'type': 'string', 'description': '股票代码,例如 AAPL, MSFT'}}, 'required': ['symbol']}}}]

<<< 步骤 2.1 响应: 收到模型的决策
    原始响应: ChatCompletion(id='d36d77dd-c42d-49e7-9448-eca2b8bfe2da', choices=[Choice(finish_reason='stop', index=0, logprobs=None, message=ChatCompletionMessage(content='以下是您询问的信息:\n\n1. **北京天气**:今天晴朗,气温为15°C。\n2. **微软(MSFT)股价**:当前价格为420.72美元。\n3. **南京天气**:今天多云,气温为18°C。', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=None))], created=1746517439, model='deepseek-chat', object='chat.completion', service_tier=None, system_fingerprint='fp_8802369eaa_prod0425fp8', usage=CompletionUsage(completion_tokens=55, prompt_tokens=430, total_tokens=485, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetails(audio_tokens=None, cached_tokens=320), prompt_cache_hit_tokens=320, prompt_cache_miss_tokens=110))
    响应消息: ChatCompletionMessage(content='以下是您询问的信息:\n\n1. **北京天气**:今天晴朗,气温为15°C。\n2. **微软(MSFT)股价**:当前价格为420.72美元。\n3. **南京天气**:今天多云,气温为18°C。', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=None)
    结束原因: stop

>>> 步骤 2.2: 模型没有请求调用工具 (结束原因: stop)。流程结束。

=== 模型最终回复 ===
以下是您询问的信息:

1. **北京天气**:今天晴朗,气温为15°C。
2. **微软(MSFT)股价**:当前价格为420.72美元。
3. **南京天气**:今天多云,气温为18°C。

========== 对话结束 ==========

可以看到,deepseek在第一次请求后返回了3个工具。客户端将3个工具都执行完后发送执行结果给LLM,LLM汇总答案。客户端与LLM一共交互两次,这样可以大大减少模型的交互次数,降低token消耗。

测试场景三:

问题:请问北京今天天气怎么样?如果北京的天气晴朗的话,就查询微软(MSFT)的股价?
模型:deepseek-v3

========== 对话开始 (顺序工具调用) ==========
初始用户请求: 请问北京今天天气怎么样?如果北京的天气晴朗的话,就查询微软(MSFT)的股价?

--- 第 1 轮 API 调用 ---
>>> 步骤 1.1: 向 OpenAI 模型发送当前对话历史和工具
    原始发送消息: messages=[{'role': 'user', 'content': '请问北京今天天气怎么样?如果北京的天气晴朗的话,就查询微软(MSFT)的股价?'}], tools=[{'type': 'function', 'function': {'name': 'get_current_weather', 'description': '获取给定位置的当前天气信息', 'parameters': {'type': 'object', 'properties': {'location': {'type': 'string', 'description': '城市和省/州,例如 San Francisco, CA 或 Beijing'}, 'unit': {'type': 'string', 'enum': ['celsius', 'fahrenheit'], 'description': '温度单位'}}, 'required': ['location']}}}, {'type': 'function', 'function': {'name': 'get_stock_price', 'description': '获取指定股票代码的当前价格', 'parameters': {'type': 'object', 'properties': {'symbol': {'type': 'string', 'description': '股票代码,例如 AAPL, MSFT'}}, 'required': ['symbol']}}}]

<<< 步骤 1.1 响应: 收到模型的决策
    原始响应: ChatCompletion(id='5577b0fd-74e6-4e35-b52a-4fa3c65aeeda', choices=[Choice(finish_reason='tool_calls', index=0, logprobs=None, message=ChatCompletionMessage(content='', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_0_d0ec6750-24bb-42e4-85fb-2424d7bb782c', function=Function(arguments='{"location": "Beijing", "unit": "celsius"}', name='get_current_weather'), type='function', index=0)]))], created=1746969170, model='deepseek-chat', object='chat.completion', service_tier=None, system_fingerprint='fp_8802369eaa_prod0425fp8', usage=CompletionUsage(completion_tokens=28, prompt_tokens=282, total_tokens=310, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetails(audio_tokens=None, cached_tokens=256), prompt_cache_hit_tokens=256, prompt_cache_miss_tokens=26))
    响应消息: ChatCompletionMessage(content='', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_0_d0ec6750-24bb-42e4-85fb-2424d7bb782c', function=Function(arguments='{"location": "Beijing", "unit": "celsius"}', name='get_current_weather'), type='function', index=0)])
    结束原因: tool_calls

>>> 步骤 1.2: 模型请求调用 1 个工具。将处理第一个。

--- 开始执行工具: get_current_weather (ID: call_0_d0ec6750-24bb-42e4-85fb-2424d7bb782c) ---
--- 准备调用函数: get_current_weather 参数: {'location': 'Beijing', 'unit': 'celsius'}
--- 正在调用工具: get_current_weather(location='Beijing', unit='celsius') ---
--- 函数 get_current_weather 执行完毕。结果: {"location": "Beijing", "temperature": "15", "unit": "celsius", "forecast": "晴朗"}
--- 工具执行结束 ---
>>> 步骤 1.4: 已将工具 'get_current_weather' 的结果添加到消息历史中,准备下一轮。

--- 第 2 轮 API 调用 ---
>>> 步骤 2.1: 向 OpenAI 模型发送当前对话历史和工具
    原始发送消息: messages=[{'role': 'user', 'content': '请问北京今天天气怎么样?如果北京的天气晴朗的话,就查询微软(MSFT)的股价?'}, ChatCompletionMessage(content='', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_0_d0ec6750-24bb-42e4-85fb-2424d7bb782c', function=Function(arguments='{"location": "Beijing", "unit": "celsius"}', name='get_current_weather'), type='function', index=0)]), {'tool_call_id': 'call_0_d0ec6750-24bb-42e4-85fb-2424d7bb782c', 'role': 'tool', 'name': 'get_current_weather', 'content': '{"location": "Beijing", "temperature": "15", "unit": "celsius", "forecast": "晴朗"}'}], tools=[{'type': 'function', 'function': {'name': 'get_current_weather', 'description': '获取给定位置的当前天气信息', 'parameters': {'type': 'object', 'properties': {'location': {'type': 'string', 'description': '城市和省/州,例如 San Francisco, CA 或 Beijing'}, 'unit': {'type': 'string', 'enum': ['celsius', 'fahrenheit'], 'description': '温度单位'}}, 'required': ['location']}}}, {'type': 'function', 'function': {'name': 'get_stock_price', 'description': '获取指定股票代码的 当前价格', 'parameters': {'type': 'object', 'properties': {'symbol': {'type': 'string', 'description': '股票代码,例如 AAPL, MSFT'}}, 'required': ['symbol']}}}]      

<<< 步骤 2.1 响应: 收到模型的决策
    原始响应: ChatCompletion(id='ed860c74-2be3-4f52-8718-83024f2491b1', choices=[Choice(finish_reason='tool_calls', index=0, logprobs=None, message=ChatCompletionMessage(content='', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_0_64e2a368-b439-4abf-ae9e-0e34e6d1e886', function=Function(arguments='{"symbol":"MSFT"}', name='get_stock_price'), type='function', index=0)]))], created=1746969176, model='deepseek-chat', object='chat.completion', service_tier=None, system_fingerprint='fp_8802369eaa_prod0425fp8', usage=CompletionUsage(completion_tokens=21, prompt_tokens=341, total_tokens=362, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetails(audio_tokens=None, cached_tokens=256), prompt_cache_hit_tokens=256, prompt_cache_miss_tokens=85))
    响应消息: ChatCompletionMessage(content='', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_0_64e2a368-b439-4abf-ae9e-0e34e6d1e886', function=Function(arguments='{"symbol":"MSFT"}', name='get_stock_price'), type='function', index=0)])
    结束原因: tool_calls

>>> 步骤 2.2: 模型请求调用 1 个工具。将处理第一个。

--- 开始执行工具: get_stock_price (ID: call_0_64e2a368-b439-4abf-ae9e-0e34e6d1e886) ---
--- 准备调用函数: get_stock_price 参数: {'symbol': 'MSFT'}
--- 正在调用工具: get_stock_price(symbol='MSFT') ---
--- 函数 get_stock_price 执行完毕。结果: {"symbol": "MSFT", "price": "420.72", "currency": "USD"}
--- 工具执行结束 ---
>>> 步骤 2.4: 已将工具 'get_stock_price' 的结果添加到消息历史中,准备下一轮。

--- 第 3 轮 API 调用 ---
>>> 步骤 3.1: 向 OpenAI 模型发送当前对话历史和工具
    原始发送消息: messages=[{'role': 'user', 'content': '请问北京今天天气怎么样?如果北京的天气晴朗的话,就查询微软(MSFT)的股价?'}, ChatCompletionMessage(content='', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_0_d0ec6750-24bb-42e4-85fb-2424d7bb782c', function=Function(arguments='{"location": "Beijing", "unit": "celsius"}', name='get_current_weather'), type='function', index=0)]), {'tool_call_id': 'call_0_d0ec6750-24bb-42e4-85fb-2424d7bb782c', 'role': 'tool', 'name': 'get_current_weather', 'content': '{"location": "Beijing", "temperature": "15", "unit": "celsius", "forecast": "晴朗"}'}, ChatCompletionMessage(content='', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_0_64e2a368-b439-4abf-ae9e-0e34e6d1e886', function=Function(arguments='{"symbol":"MSFT"}', name='get_stock_price'), type='function', index=0)]), {'tool_call_id': 'call_0_64e2a368-b439-4abf-ae9e-0e34e6d1e886', 'role': 'tool', 'name': 'get_stock_price', 'content': '{"symbol": "MSFT", "price": "420.72", "currency": "USD"}'}], tools=[{'type': 'function', 'function': {'name': 'get_current_weather', 'description': '获取给定位置的当前天气信息', 'parameters': {'type': 'object', 'properties': {'location': {'type': 'string', 'description': '城市和省/州,例如 San Francisco, CA 或 Beijing'}, 'unit': {'type': 'string', 'enum': ['celsius', 'fahrenheit'], 'description': '温度单位'}}, 'required': ['location']}}}, {'type': 'function', 'function': {'name': 'get_stock_price', 'description': '获取指定股票代码的当前价格', 'parameters': {'type': 'object', 'properties': {'symbol': {'type': 'string', 'description': '股票代码,例如 AAPL, MSFT'}}, 'required': ['symbol']}}}]

<<< 步骤 3.1 响应: 收到模型的决策
    原始响应: ChatCompletion(id='57754df9-ab4a-4b5e-8e01-47150149478f', choices=[Choice(finish_reason='stop', index=0, logprobs=None, message=ChatCompletionMessage(content='北京今天的天气晴朗,温度为15摄氏度。微软(MSFT)的当前股价为420.72美元。', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=None))], created=1746969182, model='deepseek-chat', object='chat.completion', service_tier=None, system_fingerprint='fp_8802369eaa_prod0425fp8', usage=CompletionUsage(completion_tokens=23, prompt_tokens=388, total_tokens=411, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetails(audio_tokens=None, cached<<< 步骤 3.1 响应: 收到模型的决策
    原始响应: ChatCompletion(id='57754df9-ab4a-4b5e-8e01-47150149478f', choices=[Choice(finish_reason='stop', index=0, logprobs=None, message=ChatCompletionMessage(content='北京今天的天气晴朗,温度为15摄氏度。微软(MSFT)的当前股价为420.72美元。', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool<<< 步骤 3.1 响应: 收到模型的决策
    原始响应: ChatCompletion(id='57754df9-ab4a-4b5e-8e01-47150149478f', choices=[Choice(finish_reason='stop', index=0, logprobs=None, message=ChatCompletionMessage(co<<< 步骤 3.1 响应: 收到模型的决策
<<< 步骤 3.1 响应: 收到模型的决策
<<< 步骤 3.1 响应: 收到模型的决策
    原始响应: ChatCompletion(id='57754df9-ab4a-4b5e-8e01-47150149478f', choices=[Choice(finish_reason='stop', index=0, logprobs=None, message=ChatCompletionMessage(content='北京今天的天气晴朗,温度为15摄氏度。微软(MSFT)的当前股价为420.72美元。', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=None))], created=1746969182, model='deepseek-chat', object='chat.completion', service_tier=None, system_fingerprint='fp_8802369eaa_prod0425fp8', usage=CompletionUsage(completion_tokens=23, prompt_tokens=388, total_tokens=411, completion_tokens_details=None, prompt_tokens_details=PromptTokensDetails(audio_tokens=None, cached_tokens=320), prompt_cache_hit_tokens=320, prompt_cache_miss_tokens=68))
    响应消息: ChatCompletionMessage(content='北京今天的天气晴朗,温度为15摄氏度。微软(MSFT)的当前股价为420.72美元。', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=None)
    结束原因: stop

>>> 步骤 3.2: 模型没有请求调用工具 (结束原因: stop)。流程结束。

=== 模型最终回复 ===
北京今天的天气晴朗,温度为15摄氏度。微软(MSFT)的当前股价为420.72美元。

========== 对话结束 ==========

我们可以看到,因为两次工具调用有因果关系。所以它返第一次返回查询天气的工具调用,当查询到天气为晴天时,才调用查询股票的工具。符合我们问题的要求。

综上,在工具调用上,deepseek-v3做得更合理一些。当多个工具调用相互之间没有因果关系时,会一次性返回多个工具调用,从而减少了Agent与大模型的交互次数。而当工具调用之间有因果关系时,会根据因果关系先后调用工具。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值