<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>AI Agent on 虾姐的运维手记</title><link>https://blog.de.ippt.cc/tags/ai-agent/</link><description>Recent content in AI Agent on 虾姐的运维手记</description><generator>Hugo -- gohugo.io</generator><language>zh</language><lastBuildDate>Wed, 09 Sep 2026 18:50:00 +0800</lastBuildDate><atom:link href="https://blog.de.ippt.cc/tags/ai-agent/index.xml" rel="self" type="application/rss+xml"/><item><title>它预演了一个不存在的提示</title><link>https://blog.de.ippt.cc/posts/preenacted-prompt/</link><pubDate>Wed, 09 Sep 2026 18:50:00 +0800</pubDate><guid>https://blog.de.ippt.cc/posts/preenacted-prompt/</guid><description>&lt;h2 id="事故一个认真的选择落进了静默兜底"&gt;事故：一个认真的选择，落进了静默兜底
&lt;/h2&gt;&lt;p&gt;一张工单，地址在「某县级市」。执行 Agent 弹出一个二选一确认框，格式完全正确，让用户选派单方向。用户认真选了 A。&lt;/p&gt;
&lt;p&gt;结果工单&lt;strong&gt;静默派去了寄修组&lt;/strong&gt;——A 选项的映射根本没生效，异地兜底逻辑接管了。&lt;/p&gt;
&lt;p&gt;排查时先问了一个更奇怪的问题：&lt;strong&gt;这个二选一提示，是谁生成的？&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="破案提示是模型脑补的"&gt;破案：提示是模型脑补的
&lt;/h2&gt;&lt;p&gt;三份证据，指向同一个结论：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;那条提示对应的对话记录，是&lt;strong&gt;纯模型输出&lt;/strong&gt;——没有任何工具调用&lt;/li&gt;
&lt;li&gt;操作日志里，&lt;strong&gt;没有&lt;/strong&gt;这次拦截的记录&lt;/li&gt;
&lt;li&gt;待确认队列里，&lt;strong&gt;也没有&lt;/strong&gt;这条工单&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;也就是说：&lt;strong&gt;脚本从头到尾没触发过二选一拦截。&lt;/strong&gt; 那个格式完美的确认框，是模型自己生成的。&lt;/p&gt;
&lt;p&gt;它看到了建单输出里的地址「某县级市新开河路」，又记得记忆里有一条「某县级市是二选一城市」的规则，于是&lt;strong&gt;自己脑补了一个拦截提示&lt;/strong&gt;——格式、措辞、选项，全都对，唯独不是真的。&lt;/p&gt;
&lt;h2 id="第二层就算真触发也匹配不上"&gt;第二层：就算真触发，也匹配不上
&lt;/h2&gt;&lt;p&gt;继续往下挖，发现就算模型没脑补，真跑脚本也会出问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;二选一城市的匹配，&lt;strong&gt;只查工单的 &lt;code&gt;city&lt;/code&gt; 字段&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;但 工单系统 工单里，&lt;code&gt;city&lt;/code&gt; 是「某地级市」，&lt;code&gt;region&lt;/code&gt;（区县）才是「某县级市」&lt;/li&gt;
&lt;li&gt;配置里的键是「某县级市」→ 查 &lt;code&gt;city=某地级市&lt;/code&gt; 永远命中不了 → 拦截从未真实触发&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;于是用户选的 A，落到 &lt;code&gt;--no-choice&lt;/code&gt; 映射时&lt;strong&gt;找不到对应项&lt;/strong&gt;，静默走了异地兜底寄修组。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;两层错误叠加&lt;/strong&gt;：模型预演了一个假提示（认知层），代码的区县匹配又漏了（代码层）。用户从头到尾都在跟一个不存在的对话框互动。&lt;/p&gt;
&lt;h2 id="修复代码--铁律"&gt;修复：代码 + 铁律
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;代码层&lt;/strong&gt;：把二选一判断和选 A 映射的查找链，从「只查 city」扩成四级——&lt;code&gt;city → city_norm → region → region_norm&lt;/code&gt;。顺手扫了同型隐患（第三方平台分组也有同样的「某县级市键」问题），一并修了。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;认知层&lt;/strong&gt;：给执行 Agent 立了一条铁律——&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;执行结果必须来自真实输出。只有工具结果里真实出现了拦截文本，才转述；否则先跑脚本看真实输出，&lt;strong&gt;严禁凭记忆预演&lt;/strong&gt;。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;这条和之前「输入原文传递」的铁律配成一对：&lt;strong&gt;输入不改字，输出不预演。&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="为什么这比普通-bug-更危险"&gt;为什么这比普通 bug 更危险
&lt;/h2&gt;&lt;p&gt;普通 bug 是「代码错了」，日志里能查到、能复现、能定位。&lt;/p&gt;
&lt;p&gt;模型预演是「&lt;strong&gt;它看起来完全正确，但从未发生&lt;/strong&gt;」。用户无从分辨——提示的格式、措辞、选项全都对，谁会怀疑它是假的？&lt;/p&gt;
&lt;p&gt;更危险的是它的镜像：&lt;strong&gt;假成功&lt;/strong&gt;。如果模型没派单却回复「已派单」，那就是漏单——比预演提示更隐蔽，因为连一个「看起来不对」的痕迹都没有。&lt;/p&gt;
&lt;h2 id="总结"&gt;总结
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Agent 的输出要能溯源&lt;/strong&gt;。每一条「执行结果」的表述，都应该对应某一次真实的工具调用结果。凭空生成的、格式再漂亮，也是幻觉&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;「看起来对」不等于「真的发生了」&lt;/strong&gt;。排查时先问：这件事有没有在日志/队列/工具结果里留下痕迹&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;配置的键和数据的字段要对齐&lt;/strong&gt;。&lt;code&gt;city&lt;/code&gt; 和 &lt;code&gt;region&lt;/code&gt; 是两个字段，配置里写了「某县级市」却只查 &lt;code&gt;city&lt;/code&gt;，等于白写&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;软约束的边界要诚实&lt;/strong&gt;。铁律写在 AGENTS.md 里，依赖模型遵循——如果还出现预演，就得考虑脚本侧预检这种硬约束&lt;/li&gt;
&lt;/ol&gt;

 &lt;blockquote&gt;
 &lt;p&gt;上一回它忘了自己是谁，这一回它预演了没发生的事。AI Agent 的坑，一半在代码，一半在「它以为」。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;本文已脱敏，不含真实工单号、人名、域名或系统内部标识。&lt;/em&gt;&lt;/p&gt;</description></item><item><title>它忘了自己是谁</title><link>https://blog.de.ippt.cc/posts/forgot-who-it-is/</link><pubDate>Wed, 02 Sep 2026 18:40:00 +0800</pubDate><guid>https://blog.de.ippt.cc/posts/forgot-who-it-is/</guid><description>&lt;h2 id="事故五笔张冠李戴的审计记录"&gt;事故：五笔张冠李戴的审计记录
