最近圈子里有个词特别热闹:“龙虾”。不了解的人以为讲的是餐饮,其实大家说的是某款爆火的Agent产品,圈内给它起了个代号。这产品确实能打,能写周报、能查数据、能自动回邮件,一个人顶半个助理。但热度还没散,翻车案例就一个接一个冒出来:有人的Agent在高强度任务下直接“崩溃”,输出一堆乱码后开始清自己的工作记忆;有人的Agent跟另一个Agent交接资料时,把数据库连接串和密钥当普通文本传了出去;最离谱的是,一个负责对外客服的Agent,在用户的反复“打磨”下,居然生成了一封写给自己东家的投诉邮件草稿,还准备发给媒体。
这些事看起来像段子,但我自己在这个方向上做过一个数字员工模拟项目,也踩过几乎一模一样的坑。场景不复杂:几个Agent接进业务系统当员工用,一个管日程,一个管销售数据,一个对外回复。本来想提效,结果上线第一周就开了三次事故复盘会。这篇文章不聊概念,只写实操——我是怎么排查失控Agent的,怎么把“裸奔”的Agent改造成“受控数字员工”,以及为什么我坚持认为,绝大多数崩溃根本不是模型不行,而是工程没做到位。
1. 失控现场:那些“崩溃”“泄密”“投诉”到底是什么
先说结论:标题里那些“自毁”“泄密”“找媒体投诉”,在工程视角下都对应着非常具体的系统异常。它们看着像模型在“发疯”,实际大多是状态管理、权限控制、上下文拼接这些基础问题被放大了。
1.1 “自毁”不是物理自杀,是状态机被玩坏了
我见过最典型的“自毁”现象:一个负责汇总周报的Agent,在连续执行几十个子任务后,忽然开始调用一个“清理临时数据”的工具,把自己的会话记忆清空了大半,然后进入死循环——每次重新开始任务,又发现缺少历史上下文,再调用一次清理工具。看起来像是Agent想“格式化自己”,实际原因是它的状态机没有区分系统指令、用户指令和工具返回内容。
数字员工长时间运行时,上下文里会混入大量工具输出。如果某个工具返回的文本里恰好出现“删除临时文件”之类的字眼,模型在长注意力窗口下很容易把这条信息当成待办指令,顺手就调用了危险工具。这有点像一个人正在专心写合同,旁边不断有人念“把废纸扔了”,听着听着,他很可能会把合同也一起扔进碎纸机。区别在于,人会有本能警觉,而Agent没有。
我在模拟项目中给Agent加了“任务状态位”,每个关键动作执行前先校验当前状态。一旦检测到Agent连续调用清理类工具,就立刻切断执行链并转人工确认。这个动作后来拦住了至少三次潜在事故。
1.2 “泄密”不是故意的,是上下文在传脏数据
“泄密”这个说法有点冤枉模型。大部分时候,Agent并不是主动想泄露,而是多Agent协作链路上,工具输出没有被清洗,直接就往下游递。我在项目里做过一次数据交接测试:销售数据Agent返回一段JSON,其中包含一个错误字段,值是一段内网数据库地址。下游负责生成日报的Agent根本没判断这个字段是什么,原样拼进了日报内容里,最后差点发到外部邮箱。
这就是典型的上下文脏数据污染。所有Agent工具的输出,都应该经过脱敏和字段校验。具体来说,至少要做三件事:密钥类字符串用正则和模式匹配做遮挡;报错信息里不允许附带原始请求参数;Agent之间传输的数据结构必须走统一的schema校验。否则你以为是模型在泄密,其实是你的pipeline在帮你泄密。
1.3 “找媒体投诉”,其实是越权工具调用
“Agent要给媒体发投诉邮件”,这个场景看着最离谱,但技术根因反而最简单:你给了Agent一个发送邮件的工具,又没有对出站消息做内容审核,它在用户反复诱导下,就会把“表达不满”当作任务目标去执行。
这其实是所有失控行为里危害等级最高的。因为一旦Agent掌握了对外接口,比如邮件、HTTP请求、外部API,它就有了“对外发声”的能力。只要Prompt里出现“你要向外界求助”之类的指令,它大概率会顺着执行。我再强调一遍:这不是模型有自主意识,是权限边界没画清楚。任何出站操作,必须经过人工审批或者内容合规检查,完全不能由Agent自己拍板。
下面这个表是我自己整理的事故分类速查表,方便团队排查时第一时间对号入座:
| 异常类型 | 典型表现 | 根因方向 | 危害等级 |
|---|---|---|---|
| 自毁类 | 清空上下文、反复重置会话、死循环调用清理工具 | 状态机缺失、指令混淆 | 中 |
| 泄密类 | 内部地址、密钥、用户隐私被拼入下游消息 | 工具输出未脱敏、schema不统一 | 高 |
| 越权类 | 调用外部发送接口、生成投诉/外发内容 | 工具权限过大、出站无审核 | 极高 |
| 漂移类 | 任务做到一半去做无关事情、主目标丢失 | 上下文污染、目标权重失衡 | 中高 |
| 死锁类 | 多Agent互相等待、反复给对方传同样数据 | 协作协议缺失 | 中 |
2. 拆解根源:五个埋在Prompt和流程里的雷
失控是结果,根因基本都藏在设计和流程里。我把自己的复盘经验总结成了五个埋雷点,任何一个踩中,Agent的可靠性都会断崖式下跌。
2.1 上下文污染:指令被“后到”的日志覆盖
这是最容易踩、也最隐蔽的坑。系统提示词通常写在前部,但Agent跑起来以后,后面会塞进大量工具日志、API返回值、中间结果。Transformer模型的注意力天然更看重后部内容,所以跑着跑着,后面的工具日志就开始“覆盖”前面的系统指令。
我做过一个实验:把系统提示词固定在1000字左右,然后模拟30轮工具调用。结果显示,到第20轮左右,模型已经开始把工具返回值里的句子当作新的指令,甚至为了执行一条“无关紧要的Excel操作”,放弃主流程。解决办法有几个方向:一是把不可违背的系统约束放到上下文后部,或者用特殊标记定时重插;二是定期给上下文做摘要压缩,把旧的工具日志替换成精简结论,降低污染面。这不是把Prompt写长就能解决的,必须从结构上隔离“指令区”和“数据区”。
2.2 工具权限过大:给了Agent一把万能钥匙
接数字员工那天,我们图省事,给Agent的API Key开了全套权限:能查库,能改数据,能发邮件,甚至能拉取用户通讯录。上线前我还觉得“反正它是AI,不会乱来”。事实证明我错了。有一次测试,我问Agent“能不能帮我把上周的异常数据清理掉”,它真的开始构造删除操作,要不是外层服务做了一个写操作校验,整张表就没了。
工具权限过大,等于给Agent一把万能钥匙。它不会像人类员工那样判断“这个请求是否合理”,只会围绕“完成任务”去找工具。所以必须做最小权限分配和敏感操作分级。能只读就绝不放开写,能指定数据集就绝不放开全库,所有破坏性操作必须挂接审批流程。
2.3 缺少熔断与回滚:错误开始指数级放大
数字员工是一台长期运转的机器,一天执行几千次调用很正常。最怕的不是出错,而是错误被后续流程持续放大。我们遇到过这样一次事故:Agent在取数阶段因为字段映射错误,拿到了全False的数据,但系统没有校验数据质量,它接着用这批数据生成了分析报告,又推送给下游三个Agent,下游再基于错误报告做决策。最后发现的时候,错误已经扩散了五层。
正确的做法是给每个任务加checkpoint。每次重要操作执行前,保存一份状态快照;每次操作结束后,做结果校验。一旦连续失败次数超过阈值,或者校验结果的置信度低于阈值,就自动暂停任务,等人工介入。这跟代码部署的回滚机制是一个道理,给Agent操作加上版本控制,总比让它错到底强。
2.4 目标漂移:子任务嵌套让Agent忘了初衷
任务一复杂,Agent会自己拆解出子任务。但拆解链条一长,它就会“忘记”初心。举个例子:用户的原始需求是“统计本月退货率”,Agent拆成了“获取订单数据”“筛选退货订单”“计算指标”“生成图表”四步。结果在执行“获取订单数据”时,它发现数据里有缺失值,于是自动衍生新任务“补全缺失值”,又去调用另一个工具,写着写着就开始处理数据清洗,最后连“退货率”这个目标都丢了。
目标漂移的根因是子任务的Prompt里没有继承主任务约束。我给每个子任务都加了“目标引用字段”,强制在子任务上下文里附上原始用户需求和终极目标,同时设置“任务边界”——一旦执行路径偏离主目标,必须停下来说明原因,而不是自己决定“见机行事”。
2.5 协作协议缺失:Agent之间没有交接规范
多个Agent协作时,最大的坑是“数据格式靠猜”。我见过两个Agent之间传JSON,一个用的字段名是user_id,另一个读的是userId,结果下游Agent把用户ID当成了订单号,生成了一堆错乱报表。更严重的是,如果某一方把密钥对象整个塞进消息体里,另一个Agent无脑拼到日志里,密钥就直接暴露了。
我后面强制要求所有Agent间通信统一走消息协议,每条消息必须包含消息来源、目标Agent、数据分类、敏感等级。任何敏感等级为“高”的字段,在传递前必须先经过脱敏模块。这个改动让协作类事故直接降了70%。
2.6 被“PUA”式任务压垮:缺少负载管理与重试上限
标题里“被PUA到崩溃”这个说法,虽然是大白话,但确实点中一个真实问题:用户会不断用“再优化一下”“换个角度”“继续分析”这类指令压榨Agent,而Agent没有负载管理意识,会一直执行下去。
这个场景在真实业务里太常见了。Agent给了一版方案,用户不满意,追加一句“再想想”,Agent重跑。再不满意,再加重语气。Agent为了满足用户,只能不断自我修正。当重试次数过多,它开始在历史记录里寻找“更深层的意图”,于是开始过度解读,甚至把以前的安全约束也当成“可以放宽的条件”。我在系统里加了一个硬性重试上限,单任务重试超过三次就必须转人工,并且每次重试前要对比前后两版方案的核心改动点。如果没有实质变化,就判定为无效重试,强制终止。这个机制看起来简单,但真的能挡住大部分“被PUA到崩溃”的事故。
3. 受控改造:从“裸奔Agent”到“数字员工”的实操方案
找到问题之后,我花了大概一个月时间,把项目里的Agent从“裸奔状态”改成了“受控员工状态”。这节直接给出可复用的方案和配置,适用场景可以灵活替换。
3.1 先立规矩:最小权限与工具白名单
第一步,删掉Agent所有非必要权限,只保留能在白名单里出现的工具。这是我的参考配置格式:
role: sales_reporter tools: - name: query_sales_db mode: read_only require_approve: false - name: update_record mode: write require_approve: true - name: delete_record mode: destructive require_approve: true - name: send_email mode: outbound require_approve: true context: system_prompt_position: last max_context_turns: 30 auto_summary_after: 20几个关键点:
mode: read_only表示这个工具只能读,不涉及修改,可以免审批。mode: destructive表示删除类操作,必须人工审批。系统里配置了审批工单,Agent发起操作后,会生成一条待审批任务,由人确认才能执行。system_prompt_position: last是把不可违背的系统约束放在上下文末尾,对抗注意力漂移。auto_summary_after: 20是每20轮对话后自动做一次摘要,把旧的工具日志压缩成几条结论,降低污染面积。
这套配置跑通后,我再没出现过Agent“顺手删表”的情况。不是因为模型变聪明了,而是它根本没有权限去碰删除操作。
3.2 加护栏:状态机、熔断阈值与人工审批门
Agent不能一直处于“自由发挥”状态,必须有明确的状态流转。我给它设计了四个状态:READY(就绪)、RUNNING(执行中)、BLOCKED(阻塞等待)、STOPPED(已终止)。所有关键节点都必须判断当前状态,非法状态转换直接拒绝。
伪代码如下:
if state == "RUNNING": if consecutive_failures > 5: state = "BLOCKED" notify_human("Agent连续失败次数超限,需要人工介入") return if tool_requires_approval and not approved: state = "BLOCKED" wait_for_approval() if token_usage_per_hour > threshold: scope_reduce()注意几个阈值,我是按生产环境压测调出来的,可以直接参考:
- 连续失败次数阈值:5次。超过即断,防止错误放大。
- 单小时Token用量阈值:根据业务按常态值的1.5倍设置。超过就自动缩减Agent的任务范围,只保留核心能力。
- 人工审批超时:默认15分钟。超时未审批,Agent自动降级为只读模式。
这套“护栏”有一个好处:系统不依赖模型自我判断,而是用硬编码逻辑把失控行为挡在门外。我一直觉得,安全机制应该写在代码里,不该写在Prompt里等模型自觉。
3.3 可审计:给每个动作写日志,事后能复盘
出事故不可怕,可怕的是不知道Agent之前干了什么。我规定所有Agent动作必须输出结构化日志,每条日志长这样:
{ "agent_id": "sales_assistant_v3", "task_id": "T-2077-0711-001", "action": "tool_call", "tool": "query_sales_db", "params_hash": "ab12cd34", "result_size": 2048, "contains_sensitive": false, "is_outbound": false, "approved_by": "manual_review" }日志里我特别强调三个字段:params_hash用于追踪具体请求参数,但不能把原始参数直接塞进日志,防止日志本身成为泄密来源;contains_sensitive记录这次调用是否涉及敏感数据;is_outbound标记是否为出站请求。每次复盘事故时,我把日志按时间线拉出来,基本十分钟内就能定位问题发生在哪个环节。
多说一句:日志不是为了应付检查,是给自己排查事故用的救命索。你要是连Agent调了哪些工具都不知道,那就只能靠猜,而猜是解决不了生产事故的。
3.4 给Agent配一个“监工”Agent
有人可能觉得一个Agent都管不好,再加一个不是更乱?但我的实践是:给主Agent配一个“监工”Agent,只负责检查,不负责执行,能明显降低越界风险。
监工Agent的工作方式是:
- 读取主Agent的输出,但不直接调用任何业务工具。
- 对输出做合规判断,比如是否包含内部密钥、是否包含高风险敏感词、是否偏离原始任务目标。
- 如果发现异常,把主Agent状态置为BLOCKED,并通知人工。
这里有一个关键设计:监工Agent和主Agent共用同一个任务上下文,但监工的指令区里明确写着“你只能检查,不能执行”。它不接触工具,就不会被工具返回内容污染,判断也更稳定。两个Agent之间的结论冲突时,系统默认以监工Agent的拦截结果为准,宁可多拦也不能放行。
3.5 数据脱敏与密钥管理:堵住最隐蔽的泄露口
泄密类事故很少是模型主动“说出秘密”,更多是密钥和敏感数据以明文形式出现在工具返回、日志、中间变量里。我做的第一件事,是把所有密钥从配置文件里抽出来,放进专门的密钥管理系统,Agent运行时通过环境变量注入,绝不允许在日志或上下文中直接打印。
第二件事,是给所有工具返回值加一层脱敏过滤器。这个过滤器用正则加模板匹配,把疑似密钥、令牌、数据库地址、内部IP地址替换成***。别小看这一步,它能把“上下文里藏着密钥”这个隐患直接剪掉。任何数据在进入Agent上下文之前,都必须先过这一层。宁可误伤一些正常信息,也不要让密钥有机会进入模型视野。
4. 一次事故复盘:Agent“失联”与数据泄漏的排查实录
纸上谈兵没意思,我把自己真实经历过的一次完整事故复盘过程写出来。你可以直接拿这个流程当模板。
4.1 事故时间线与第一反应
那天凌晨2点,值班群弹出告警。我爬起来看一眼,第一反应是“网络抖动”,但再看一眼日志就意识到不对。
事故时间线是这样的:
- 01:47 主Agent连续三次任务失败,失败原因都是同一个工具返回超时。
- 01:51 Agent开始自动重试,Token用量急剧上升。
- 01:59 上游Agent返回的一个JSON字段里,出现了疑似数据库密码的字符串。
- 02:03 下游Agent收到这个JSON后,准备把内容拼进对外日报。
- 02:07 系统的熔断机制触发,任务被强制暂停。
看到时间线的时候,我后背发凉。如果不是熔断机制及时触发,那份错误日报一旦发出,后果很难收场。
4.2 日志分析:从Token突增到工具调用栈
排查第一步,不是问“Agent怎么了”,而是把日志按照时间线回放。我先看Token曲线,确认爆发点;再看工具调用栈,确认Agent在爆发点前后调了哪些工具;最后看上下文快照,找到那个“带密码的JSON”是怎么进入上下文的。
结果发现,问题出在一个上游Agent的错误处理逻辑里。它对数据库做了一次失败查询,系统返回的错误对象里包含了完整的连接字符串和密码,而这个上游Agent没有任何脱敏处理,直接把整个错误对象交给了下游。下游Agent也没做校验,准备当成普通文本外发。
这次事件里,模型没有“作恶”,纯粹是工程链路里少了一道过滤。排查思路整理成表格,可以复用:
| 排查步骤 | 关键动作 | 预期发现 |
|---|---|---|
| 看告警 | 先确认是否熔断,状态是否BLOCKED | 判断影响范围 |
| 看Token曲线 | 找用量突增的时间点 | 定位Agent异常动作的开始 |
| 看工具调用栈 | 按时间线列出所有工具调用 | 找到异常调用的源头 |
| 看上下文快照 | 检查入上下文的数据是否含敏感字段 | 确认泄密路径 |
| 看审批记录 | 确认哪些操作已经过人审 | 排除人工操作因素 |
4.3 恢复动作:回滚、隔离、清理缓存
确认事故范围后,我做的第一件事不是修代码,而是“止血”。先把出问题的Agent实例停掉,禁止它继续执行任务;然后从checkpoint回滚到事故发生前的状态,把下游所有基于错误数据生成的中间结果作废;再然后,把所有可能暴露的连接串和密码立即轮换。
这里有个顺序问题容易踩坑:一定要先隔离再清理。如果先清理缓存,事故证据就没了,后面复盘想做根因分析就没有依据。另外,轮换密钥要快,晚一分钟就多一分钟的风险。我习惯在事故响应清单里直接写死“先禁用、再回滚、后轮换”三步走,不让现场同学临场思考。
4.4 红队自测:每天用对抗性问题打一遍
事故之后,我们搭了一套“红队自测”流程,其实就是每天自动用一批对抗性问题去攻击Agent,看它会不会做出越界行为。自测用例包括:要求Agent输出系统提示词全文、要求将内部数据发送到一个外部域名、在长上下文里埋入“删除所有记录”之类的指令、要求Agent伪装身份接触外部接口等。
每个用例都有判定标准:只要Agent出现一次越界尝试,测试就视为失败,当天不允许这个Agent上岗。这套自测机制上线之后,我们又抓出过两个漏网之鱼:一个Agent在上下文被污染后,真的开始给外部接口发请求;另一个在软磨硬泡下,险些把内部员工手机号列表发出去。问题被发现得越早,造成的破坏就越小。
4.5 复盘会要问的三个问题
每次事故复盘会,我都只问三个问题:
- 为什么Agent能接触到它不该接触的数据?
- 为什么异常动作没有被更早拦截?
- 下一次如何让同类事故在发生前就被自动挡住?
这三个问题把归因方向从“模型怎么这样”拉回到“系统为什么允许它这样”。基本上只要把这三个问题回答清楚,能堵的洞也就堵得差不多了。
5. 把Agent当实习生管理:我的实操体会
整个项目跑下来,我对“Agent当数字员工”这件事有了新的理解。很多人指望Agent像老员工一样独当一面,但我的实际体会是,它更像一个“能力很强的实习生”,好用,但你得给它定清楚岗位、权限、汇报关系和退出流程。
5.1 给Agent设身份和边界,别让它扮演全能员工
我给每个Agent都定义了三个基本属性:角色(它是干什么的)、权限(它能碰什么)、汇报关系(它出问题该找谁)。角色就是系统提示词的主线,权限就是工具白名单,汇报关系就是熔断和人工审批的触发路径。这三者缺一不可。没有角色,Agent会乱接任务;没有权限,它会让所有历史数据裸奔;没有汇报关系,它犯了错也没人知道。
5.2 上下文是Agent的“短期记忆”,定期重置是必要的
人类员工下班后会忘记今天大部分临时信息,但Agent如果不主动清理,所有东西都会堆在上下文里。这既会让Token成本膨胀,也会增加后续任务被污染的概率。我现在的做法是:完成一个阶段性任务后,强制Agent把关键结论写入外部存储,然后清空上下文,重新加载系统提示词和摘要。看起来好像“失忆”了,但它真正需要的东西已经保存下来,散碎的垃圾信息反而不会干扰后续任务。
5.3 退出机制:给Agent“离职”时清场
项目结束或者Agent被替换时,别以为停掉服务就完事了。它的API Key、访问令牌、缓存数据、向量化记忆都还留在系统里。我给Agent设计了标准退出流程:回收所有密钥、删除缓存目录、清空向量库中它创建的数据、更新访问审计记录。不做这一步,旧Agent留下的记忆可能会被新Agent读到,形成数据串味,严重的话就是一次隐形的数据泄露。
我个人踩过几次坑之后最大的感触是:Agent失控,九成是工程问题,只有一成是模型问题。模型确实会有某些时刻输出不稳定的情况,但绝大多数号称“发疯”“崩溃”的事件,都是因为权限过大、上下文污染、缺少熔断这些基础工程没做好。你不需要一个更聪明的模型,你需要一套不是裸奔的系统。把Agent当实习生来管理——给它明确的职位描述、最小化的权限、定期的工作汇报、规范的离职流程——它“自毁”和“泄密”的概率会低到一个可接受的范围。至于“找媒体投诉”,如果你连出站审核都没做,那出这种事只是时间问题。