agent 跑久了,谁都会撞上同一个问题:一份结果摆在那儿,敢不敢直接用。这事被讲得很玄,答案要么劝你“再加一层 review”,要么让你“等更强的模型”。我最近看到一篇文章,把这件事建模成一根带类型的溯源向量,第一眼觉得学术味冲鼻子,拿笔画了一下才发现,它说的是件特别朴素的事:把“这个结果经历过什么降级”变成跟着结果走的数据,下游自己决定信多少。
先坦白,我第一版画错了。当时我以为信任长这样:
结果 ──► [信任分 0.87] ──► 用 / 不用
一个分,干净利落。动手一抠就漏风:0.87 是哪来的?数据是三小时前的缓存?主模型超时、弱模型兜底生成的?还是历史被压缩过、来路已经说不清了?同样一个 0.87,这几种情况的险完全不是一回事。单一分数最大的毛病是它把不同维度压扁了,陈旧数据和弱模型兜底能算出同一个分,但一个该刷数据、一个该换模型,处置方向都不一样。
重来。画成多根轴:
┌─ 数据新鲜度 0.6 ── 读的是 3 小时前的缓存
结果 ────►├─ 模型档位 0.5 ── 主模型超时,弱模型兜底
├─ 谱系完整度 0.9
└─ ……每根轴记一次"在哪个维度发生了什么降级"
注意关键一步:不合成分。向量原样交给下游,“给用户展示”的策略看一眼放行,“触发扣款”的策略看一眼拦下。同一个向量,不同的门。
打个比方:随货同行的单子。货走到哪,单子跟到哪,收货人按自己的规矩验,这批能上架、那批不能进冷库,各自判断。这个比喻挺好用,但它有个地方漏风,我得老实标出来:现实里的单子靠人自觉填写、最后补个章,这套东西恰恰相反,它的全部赌注押在“没人能绕过”上。而“没人能绕过”是最难的部分——文章自己就承认,这根向量在真实生产环境里会死于两种失效模式:
死法一:单子开了,没人看(强制检查缺失)
死法二:货转手的时候,单子被塞进碎纸机(持久化失败)
死法一我见太多了。多少“可观测性”建设最后就是一屋子没人看的日志,数据生成了,躺在那,等于没生成。文章的主张挺狠:用类型系统让“不读单子”的代码根本写不出来。副作用函数的签名只收一种类型,过了门控、解包之后的那个;解包这个动作本身,强制跑一遍策略检查。想跳过?类型不匹配。
等等等等,先别急着鼓掌。模型不走你的类型系统怎么办?它生成的工具调用是自由格式的,包不包装它说了算。文章的答案是往基础设施层放个代理:没有有效溯源负载的调用,直接拒。编译期的保证换成运行时的拒绝。背后那句话我很认同——强制要放在行为者绕不过的边界上,类型或者代理,而不是指望开发者自觉、模型自觉。对“自觉”这两个字我已经不抱任何期待了,所以这条看着狠,其实是唯一现实的写法。
死法二是我觉得整套东西里最漂亮的一下……先讲我怎么又画错的。
长周期 agent 必然压缩历史,context 撑不住,只能把老对话总结成散文摘要。我第一版把这步画成了一根无损的箭头:
长历史 ──► 散文摘要 ──► 溯源向量原样带过去
不对。摘要是一次不可控的有损压缩,下游门控要用的关键事实,可能正好被压掉。这不是我第一次在“压缩”这个词上栽跟头,上次画 context window 那张长桌就漏了中间被挤下桌的部分,但这次栽得更狠,因为它反过来指出一件事:
压缩本身就是一种降级来源。
你不只是在搬运信任,你在制造一次新的降级。所以向量上要多一根重建轴:标明这根向量跨过存储边界、被重建过,谱系是近似值。✨懂了的那一刻真的咔哒一下扣上了,向量不是随货的单子,向量自己也是会受伤的货,过一次手就得记一笔。
那谱系具体怎么压?方案是分轴处理:各轴分数做无损的运行最小值归约,大小恒定;谱系本身允许有损,只留指针或概要。翻译一下:分数可以合并,取历次最差的那个;来龙去脉可以精简,但“精简过”这件事本身要记在案。打个比方,谱系每压缩一次就是复印一次,复印件上得盖章,“复印件,非原件”。
然后我在评论区挨了一记。有人指出这个章盖得不够细:运行最小值会把“一次重度压缩”和“十次轻微压缩”混成同一个分。我盯着想了半天才反应过来,min 只记得最惨的那一次
