[控场AI]
· 7 分钟阅读· 3,539 字

契约锁定而非模型:构建可换供应商的Agent评分系统

契约锁定而非模型:构建可换供应商的Agent评分系统

训练营用提示词哈希与JSON cassette实现离线回放评分,锁定接口契约而非模型。

文章针对AI Agent训练营中实时模型评分导致结果不确定的问题,提出「记录一次,永久回放,离线评分」方案。核心是锁定Provider接口契约而非具体模型:提示词做SHA256哈希、响应存为JSON cassette、评分只跑回放、实时运行仅作演示。通过三个一分钟检查点验证基础设施与设计哲学,其中「两个供应商一个分数」是证明评分模型无关的关键。评分表只奖励契约稳定性、响亮失败和追踪,不给模型选择、token消耗、速度任何分数。作者还明确三条禁区:工具调用语义、延迟/质量基准、生产评估框架,并强调「录制好的cassette是持久资产;配额是天气」。

在AI Agent训练营里,一个反复出现的尴尬场景是:学生用某个免费模型完成了作业,但两天后评分者重跑时,模型变了、结果变了,评分标准形同虚设。问题的根源不在学生,而在实验设计——如果评分必须调用实时模型,那么评分本身就是非确定性的。

最近一篇来自训练营实践者的技术笔记提出了一个简洁的解决方案:记录一次,永久回放,离线评分。核心思想是「锁定接口契约,而非锁定模型」。本文基于该素材,拆解这套方法的设计哲学、实现细节与适用边界。

rss source: Pin the Contract, Not the Model: A Bootcamp Lab on Provider-Swappable Agent Runs

为什么实时评分是灾难

训练营进行到第二周,几乎每个学员都会撞上同一堵墙:有人基于当天免费的模型搭了一个Agent循环,提交后运行正常。两天后评分系统重新执行提交,却得到不同答案、不同工具调用、超时或限流页面。

那么评分标准到底衡量了什么?不是设计能力,而是重跑时的运气。

这当然有学生的责任,但主要是实验设计的bug。作者指出:如果评分器必须调用实时模型才能决定分数,那么评分器在构造上就是非确定性的,没有任何评分标准能在此之上存活。 这个诊断一针见血,把问题从「学生依赖不稳定资源」转移到「评分架构本身不可靠」。

四条规则:把契约钉死

作者现在要求每份提交满足四条规则:

  1. 提示词做哈希:同样的提示词字符串(忽略首尾空白)映射到同一个键。
  2. 响应录制为JSON cassette:纯JSON文件,提交到代码仓库。
  3. 评分只跑回放:不联网、不需要API Key、不依赖时钟。
  4. 最后一次实时运行仅作演示:永远不作为真相来源。

这四条规则的巧妙之处在于把「模型行为」和「评分逻辑」彻底解耦。模型成为环境变量,而评分针对的是学生设计的Agent逻辑。

实现:三个文件,十五分钟

整个实验的初始设置非常轻量:创建虚拟环境,安装httpx和pytest,建立cassettes和lab目录。核心是一个Provider协议——注意是Protocol而非抽象基类,任何拥有complete方法的对象都符合契约。

class Provider(Protocol):
    name: str
    def complete(self, prompt: str) -> str: ...

prompt_key函数对提示词做strip后SHA256哈希取前16位,确保空白差异不会产生新记录。Replay类读取cassette文件,如果找不到对应哈希则抛出CassetteMiss异常,而不是返回空字符串。

Recorder类包装任意实时Provider,先查cassette再调用外部接口,避免重跑半截会话时浪费调用次数。

实时适配器(Live类)被明确标注为草图而非验证代码——不同供应商的端点、认证头、响应结构各不相同,必须参考各自文档。这种诚实的标注值得肯定,避免了读者直接复制导致生产事故。

Provider 协议利用了 Python 的**结构化子类型(structural subtyping)**机制:只要一个类实现了 complete 方法并带有 name 属性,它就自动满足 Provider 类型,无需显式继承。这与 Java 式接口不同,好处是实时适配器、录制器、回放器可以彼此独立演化,测试时也能轻松用桩对象替换。Protocol 在 typing 模块中引入(Python 3.8+),配合静态类型检查器(如 mypy)可以在不运行代码的情况下发现契约违规,进一步强化了「锁定接口」的目标。

