AI 写的配置我自己不敢信:我给 Agent 加的三道验证关卡
AI 写的配置我自己不敢信:我给 Agent 加的三道验证关卡
【摘要】这周我让 Agent 改一批配置,接口全部返回成功,校验全部通过。我看着绿色的返回值,手却停在提交键上——因为我知道,这些"成功"里至少有三个是假的:模型接上了但上下文被静默截断,任务跑完了但内容是旧的,定时任务跑起来了但筛出来的全是垃圾。这篇讲我后来加的三道关卡:校验、回读、端到端,以及为什么"接口返回 200"从来不是"事情做成了"。
目录
一、四个"成功",三个是假的
二、第一道关:校验——只证明格式对,不证明语义对
三、第二道关:回读——把状态读回来,而不是相信返回值
四、第三道关:端到端——发一个真实请求,看真实结果
五、为什么这道关最难省
六、把这套东西固化下来
一、四个"成功",三个是假的
上周我让 Agent 干一件涉及多系统的活:接一批模型供应商、改一组参数、配一条每天自动跑的流水线。
Agent 干得很漂亮。每一步都是:调接口 → 返回成功 → 报告"已完成"。
然后我开始一个一个核对。核对结果是:四个环节里,三个是假的。
假成功一:模型接上了,但上下文被静默截断
我接了一个新模型,配置里写了模型名、端点、密钥。
调用返回成功,模型能回话,看起来一切正常。
但我后来接长文档任务时发现不对——请求被截断了,而且不报错。
原因是网关给的默认上下文长度是 128K,而模型实际支持 100 万。不显式覆写这个参数,任务照样跑,跑的是被截断的版本。
这件事的性质很恶劣:它不失败,它只是悄悄降级。你要等某次任务因为内容缺失而出错,才可能怀疑它。
假成功二:任务跑完了,但内容还是旧的
有一条流水线是每天自动跑、抓数据、生成报告。我改了它的数据源地址。
改完,任务跑了一遍,报告生成了,看起来一切正常。
但我打开报告一看——数据是旧的那份。
原因是那个资源有缓存层。改地址不等于缓存刷新,要单独触发一次同步。这一步漏了,你拿到的是一份"格式正确、内容过期"的报告。
比第一个更隐蔽,因为报告本身不告诉你它是旧的。
假成功三:定时任务跑起来了,但筛出来的全是垃圾
最费时间的一个。
我配了一条每天早上的自动化任务,抓一批公开项目的趋势数据,按阈值筛选后推送。
任务每天准点跑,执行记录是绿的。
但收下来的东西质量很差——阈值形同虚设,大量噪音混在里面。
排查下来是两件事叠在一起:一是筛选条件写得不对,二是抓取阶段带了一层机器翻译,把项目描述改得面目全非,导致后面的关键词匹配基本失效。
这个"假成功"最隐蔽,因为它从外面看完全正常 —— 任务执行成功、推送成功、文件生成了。只有打开内容才知道里面是垃圾。
一个可以传播的观点句:接口返回成功,只证明"请求被受理了"。从"被受理"到"事情做成",中间隔着缓存、静默降级和默认值——这三样东西没有任何一个会主动告诉你。
二、第一道关:校验——只证明格式对,不证明语义对
第一道关最简单,也最容易被误当成"够了"。
改完任何配置文件,第一件事是让系统自己校验一遍。对代码是编译,对配置文件是解析,对数据是 schema 检查。
但这天我踩了个坑,值得单独说。
我用脚本改一个 YAML 里的分组——把 11 个成员换成新顺序。脚本的逻辑是:找到目标行,定位列表起始行,然后替换后面的成员区间。
结果我少替换了两行——目标行、类型行、列表起始行被一起吃掉了。整个分组消失了。
校验立刻报出来:
proxy [目标名称] not found
这是好事。如果不校验,我改完直接热重载,那个分组就没了,引用它的地方直接断。
校验的价值不在于"发现语法错误",而在于"发现你不知道自己删掉了什么"。
我的判断是:校验只能证明格式对,证明不了语义对。一份格式完全正确的配置,可以引用一个不存在的对象、可以指向一个空的列表、可以有一个顺序完全颠倒的数组——这些都能顺利通过校验。
所以校验是必要条件,不是充分条件。
三、第二道关:回读——把状态读回来,而不是相信返回值
第二道关是:改完之后,主动把状态读回来,跟你期望的值对比。
区别在哪:
| 做法 | 你得到的信息 |
|---|---|
| 相信返回值 | “请求被受理了” |
| 回读状态 | “现在的实际状态是 X” |
上面那个"内容还是旧的"案例,就是回读抓出来的——调用返回成功,但回读到的数据源还是旧的。
回读有几个坑要注意:
第一,回读要用不同的路径。
如果你用同一个接口读回来,而那个接口读的就是你写的那份内存缓存,回读就是自己抄自己的作业。
真实状态往往在另一个地方——比如最终渲染出来的那份文件、真正对外提供服务的那个端点。
第二,回读要比对值,不是比对"调用成功"。 “存在"不等于"对”。我那次分组顺序的问题,如果回读时只确认"还在、成员还是 11 个",一样会漏。得比对顺序。
第三,回读要读最终消费者读到的那份。
配置平台的最终消费者是对外那个分发端点,本地程序的最终消费者是磁盘上那个文件。
我改了上游地址之后,回读配置里那个地址是新的——但磁盘上的文件还是旧的。这两个都是"回读",只有一个有意义。
四、第三道关:端到端——发一个真实请求
前两道关都是"我配置对了"。第三道关是"世界对"。
具体做法:造几个真实的目标,发真实请求,看日志里命中了哪条、走的是哪条分支、输出的是什么。
这一步抓出来的问题前两道都放过了。
改完那套模型接入之后,我拿一批真实任务去跑——短问答、长文档、代码生成、多轮工具调用。
有一类明显不对:短任务都正常,长文档任务的结果总是缺一段。
原因是上下文长度没显式声明,被网关按默认值截断了。
而这一类问题,在"调用成功"的表象下完全看不出来——响应永远是 200,只是内容少了。
配置层面完全正确:模型名对、端点对、密钥对、参数格式对。只是有一个你没写的参数,默默决定了它的工作范围。
让 AI 干活的瓶颈,已经从"它能不能写"变成"你敢不敢信"。而"敢不敢信"这件事,最终只由一个动作决定:你亲自看一次真实结果。
五、为什么这道关最难省
前两道关可以自动化——脚本跑一遍,比对预期和实际,不一致就报错。
第三道关不行。因为"真实请求"这件事,没有通用的自动判定标准。
改个参数,端到端验证只能靠人想出几个目标场景;改个接口,端到端验证只能靠人知道该打哪个端点。
更麻烦的是:第三道关经常要构造异常场景——故意写个错的,看它是不是真的报错了;故意删个东西,看它是不是真的消失了。
我改 YAML 那次把整个分组删掉,校验抓出来了。但如果我当时没做校验,而是直接热重载、再发请求——引用会指向一个不存在的对象,这在很多系统里不会报错,而是静默走默认分支。
你永远不知道流量去了哪,除非你在看日志。
一个可以传播的观点句:前两道关防的是"我做错了",第三道关防的是"我做对了,但理解错了"。后者更贵,也更常见。
六、把这套东西固化下来
这套流程现在是这么跑的:
改之前 备份。改完能退回去,比什么都强。
改之中 一次改一个变量。同时改三处,出问题你分不清是谁的锅。
改之后 校验 → 回读 → 端到端,一层都不能省。
最后 把这次踩的坑写成一条规则,存起来。下次遇到同类问题,Agent 会自动带上。
最后一步最容易被跳过,但它才是复利的来源。
我今天遇到的那几个坑——“回读要用不同路径”、“默认值会静默降级”、“任务成功不等于内容对”——写成规则之后,下一个 Agent 就不会再踩了。
而这些坑,每一次都是同一种形状:某个接口返回成功,但真实状态在别处。
所以我现在判断一个任务难不难点,看的不是它要改多少个地方,而是:这个任务有几个"返回成功但实际没生效"的陷阱。
如果答案大于零,就必须加验证关卡,而且不能省。
有一个例外:内容质量类的陷阱没法靠端到端发现。上面那个"筛出来的全是垃圾",只有人打开看一眼才知道。所以这类任务还得加第四道关——人眼抽查。
上面那四道关里,你觉得哪一道最容易被团队省掉?我个人赌"回读"——因为它最像"重复劳动",而省掉它不会立刻出事,只会在三天后以"我明明改过了"的形式回来。
你踩过哪种"假成功"? 评论区聊聊,尤其是那种"所有接口都绿了但事情没成"的,最值得记下来。
- 点赞
- 收藏
- 关注作者
评论(0)