跳到主要内容

8 篇博文 含有标签「reproducibility」

查看所有标签

在 18 个月后复现 AI 决策

· 阅读需 11 分钟
Tian Pan
Software Engineer

一位客户对贷款被拒提出异议。监管机构开启调查。原告律师提交证据开示请求。这三者都带着同一个看似简单实则棘手的问题:你的系统做了什么决定,以及为什么? 决策发生在 18 个月前。你调出案例,发现产生原始输出的每一个组件都已经发生了变化。托管模型版本已被弃用并迁移。系统提示词(System Prompt)被修改了 9 次。你的智能体(Agent)检索到的文档被重新分块(re-chunked)、重新嵌入(re-embedded)并重新排序(re-ranked)到了一个新的索引中。而让整个过程具有非确定性的采样设置从一开始就根本没有记录。

你无法复现该决策。这不是因为你疏忽大意,而是因为你的技术栈中没有任何东西是为了可复现性而构建的。事后解释性(Explainability-after-the-fact)归根结底是一个伪装成解释问题的可复现性问题——而可复现性这种东西,你要么在决策发生的时刻就通过工程手段将其固化,要么就会永远失去它。

令人不安的事实是,大多数团队发现这个漏洞时,恰恰是他们最承受不起后果的时候。重建决策的需求几乎从不会在正常运营期间出现。它往往伴随着诉讼、审计,或是带着监管机构电话号码的愤怒客户而来。到那时,捕获正确证据的窗口期早在一年半前就已经关闭了。

Notebook 对编程智能体来说是“敌对领域”

· 阅读需 12 分钟
Tian Pan
Software Engineer

给编程智能体一个 Python 模块,它是在坚实的基础上运行的:磁盘上的文件就是程序。阅读它,编辑它,运行它,观察结果 —— 闭环达成。给同一个智能体一个 Jupyter notebook,所有这些假设都会悄然瓦解。智能体充满信心地编辑第 12 个单元格,却不知道你一小时前曾用不同的数据重新运行过第 3 个单元格,也不知道一个在早已删除的单元格中定义的变量在内核中仍然处于活跃状态,更不知道它刚刚在第 7 个单元格下读取的输出是三次内核重启前由一段已不存在的代码生成的。

Notebook 是一个披着文件外衣的 REPL。磁盘上的 .ipynb 看起来像源代码,但真正决定行为的东西 —— 内核累积的内存 —— 是不可见的、未序列化的,并且是由产生它的那一连串人工点击所塑造的。智能体是基于“代码决定行为”这一契约训练出来的。Notebook 废除了这一契约,而大多数智能体框架甚至对此一无所知。

零温度并非确定性:复现那些你无法重新运行的事故

· 阅读需 11 分钟
Tian Pan
Software Engineer

模型给了客户一个错误的、代价高昂的回答。你打开复盘报告(post-mortem),将完全相同的提示词(prompt)粘贴回完全相同的端点,并将 temperature=0,结果你得到了一个不同的答案。不是更坏,也不是更好——只是不同。你本该找出根本原因(root-cause)的 bug 无法按需复现,而那个每个人都告诉你保证能复现的开关刚才却当面撒了谎。

就在这一刻,大多数团队发现“将 temperature 设置为 0”只是民间传说,而非工程控制手段。零 temperature 改变了模型的“采样”(sampling)方式——它强制进行贪婪解码(greedy decoding),始终选择概率最高的 token。它并不能保证两次计算得出的概率本身是完全一致的。而在生产环境的服务栈中,它们通常并不一致。

离职工程师带走的微调产物

· 阅读需 12 分钟
Tian Pan
Software Engineer

一个微调(fine-tune)不仅是一个文件。它是训练集上流水线的闭包(closure),如果一个团队在交付文件时没有提供这种闭包,那么他们就构建了一个生产依赖,其源代码其实只存在于某个人的脑子里。当那个人带着两周的离职通知和一份整洁的交接文档离开的那天,你某个收入相关特性的巴士系数(bus factor)就降到了零,而且没人会察觉,因为权重依然在注册表中,注册表标签依然稳定,模型也依然在处理流量。清算会在以后出现,比如在一次常规的基座模型迁移中,原本应该只需要一个 sprint 的工作却耗费了一个季度。

在我观察到陷入此困境的团队中,这种模式是一致的。一名 ML 工程师花了六个月时间迭代一个微调模型——包括数据策展(data curation)、超参数搜索、以及根据感觉在留存集上评估的行为补丁(behavioral patches)。最终的适配器(adapter)权重被推送到模型注册表并打上标签。而产出这些权重的训练流水线,只是该工程师笔记本电脑里的一个 Notebook,里面充斥着硬编码路径和浮动依赖,这些依赖指向的是每个单元格最后一次执行当天的最新版本。团队理所当然地接受了交接,因为权重有效、评估分数不错,且注册表标签很稳定。18 个月后,该工程师离职了。又过了 6 个月,一次基座模型迁移需要针对更新后的基座重新生成适配器。此时 Notebook 运行后生成的权重分数降低了 3 分,并且在最困难的客户细分领域出现了明显的性能退化,团队花了 4 个月时间尝试复现原始产物,但最终以失败告终。

