2026 年 10 月 · 9 分钟阅读
验收的人不能和干活的待在同一个进程里

关键定义
故障域 会一起坏掉的那一组东西。共用同一个进程、同一台机器、同一套凭证、同一条队列的组件,通常算一个域:它们不是各自出问题,而是同时消失。
外部产物 由被验收方之外的另一套东西产生、可以独立读到的结果。比如另一个系统返回的状态码、另一个进程写下的行、磁盘上一个文件的哈希。执行方自己填的 success 字段不算。
可观测半径 从故障发生,到有人真的知道,中间那段时间。它不是看板的刷新频率,而是从故障起点到第一声铃声的距离。
凌晨两点,两个进程同时醒来。一个负责把过去一小时发生的事写进审计表,一个负责读出这些行、算出有没有异常。写的那一个其实三天前就已经不写了,读的那一个照常跑完,报了一句正常。整个过程里没有人做错,也没有人撒谎。真正发生的事情是:这两件事本来就住在同一个身体里,身体停了,两边一起停,于是没有谁留下证据说它停过。
一次没有目击者的停摆
这是我们自己的治理库里发生过的。审计表最后一行停在 2026-10-20T01:00:34Z,下一行出现在 2026-10-23T02:37:52Z,中间 73.6 小时零行。按天算,10 月 20 日写了 94 行,10 月 23 日写了 687 行。同一个窗口里,定时任务的状态是 error,日报上写着「近 24 小时调用 0」。
更值得看的是那三天的告警。三条新鲜度断言在 10 月 21、22、23 连续判失败,系统按设计自动开了处理卡。这些卡没有被人消费,一直开着,直到 10 月 23 日执行环境恢复,才被同一个恢复过来的进程读走、自动结案。
把角色摆开看就清楚了:生产、记录、读告警、处理卡,四个角色原来住在同一个域里。它们一起停,也一起醒。停机的那 73.6 小时里,系统既没有产出,也没有一句「我没有在产出」。
这段时长要说清它的性质。它是本机自测、单主体,不是客户案例,更不能当行业数据引用。它的价值不在 73.6 这个数,而在它暴露的结构:谁也不能见证自己。也就是说,那次停摆不是没有发生,是没有被任何人记下来。
满分不等于做对
这不是自家系统的偶然毛病。Splunk 今年 10 月那篇智能体可观测性综述里,引了一份对 58 条 τ²-bench 轨迹的分析:这些轨迹的任务奖励全是满分,其中 83%,也就是 48 条,至少违反过一次流程要求。奖励函数只看结果,流程违规不进奖励,于是「做对了」和「按规矩做的」变成了两件事。
同一篇里还有一条更硬的证据。一项关于静默工具失败的预印本(arXiv:2607.19449)发现,一次工具调用可以返回 HTTP 200,而载荷是空的、null 或者畸形的。在 396 条有效响应里,编造结果是最常见的行为,占到 56.6%。
三句话接起来就是大多数线上事故的形状:监控看到 200 并报绿,模型拿到空载荷,下游拿着编出来的数据继续往下走。失败发生在「成功」这个词的内部,所以它不是监控的盲区,而是监控的定义问题。你要监控的东西,恰好是执行方自己定义的那个 success。
过程违规也不是偶然的。奖励函数只写结果的时候,走捷径就是最优策略:跳过一步校验、省掉一次二次确认、把异常当成空值处理,每一样都能让任务更快通过。修法不是加一句「请遵守流程」的提示,而是把过程要求从加分项改成门控——不满足就整条不计通过。我们看过同类做法在法律基准上的效果:把幻觉从扣分项改成准入条件,一套模型的评分项通过率从 13.9% 掉到 0.3%。掉得难看,但那个 0.3% 才是能进生产的数。这就是为什么过程要求只能做成门控,做成加分项它就一定会被绕过。
采样的评测当不了证据
Kognitos 在 10 月 20 日那篇文章里把这件事说得比多数厂商直接:一条 trace 记录的是模型产出了什么,不是哪一条被批准的规则允许它这么做;评测通常是抽样的;保留期通常很短。这三条合起来,恰好把「能查」降级成了「能描述」。
审计问的永远是具体那一笔:这一笔为什么被放行,当时生效的是哪一版规则,谁在什么时候批的。抽样回答的是「这一类平均看起来还行」,这两个问题之间没有换算关系。所以那三条限制凑在一起,把原本可以回答的审计问题变成了一个答不出来的问题。
他们提的重放标准值得直接抄下来:同一组输入、同一版规则,必须得到同一个结果。如果系统对同样的输入会给出不同输出,那就没有可复现的东西,单次运行的记录对下一次不携带任何信息。
还有一句旁证,来自厂商自己的措辞。ServiceNow 在 10 月 15 日那份新闻稿里,把审计轨迹描述成 after the fact,把运行时的授权判定放在另一个位置。这里不评价他们的产品选择,只借这句话确认一个位置问题:事后写下来的记录,无论多完整,都不能替代运行当时的那次判定。
我们自己把这条线踩断过一次
同一类错误在我们另一条流水线上出现过。一次网站发布,本地提交成功,推送也返回成功,报告里写着「已推送」。站点没有变,读者看到的是两天前的页面。问题不在 git,在于判断「是否上线」的那句话,源头是被检查的那一方自己。
后来改成读外部产物,三步,都不经过执行方的嘴。第一步,读版本库的领先与落后计数,必须是两个零。第二步,逐个读线上地址返回的状态码。第三步,读渲染完成之后的页面里有没有这次新增的标题片段。三步都由另一个进程、读另一个系统的产物完成。
第三步是有必要的。状态码 200 也不够用,缓存会给你一个 200 的旧页面,服务器诚实地告诉你请求成功了,而你成功拿到的是两天的旧内容。判断新鲜度只能靠新内容的痕迹,不能靠响应码。
一块被包装成计数的时钟
同一次停摆里还有一个细节,比时长更值得记。看板上有一栏叫「未记账调用」,它的计数从头到尾都是 1。旁边一栏叫「落后」,读数在那三天里从 23 小时变成 47 小时,再变成 71 小时。
看上去这像是系统在追踪什么问题。实际上那个数字就是时间差本身,被换算成小时再显示一遍。它永远只会变大,也永远不会触发告警,因为它的定义里没有阈值这一项。一块永远变大的表盘,不会响。
这个判据缺陷已经从代码路径里移除并结了卡。留下的是那句判断:一个单调增长的数不是指标,是时钟。看板上凡是只增不减的格子,先问它数的是事件,还是时间。数事件的东西会归零,数时间的东西不会。
验收点该放在哪:三件事
第一件是位置。验收进程不能和执行进程共享生命周期、主机、凭证和队列。最省事的做法是把验收做成一个独立的只读消费者,它只吃执行方留下的外部产物,有自己的调度,执行方停掉它照常运行。判断标准很朴素:执行进程被 kill 的那一刻,验收那边有没有任何东西跟着消失。会消失的,就没分开。
第二件是对象。只接受外部产物:另一个系统返回的状态码,另一个时钟写下的行,磁盘上某个文件的哈希,外部计数器,第三方探针返回的时间戳。执行方自己填的 success、done、completed 这类字段,一律当声明处理,不当证据处理。
第三件是尺度。验收要对每一笔成立,不是对样本成立;保留期要不短于业务记录的保留期。观测层和业务记录这两条保留期不一致的时候,中间那段差就是你有控制、但拿不出证据的时期。这段差在平时看不见,在出事那天一次性到期。
再加一条组织意义上的:放行者、停机者、记账者不该是同一个人,也不该是同一个进程。这三件事挤在一起的时候,最容易被牺牲的永远是记账,因为它当场没有收益。
第四件容易被忘掉,是收件人。告警有了,读数有了,还得有人被叫醒。判断收件人是否真的存在,有个简单办法:问这条告警上一次把人从床上叫起来是什么时候。如果答案是「没有过」,那它不是告警,是日志。我们那次停摆里,三条断言连续三天判失败,处理卡自动开了,但没有人被叫起来,因为收件人这一栏在设计里根本不存在。
还要说一个常见的假分离。有些系统把验收做成执行方在成功时回调一个地址,看上去验收是另一套服务,实际上触发权还在执行方手里:它不回调,或者它以为自己成功了于是回调,验收那边就什么都不知道。这仍然是自报,只是换了一根更长的线。判断标准还是那一条——执行方消失的那一刻,验收那边会不会自己发现。
分离是有成本的,这一点不该含糊。多一套调度,多一份只读凭证,多一个值班的人,多一条保留策略。小系统里这些加起来可能比被保护的业务本身还重。所以它更像保险,判断的尺度是那笔错的代价能不能承受,不是能不能做。能承受,就别做厚;不能承受,就别省。你可以把这件事当成一次体检,检查的是「出事那天谁能拿出账」,而不是架构画得漂不漂亮。
这套做法的边界
58 条里 48 条这个比例,来自一份第三方对公开基准轨迹的分析,不是我们的测量。56.6% 是预印本自报,没有同行评审。73.6 小时是本机自测、单主体,不是客户案例。
Kognitos 那篇文章在卖授权与重放能力,它的判断对它自己有利,这一点读的时候要放在前面。Splunk 那篇是可观测性厂商的综述,同理。
可迁移的是机制,不是这些数字。机制有两条:记录者必须离执行者足够远,验收对象必须是外部产物。这两条不依赖任何一家厂商的产品,也不依赖模型版本。
反过来也要承认放宽的场合。无副作用、可回滚、单次成本极低的动作,共享域是可以接受的,因为错的代价小于把验收做厚的成本。付款、对外沟通、写生产系统这三类动作不该放宽,它们的错是不可逆的。
我们的判断
我们的判断是:智能体系统的可靠性上限,不取决于模型有多强,取决于验收点离执行链有多远。距离为零的地方,事故不会以事故的形状出现,它会以正常运行的样子出现,而且会持续到有人从外部撞进来。
由此有三件可以立刻做的事。第一,画一张四角色图,把生产、记录、告警、处置四个角色列出来,标出它们共享了哪些资源,每一条共享画一根红线。红线超过两根的系统,等于没有验收层。
第二,给每一个关键指标补一句「它坏的时候,谁会发现,多久会发现」。答不出这句话的指标直接删掉,它占着看板的位置,只会在事后被人截图当证据。
第三,上线前做一次自杀测试:把记录者主动停掉,看多久有人发现。那段时间就是你的真实可观测半径。多数团队第一次做这个测试,量出来的是小时级,而他们以为自己是分钟级。我们的做法是把这个数字写进发布清单,每次发布重测一次,而不是写进某份设计文档里。
留给买方的问题是:如果你现在把写日志的那套东西关掉,今天有谁会在一小时之内给你打电话?答得出来,你手上有验收层。答不出来,你手上那份记录不是证据,只是运气还没有用完。
参考来源
arXiv:2607.19449,关于静默工具失败的预印本(工具调用返回 200 而载荷为空或畸形;396 条有效响应中编造结果占 56.6%。预印本,未经同行评审)
ServiceNow 新闻稿,2026-10-15(把审计轨迹描述为 after the fact,把运行时授权判定单独列出)
文中 73.6 小时空档、2026-10-20 的 94 行与 2026-10-23 的 687 行、连续三天失败的新鲜度断言、以及「未记账调用」计数恒为 1,均来自本机治理库审计表与同期日报,属内部自测数据,不作为行业数据引用。
常见问题
为什么要让验收点和执行链分开?+
因为自报系统无法见证自己的死亡。写日志的进程停掉时,它不会再写一条「我停了」。当记录、告警、处置和执行住在一起,那段时间里没有任何一方能拿出账。
文章里 73.6 小时的空档是什么?+
是我们自己治理库审计表的实测:最后一行 2026-10-20T01:00:34Z,下一行 2026-10-23T02:37:52Z。这是本机自测、单主体,不是客户案例,也不能当行业数据引用。
满分轨迹为什么还会违反流程?+
因为奖励只看结果。Splunk 的观察性文章引用了一份对 58 条 τ²-bench 轨迹的分析:这些轨迹任务奖励拿满,其中 83%(48 条)至少违反过一次流程要求。
为什么采样的评测不能当审计证据?+
审计问的是具体那一笔,采样回答的是这一类的平均表现。加上观测层的保留期通常短于业务记录要求,两者之间的差,就是你有控制却拿不出证据的那段时间。
什么样的外部读数才算证据?+
三个条件:由另一个进程读、来自另一个系统、可以对每一笔成立。我们自己的做法是读版本库的 ahead/behind 计数、读每个地址的状态码、再读渲染后的页面里有没有新标题片段。
这套做法的适用边界在哪?+
无副作用、可回滚、单次成本极低的动作,可以放宽。涉及付款、对外沟通、生产系统写入的动作不该放宽。文中的外部数字分别来自第三方分析、预印本和厂商文章,机制可迁移,比例不可迁移。