为什么你的 Agent Demo 永远上不了生产?
写一个 Agent Demo 真的不难。
一个模型,几个 tools,再加一个 while loop。模型说要调用工具,你执行工具;工具返回结果,你再塞回模型。顺利的时候,二三百行代码就能跑出一个让人挺兴奋的 demo。
麻烦一般发生在上线后。
比如一个很普通的订单场景:
用户:帮我创建一张订单
↓
Agent 调用 create_order()
↓
接口超时
↓
Agent 认为失败,开始 retry
↓
create_order() 又执行了一次
↓
用户收到两张订单
这时候你很难把锅甩给模型。
模型只是看到了一个超时。真正的问题是:你的系统没有告诉它这次工具调用到底有没有产生副作用,也没有为这次业务意图生成稳定的幂等键,更没有一份 tool ledger 记录'这个 create_order 已经发出去过'。
Agent 没有神秘地变坏。你的 runtime 太薄了。
社区里早就有这些味道了
如果你搜过 LangChain、LangFlow 或各种 agent 项目的 issue,会看到一个反复出现的句子:
Agent stopped due to max iterations.
LangChain 在 2023 年就有人遇到类似问题:Agent 一直尝试工具调用,最后被最大迭代次数拦住。LangChainJS 也有 'agent stuck in a loop' 的 issue,输出里同样出现 max iterations。LangFlow 到 2025 年还有用户报告这个现象。
这不是某个框架'写得不够聪明'。这是 Agent Loop 的基本事实:只要下一步由模型决定,循环就有不收敛的可能。
如果你的系统只有 prompt 和 tools,没有 stop reason、重复调用检测、失败分类、trace 和预算,max iterations 最后就会变成一个很粗暴的保险丝。保险丝当然要有,但保险丝跳了以后,你仍然不知道房子里哪根线短路。
更麻烦的是,Agent 不只是会卡住。它还会一边卡住,一边花钱,一边改东西。
Demo 为什么总是显得很顺
Demo 环境通常太友好了。
工具不会真的失败。即使失败,也只返回一句 'error'。上下文刚好够用。文件很少。用户不会中途取消。模型不会连续三次传错参数。Shell 命令不会挂住。接口不会在'已经创建成功但响应丢失'的尴尬时刻超时。
生产环境不吃这一套。
生产环境的问题更像这样:
- 模型传了一个 schema 上合法、业务上危险的参数。
- 工具返回 500,但副作用可能已经发生。
- 第 17 步时上下文被压缩,用户最开始的限制条件没了。
- 读文件工具吐出两万行日志,直接挤掉了真正重要的 observation。
- Agent 反复调用同一个搜索工具,因为它不知道前两次失败是同一个失败。
- 用户拒绝了高风险命令,但最终回答里看不出它被拒绝过。
- 崩溃恢复后,系统把已经执行过的写操作又跑了一遍。
你看,问题不再是'模型会不会思考'。问题变成了'系统能不能承载一个不稳定的决策源'。
这就是 Agent Runtime 要解决的事。
Tool calling 不是 Runtime
现在模型 API 的 tool calling 已经比早期舒服很多。OpenAI 的文档里,工具调用有明确的 call id,工具输出也可以是结构化 JSON 或普通文本;Structured Outputs 还能让模型输出贴合你给的 JSON Schema。
这些都很有用。
但它们解决的是接口层的一部分问题,不是运行时问题。Schema 能减少'少字段、类型错、JSON 乱飞'这类低级错误,却不能替你判断:
- 这个工具有没有副作用?
- 这次失败能不能重试?
- 第二次调用是不是同一个业务意图?
- 用户是否授权它访问这个目录?
- 输出太大时应该截断、摘要,还是保存成 artifact?
- 工具成功了,但模型最终回答没引用观察结果,要不要拦?
Schema 是门槛。Runtime 是秩序。
这句话我不想说得太玄。落到代码里,其实就是几个很朴素的结构。
type ToolSpec = {
: ;
: ;
: ;
: | | | ;
: ;
?: ;
: ;
};
=
| { : ; : ; ?: }
| {
: ;
:
|
|
|
|
| ;
: ;
};

