news 2026/9/7 9:58:16

vLLM 交错思考(Interleaved Thinking)实战:在工具调用之间让模型“边想边调“

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
vLLM 交错思考(Interleaved Thinking)实战:在工具调用之间让模型“边想边调“

vLLM 交错思考(Interleaved Thinking)实战:在工具调用之间让模型"边想边调"

【免费下载链接】vllmA high-throughput and memory-efficient inference and serving engine for LLMs项目地址: https://gitcode.com/GitHub_Trending/vl/vllm

本篇基于 vLLM 仓库中的 Interleaved Thinking 特性文档,系统讲解交错思考(在工具调用之间穿插推理)在 vLLM 中的工作原理、启用方式与完整调用链:读者将掌握如何用--reasoning-parser--tool-call-parser组合启用该特性,如何在多轮对话中把推理过程(reasoning)原样回传给模型,以及背后 Kimi-K2 / MiniMax-M2 两类推理解析器(ReasoningParser)的源码实现与流式行为。

什么是交错思考(Interleaved Thinking)

交错思考允许模型在多次工具调用之间进行推理:模型发起一次工具调用、拿到工具结果后,不是直接生成最终回答,而是先对结果进行一段推理(thinking),再决定下一步动作。由此可以实现:

  • 在决定下一步之前,先对工具调用的结果进行推理;
  • 在多个工具调用之间穿插推理步骤,形成"调用 → 推理 → 再调用"的链条;
  • 基于中间结果做出更细致的判断;
  • 对外暴露其选择工具的透明推理过程。

文档同时给出重要提示:交错思考会增加 token 消耗和响应延迟(每多一轮推理就多一段思考 token)。启用前应权衡预算与性能要求——如果你的 Agent 场景以少量、确定性强的工具调用为主,未必需要开启。

与普通"思考模型"的区别

普通思考模型(如 DeepSeek-R1 类)只在最终回答前输出一段<think>推理;交错思考则把推理步骤插入到工具调用循环内部

用户消息 → 推理 1 → 工具调用 1 → 工具结果 1 → 推理 2 → 工具调用 2 → 工具结果 2 → 推理 3 → 最终回答

因此对服务端的两个核心要求是:

  1. 能正确切分模型输出中的"推理部分"与"正文部分"(reasoning parser);
  2. 能正确识别与解析工具调用(tool call parser),并支持客户端在下一轮把上一次的 reasoning 作为 assistant 消息的一部分回传,保证多轮上下文完整。

vLLM 中支持交错思考的模型

根据 docs/features/interleaved_thinking.md,vLLM 当前支持的交错思考模型及其对应的 Reasoning Parser 名称如下:

模型系列Reasoning Parser 名称
moonshotai/Kimi-K2-Thinkingkimi_k2
MiniMaxAI/MiniMax-M2minimax_m2

这两个名称是--reasoning-parser命令行参数的取值。它们并非独立的 Python 手写解析器,而是注册到 vLLM 统一解析引擎(Parser Engine)中的适配器

  • kimi_k2:在 vllm/reasoning/kimi_k2_reasoning_parser.py 中直接别名到vllm.parser.engine.registered_adapters里的KimiK2ParserReasoningAdapter,解析逻辑统一由vllm/parser/下的解析器(如 vllm/parser/kimi_k2.py)驱动;
  • minimax_m2:在 vllm/reasoning/minimax_m2_reasoning_parser.py 中定义了MiniMaxM2ReasoningParserMiniMaxM2AppendThinkReasoningParser两个实现。

所有可用的 parser 名称在 vllm/reasoning/init.py 的_REASONING_PARSERS_TO_REGISTER注册表中统一定义(kimi_k2指向kimi_k2_reasoning_parser.KimiK2ReasoningParserminimax_m2指向MiniMaxM2ReasoningParser,另有minimax_m2_append_think变体),并通过ReasoningParserManager懒加载注册——--reasoning-parser传的名称必须能在此注册表中找到。

MiniMax-M2 解析器的实现细节

