Agent 为什么经常“不听话”:从 Context、Tool 到结果验证
Agent Demo 往往很好看:演示的人知道该问什么,路径短,Tool 少,旁边还有人盯着。真正丢进业务系统里,常见体验是:不调工具、乱调工具、绕圈、一本正经地胡说、偶尔还干出越权的事。
这篇不讲模型原理。我想回答的是:
Demo 为什么很好看,真正用起来为什么经常不稳定?
背景仍是我自己:多年 Java / 企业系统,正在把 Agent 往真实业务场景里推。踩过的坑,和以后会优先防的坑,都记在这里。结论可以先说:
Agent「不听话」,多数时候不是性格问题,是约束不够。
1. Demo 能跑,不代表 Agent 能用
两边差别很大:
Demo:
单任务 · 短 Context · 少 Tool · 人工观察 · 路径可预期
真实应用:
复杂任务 · 长 Context · 多个 Tool · 异常与重试
权限 · 成本 · 并发 · 结果验证 · 脏数据Demo 里「看起来智能」,常常是场景被裁剪过了。真实系统里,用户一句话可能含糊、数据可能过期、接口可能超时、权限可能不够。模型还是那个模型,环境变了,表现就崩。
所以我评估 Agent,不看演示流畅度,看:异常怎么处理、错了能不能拦住、日志能不能复盘、失败时用户看到什么。能演示和能值班,是两件事。企业系统里,值班能力比 Demo 分数重要得多。
2. Context 问题
最常见的几类:
信息缺失
没给设备号、时间范围、业务规则,却指望模型「自己懂现场」。模型会补全——补出来的往往是幻觉。用户听起来很完整,系统里却对不上号。
信息过多
把整份制度、半年工单、全部会话历史塞进去。噪声变大,关键信号被淹没,Token 和延迟一起涨。模型开始抓次要细节,主线反而丢了。
历史污染
上一轮任务的中间假设还留在对话里,下一轮任务被带偏。尤其是多轮「分析—追问」之后,旧结论会像事实一样继续流传。
上下文冲突
系统提示说「只查询不修改」,用户又说「直接帮我改掉」,业务规则又禁止某状态变更。模型夹在中间,表现就会飘:有时服从用户,有时服从系统,你很难预测。
我的处理思路:
需要什么给什么
长期信息与当前任务分离
历史过长就摘要,不原文堆叠
Tool 结果结构化,少塞散文
冲突规则写死在程序侧,不交给模型仲裁Context 工程做不好,后面怎么换模型都像「不听话」。换模型是最后一招,不是第一招。做后端的人换个说法:给下游的 payload 裁不好,调用方再聪明也会翻车。
3. Tool 问题
真实故障里,很大一部分其实是 Tool 设计问题:
- 该调不调:描述含糊、触发条件不清
- 调错 Tool:名字相近、职责重叠
- 参数错误:类型不对、缺必填、时间格式乱
- 返回值模型看不懂:一大段 HTML、无字段说明、错误时仍返回 200 + 空对象
- Tool 失败却被当成成功:超时后模型继续「分析」
核心观点:
很多 Agent 问题并不是模型能力问题,而是 Tool 设计得不像一个好的工程接口。
用 Java 后端的标准想:如果你给同事一个 queryEquipment(String id),文档不写清返回字段、失败怎么表示,对方调用也会一团糟。模型比同事更依赖清晰契约——它不会来问你「这个 null 是什么意思」。
我会要求 Tool:
- 名称表达单一职责
- 参数有类型与校验
- 成功 / 失败结构固定
- 错误信息机器可读、人也读得懂
- 写操作与读操作严格分开
- 对「无数据」和「调用失败」做出区分,避免模型把空结果编成故事
举个反例:接口超时返回空对象,模型读成「当前无异常」,再据此写一份很自信的分析报告。根因不在模型,在契约。Tool 像好 API,Agent 才有机会像好调用方;Tool 像垃圾桶,Agent 再强也只是高级乱调用。
4. Agent 无限循环
典型形态:
判断 → Tool → 判断 → Tool → 判断 → …原因可能是:终止条件不清、Tool 结果不足以推进、模型反复「再查一下」、验证失败却没有退出策略。有时用户问题本身就含糊,模型用更多调用假装在努力。
我会在编排层硬约束:
- 最大执行步数(例如 8~12 步,按场景定)
- Timeout(整次任务与单次 Tool)
- Retry 上限(按错误类型区分可重试 / 不可重试)
- 状态机:明确「进行中 / 待确认 / 成功 / 失败 / 需追问」
- 终止条件:目标达成、信息不足需问用户、权限不足、步数耗尽
没有这些,Agent 不是「更努力」,是在烧钱和打接口。循环本身不可怕,可怕的是没有人规定它何时必须停。这和消息消费里的重试风暴是一类工程问题,只是中间多了一个会即兴发挥的决策者。
5. 输出看起来正确,但实际上错误
这是我认为最危险的一类。
例如:
模型:「设备 A 当前存在异常。」
实际上:设备状态接口返回的是昨天缓存的数据。或者:
模型:「维修工单已创建。」
实际上:createWorkOrder 因权限失败,返回了错误码,模型没读懂。所以:
模型输出 ≠ 事实正确链路更接近:
事实数据(接口 / 数据库)
↓
程序验证
↓
模型解释(基于已验证事实)而不是让模型自己生成事实。
企业系统里,用户对「说得像真的」很敏感。一次自信的错误,会比十次「我不确定」更伤信任。Agent 应用里,宁可少说,也不要无依据地断言。
我会要求分析类输出尽量带「依据来源」:哪次查询、哪个字段、哪个时间点。对不上来源的句子,编排层可以直接删掉或改成追问,而不是原样展示。如果让我给 MES 分析类 Agent 定验收标准,第一条就会是:结论必须能回溯到 Tool 结果。
6. 权限问题
Agent 接进真实系统之后,权限必须由程序控制,不能交给模型「自己判断能不能做」。
查询 ≠ 修改 ≠ 删除 ≠ 执行生产操作尤其是 MES / ERP:
Agent 可以建议执行什么,但不应该天然拥有执行一切操作的能力。
我会把动作分成至少三档:
- 只读查询:在用户权限内可自动执行
- 低风险写操作:如草稿、备注 → 可自动或轻确认
- 高风险写操作:改关键状态、触发生产、对外发送 → 强制人工确认 + 审计日志
模型可以说「建议创建工单」;真正 INSERT 发生在确认之后,由业务服务执行。这和传统系统的权限模型一致,只是前面多了一层 Agent 编排。
还有一点:Agent 使用的服务账号,权限应小于等于当前用户,而不是「万能运维账号」。否则一次 Prompt 注入或一次误判,影响面会大得离谱。企业系统里这是基本功,接到 Agent 上不能松。
7. 成本问题
不稳定往往伴随着贵:
- Context 越堆越长
- Tool 无效调用多
- 本来用小模型够用,却全程上大模型
- 失败无限重试
- 不必要的多轮 Loop
- 同一只读查询在一轮任务里被反复打
Agent 成本控制本质上也是工程问题:裁 Context、限步数、按任务选模型、缓存只读查询、失败快速失败、把「解释」和「决策」拆开(决策用强模型,格式化用弱模型)。
我不会先问「换个更强的模型能不能好」,而会先问「这次任务有多少步是无效的」。成本失控的 Agent,短期还能靠演示撑着,长期一定会被业务方关掉——不是因为不智能,是因为账单和对账都过不去。
8. 稳定性问题
一套我会默认带上的工程能力:
Timeout
Retry(有上限、可分类)
Fallback(降级到普通 AI 调用或人工)
Logging
Tracing(一次任务一条链路)
Validation
Human-in-the-loop没有日志和追踪,出了「不听话」你只能猜。有了链路,你才能看到:是 Context 缺了、Tool 错了,还是验证没拦住。
稳定性不是调一次 Prompt 得到的,是约束、观测和回滚能力堆出来的。传统后端里的超时、熔断、降级、审计,原样搬到 Agent 编排层,通常比再写十段「请你务必准确」更有效。我会把这些当成 Agent 上线的默认清单,而不是「以后再补」。
9. 我现在认为 Agent 最重要的能力
不是:
「让模型更聪明。」
而是:
「让一个不确定的模型输出,被一套确定性的工程系统约束起来。」
模型负责理解与选择;程序负责边界、权限、验证、终止与审计。谁主谁辅搞反了,Demo 越炫,线上越难收场。
这也是为什么我从业务系统背景看 Agent,会本能地强调:接口契约、状态机、权限、校验、人工确认——这些在传统后端里早就是常识,换到 Agent 里同样成立。差别只是:中间多了一个会即兴发挥的组件,所以约束要写得更硬,而不是更软。
10. 如果重新做
我会把自己按回这几句:
少一点自由,多一点约束
少一点自动执行,多一点验证
少一点复杂 Agent,多一点确定性工程具体一点:
- 先把 Tool 做成好 API
- 先把验证和权限做成硬门槛
- 先把步数和超时做成硬限制
- 先把日志做成能复盘的链路
- 再谈更复杂的多 Agent、长记忆、全自动
Agent「不听话」,多数时候不是性格问题,是系统设计没有把不确定的部分关进笼子里。把笼子设计好,它才配进真实业务。