可复现性
docs.stripe.com · 审计于 2026-07-27 · AgentFit v3.4.0 · 评分标准 v4 · Claude Opus 5
无法重复的分数不是测量。所以这里是我们该交出的检验:同一天里我们对同一个站点审计了六次——三次让语言模型套用文档评分标准,三次运行 AgentFit——并把记录下来的内容都公开,包括对我们不利的部分。
问一个模型 · 3 次运行
78 · 80 · 65 15 分的离散度
三个互相独立的智能体,同一份公开提示词,同一个模型,同一天。它们彼此不知情,也不知道为什么被问到这件事。
运行 AgentFit · 3 次运行
57 · 57 · 57
0 分的离散度
eabbb2f6abcdea473b600dc4ee8e25282fd002344f1e8425519caa32ff858b01
一个 sha256,三个文件
连续三次真实审计,三个 JSON 文件,一个校验和。这不是缓存:每一次运行都重新访问了站点。
请纵向读这两列,不要横向对比。左边那套评分标准有 26 条,AgentFit 有 28 条,而且权重并不相同:机器可读规范在左边值 25 分,在这里值 17 分。57 不是「正确答案」,78 也不是「错的」。本页的主张要窄得多,而且这也是数据唯一支持的一条:这两套流程里,有一套每次都给出同一个数字,另一套不是。
三次运行在哪里分歧
三次模型运行都是认真做事的:它们抓取了实时 URL,逐条走完评分项,并报告了所见。按我们自己在记录中的统计——这些记录我们并未公开——每次运行调用工具 36 到 47 次,且没有一次编造事实。这两点请当作我们对运行过程的转述,而不是下面的文件能让您核对的内容。它们最终仍相差 15 分,其中十一分落在同一个类别里。
| 类别 | 满分 | A | B | C | 离散度 |
|---|---|---|---|---|---|
| A — 可发现性 | 18 | 13 | 13 | 12 | 1 |
| B — 页面产物 | 22 | 12 | 13 | 12 | 1 |
| C — 机器可读规范 | 25 | 20 | 21 | 10 | 11 |
| D — 内容 | 20 | 18 | 19 | 18 | 1 |
| E — 渲染与卫生 | 15 | 15 | 14 | 13 | 2 |
| 总计 | 100 | 78 | 80 | 65 | 15 |
这里混着两类分歧。小的那类——A5、B3、B5、B6、D1、D3、D6,一两分的出入,方向不一——正是三位认真读同一页面的读者会有的差别:一次在 DOM 里找到了 SDK 标签页,另外两次认定它们只存在于 JavaScript 负载中。这是否属于寻常的测量噪声,三次运行说明不了,这一点我们只是在猜。公开的摘要里只看得到 D1 上的分歧,其余来自我们自己对三份记录的比对。大的那类分歧性质不同,这个页面正是为它而写。
C 类中其余各条在三次运行里都恰好得了 10 分。整整十一分的差距,全部来自两条标准。
C1a:同样的事实,三个不同的分数
评分项 C1a——出自模型那套 26 项标准;AgentFit 把「能否发现」和「是否有效」合并为一个 C1——问的是这个 API 有没有一份代理能够到达的机器可读规范。三次运行就 docs.stripe.com 确认了完全相同的两个事实:
- 约定位置——/openapi.json 及其同类——返回 404;
- 一份有效且完整的 OpenAPI 规范发布在 Stripe 的 GitHub 仓库里。
没有人判断错误,也没有人产生幻觉。三次的评分是 8 分里的 0 分、3 分和 4 分。
| 标准 | A | B | C |
|---|---|---|---|
C1a · spec discoverable | 3/8 | 4/8 | 0/8 |
C1b · spec valid | 7/7 | 7/7 | 0/7 |
E5 · terms of use | 3/3 | 2/3 | 1/3 |
分歧不在站点,而在一个评分标准从未提出的问题上:如果规范放在别人的域名下、还要多跳一次才能拿到,它算不算已发布?这是一个没有显然答案的真实岔路,而每一次运行都是在一项长任务的中途、独自走到这个岔口,手边没有可查的规则。三次运行,三种判法。
从这里开始就是连锁反应。C1b 评的是你找到的那份规范的质量。判定 GitHub 不算数的那次运行无物可验,得了 7 分里的 0 分。抓到规范的两次都给了 7 分中的 7 分——随后报告的路径数却不同,一次 414、一次 136,因为 Stripe 仓库里不止一个规范文件,它们各取了一个(spec3.json 与 spec3.yaml)。两次运行给出同一个分数,理由却相反。
E5——「是否声明了使用条款?」——结果是 3/3、2/3 和 1/3。一次运行在页脚找到了指向 stripe.com/legal/ssa 的链接。一次否掉了那个链接,转而认可 robots.txt 里的 Content-Signal 指令。还有一次在文档主机上找 /terms、/legal 和 /privacy,拿到三个 404,就按所见评分。同一行评分标准,三种都站得住脚的读法。
自检本身也在漂移
该提示词自带一个校准锚点:一张此前测量过的站点表,Stripe 在表中为 56 分,测于 2026 年 5 月 13 日。它的存在正是为了发现漂移。这张表也解释了这里为什么是 docs.stripe.com——在我们运行任何东西之前,Stripe 就已经写在别人的校准锚点里,站点是替我们选好的,而且是在实验之前选定的。
三次运行都用上了它。三次都注意到了自己的偏差——+22、+24、+9。三次随后都重新检查了各条标准,推断五月以来可能发生了什么变化,并得出结论:锚点过时了,自己的数字更可信。三次,各自独立,方向一致,数字却各不相同。
这一段是我们觉得最有启发的地方。这不是敷衍——当流程里仍留有一个自由参数时,认真的工作看上去就是这样。一项自检,如果它的结论在每次运行之间都会变,那它就不是自检。
这个锚点恰好距离 AgentFit 的 57 只差一分。请不要多想:那是一次人工测量,用的是另一套评分标准,来自另一个月份,它并不能证明 57 就是真实分数。
代码为什么每次都给同一个答案
没有什么巧妙之处。每条标准都是一次请求加一条规则,而两者都在审计开始之前就写死了。
| 标准 | 结果 | 请求 | 记录 |
|---|---|---|---|
| A1 · llms.txt 索引 | 通过 3/3 | GET /llms.txt → 200 | Stripe Documentation |
| C1 · 规范发现 | 缺失 0/8 | 固定探测列表,无响应 | — |
| D2 · 示例真实度 | 通过 5/5 | 抽样的端点页面 → 200 | placeholder_ratio = 0.00, 0/3 blocks |
| E6 · 无障碍 | 部分通过 1/4 | 首页 → 200 | a11y: 3 violations (e.g. button-name) |
D2 就是用到机器学习的那一条。审计内部跑着两个小分类器:一个给代码示例的真实程度打分,另一个判断端点页面的完整度。它们是权重冻结、编译进二进制的 ONNX 图。同样的输入永远给出同样的张量和同样的浮点数。确定性并不要求回避模型;它要求回避那些输出无法钉死的模型。
C1 正是三次运行走岔的那个分叉,AgentFit 在这里给 0 分(满分 8)。我们的规则不是「GitHub 不算数」,而是一份固定的查找清单:被审计主机上的十七个惯用路径、一个符合 RFC 9727 的 api-catalog 链接,以及我们抓取的首页上出现的任何规范链接。在 docs.stripe.com 上,这些路径都没有返回规范,那个页面上也没有这样的链接,所以什么也没找到。一条多走一跳、追到厂商仓库里去的规则并不会更不正确。我们这条规则只是在审计开始前就已固定,对语料库中每个站点一视同仁地套用,评分方式公开在评分标准页上。差别全在于此——不是更好的答案,而是固定的答案。 C1 如何评分.
用一次实时运行来核对
仓库是私有的,我们也不发布任何二进制文件,所以我们不会让你「自己编译再运行」——你做不到。不靠我们能验证的东西要窄一些,我们宁可把它说准。上面那些文件,是 2026 年 7 月 27 日在我们机器上由命令行构建写出来的。公开服务是另一份构建,跑在别处,它会就同一个站点交给你它自己的报告。如果流程真如我们所说,这两份报告在每一项得分上都会一致。下面是怎么做这个比对,以及它的分量。
什么都不用跑(不消耗配额)
公开数据集里为每个主机记录了最近一次有效审计的运行标识。取出 Stripe 的那一个,从 API 把这次运行读回来,把两份文档都缩减到打分字段,然后比对:
$ RUN=$(curl -s https://agentfit.dev/dataset.csv \
| awk -F, '$1=="docs.stripe.com" && $3=="true" {print $7; exit}')
$ curl -s https://agentfit.dev/static/reproducibility/agentfit-run1.json \
| jq -S '{total_score, max_score, categories,
criteria: [.criteria[] | {id, status, score, max}]}' > ours.json
$ curl -s https://agentfit.dev/api/public/audit/$RUN \
| jq -S '.report | {total_score, max_score, categories,
criteria: [.criteria[] | {id, status, score, max}]}' > theirs.json
$ diff ours.json theirs.json
2026 年 7 月 27 日,这条 diff 什么也没打印:200 行,零输出。两份不同的构建,两台不同的机器,同样的 28 条判定。
自己跑一次(每个站点每天一次)
把 https://docs.stripe.com 粘进首页的表单。跑完之后,地址栏里是 /r/<运行标识>。把它换到 $RUN 的位置,再跑同样的两条命令。要清楚它加了什么、没加什么:审计仍然从我们的服务器发出,所以属于你的是这次请求和这个时刻,而不是出口。
同一个站点、同一个网络,24 小时内只能审计一次。第二次会拿到 429,没有运行标识——这是故意的,免得有人把这个服务当锤子去砸别人的文档。如果你撞上了配额,上面那条路根本不需要提交任何东西。MCP 服务器与网站共用同一份配额:让智能体在同一天再审一次同一个站点,会撞上同一堵墙。而在这堵墙前面还有第二堵,它跟你没关系:十分钟内对同一个主机的两次审计——不论是谁发起的——会耗尽一份共享额度,下一个调用者拿到的是 429,错误码是 rate_limited 而不是 site_quota。这一堵十分钟后自己就消了。
不用 curl 和 jq
在一个标签页里打开 agentfit-run1.json,在另一个标签页里打开这次运行的报告页 /r/<运行标识>。决定结果的是七个数字:六个类别得分和总分。它们就印在下面,所以光看这一页也能核对。
| 类别 | 得分 | 满分 |
|---|---|---|
| A — 可发现性 | 11 | 14 |
| B — 页面要素 | 11 | 21 |
| C — API 契约 | 4 | 17 |
| D — 内容 | 16 | 23 |
| E — 渲染与卫生 | 15 | 21 |
| F — 代理能力 | 0 | 4 |
| 总计 | 57 | 100 |
有三处会不一样,而且应该不一样
- auditor——写下这份报告的那份构建的标签。我们这里是 agentfit/v3.4.0,因为当天的本地构建就是它;新做的报告写的是此刻线上部署的版本。它标的是二进制文件,不是测量本身,所以上面的比对把它扔掉了。
- evidence_snippet——站点交给那台去抓取的机器的原话。我们那三次运行的出口地址,Stripe 回的是德语;服务这一次拿到的是英语。7 月 27 日的差异全部在此:B1、B3 和 E1 在完全相同的 URL、完全相同的响应码和完全相同的得分之下,带着不同的文字。片段记录的是输入,不是判定。
- 键的顺序。我们的文件是工具直接写下的;API 返回的是同一个对象绕了一圈 Postgres 之后的样子,而 Postgres 按键名从短到长存放。逐字节比对这两份文档毫无指望,而且跟得分没有关系——jq -S 把两边排成同一个顺序,diff 才有意义。
一致只说明一件事:同一套流程、同一个站点,在另一份构建、另一台机器上给出了同一个判定。它不说明 57 就是对的——本页从未这样主张。而且它取决于站点本身:如果 docs.stripe.com 自 2026 年 7 月 27 日以来变过,数字就会动,那是仪器在工作,不是在坏掉。从不一致里下结论之前,先看数据集那一行的 audited_on 日期。
这些文件是冻结的,我们不会每次发版都重新生成。两个原因:本页的校验和正是对这些字节取的,上面文字里的每一个数字也都是从它们里读出来的——一份会自动刷新的展品,这两样都担不起。报警线在活的那一侧,不在冻结文件里:你上面读到的那一行数据集,第 9 列就是 rubric_version,只要本页还挂着,它就必须是 4。展品比这一列还老,本身并不带它——所以能拿来对照的,只有这句话里印出来的数字。我们是直接声明它,而不是让你自己找到它;这是更弱的保证,正因如此才把它写出来。哪天那一列变成别的数字,就说明评分尺在本页写成之后换过了:按字段的比对作废,你看到的是一份历史文件。与其让页面悄悄重跑到自己跟自己一致,不如让它把这句话说出口。
28 条标准里有两条——D2 和 C3——不是由规则判的,而是由两个小分类器判的。它们编译进二进制文件,并随之一起版本化:没有模型服务,谁的重训练也动不了一个已经发布的分数。
我们没有主张什么
- 我们并不主张 57 就是对的。我们不公布 docs.stripe.com 的真实分数,因为没有人有。我们公布的是一套流程和它的输出。而且可复现是必要条件,不是充分条件:echo 57 同样完全可复现——它欠缺的是对站点的敏感性。我们的流程是否敏感,请到 /browse 判断:同一套流程把真实站点铺开在整个分数区间里。
- 我们不是在比较两个数字。评分标准不同:条目数不同,权重也不同。比较只在每一列内部有意义,跨列则没有。
- 这是一个站点、一天、一个模型、三次运行。它是一次演示,不是一项研究,我们也不承诺会定期重跑。
- 这个模型是 Claude Opus 5——正是帮我们做出这个页面以及本服务相当一部分内容的那个模型。我们展示的是我们自己每天使用的工具中被复现出来的离散度;请带着这一点来读。我们并不是说语言模型不擅长这件事,我们说的是:我们没能让它两次给出同一个数字。
- 确定性是流程的属性,而不是对互联网的承诺。我们的三次运行都在同一天从同一台机器发出,而站点向那个地址返回了德语正文——这在 B1 和 E1 的证据里看得到,并且有一次模型运行也独立记录了同一件事。换一个出口,就换了输入。同样的输入给出同样的输出,保证仅此而已。
模型在这件事上真正擅长什么
这一切并不使模型的那三次运行变得无用——它们只是另一种工具。三次运行各自的诊断本身都不错:缺少规范为什么会伤害智能体、应该改为发布什么、Stripe 的哪些页面经得起不用浏览器的阅读。这是解释,而模型解释得非常好。
语言模型极其擅长解释该修什么。作为衡量你是否修好了的量具,它很不称职。我们做的是后者。AgentFit 是量具;把它的数字拿给模型,问该怎么办。这才是正确的分工,也是我们自己的用法。
数据
六次运行都由本站提供。三份 AgentFit 报告就是工具写出的文件,逐字节原样——校验和正是对它们取的。三份模型运行文件则出自我们之手:
原始记录并未公开。模型这一栏给出的是我们事后依据这些记录整理的结构化摘要:最终分数、各类别小计,以及上文引用的逐项说明。分数和小计是每次运行自身的输出;说明的措辞出自我们。
- 三份 AgentFit 报告和校验和 —
run1.json·run2.json·run3.json·sha256.txt - 三次模型运行——由我们依据记录整理的摘要 —
runA.json·runB.json·runC.json - 模型那一列所用的提示词,锁定在我们运行的那个提交上 —
ai-ready-audit
我们没有筛掉任何东西——既没有筛运行,也没有筛站点。三次模型审计、三次工具审计,都在提示词校准表早已点名的那一个站点上;我们没有跑过别的站点,也没有丢弃任何结果。如果第四次运行给出 57,我们同样会公开,而这个页面会更短。
免费、无需注册,约 30 秒。28 条标准,每条下面都有 HTTP 证据,还有一个可分享的报告链接——如果什么都没变,明天还是同一个数字。