同步接口的异步兼容方案
同步接口的限制从何而来?
- 场景: Agent 正在起草邮件(工具调用
search_contacts尚未返回),用户突然说“等一下,先帮我查一下明天的天气”; - 限制: 若接口要求未完成的工具调用先获得对应结果,Agent 就无法带着悬空调用处理新消息;
- 归因: 限制来自所选协议与模型组合,并非所有 LLM 都必须遵循的规律;
- 核心思想: 在没有打断发生的常态下让 LLM 看到标准同步轨迹,只在打断时插入占位符来修复格式;
兼容方案的五条规则是什么?
- 规则 1: 及时记录 API 已完成的 assistant 消息与工具调用项;对服务端管理的 reasoning 状态,按提供商的续接协议保留,不自行拼接不可见的思考文本;
- 规则 2: 工具调用完成时才记录 tool result,执行中轨迹处于“部分完成”状态;
- 规则 3: 工具执行中的打断需要占位符——为未完成的工具生成占位符响应(如“工具正在后台执行,请优先处理新事件”),追加打断事件,重新调用 LLM,使其视角下 assistant message 仍与 tool result 配对完好;
- 规则 4: 没有原生 steering 或受支持的中途续接接口时,取消未完成的生成,保留已确认完成的消息与工具状态,追加新事件后重新请求;不能假定半截输出或隐藏推理可作合法前缀任意回灌;
- 规则 5: 非打断事件进入队列等待批处理,当前周期完成后才一次性追加;
用具体案例说明五条规则如何运作?
- Agent 调用
search_contacts,assistant message 立即写入轨迹(规则 1); - 工具未返回时用户打断;系统为
search_contacts生成占位符 tool result(规则 3),再追加天气查询并重新调用 LLM,此时轨迹格式完全合法; - 天气查询完成并回复后,
search_contacts结果作为新事件到达并追加(规则 2),Agent 继续起草邮件;
- 适用范围: 只有需要打断时才引入明确表示“未完成”的占位符;后台真实结果到达后,通过带来源与任务 ID 的事件送入轨迹;
- 原生异步对照: 已支持原生异步的模型可保留任务未完成状态,等真实结果到达再处理;
- 占位符的语义风险: 模型可能把“任务已启动”误当成“任务已完成”,在真实结果返回前作出依赖该结果的判断;
- 缓解: 通过明确的任务状态与结果校验防止混淆,并在评估中检查是否编造了未到达的数据;
- 归因纪律: 不能仅凭一次失败,就把原因归结为某个未公开的训练过程;
任务句柄如何从工具命名上表达异步语义?
- 隐含假设: 传统工具设计隐含“调用即完成”的语义——
phone_call这个名字暗示“拨号并等待通话结束,返回通话记录”; - 解耦方式: 把“启动”与“完成”分开,如
initiate_phone_call立即返回任务标识符与初始状态(“呼叫已发起,正在拨号”); - 进展通知: 通话进展通过事件传递,如
phone_call_connected、phone_call_ended; - 关键: 工具的名称与描述本身要传达异步语义,使模型看到
initiate_phone_call时自然推断这是“发起”而非“完成”; - 描述示例: “此工具将启动由子 Agent 处理的电话任务。任务成功发起后立即返回任务 ID,你可以继续处理其他事项。通话结束后会收到单独的通知事件。”;
- 适用性: 无论是否使用原生异步协议,都可在工具接口设计层面明确异步语义,对同步接口尤其有用;
队列式批处理为什么会遗漏事件,如何缓解?
- 问题: 批量事件处理时,模型可能只回应最后一个事件而遗漏前面的要求;
- 提示词层面: 告知模型“当收到多个连续事件时,请确保全面考虑所有信息”;
- Agent 状态栏标记: 在每个事件前加显式标记,例如:
[未处理事件 1/4] Tool result from database_query:...
[未处理事件 2/4] User 补充说明:只看北京地区的数据
[未处理事件 3/4] 系统提醒:报告截止时间还有 30 分钟
[未处理事件 4/4] User 询问:进度如何?
- 收尾汇总: 在末尾追加“上面有 4 个未处理事件,包括 1 个工具结果、2 条用户消息、1 个系统提醒。请确保回应涵盖所有信息。”;