MiniMax-M2 的推理格式有一个特殊之处(见源码注释):模型不生成<think>起始 token,只生成</think>结束 token</think>之前的所有内容都是推理,之后是正式回答。vllm/reasoning/minimax_m2_reasoning_parser.py 中的核心方法体现这一语义:

  • is_reasoning_end(input_ids):从后向前扫描 token,找到最后一个</think>(end token)或<think>(start token);只有当它是 end token 时返回 True。这使得服务端能判断"推理阶段是否已结束",从而在流式输出时决定 delta 进入reasoning还是content字段;
  • extract_content_ids(input_ids):直接返回全部 token id(内容边界由 token 位置确定,而非标记切分)。

仓库内对应的单元测试 tests/reasoning/test_minimax_m2_reasoning_parser.py 覆盖了多种边界场景,均可作为行为依据:

测试用例模型输出期望 reasoning期望 content
simple_reasoningThis is a reasoning section</think>This is the restThis is a reasoning sectionThis is the rest
no_end_token(流式中)This is reasoning in progress同左None(推理未结束,全部算 reasoning)
multiple_lines多行推理 + 多行回答多行推理部分多行回答部分
code_in_reasoning推理中含代码块含代码块的推理Here is the code.
empty_streaming空输出NoneNone

这些用例验证了:只要</think>尚未出现,流式输出期间所有 delta 都归入 reasoning;出现</think>后 delta 才切换到 content。这正是交错思考在"工具结果 → 模型再次思考"阶段的关键行为——工具结果回传后,模型先吐出的新 token 会先进入 reasoning 流。

启用方式:服务端启动与请求参数

服务端启动

以 MiniMax-M2 为例,需要同时启用 tool call parser 与 reasoning parser,并打开自动工具选择:

vllm serve MiniMaxAI/MiniMax-M2 \ --tensor-parallel-size 4 \ --tool-call-parser minimax_m2 \ --reasoning-parser minimax_m2 \ --enable-auto-tool-choice

各参数含义(结合 vllm/engine/arg_utils.py 中的参数定义):

  • --tool-call-parser minimax_m2:注册工具调用解析器,负责把模型输出中的工具调用标记解析为 OpenAI 格式的tool_calls结构;
  • --reasoning-parser minimax_m2:注册推理解析器。源码中该参数属于StructuredOutputsConfig.reasoning_parser(arg_utils.py 中reasoning_parser字段默认取StructuredOutputsConfig.reasoning_parser,随后写入self.reasoning_config.reasoning_parser),即推理切分能力挂在结构化输出/推理配置体系下;
  • --enable-auto-tool-choice:允许模型自行决定是否调用工具(对应请求中的tool_choice="auto")。

对 Kimi-K2-Thinking 服务,同样思路将两处 parser 名换成kimi_k2

vllm serve moonshotai/Kimi-K2-Thinking \ --tool-call-parser kimi_k2 \ --reasoning-parser kimi_k2 \ --enable-auto-tool-choice

客户端:带推理回传的两轮工具调用

下面是原文档给出的完整可运行示例(天气查询工具),其要点是:第二轮回传 assistant 消息时必须带上reasoning字段,否则模型丢失上一轮的思考上下文,交错推理链会断裂。

