为什么调用 OpenAI Tools 后,还要再请求一次大模型?——从代码看 LLM 工具调用的本质

在使用 OpenAI SDK(或兼容接口如 DeepSeek)实现外部工具调用时,你可能会写出类似下面这段代码:

ini 复制代码
import json

messages = [{"role": "user", "content": "北京天气怎么样"}]

response = client.chat.completions.create(
    model='deepseek-reasoner',
    messages=messages,
    tools=tools,
    tool_choice="auto",
    temperature=0.3
)

response_message = response.choices[0].message

# 如果模型建议调用工具
if response_message.tool_calls:
    for tool_call in response_message.tool_calls:
        function_name = tool_call.function.name
        function_args = json.loads(tool_call.function.arguments)

        if function_name == "get_weather":
            function_response = get_weather(function_args["location"])
        else:
            function_response = "未知工具"

        # 将工具调用结果加入对话历史
        messages.append({
            "tool_call_id": tool_call.id,
            "role": "tool",
            "name": function_name,
            "content": function_response
        })

# 最后一次请求,获取最终答案
final_response = client.chat.completions.create(
    model='deepseek-reasoner',
    messages=messages,
    temperature=0.3
)

print(final_response.choices[0].message.content)

这段代码明明已经本地执行了 get_weather 函数,拿到了真实结果,为什么还要再发一次请求给大模型?

要理解这个问题,我们必须先搞清楚:这两次请求,各自承担了什么角色?


第一次请求:LLM 只负责"规划",不负责"执行"

当你第一次调用 chat.completions.create 并传入 tools 参数时,大模型并不会去查天气、不会发 HTTP 请求、更不会操作你的数据库。

它做的只有一件事:判断是否需要调用工具,并生成结构化的调用指令。

例如,对于用户问"北京天气怎么样?",模型返回的 response_message 可能是这样的(伪结构):

json 复制代码
{
  "role": "assistant",
  "tool_calls": [
    {
      "id": "call_abc123",
      "function": {
        "name": "get_weather",
        "arguments": "{"location": "北京"}"
      }
    }
  ]
}

👉 这就是 "调用指令"

  • 工具名get_weather
  • 参数{"location": "北京"}(由 LLM 从自然语言中自动提取并格式化)

但请注意:此时没有任何外部调用发生!

LLM 只是在说:"请帮我调用这个函数,参数是这些。"


开发者执行工具:真正的"行动者"是你

接下来,你的代码会解析 tool_calls,并在本地执行:

ini 复制代码
function_response = get_weather("北京")  # ← 真正的 API 调用发生在这里!

这一步完全运行在你的服务器上,LLM 对此一无所知。它只是"提了个建议",而你是那个"跑腿的人"。


第二次请求:把结果"喂"回去,让 LLM 生成人类语言

执行完工具后,你构造了一条特殊消息:

json 复制代码
{
  "role": "tool",
  "tool_call_id": "call_abc123",
  "name": "get_weather",
  "content": "北京晴,25℃"
}

🔑 关键:"role": "tool" 的作用是什么?

这是 OpenAI API 中专门用于传递工具执行结果的消息类型。它的意义是:

"刚才那个 ID 为 call_abc123 的工具调用,我已经执行完了,结果是'北京晴,25℃'。"

只有当你把这条消息加入对话历史并再次请求 LLM 时,模型才能"看到"真实世界的数据,并据此生成自然语言回答:

"北京今天天气晴朗,气温25摄氏度,非常适合外出。"


那么问题来了:既然工具是我自己执行的,为什么不能直接返回结果?

这是一个非常合理的问题!

技术上,你完全可以这样做:

ini 复制代码
if response_message.tool_calls:
    # 执行工具
    result = get_weather("北京")
    print(result)  # 直接输出 "北京晴,25℃"

但这样做的代价是:你放弃了 LLM 最核心的能力------语言理解与上下文推理。

举个例子:

  • 用户问:"北京天气怎么样?"

    • 直接返回工具结果 → "25℃"
    • 经过第二次 LLM 调用 → "北京今天25度,阳光明媚,建议穿短袖。"
  • 用户接着问:"那适合跑步吗?"

    • 如果你没做第二次调用,LLM 不知道之前查过天气,只能瞎猜;

    • 如果你做了,对话历史里就有"25℃ + 晴"的上下文,LLM 可以回答:

      "气温适宜,空气质量良好,非常适合晨跑!"

第二次调用的本质,是让 LLM 基于真实数据"重新思考并表达" ,而不是简单回显。


总结:两步走,缺一不可

步骤 谁在做 做什么 为什么必要
第一次请求 LLM 判断是否调用工具,提取结构化参数 让 AI 理解用户意图
本地执行 你(开发者) 真正调用 API/数据库/函数 安全可控地连接外部世界
第二次请求 LLM 基于工具结果生成自然语言回答 提供有上下文、有温度的智能体验

🌟 工具提供事实,LLM 提供智慧。

少了任何一步,AI 都只是"半智能"。

所以,别嫌多一次调用------那正是你的应用从"数据接口"蜕变为"智能助手"的关键一跃。

相关推荐
GeekPMAlex1 小时前
Python OOP 深度解析:从核心语法到高级模式
python
e***0961 小时前
SpringBoot下获取resources目录下文件的常用方法
java·spring boot·后端
Sunhen_Qiletian1 小时前
《Python开发之语言基础》第一集:python的语法元素
开发语言·python
速易达网络1 小时前
tensorflow+yolo图片训练和图片识别系统
人工智能·python·tensorflow
程序员小远2 小时前
如何搭建Appium环境?
自动化测试·软件测试·python·测试工具·职场和发展·appium·测试用例
大模型教程2 小时前
AI智能体(Agent)保姆级入门指南,零基础小白也能轻松上手
程序员·llm·agent
简创AIGC陶先生2 小时前
【剪映小助手源码精讲】09_音频素材管理系统
后端
智泊AI2 小时前
一文讲清:多模态学习:多模态融合 + 跨模态对齐
llm
will_we2 小时前
Spring Boot4正式篇:第二篇 多版本API特性
java·后端