调用循环
公开教程 · 无需登录

调用循环

连接执行一次,调用可能走多轮。本页走一遍这条循环:模型在其中出场三次,宿主管执行与终止,失败也须按规则回传给模型。

1连接:握手与发现,只执行一次

连接是一次性动作:握手确认双方可用同一套协议,发现取回工具清单。产物是一份工具清单。

步做什么失败时
1取出这台服务器的连接信息(地址与令牌)不在可用范围内:按不存在处理
2拼接端点地址—
3initialize 握手,带上令牌停在未连接,提示检查地址与令牌
4tools/list 取回工具清单同上:停在未连接
5用本次的清单整体替换旧的,标记为已连接写入失败同样停在未连接

第 5 步是替换而非追加:先清除旧清单,再写入本次的新清单。因此本地清单始终等于「最近一次成功的发现结果」,远端已删除的工具不会留下残留条目。

取登记信息 按名字 + 班级 握手 initialize 发现 tools/list 替换清单 用这次的结果 标记已连接 状态改为已连接 握手或发现任一步失败:把该服务器置回未连接,并提示检查地址与令牌
五步里只有 3、4 两步真的走出本机;它们失败时清单与连接状态都不更新——不记住一次失败的发现结果。
教师(浏览器) 后台配置 发起连接的一方 编排与认证 MCP 服务器 mcp-server 宿主侧存储 登记信息与工具清单 ① 教师登记一台服务器 令牌与地址只存服务端 ② 点「连接」 ③ initialize(Bearer 令牌) 响应:协议版本与能力 ④ tools/list 响应:result.tools 清单 用新清单整体替换旧的 写毕,标记为已连接 完成:已发现 N 个工具 ③ ④ 任一步失败:置回未连接并清空清单
同一件事的两个视角:上面看宿主内部的五步,这张看跨进程的往返——只有 ③④ 真的离开本机。

2调用循环:模型出场三次

在通用做法中,调用不是一个固定步骤,而是夹在模型两轮生成之间的一个循环。

① 提问 + 工具声明一起发出

宿主把候选集内的工具转换为工具声明,连同本次提问一起发给模型。声明中的三项都来自发现结果。

② 模型回结构化的调用请求

模型并非输出一句非结构化的调用意图,而是输出一段结构化内容:工具名加参数对象。它可能一次提出多个调用。

③ 宿主校验后执行

宿主按那份输入结构校验参数,通过后才真正发报文、带令牌、等结果。模型始终不接触地址与令牌——它只是提议者。

④ 结果作为工具消息回传,再生成

结果以「工具消息」这一角色追加进消息序列(messages),再次请求模型。模型可能还需要一个工具,于是回到 ②;不再需要,则输出回答。

组装请求 提问 + 工具声明 模型决策 回 tool_call 或直接答 执行调用 tools/call 带令牌 结果回传 作为工具消息追加 生成与收尾 delta → done 还要工具?回到「模型决策」 模型只提出调用,不执行调用:令牌、参数校验与记录都在「执行调用」这一环,由宿主完成
调用不是一个固定步骤,而是一个循环:只要模型还要工具,中间两步就会再来一轮;模型决定「要不要」,宿主决定「能不能」。
学生(浏览器) 只做提问与显示 发起连接的一方 编排与执行 MCP 服务器 执行工具 对话模型 提出调用、组织回答 ① 提问(SSE) ② 路由选技能,取候选工具 ③ 系统提示 + 用户消息 + 工具声明 ④ 模型回 tool_call:工具名 + 参数 ⑤ tools/call(Bearer + 参数) ⑥ 响应:result.content 文本 ⑦ 安全校验 + 留一条记录 ⑧ 追加工具消息,再次请求(回到 ④) ⑨ 流式 delta 逐段返回 ⑩ tool_calls 事件先到,正文后到 ④—⑧ 可循环多轮:终止条件是模型不再请求工具,或达到轮次上限
模型出场三次:收到工具声明、提出调用、取得结果后再组织回答。它决定「要不要调」,但从不自己发请求。
同一份输入结构被使用两次:模型据此填写,宿主据此校验。这正是「工具声明必须来自发现结果」的原因——手写一份,模型与校验器就会各对一套不同的结构,错误会推迟到调用时才暴露。

3循环如何终止

循环必须有明确的终止条件,否则一次提问可能无限往返下去。

终止条件什么时候触发触发之后
模型不再请求工具它认为已有信息足够作答正常输出回答
达到轮次上限往返次数用完停止调用,让模型用已有信息作答
达到调用次数或时间上限单次会话内的累计用量用完同上;一次提问里的并发调用也要计入
工具返回让它自行收敛结果已经足够,或反复失败模型自行决定不再调用

上限不是优化项,而是必需品:模型可能陷入「调了不满意、再调一次」的循环,也可能被一段诱导文本反复带动。超限后的处理是优雅降级(graceful degradation)而非报错——停止调用,让模型基于已有信息作答,用户得到的是一段回答而不是一次失败。

轮次与累计次数是两个不同的闸门:轮次管的是「一条链能走多深」,累计次数管的是「一次会话总共能做多少动作」。前者防死循环,后者防成本失控,两者都要有。

4失败是局部失败:错误也须回传给模型

工具未调通,不等于本次对话无法继续。关键在于把失败如实告知模型,由它自行决定后续策略。

工具自报错误、超时、连接失败——这些都应当作为一条工具消息回传给模型,并带上「失败了、为什么」。模型据此可以换个参数重试、换一个工具,或者如实告知用户「这一步未查到」。反过来,若将失败静默丢弃,模型会误以为工具返回了空结果,于是自行编造一段内容填上。

无论成败,工具失败都不应中断整段对话:检索与生成照常进行。工具是增益,不是主路径。

结果什么情况是否记录记录的内容
成功服务器可达,工具执行成功留返回内容的一段摘要
工具出错服务器可达但工具报错、超时、返回异常留错误原因
宿主拒绝服务器不在可用范围,或未连接视能否归属而定「未连接」一类固定说明
调用一条 tool@server 能按名查到服务器? 否 被拒 · 不留记录 服务器已不存在 是 当前已连接? 否 被拒 · 留记录 原因:未连接 是 发出 tools/call 工具执行成功? 否 出错 · 留记录 记下错误原因 是 成功 · 留记录(结果摘要)
两种「拒绝」,一条记录、一条不记录,差别在于能否归属到某台服务器。
记录的前提是「本次调用能归属到某台服务器」:服务器已不存在时,这次失败只体现在本次应答的调用清单中,落不进一条独立记录。因此「有记录」不等于「记下了一切」——它记录的是可被归属的动作。