多轮工具执行
多轮工具执行 · 阅读后完成下方实验与检查题,再用「标记完成」记录进度。
学习目标
读完本课你能实现受限的多轮工具执行:循环怎么转、每一步怎么记录、次数上限和取消条件怎么设,以及超限之后怎么让模型体面收场。
问题场景
用户问「A16 课讲了什么,和站里哪些练习相关」。模型先检索课程,再读正文,又想检索练习——它连环举手要工具,根本没打算停。一次失控的循环意味着:十几秒的等待、成倍的 token 账单、和一段谁也看不懂的执行过程。
核心解释
循环结构本身不复杂:上一轮以 finish_reason: "tool_calls" 结束、工具结果已回传,就再跑一轮。关键是给它笼子。本站 app/api/ai/chat/route.ts 的真实结构(简化):
const MAX_ROUNDS = 3; // 笼子一:轮次上限
let round = 0;
async function runRound() {
round += 1;
await streamChatCompletion(
{ messages, tools: ASSISTANT_TOOLS, signal }, // signal 来自 abort 链(A12)
callbacks // onTextDelta / onToolCalls / onDone / onError
);
// 笼子二:取消条件——出错、被取消、或到达上限都不再继续
const lastRole = messages[messages.length - 1]?.role;
if (round < MAX_ROUNDS && lastRole === "tool" && !errorHappened) {
await runRound();
}
}
await runRound();
三个笼子的含义:
- 轮次上限(本站 3):检索 → 读正文 → 再检索一次足够覆盖本站场景。上限的存在把「失控循环」变成「最多损失 N 轮」。
- 取消条件:用户停止(abort 链传导,A12)、工具执行出错、模型不再申请工具,三种情况都要立刻退出循环——条件写在递归判断里,一处管住。
- 超限收场:到上限时如果模型还想调工具,最后一轮不带 tools 再请求一次,或追加一条「请基于已获得的信息直接回答」的提示。它必须作答,哪怕答案不完整——把不完整交还用户,好过无限烧钱。
每一步都要记录——给界面、给排障、给成本核算:
// 每次工具执行后(A15 的执行点扩展)
push({ type: "tool", name, status: "done", summary: result.slice(0, 120) });
// ↑ A10 事件协议里的 tool 事件:前端能显示「正在检索课程…」
toolLog.push({ round, name, args: call.function.arguments, summary: result.slice(0, 120), ms: cost });
// ↑ 服务端留存完整输入输出(脱敏后可入 A13 的调用记录),复盘时有据可查
示例拆解
一次健康的两轮执行长什么样(trace 示例):
round 1 finish_reason=tool_calls
└─ search_courses {"query":"工具参数 校验"} -> 4 门课程(120ms)
round 2 finish_reason=tool_calls
└─ get_lesson {"course_code":"A16"} -> 正文 3000 字摘要(80ms)
round 3 finish_reason=stop,正文开始输出,done
工具日志:2 次调用,全部命中白名单,结果截断后回传
关键点:
- 每轮的 messages 是累积的:原始问题 + 助手的 tool_calls + 工具结果 + ……模型每一轮都看得到全部历史,所以工具结果的摘要质量直接影响最终回答。
- 结果回传前的截断(本站 8000 字符)在多轮场景尤其重要:三轮各塞全文,上下文直接爆窗(A02)。
toolLog里round字段让前端能按轮次分组显示进度,也让「哪一轮慢了」一眼可见。- 验证循环是否受控:数 trace 里的轮数是否 ≤ MAX_ROUNDS,以及超限场景是否走了「不带 tools 强制作答」的分支。
动手任务
- 把本站的
MAX_ROUNDS临时改成 1,问一个必须两轮才能答好的问题,观察它如何被强制收场;改回 3。 - 在
executeTool外围加轮次日志(round、工具名、耗时),跑一次多轮对话,把 trace 保存成笔记。 - 实现「超限收场」分支:到达上限仍有 tool_calls 时,用不带 tools 的最后一次请求让模型作答,并在界面提示「已达到工具执行上限」。
常见错误
- 没有上限的 while 循环:模型坚持要工具就永远轮下去,token 和时间双输。上限必须有,且要可配置。
- 到上限直接掐断不收场:用户只看到回答凭空消失。超限要触发「基于已有信息作答」的最后一轮。
- 每轮结果全量回传:两三轮就把上下文撑爆,报「context length」错误(A02 的单次容量口径)。
- 出错后继续下一轮:错误已经发生还跑下一轮,只会基于坏结果继续编。取消条件里必须有「出错即停」。
检查与复盘
自查清单:
- 能说出循环的三要素:继续条件、轮次上限、取消条件
- 每步工具执行有记录(轮次、工具名、入参摘要、结果摘要)
- 超限收场路径验证过:模型基于已有信息作答而非卡死
参考资料
- OpenAI 文档:Function calling —— 官方的调用循环说明与「处理可能失败」的建议
- OpenAI 文档:API Reference - Chat —— finish_reason 取值与 tool 消息结构
- 到这里,A01-A17 的主线就走完了:一次真实调用、一套流式协议、一个受控工作台、一组带白名单的工具——回去把 A14 的验证清单再跑一遍,感受一下现在你对每个环节的把握。
读完这一课了?
标记完成后,工作台会安排接下来的学习。