核心论点先行:可靠的 Agent,表面靠模型,实际靠确定性代码。
一、先说结论:你看到的 Agent 演示,和生产里的 Agent 是两回事
市面上一堆 Agent 在演示里表现亮眼,放到真实生产流量(尤其是直接面对客户的场景)就露怯。这个落差不是偶然,而是结构性的。
根本原因在于:扛得住生产的 Agent,对大模型的依赖程度,远低于它在演示里展现的样子。它们绝大部分行为跑在常规的、确定性的代码里,只在少数几个明确的决策点才调用模型。一个 Agent 靠不靠谱,往往就取决于一组工程决策——在哪放权给模型,在哪把模型按住。
换句话说:生产级 Agent = 在少数刻意决策点调用 LLM 的确定性软件。模型是锦上添花的能力放大器,不是系统的骨架。
这篇文章不让你背清单,而是讲清楚四件事背后的"为什么"和"怎么做":
-
上下文(Context):每次调用,模型到底该看到什么?
-
控制流(Control Flow):循环和停止条件,凭什么要攥在确定性代码手里?
-
状态(State):记忆放哪?为什么模型必须保持无状态?
-
作用域(Scope):一个 Agent 该管多宽?为什么"窄而受监督"胜过"大而全"?
二、最简 Agent 长什么样:一个循环,但生产一跑就崩
最朴素的 Agent 就是一个循环(Loop):
模型接收"上下文"——也就是到目前为止发生了什么的运行记录;
返回一个单步的结构化决策(机器可读的 JSON,而不是自由散文);
外围代码执行这一步,把结果追加回上下文;
把整包内容再丢回模型,拿下一个决策;
循环直到模型宣布"干完了"。
这里有个关键比喻:把模型当成"每次调用都重新初始化的函数"。它在每一次调用时只看你给它的上下文,做一个决策,响应完就把一切丢干净。上下文窗口就是系统的全部记忆。
这套设计的优点很明显:你给个目标、给套工具,模型自己琢磨执行顺序,分支逻辑少写得省心——所以它在 Demo 里效果惊艳。但麻烦从真实流量一来就开始了。
三、四类必然踩中的失败:每一个都对应后面的一条实践
理解失败,才能理解实践为什么存在。生产环境下,这套朴素设计会按以下可预测的模式崩:
失败 1:错误累积(Compounding Error)——乘法效应最狠
每次模型调用都有一定概率决策错误,而自由的 Agent 会把很多次调用串起来。假设单步正确率 95%(听起来很稳),连跑 20 步,整体成功率掉到约1/3(0.95²⁰ ≈ 0.36)。可靠性是乘出来的,链一长就断崖式下滑。这就是为什么生产团队要加那么多护栏。
失败 2:自信地错答用户(Confidently Wrong)——最直接赔钱
Cursor(2025):客服 Agent 告诉用户"一个订阅只能绑一台设备",公司后来确认这条规则是它编的,随后引发一波退订潮。
Air Canada:聊天机器人虚构了"丧假优惠票价"政策,法庭判航空公司必须按这个虚假政策赔偿。
研究数据:即便在受控环境,模型的幻觉率(产出看似合理但虚假陈述的比例)也在3%–27%之间。一个直接对客的 Agent,把这个比率直接变成了商业风险。
失败 3:循环停不下来(Runaway Loop)——悄无声息烧钱
停止条件弱的循环会跑得远比预期久,每一圈都在吃 token、烧成本。
失败 4:状态随崩溃丢失(Lost State)——工作线程说没就没
当"计划"只活在模型的上下文里,一旦崩溃或遇到长任务,整个工作线程就丢了。
四个失败 → 四条实践。 下面每一节,都是对上面某一个失败的直击。
四、实践一:上下文(Context)——控制模型每次看到什么
上下文窗口是模型在某次调用里知道的一切,因此控制它的内容,是可靠性第一大杠杆。
三条习惯用好了,就解决了大部分问题:
1)拥有你的 Prompt(把它当源码管)Prompt 是发给模型的指令文本,理应和任何源码一样——进版本控制、走评审、写测试。很多 Agent 框架会自动生成 Prompt 并藏起来,方便是方便,可一旦行为变了,你根本定位不到原因。把 Prompt 放在应用自己的代码库里,效果才可复现。
2)亲自拥有上下文窗口(相关性 > 体量)每次调用塞什么内容,是个有意为之的选择,且这个选择的分量比看上去重。给模型"聚焦、相关"的上下文,表现好过"一大堆松散相关的历史"。窗口里一旦塞满边缘内容,质量就降。主动裁剪掉当前步骤不需要的东西,能保住模型准确率。
反模式:以为"历史越多模型越聪明"。真相是——相关性胜过体量,几乎在每一次调用上都成立。
3)把工具当接口设计(Tool as Interface)工具就是模型可以请求调用的函数,用清晰的名字和入参 schema 来描述。描述精确,模型就选对工具、填对参数;描述模糊,模型就更可能选错。把工具定义当成一份认真的接口设计来做,回报立竿见影。
这套习惯后来被统称为 Context Engineering(上下文工程)——大话题。但核心就一句:每次调用,刻意决定模型看到什么。
五、实践二:控制流(Control Flow)——循环和停止线,攥在确定性代码手里
输入攥住了还不够,得让外围代码来决定什么时候跑模型、什么时候停循环。在靠谱的 Agent 里,控制流属于模型周围的确定性代码,模型只在少数几个真正需要判断的节点被咨询。
想象整体流程是普通代码:一串步骤、条件分支、外部系统调用。在其中 2–3 个真正需要"判断"的点,才调用模型。其余地方全用确定逻辑——因为它更便宜、输出可预测、好测试。
两条规则让它落地:
规则一:每个循环必须有"逃生舱"(Escape Hatch)硬性限制,强制它停。迭代次数上限 + 超时 + 显式完成条件,三者一起,保证哪怕模型想无限跑,Agent 也必定 halt。
规则二:刻意调用模型(Invoke on Purpose)模型该待在"需要开放式推理"的地方;凡是能预先规定好的,交给代码。
真实案例——Intercom 客服产品:它的 Procedures 让 Agent 用自然语言推理,但外围系统提供确定性控制:决策点的条件步骤、保证"同一输入永远同一输出"的小代码片段、以及在敏感动作前暂停等人审批的检查点(checkpoint)。
方法论:从能解决问题的最简版本起步,只在更简单的逻辑不够用时,才给模型更多自主。
六、实践三:状态(State)——状态放软件里,模型保持无状态
外围代码一旦掌控了循环,自然要问:Agent 的记忆到底存哪?
答案:让模型无状态,把状态握在你自己可控的软件里。
回想一下——模型每次调用都是 fresh 的,只读你给的上下文。这个特性一旦被当成"特性"来用,就很强:应用持有工作的真实状态(对话、计划、进度),每次调用时从它重建上下文。因为状态存在可序列化存储里(能存能读的形式),Agent 可以中途暂停、稍后恢复,崩溃后也能干净地"读档续命"。
这个习惯还顺手解决了对齐问题:一边是模型"以为现在啥样",一边是应用记录的"真实状态"(比如数据库实际内容)。让两边一直对得上,模型就不会基于过期或错误的视图行动。
还有扩展性红利:状态全在模型之外 → 任意服务实例都能接任意请求(上下文里啥都有)→ 负载均衡后面能横向堆很多副本,扛更多流量。
干净的思考方式:把模型看成这样一个函数——输入"当前状态 + 新事件",输出"下一状态 + 要执行的动作"。 同样输入得同样结果,整个系统才好测、好信。
七、实践四:作用域(Scope)——小而受监督的 Agent,胜过一个大而全
管好了输入、循环、状态,还剩最后一个决策:单个 Agent 该揽多大的活?
好答案倾向于:多个"窄而专、受监督"的 Agent,胜过一个"啥都干"的泛化 Agent。
窄 Agent 只干一件定义清晰的事,失败面小、好测、好排查。任务横跨多个不同工种时,用确定性编排把多个窄 Agent 组合起来——外围代码决定谁跑、何时跑,而不是把一长串职责都甩给一个通用 Agent。
真实案例——某客服助手:首月处理约230 万会话,绝大多数走的是可预测路径(查退款状态、查物流),这类由结构化逻辑稳稳接住,模型只留给真正需要它的少数情况。
监督(Supervision)是作用域的另一半:把"转人工"当成一等公民来设计。敏感动作、低置信、或用户主动要人时,主动转交。Intercom 直接内建了——只要"继续下去风险更大",就自动把对话交给人,并且带上 briefing 状态(让接手的人一眼看清上下文)。把人工介入当成一条"计划内的路径",它就成了系统的强项,而不是"出事了"的标志。
八、结论
把生产级 Agent 还原到本质:它基本是确定性软件,在少数刻意的点调用语言模型。设计决策的全部重量,都落在"选哪些点、放多少权"上。
四类失败,各自对应一条实践:
| 失败 | 实践 |
|---|---|
| 错误累积 | 拥有上下文(裁剪边缘内容,相关性 > 体量) |
| 自信地错答 | 控制流归确定性代码 + 敏感动作人工 checkpoint |
| 循环停不下 | 每个循环有"逃生舱"(上限 + 超时 + 显式完成) |
| 状态丢失 | 状态放软件、模型无状态、可序列化、能续命 |
一句话收尾:从能解决问题的最简设计起步,度量它哪里不够,只在"自主能带来明确价值"的地方放权。
另外我整理了《生产级 Agent 上线前检查清单》,如果有需要,欢迎公众号留言.
企业 AI 落地坊 · 只聊能落地的企业 AI 与工程实践

407

被折叠的 条评论
为什么被折叠?