""" vllm serve MiniMaxAI/MiniMax-M2 \ --tensor-parallel-size 4 \ --tool-call-parser minimax_m2 \ --reasoning-parser minimax_m2 \ --enable-auto-tool-choice """ import json from openai import OpenAI client = OpenAI(base_url="http://localhost:8000/v1", api_key="dummy") def get_current_weather(location: str, unit: "str"): """Get the current weather in a given location""" if unit == "celsius": return f"The current temperature in {location} is 22°C." else: return f"The current temperature in {location} is 72°F." tools = [ { "type": "function", "function": { "name": "get_weather", "description": "Get the current weather in a given location", "parameters": { "type": "object", "properties": { "location": { "type": "string", "description": "City and state, e.g., 'San Francisco, CA'", }, "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}, }, "required": ["location", "unit"], }, } } ] messages = [{"role": "user", "content": "What's the weather in Fahrenheit like in San Francisco?"}] response = client.chat.completions.create( model=client.models.list().data[0].id, messages=messages, tools=tools, tool_choice="auto", ) tool_call = response.choices[0].message.tool_calls[0].function messages.append( { "role": "assistant", "tool_calls": response.choices[0].message.tool_calls, "reasoning": response.choices[0].message.reasoning, # append reasoning } ) # Simulate tool execution available_tools = {"get_weather": get_current_weather} completion_tool_calls = response.choices[0].message.tool_calls for call in completion_tool_calls: tool_to_call = available_tools[call.function.name] args = json.loads(call.function.arguments) result = tool_to_call(**args) messages.append( { "role": "tool", "content": result, "tool_call_id": call.id, "name": call.function.name, } ) response_2 = client.chat.completions.create( model=client.models.list().data[0].id, messages=messages, tools=tools, tool_choice="auto", ) print(response_2.choices[0].message.content)

这段示例完整演示了交错思考的三步闭环:

  1. 第一轮:用户提问 → 模型输出推理 + 工具调用(tool_calls[0]),此时message.reasoning携带本次工具调用前的思考内容;
  2. 上下文拼接:把 assistant 消息(含tool_callsreasoning)和role: "tool"的工具结果追加进messages
  3. 第二轮:模型收到工具结果后,会先推理再回答——response_2message.reasoning即为对工具结果的思考,message.content为最终回答。

推理字段的协议层支持

"回传 reasoning 才能维持推理链"之所以成立,是因为 vLLM 的 Chat Completions 协议把推理内容做成了一等字段:

  • 请求侧,assistant 历史消息支持reasoning键(vllm/entrypoints/openai/chat_completion/protocol.py 中存在对msg.get("reasoning")的处理逻辑,将客户端回传的推理内容并入渲染后的提示词);
  • 响应侧,消息体定义了reasoning: str | None字段(同文件第 72 行附近),流式与非流式都会填充;
  • 另有include_reasoning: bool = True的默认值(该文件两处出现),控制是否默认包含推理输出;以及reasoning_effort参数用于支持按推理强度档位请求(对支持该参数的模型,会转成enable_thinking等用户侧开关)。

也就是说,交错思考在协议层面的闭环是:reasoning parser 把输出切分成 reasoning/content → API 返回message.reasoning→ 客户端回传 → chat template 将其重新注入提示词 → 模型基于完整推理历史继续思考。缺任何一环,模型"看到"的历史都会丢失思考内容。

实现链路总览

结合源码结构,一次交错思考请求在 vLLM 内部的处理链路如下(从源码结构看,各组件职责分离清晰):

  1. 解析引擎层(vllm/parser/engine/):parser_engine.pystreaming_parser_engine.pyincremental_lexer.pytoken_id_scanner.py构成增量式流式解析框架。reasoning 与 tool call 两类事件由events.py定义,registered_adapters.py将具体模型的解析规则适配到该引擎;
  2. ReasoningParser 抽象层(vllm/reasoning/abs_reasoning_parsers.py):定义ReasoningParser基类与ReasoningParserManager,各模型解析器通过 vllm/reasoning/init.py 中的注册表懒加载;
  3. 工具解析层(vllm/tool_parsers/):kimi_k2_tool_parser.pyminimax_m2_tool_parser.py等分别解析两种模型的 `

【免费下载链接】vllmA high-throughput and memory-efficient inference and serving engine for LLMs项目地址: https://gitcode.com/GitHub_Trending/vl/vllm

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/7 9:56:58

AI Agent实战:用workbuddy搭建教学助手,实现备课批改自动化

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/7 9:52:35

AI编程准确率提升指南:从需求拆解到验证闭环

我先讲一个最近常被问到的问题&#xff1a;很多人觉得 AI 编程不靠谱&#xff0c;让模型写个脚本、补个接口、修个 Bug&#xff0c;结果经常是“看起来很有道理&#xff0c;跑起来全是意外”。更让人无语的是&#xff0c;你追问它是怎么得出这个结论的&#xff0c;它会非常诚恳…

作者头像 李华
网站建设 2026/9/7 9:51:35

SpringBoot+Vue前后端分离在线考试系统毕业设计全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华