# Tool Emulation 实现清单 这份清单是给后续迭代用的。 目标不是解释原理,而是把“纯聊天 API 模拟 tools 调用”拆成可逐项完成、可逐项验证的实现面。 ## 1. Prompt Contract - 明确告诉模型当前有可用工具,不要声称“工具不可用” - 列出全部工具: - 名称 - 简短描述 - 参数 schema 摘要 - 固定动作输出格式: - ` ```json action ... ``` ` - 明确多轮规则: - 独立动作可并行 - 依赖动作要等 tool result - 无需工具时才输出普通文本 - 明确 `tool_choice` 约束: - `any` - 指定 tool - 给至少一个合法 action block 示例 - 最好再给一个“tool result 回来后继续决策”的 few-shot 验收标准: - 模型第一轮能稳定输出合法 action block - 第二轮收到 tool result 后,不会轻易掉回普通解释文本 ## 2. Request Normalization - OpenAI: - 解析 `tools` - 解析 `tool_choice` - 解析 `assistant.tool_calls` - 解析 `tool` - Anthropic: - 解析 `tools` - 解析 `tool_choice` - 解析 `tool_use` - 解析 `tool_result` - 统一归一化成内部结构: - tools - choice - messages - history state - 识别“当前轮没带 tools,但历史里已有 tool 调用”的场景 验收标准: - 第二轮即使不重复传 `tools`,也能继续走 emulation ## 3. Tool History Projection - 把历史 assistant 工具调用重投影成 action text - 不要把结构化历史原样丢给上游模型 - 保留: - tool name - arguments - call id - 投影结果应和真实 action block 尽量一致 验收标准: - 模型在多轮中能“看到”自己之前做过什么动作 ## 4. Tool Result Continuation - tool result 不要裸塞回去 - 包装成明确续写指令: - 当前哪个 call 的结果回来了 - 基于结果继续下一步动作 - 对空结果、错误结果、部分结果做统一包装 验收标准: - 模型收到 tool result 后能继续: - 再发起新工具调用 - 或输出最终答案 ## 5. Parser Contract - 识别: - ` ```json action ` - 普通 ` ```json ` - 容忍: - 智能引号 - 尾逗号 - 参数是字符串化 JSON - 支持提取: - `tool` - `name` - `parameters` - `arguments` - `input` - 能从正文里剥离 action block - 支持多 block 验收标准: - 同一回复里多个 action block 都能被解析 - 正文和动作块可以正确拆分 ## 6. Retry Policy - 触发条件: - 明确要求工具调用但没产出 action block - 命中 refusal 文本 - `tool_choice=any` - `tool_choice=tool` - retry 消息要更强约束: - 必须输出 action block - 不要解释 - 必要时必须调用指定工具 - 控制 retry 次数 - 记录 retry 原因 验收标准: - refusal 回复能被纠偏 - retry 不会无限循环 ## 7. Refusal Detection - 维护 refusal 关键词表: - `I don't have tools` - `tools are unavailable` - `没有可用的工具` - `无法调用工具` - 识别“软拒答”: - 只解释、不行动 - 强调环境限制 - 区分: - 真正不该调用工具 - 本该调用工具却在推脱 验收标准: - 常见“我没有工具”类回复能稳定触发 retry ## 8. Response Re-encoding - OpenAI: - `message.tool_calls` - `finish_reason = tool_calls` - Anthropic: - `content[].tool_use` - `stop_reason = tool_use` - 无工具时回普通文本 - 文本和工具调用共存时保持协议兼容 验收标准: - 下游客户端无需知道上游其实不支持 native tools ## 9. Streaming Strategy - OpenAI stream: - 先发 role chunk - 再发 text delta - 再发 tool_calls delta - Anthropic stream: - `message_start` - `content_block_start` - `content_block_delta` - `content_block_stop` - `message_delta` - `message_stop` - 如果当前实现是“先完整拿结果再合成流”,文档里要明确说明 验收标准: - 下游看到的流式协议字段合法 ## 10. Multi-turn State Machine - 状态至少区分: - 等待模型首次决策 - 已发起工具调用 - 等待 tool result - 收到 tool result,等待下一轮决策 - 最终回答完成 - 状态切换依据应来自消息历史,而不是只看本轮字段 - 不要把“工具历史存在”误判成“必须再调工具” 验收标准: - 一轮以上的 agent loop 稳定 ## 11. Observability - 打日志: - 是否进入 emulation - 解析到几个 tool calls - 是否触发 retry - refusal 命中原因 - 最好记录: - prompt contract 是否注入 - tool history 是否被识别 验收标准: - 出问题时能判断是: - prompt 不够强 - parser 失败 - retry 没触发 - 状态机断了 ## 12. 测试矩阵 - OpenAI: - 单轮 tool call - 多轮 tool result 回灌 - 第二轮不重复传 `tools` - 指定 tool - `tool_choice=any` - Anthropic: - 单轮 tool_use - 多轮 tool_result 回灌 - 第二轮不重复传 `tools` - 流式 tool_use - 异常场景: - refusal - 无效 JSON - 多 action block - 普通文本结束 验收标准: - 至少覆盖“第一轮调用工具”和“第二轮继续决策”两大关键场景 ## 13. 下一步优先级 如果当前系统已经能跑,最值得优先继续做的是: 1. 多轮再次发起新工具调用的 few-shot 2. 基于历史状态的 retry 强化 3. 更细的 refusal 分类 4. parser 容错增强 5. 流式工具事件细化