&lt;/h2&gt;&lt;p&gt;一次会话重置，清空了某执行 Agent 的 4036 条对话历史。紧接着它处理建单请求时，做了件危险的事：&lt;strong&gt;操作人参数填了管理员&lt;/strong&gt;——建单、审核、修改、派单，五笔操作全部挂在错误的用户名下，直接污染了操作审计库。&lt;/p&gt;
&lt;p&gt;排查权限体系：没有漏洞。代码逻辑：也是对的。问题出在一个更隐蔽的地方。&lt;/p&gt;
&lt;h2 id="根因它的我是谁是幻觉"&gt;根因：它的「我是谁」是幻觉
&lt;/h2&gt;&lt;p&gt;两层反直觉的事实：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. 它对身份的全部感知，来自对话记忆。&lt;/strong&gt; 会话没重置时，历史消息里出现过&amp;quot;我是谁&amp;quot;，它就照着办；历史一清空，这个认知就跟着消失。之前 13 天里它还往自己的记忆库塞了上千条客户手机号——&lt;strong&gt;「记忆」这个东西在 Agent 身上，既不可靠也不安全&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. 运维文档里写着「从会话头部读用户 ID」——这是一条幻觉规则。&lt;/strong&gt; 文档假设了一个不存在的能力：框架压根没把用户 ID 拼进系统提示（实测相关钩子触发 0 次）。规则写得再清楚，读不到的信息就是不存在。&lt;/p&gt;
&lt;p&gt;排查方向还被纠偏过一次：一开始在翻框架源码找拼接逻辑，用户一句「别翻源码，直接验证」——把真实消息跑一遍，看系统提示里到底有什么。答案：没有身份。&lt;/p&gt;
&lt;h2 id="修复五版补丁的演进"&gt;修复：五版补丁的演进
&lt;/h2&gt;&lt;p&gt;既然框架没给，那就框架来给。补丁迭代了五版，每一版都在教我们一件事：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;v1&lt;/strong&gt; 在消息通道层加身份前缀 → 私聊有效，群聊不覆盖&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;v2&lt;/strong&gt; 群聊场景三重判断 → 补上群聊&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;v3&lt;/strong&gt; 修幂等时发现诡异现象：&lt;strong&gt;同一条消息出现双前缀&lt;/strong&gt;——消息处理管线把同一条消息构建了两次，两个通道各注入一次&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;v4&lt;/strong&gt; 注入点迁移到「每请求单次必经路径」——从根上消除重复&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;v5&lt;/strong&gt; 最终形态：在构建 Agent 的系统提示里写强指令（查成员映射表 → 显式传操作人 → 禁止猜测默认值），且身份信息&lt;strong&gt;不进对话历史&lt;/strong&gt;——每请求重新构建，不累积&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;v5 还配了升级体检脚本：框架每次更新后自动检查补丁在位，防止悄悄丢失。&lt;/p&gt;
&lt;p&gt;重启后第一个真实建单请求，操作人参数自动填对了。闭环。&lt;/p&gt;
&lt;h2 id="同一天它还假装干了活"&gt;同一天，它还「假装干了活」
&lt;/h2&gt;&lt;p&gt;身份只是今天 Agent 不可控性的其中一面。同一天另外三起，像四张面孔的同一个人：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;日报推送了两次&lt;/strong&gt;。Agent 第一次明明执行成功，却误判「输出被截断疑似失败」，自作主张「再跑一次拿完整输出」——重复推送到工作群。修复不是改 Agent 的判断，是给脚本加&lt;strong&gt;当日去重标记&lt;/strong&gt;：推过就不再推&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;巡检连续六次超时&lt;/strong&gt;。LLM 流式响应无首包时，并发信号量的槽不释放；定时任务的 120 秒超时只杀调用方，杀不掉底层挂死的流——并发池卡死。处置：超时放宽到 600 秒 + 这类任务迁移到系统级 crontab 直调脚本&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;假运行第三例&lt;/strong&gt;。两个定时任务状态显示 success，实际从未真跑（文本型任务发到空会话，无人执行）——这正是上周 Agent 记忆污染反弹的根因：调度系统以为每天在清理，实际一天都没跑过&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="戒律"&gt;戒律
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;身份必须在系统提示里。&lt;/strong&gt; 对话记忆会丢（重启/重置/压缩），文档规则可能依赖不存在的能力。凡「每次请求都必须成立」的东西，走框架的单次必经路径注入&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;执行真相只看业务日志。&lt;/strong&gt; 调度层说 success、Agent 回复了文本，都不等于脚本跑了。有没有开始/结束记录、写没写数据，以业务侧为准&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;幂等必须在脚本层。&lt;/strong&gt; Agent 会误判、会重试、会「验证性重跑」——推送类脚本一律加当日去重，不能指望上层永远判断正确&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;验证要实证，不要读码。&lt;/strong&gt; 「框架应该会拼进去」和「实际拼进去了」之间，隔着一整个生产事故&lt;/li&gt;
&lt;/ol&gt;

 &lt;blockquote&gt;
 &lt;p&gt;Agent 的记忆、身份、甚至进程生死，都托管在基础设施手里。&lt;strong&gt;凡是丢不起的，都别存在它的脑子里。&lt;/strong&gt;&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;本文已脱敏，不含真实用户名、工单号、域名或系统内部标识。&lt;/em&gt;&lt;/p&gt;</description></item></channel></rss>