跳到主要内容
推理优化

8.2 Tool Calling 与 Reasoning Parser:让模型调用工具

OpenAI 工具调用协议、tool_choice 四种模式、auto tool choice 与 tool-call-parser、Chat Template 的作用、推理输出(Reasoning Outputs)分离

Tool CallingAgenttool_choiceChat TemplateReasoningOpenAI API

8.1 解决了”输出格式”,8.2 解决”模型怎么用工具”——Agent 应用的基石。模型输出一个工具调用请求(“调用 get_weather(location=‘上海’)”),系统执行工具拿到结果,再喂回模型继续对话。vLLM 用 OpenAI 兼容的 tools / tool_choice 协议支撑这个循环。这一节讲协议、解析、模板这三个环节,以及推理模型(思维链)与工具调用的组合。

📑 目录


1. 工具调用循环:Agent 的最小闭环

用户 ──→ 模型(带工具描述)──工具调用──→ 系统执行工具
  ↑                                            │
  └──────────── 工具结果回填对话 ←──────────────┘
  1. 请求里携带工具描述(名称、参数 Schema)
  2. 模型判断是否需要工具:需要则输出工具调用(名称 + 参数 JSON)
  3. 系统解析调用、执行真实函数
  4. 把结果作为 tool 角色的消息回填,模型继续推理

📌 关键点:vLLM 只负责第 2 步(协议 + 生成)——工具执行是你的代码。理解这个边界,你就知道 vLLM 的 tool calling 能力 = “可靠地生成符合协议的工具调用”,而不是”自动调用工具”。


2. OpenAI 工具协议:tools 与 tool_choice

2.1 请求格式

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "Get the current weather in a given location",
            "parameters": {          # JSON Schema
                "type": "object",
                "properties": {
                    "location": {"type": "string"},
                    "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
                },
                "required": ["location", "unit"],
            },
        },
    },
]

resp = client.chat.completions.create(
    model="...",
    messages=[{"role": "user", "content": "上海天气怎么样?"}],
    tools=tools,
    tool_choice="auto",   # auto / none / required / 指定工具名
)

2.2 tool_choice 四种模式

行为适用
"none"禁用工具普通对话
"auto"模型自己决定是否调用(及调用哪个)默认推荐
"required"强制调用工具(可任意选择)流程要求必须用工具
{"type":"function","function":{"name":"..."}}指定调用某工具路由型 Agent

2.3 响应格式

{
  "choices": [{
    "message": {
      "role": "assistant",
      "content": null,
      "tool_calls": [{
        "id": "call_xxx",
        "type": "function",
        "function": {"name": "get_weather", "arguments": "{\"location\": \"上海\"}"}
      }]
    }
  }]
}

💡 提示arguments字符串化的 JSON——解析时记得 json.loads。生产建议配合 8.1 的结构化输出约束参数格式(见第 5 节),让 arguments 从生成源头就是合法 JSON。


3. 服务端解析:tool-call-parser 与 Chat Template

工具调用不是”模型天生就会”的——模型只是按训练时见过的格式输出文本(如 <|start_header_id|>assistant<|end_header_id|> 我需要查询天气:get_weather({"location":"上海"}))。vLLM 要把它解析成标准 tool_calls 结构,需要两个组件:

3.1 Tool Call Parser:文本 → 结构化

不同模型系列的工具调用格式不同,vLLM 提供系列对应的 parser:

vllm serve meta-llama/Llama-3.1-8B-Instruct \
    --enable-auto-tool-choice \
    --tool-call-parser llama3_json \
    --chat-template examples/tool_chat_template_llama3.1_json.jinja
  • llama3_json:Llama 3.1/3.2 系列的 JSON 格式
  • hermesmistralinternlmqwen 等:对应各系列格式
  • 找不到现成 parser?自研或社区贡献(parser 是相对简单的插件点)

3.2 Chat Template:请求 → 模型输入

Chat Template(Jinja 模板)决定多轮对话 + 工具描述如何拼成模型输入的 prompt

  • 工具描述怎么嵌入(system 提示 or 独立消息)
  • 工具调用结果怎么回填(tool 角色消息的模板)
  • 模型系列专属的格式控制符(<|start_header_id|> 等)

📌 关键点parser 和 template 必须与模型系列匹配——用错模板,模型的输出格式就不对,parser 解析失败。vLLM 对部分模型内置了 template,但工具调用场景官方文档明确建议显式传 --chat-template(尤其 Llama 3.1 系)。


4. Auto Tool Choice:让模型自己决定用不用工具

4.1 为什么需要它

固定 tool_choice="required" 的代价:普通问题也被迫调用工具(“你好”也要查天气)。Auto Tool Choice 把决定权交给模型:

  • 需要工具的问题 → 模型输出 tool_calls
  • 普通问题 → 模型直接回答

4.2 实现方式

--enable-auto-tool-choice 把工具描述(含 “Do not call tools unless necessary” 之类的指令)注入提示,让模型在”调用工具 / 直接回答”之间自由选择——本质是把决定变成了模型能力的一部分