Cassette 一词借自 HTTP 录制回放工具(如 VCR.py),原指把一次网络交互序列化为可重放的文件。在本实验中,cassette 是纯 JSON,键为提示词哈希,值为模型响应字符串。与二进制或压缩格式相比,纯文本 JSON 便于代码审查、差异对比和版本控制,也让评分器无需任何反序列化依赖即可读取。

三个检查点:一分钟内判胜负

作者设计了三个快速检查点,每个都能在一分钟内完成:

检查点1:空白不是新提示词。 两次调用只有空白差异,应命中同一条录制。

检查点2:未命中必须响亮。 没有空字符串、没有None、没有吞异常。CLI必须非零退出。

检查点3:两个供应商,一个分数。 同一cassette喂给评分器两次,输出哈希必须一致。这是证明评分标准模型无关的关键检查,也是大多数学生首次失败的地方——因为他们把model_name藏进了评分逻辑。

前两个检查点验证基础设施的正确性,第三个检查点验证设计哲学是否真正落地。层层递进,设计得很扎实。

第三个检查点实际上在检验确定性评分函数:给定同一份 cassette,评分器的输出(包括分数和任何中间哈希)必须逐字节一致。这要求评分逻辑中不能混入时间戳、随机数、环境变量或模型名称等非确定性来源。学生常犯的错误是把 model_name 写入评分逻辑用于「区分供应商」,结果同一 cassette 在不同供应商标签下产生不同分数,直接违反「两个供应商一个分数」的契约。正确的做法是让评分器只依赖 cassette 中的提示词-响应对,供应商信息仅作为元数据记录在追踪日志中,不参与评分决策。

评分标准:只奖励设计,不奖励运气

作者实际使用的评分表值得完整呈现:

标准权重满分表现典型失分
契约稳定性30两个供应商下分数一致只在碰巧用的模型上通过
响亮失败25CassetteMiss传播,退出码非零返回空串却看似成功
追踪20每次调用记录键、供应商、耗时只记录最终答案
书面总结15一段诚实的失败复盘只写「能跑」
扩展目标10完成一个并记录发现的失败三个都开了头却没完成

说个细节缺席的项:模型选择、token消耗、速度都不给分。作者明确说,那些是环境变量,不是技能。这个评分设计把学生的注意力从「调模型」拉回到「设计可靠的系统」。

边界:什么时候这个实验不适用

作者主动列出了回放方法的三个禁区,这种自我限制反而增加了方案的可信度:

  • 不要用于工具调用语义:不同供应商对函数参数的序列化方式不同,回放可能掩盖真实的不兼容性。
  • 不要用于延迟或质量基准测试:那只是在评测一个JSON文件。
  • 不要用作生产评估框架:生产需要随机采样;这是评分装置,不是评估平台。

「边界是交付物的一部分。如果学生说不出自己的实验测不了什么,那他的总结就没写完。」这个观点值得所有技术教学者借鉴。

成本为零的实验室

对于预算为零的训练营,作者推荐了MonkeyCode的开源项目,提供免费模型访问和免费服务器选项——足够第一天录制cassette,整个学期回放。利益披露:本文是作为MonkeyCode产品推广的一部分撰写的。 作者同时给出了一个诚实的警告:免费额度会变,录制当天去控制台确认当前额度,不要照抄博客里的数字——包括这篇。

「录制好的cassette是持久资产;配额是天气。」这个比喻精准地表达了回放方法的核心价值:把不稳定的外部依赖转化为稳定的本地资产。

学生真正学到的东西

这套实验的最终收获与供应商无关。核心教训是:任何你不能一致地重跑的东西,你都无法公平地评分——包括你对模型本身的假设。

作者建议在仓库第一天就建立FAILURES.md,并说「第二条记录总是比第一条更有意思」。这种对失败记录的重视,恰恰是工程教育中最容易被忽略的部分。

对于正在构建Agent评估体系的团队,这套方法提供了一个值得借鉴的中间层:它不解决生产级评估的复杂问题,但能确保教学和作业评分不被模型的随机性绑架。在模型供应商快速更迭的当下,「契约锁定」的思路或许比任何具体工具都更持久。

#Agent评分系统#供应商可替换#cassette回放#AI训练营实验#模型无关评分#Agent开发最佳实践#离线评分
分享:

相关推荐