
很多人会答:
模型都已经开始输出 tool call 了,当然一边收一边执行,速度更快。
不够。
因为模型流式吐出来的,可能只是半截参数。
比如它最终想读一个文件,过程中可能先吐:
{"pa
然后才陆续补上:
th":"firstcoder/agent/loop.py"}
如果你一边收到,一边解析、一边执行:
• 参数可能还没收全
• tool name 和 call id 可能还没到
• 流中断了,也不知道这次调用到底完整没有
• 更糟的是,半截请求可能已经产生副作用
🎯 这题为什么容易翻车?
因为很多人把两件事混在一起了:
• 文本回答可以渐进展示
• 工具调用必须完整后再执行
前者是用户体验问题。
后者是执行正确性问题。
模型说一句话,说到一半停了,最多是回答没显示完。
但模型调用一个工具,参数说到一半就执行,可能就是读错文件、发错请求,甚至执行错命令。
💡 怎么处理?
不让 TUI 直接处理不同模型厂商返回的原始流。
而是先统一成内部事件:
message_started
→ reasoning_delta
→ text_delta
→ tool_call_started
→ tool_call_delta
→ tool_call_completed
→ message_completed
这样 UI 可以实时显示:
• 模型正在输出什么
• 是否正在思考
• 是否正在组装工具调用
但“看得到工具调用片段”,不等于“现在就去执行”。
真正的链路是:
Provider 原始流
→ 按 tool call 的 index 累积分片
→ 收集 id、name、arguments
→ 等流结束
→ 确认 finish_reason 是 tool_calls
→ 校验 arguments 是完整 JSON
→ 生成完整 ToolCall
→ 再进入权限和执行链
关键点:
只有到了 tool_call_completed,才是一个能执行的工具调用。
🛡 不完整的调用怎么处理?
FirstCoder 这里走的是保守策略:
• 少了 id、name 或 arguments:整组丢弃
• arguments 不是合法 JSON object:丢弃
• 最后不是以 tool_calls 结束:前面收到的工具片段也丢弃
• 只有完整 ToolCall,才会继续走 Permission 和 executor
这比“尽量解析一下,能执行就执行”慢一点。
但 agent 这种会产生副作用的系统,宁可少执行,也不能错执行。
📚 一句话总结
流式输出可以边到边展示。
但工具调用必须:收齐、校验、确认结束,再执行。
🔗 开源地址
https://github.com/KomorGiaoGiao/FirstCoder
欢迎 star⭐和提issue,一起把项目做得更好!