客户无法复现的隐形个性化层

· 阅读需 12 分钟
Tian Pan
Software Engineer

一个平台团队发布了一项质量改进。推理阶段的一个图层会读取用户的近期交互记录,并悄无声息地调整响应风格:这里更正式一些,那里更简洁一些,或者当历史记录显示是一个工程师在提问时,响应会更具技术性。A/B 测试显示,整体满意度提升了几个百分点。发布公告的标题是“更智能的响应,无需更改 API”。没有人修改 API 中的标志位。没有人更新文档。响应内容中也没有任何迹象表明模型刚刚采用了哪种人格。

六周后,一位企业客户提交了一个支持工单,称“你们的模型比宣传的要差”。他们的内部评估套件——运行的是你们团队发布基准测试时所用的相同提示词——得分低了 8 分。你团队的第一步是核实提示词的一致性。提示词完全匹配。解码参数匹配。模型版本字符串也匹配。差异最终追溯到了个性化层,它为客户新开设的测试账号推断出一个“历史记录贫乏的默认人格”,而为你衡量基准测试的长效用户账号推断出了一个更丰富的人格。关于个性化究竟是功能还是缺陷的讨论,不再仅仅是一个产品决策,而演变成了一场合同谈判。

你的评估套件设置了确定性种子,但供应商却悄悄忽略了它

· 阅读需 13 分钟
Tian Pan
Software Engineer

你设置了 seed=42。你设置了 temperature=0。你记录了运行情况,发布了仪表板,并在模型更换上签了字。第二天早上,重新运行相同的提示词却返回了不同的数字,你给出的解释——“肯定是采样噪声”——错得离谱:根本没有采样,而且噪声是结构性的。Seed 离开了你的客户端,网关将其丢弃,内核将你的请求与 17 个不相关的请求打包在一起,浮点归约(floating-point reduction)顺序在你眼皮底下发生了变化。你所谓的“可复现”基准测试,距离变成另一个基准测试其实只差一个批次(batch)。

这种失败模式是悄无声息的,因为技术栈中的每一层在技术上都是正确的。SDK 接受了 seed。供应商记录了 seed。模型返回了 system_fingerprint。评估工具记录了这三者。没有 5xx 错误,没有警告,没有抗议。仪表板上的数字只是发生了偏移,而团队将这种偏移合理化为一直存在的某种抖动——因为他们没有工具能告诉他们,他们看到的究竟是随机解码(stochastic decoding),还是导致三周的对比实验全部失效的后端轮换(backend rotation)。

没有复现步骤的故障工单:可复现性是工程化的结果

· 阅读需 11 分钟
Tian Pan
Software Engineer

这张故障工单具有只有真实事故才具备的典型特征。在 02:14,支持代理关闭了一个本应进入 30 天宽限期的客户账户。客户发现了。工单落到你的桌面上,“复现步骤”一栏下面只有一行字:未知

你打开追踪记录。你看到代理调用了 close_account 而不是 set_grace_period。你看到工具执行成功了。你看不出的是模型为什么选择了那个分支 —— 而且当你通过同一个代理重新运行同一条客户消息时,它做出了正确的选择。做了两次。现在的事故复盘报告中,原本该写根本原因的地方出现了一个段落大小的空洞,而你唯一能诚实写下的只有“无法复现”。

确定性重放:如何调试永远不会以相同方式运行两次的 AI Agent

· 阅读需 12 分钟
Tian Pan
Software Engineer

你的 Agent 上周二在生产环境出了故障。一个客户报告了错误的回答。你调出日志,看到最终输出,也许还有几条中间的 print 语句——然后你就卡住了。你无法重新运行 Agent 来重现同样的故障,因为模型不会产生相同的 token,你的工具调用的 API 现在返回了不同的数据,嵌入在提示词中的时间戳也已经变了。Bug 消失了,你只能盯着间接证据发呆。

这就是 AI Agent 的根本调试问题:传统软件是确定性的,所以你可以通过重建输入来复现 bug。Agent 系统不是。每次运行都是模型采样、实时 API 响应和时间依赖状态的独特组合。没有专门的工具,事后调试就变成了取证猜测。

确定性重放通过在执行过程中记录每个非确定性来源,并在重放时替换这些记录来解决这个问题——把你无法复现的 Agent 运行变成你可以像调试器一样逐步跟踪的东西。