4.3 局限

  • 依赖模型的指令遵循能力:小模型可能在不该用时用(幻觉工具调用)
  • 工具数量多时,模型在”选哪个工具”上可能出错
  • 关键业务路径如果必须走工具,用 required 或指定工具,别赌模型自觉

💡 提示:生产经验——默认 auto,关键路径显式。通用入口用 auto(用户体验好),强流程节点(如支付、下单)用 required 或指定工具(确定性优先)。这跟 7.4 的 SLO 思想一脉相承:关键路径要预算管理,不要尽力而为。


5. 结构化输出的配合:工具参数合法性

工具调用的参数是 JSON——8.1 的受约束解码在这里有天然用武之地:

  • 参数 Schema 已存在tools[].function.parameters 就是 JSON Schema,直接可用作约束
  • vLLM 的自动解析链路可以在生成 arguments 时用参数 Schema 做约束,保证参数从源头合法
# 工具参数用 pydantic 定义(8.1 同款模式)
class GetWeatherArgs(BaseModel):
    location: str
    unit: Literal["celsius", "fahrenheit"]

📌 关键点工具调用的可靠性 = 协议格式 + 参数合法性。协议格式靠 parser/template 匹配,参数合法性靠受约束解码。两者组合,工具调用才能从”偶尔能用”变成”稳定可用”——这是 Agent 生产化的前提。


6. Reasoning Parser:思维链与工具调用的组合

推理模型(DeepSeek-R1、Qwen3 等)输出先推理后回答<reasoning>思考过程</reasoning><answer>最终答案</answer>。生产系统往往需要:

  • 推理内容与回答分离:思维链不展示给最终用户,或单独计费/审计
  • 推理模型 + 工具调用的组合:先推理”该不该调工具”再输出调用

6.1 Reasoning Outputs(v0.26)

OpenAI 兼容层支持推理内容的分离:

  • reasoning_content:模型的思维链(与 content 分开返回)
  • 请求侧 reasoning_effort 等字段控制推理深度(OpenAI o 系列协议)
{
  "choices": [{
    "message": {
      "role": "assistant",
      "reasoning_content": "用户问天气,需要调用 get_weather……",
      "content": null,
      "tool_calls": [...]
    }
  }]
}

6.2 工程意义

  • 展示控制:思维链不下发前端(或折叠展示)
  • 审计与成本:推理 token 单独计量
  • 组合模式:推理模型可以”边想边决定是否调工具”——Agent 的规划能力来源

💡 提示:推理模型的工具调用有讲究——推理 token 的约束:有些模型要求工具调用前必须有完整推理,有些允许流式推理+中途工具调用。生产上先用官方推荐的 template/parser 组合跑通,再优化”推理与工具调用衔接”的提示词。


7. 生产实践与常见坑

7.1 常见坑位图

症状根因处理
tool_calls 为空或格式乱Chat Template 与模型不匹配换官方 template / 确认模型系列
parser 报错模型输出与 parser 期望格式不一致换对应系列 parser;检查 template
模型幻觉调用不存在的工具小模型指令遵循弱换大模型 / 收紧 tool_choice / 加强描述
arguments 解析失败参数 JSON 非法开结构化输出约束参数
多轮工具结果回填错乱template 未正确处理 tool 角色检查 template 的工具结果分支
长工具列表 + 大模型prompt 变长、成本上升工具分组/描述精简;检索式工具选择

7.2 生产清单

  • 工具描述精简(name/description/parameters 三件套齐全)
  • Chat Template 显式指定且与模型匹配
  • 工具参数开结构化输出约束
  • 工具执行层有超时与错误处理(工具失败也要能回填对话)
  • 工具调用计费/审计(token 用量 + 调用记录)
  • 压测:多轮工具对话的 TTFT/TPOT 与普通对话对比

📌 关键点:工具调用最容易翻车的不是”生成”,而是**“多轮回填”**——工具结果作为 tool 消息回填后的下一轮,模板/上下文处理不当就会崩。生产压测必须覆盖”工具 → 结果 → 再生成”的完整多轮链路。


📝 总结

  • 闭环:vLLM 只负责”可靠生成工具调用”,执行是你的代码
  • 协议tools(描述)+ tool_choice(none/auto/required/指定)——OpenAI 兼容
  • 解析:tool-call-parser(文本→结构化)+ Chat Template(对话→prompt),必须与模型系列匹配
  • Auto Tool Choice:默认 auto、关键路径显式
  • 配合结构化输出:参数 Schema 直接用作约束,参数源头合法
  • Reasoning:推理内容与回答分离(reasoning_content),推理模型 + 工具的规划组合
  • :模板不匹配、幻觉调用、多轮回填——压测覆盖多轮链路

🎯 自我检验清单

  • 画出 Agent 工具调用闭环,标出 vLLM 的职责边界?
  • tool_choice 四种模式的语义?什么场景用 required?
  • Tool Call Parser 和 Chat Template 各解决什么问题?为什么必须匹配模型系列?
  • Auto Tool Choice 的实现方式与局限?
  • 怎么用 8.1 的结构化输出保证工具参数合法?
  • Reasoning Outputs 里 reasoning_content 与 content 的关系?
  • 列出 6 个常见坑与对应处理?

📚 参考资料