主题
第 16 章 基准:可复现的方法学
一家做保险理赔材料审核的公司,在 2026 年上半年的选型会上拿到三份材料。 三家候选记忆系统都声称长程对话记忆得分在 90 分以上,彼此差距不超过 3 分。 采购按分数排序,选了报价最低的一家。 上线三个月后,这套系统在长会话里反复把已经撤销的保单条款当成现行条款引用。 理赔员必须人工比对原始批单才能发现。 落选的另一家在同一个业务上表现正常。
回看选型材料,两家的分数都来自公开基准。 但一家跑的是十几轮对话、另一家跑的是两百轮以上。 判定标准一个是严格匹配、一个是宽松语义判定。 两个数字都真实,却没有一个能回答「在我的业务上谁更可靠」。
分数不是事实,是一个带条件的断言。 「LongMemEval 96.2」的完整形式是:在某个版本的数据集上、用某个判定器、 在某组检索轮数限制下、由某一次运行得到的准确率是 96.2。 条件省略之后,剩下的数字既无法反驳也无法复算。
基准的可信度不由数字大小决定,而由别人能不能复算决定。 一个 82 分但附带完整 harness 的结果,比一个 96 分只给结论的结果更有工程价值。 前者可以被推翻,后者只能被相信。 工程决策需要的不是被相信,而是被检验。
绝大多数厂商只公布 headline 分。 少数公布方法学,交代数据集版本、判定方式与检索轮数。 公布方法学并把 harness 一起分发的极少。 这三种做法对应三种信任关系,把它们放进同一张表排序会产生系统性误判。 不是分数错了,是分数回答的问题不同。
本章按这个梯度展开。 16.1 建立三层可信度的判据。 16.2 拆开可复现仓库必备的五件东西,每件对应一种具体的作弊路径。 16.3 给出三条可以直接写进工程规范的红线。 16.4 交代我们自己公布的分数处在什么状态,包括尚未做到的部分。 16.5 说明一个诚实的分数该带哪些元数据。 16.6 把全章压缩成五个可以问出口的问题。
16.1 基准的三层可信度
16.1.1 只公布 headline 分:只能相信
一个只给结论的分数,读者能做的唯一动作是接受或拒绝,无法定位分歧。 某家做电商客服知识库的团队遇到过这种局面:两家候选都公布「长会话记忆 90 分以上」。 团队复测自己的 60 条真实工单,一家 71%、一家 46%,与公布值的排序完全相反。 他们既不能证明厂商造假,也不能证明自己测错,因为没有任何一方的中间产物可以对照。
这层做法的机制性问题是不可反驳性。 一个断言不附带产生它的过程,任何反驳都必须先重建那个过程。 而重建过程本身就需要读者自己写一套基准。 这等于要求质疑者承担全部举证成本。 举证责任倒置之后,分数变成营销材料而不是工程数据。
判据很直接:当供应商只能提供结论、而你的业务对记忆错误不宽容时, 这个分数只能用于排除明显不合格者,不能用于排序。 代价是你会漏掉真正更好的系统,因为更好的系统可能只是不擅长宣传。
16.1.2 公布方法学:可以审视
公布方法学意味着交代四件事。 数据集名称与版本;判定器类型与判定 prompt;检索轮数与是否允许多跳;运行是否完整。 有了这四项,读者可以判断分数与自己的场景是否同构,即便无法复算具体数字。
一家做医疗随访的团队用这个方法避免了一次错误选型。 候选系统的高分来自「允许无限多跳检索」,而他们的生产链路只允许一次召回。 把条件对齐后重估,候选系统的优势缩小到 2 分以内,不足以支撑迁移成本。
这层做法的价值在于把不可比变成可比,机制是让条件显式化。 局限同样明确:方法学是自述的,而自述可以被选择性省略。 省略往往发生在最不利的那一项上。 代价是读者仍需要专业判断力才能识别省略。
16.1.3 连 harness 一起分发:可以复算
第三层是把评测代码、适配器、数据下载脚本、机器可读结果一起放进公开仓库。 任何人 git clone 之后能用一条命令重跑。 此时分数从「主张」变成「可验证的计算结果」。
可复算带来的第一个变化是分歧可以被定位到具体环节。 两个人跑出不同分数时可以 diff 中间产物,判断差异来自数据版本还是适配器实现。 第二个变化是基准本身开始接受审查。 scorer 有没有偏袒、数据有没有泄漏、基线是否被削弱,都会暴露在代码里。
代价是成本转移。 维护一个可复算基准的工作量远高于写一份评测报告。 而且一旦公开,任何一处不自洽都会被指出。 多数团队不愿承担这个成本,这正是第三层稀缺的原因。
| 层级 | 公布内容 | 读者能做的动作 | 主要风险 |
|---|---|---|---|
| 第一层 | 仅 headline 分 | 接受或拒绝 | 无法定位分歧,举证责任倒置 |
| 第二层 | 分数 + 方法学 | 判断场景是否同构 | 方法学为自述,可选择性省略 |
| 第三层 | 分数 + 方法学 + harness | 复算并 diff 中间产物 | 维护成本高,缺陷完全暴露 |
表 16-1 基准的三层可信度与各自允许的动作
16.2 一个可复现基准仓库必须有的五件东西
可复现不是一句态度,是一组具体的文件。 以下五件缺任何一件,复算链条就断在某个环节上。 而断裂点通常正是最容易被做手脚的地方。
16.2.1 统一 scorer
scorer 是唯一计算分数的代码,不允许任何 per-system 的评分逻辑。 这条约束针对一种常见做法:为每个被测系统配一段「适配其输出格式」的评分代码。 表面上是格式兼容,实际上评分口径已经分裂。 某系统输出长句被判为「包含答案」,另一系统输出短句被判为「不完整」。 两者分数不可比,而无人察觉。
比格式分裂更隐蔽的是采信系统自报的相关性分数。 多数记忆系统在检索结果里带一个 score 字段。 若 scorer 直接用这个字段排序,就等于让被测者给自己打分。 正确的做法是一律从 ground truth 重算。 拿到系统返回的 id 列表,用黄金标注判断命中,自己算 hit@k 与 MRR。 这条约束会让部分系统的分数下降,因为它暴露了「自报分数很高但排序不对」。
代价是 scorer 必须理解每个数据集的标注格式。 工作量大,且容易在数据集扩充时失配。
16.2.2 统一适配器接口
适配器接口只留三个方法,是被测系统与 harness 之间唯一的接触面。
python
class Adapter:
def add(self, session: dict) -> None: ...
def search(self, query: str, k: int) -> list[str]: ...
def reset(self) -> None: ...add 灌入一个会话,search 返回记忆 id 列表,reset 清空状态。 三个方法的约束力在于它不允许被测系统获得题面之外的额外信息。 也不允许它跨题目保留状态。 曾经有一种取巧做法是把整个数据集一次性灌入后全局检索,靠题目间相互提示提高命中率。 reset 与逐题 add 的组合堵住了这条路。
代价是三方法接口无法表达「写入时就需要查询上下文」这类设计。 某些架构需要额外的适配层才能接进来。
16.2.3 naive 全上下文基线
必须有一个 naive 基线:把全部历史上下文直接塞进 prompt,不做任何检索。 这个基线的意义不是陪跑,而是给出下界。 如果记忆系统打不过全上下文注入,问题在系统而不在基线。
这条判据在实践中被低估。 一家做法律文书检索的团队曾发现自家系统在自有题集上只有 58 分。 他们正准备重写检索层,加上全上下文基线后才发现基线只有 51 分。 题目本身需要跨文档推理,任何检索式方案都会丢信息。 他们最终改了产品形态,而不是改检索算法。
代价是全上下文基线在长上下文场景下成本极高。 跑一次全量可能耗费数万美元的推理费用,因此多数仓库只在子集上跑它。
16.2.4 机器可读输出
结果必须是 JSON,字段固定,可以 diff 两次运行。 人读的 Markdown 报告可以生成,但不能是唯一产物。 原因很实际:分数变化时第一个要回答的问题是「哪几道题变了」。 没有逐题记录,这个问题无法回答。
代价是逐题记录会暴露失败样本。 对一个商业系统而言,公开自己答错的题目需要额外勇气。 这也是很多仓库只存汇总数的真实原因。
16.2.5 数据下载脚本与校验和
数据集不进仓库,但下载脚本与 SHA-256 校验和必须进。 这条约束保证两件事:任何人拿到的是同一份数据;数据被上游静默修改时能被发现。 基准的可比性建立在数据不变之上。 而数据集被悄悄修订是这类评测中最常见也最难察觉的漂移源。
| 组成 | 缺失后的后果 | 主要防住的作弊路径 |
|---|---|---|
| 统一 scorer | 评分口径分裂,分数不可比 | 为每个系统定制评分逻辑 |
| 三方法适配器 | 系统可跨题保留状态 | 全局灌入后互相提示 |
| naive 基线 | 无法判断是否优于不检索 | 用弱基线衬托提升 |
| 机器可读输出 | 无法定位分数变化来源 | 只报总分、隐藏失败样本 |
| 下载脚本与校验和 | 数据版本漂移不可见 | 静默替换数据集 |
表 16-2 可复现基准仓库的五件必需品
16.3 三条红线
以下三条可以直接抄进团队的评测规范。 它们描述的是「基准」与「广告」的分界线。
第一,不填没测过的格子。 空格好过错数。 一个只覆盖三个数据集的表格,比一个四格全填但其中两格来自推算的表格更可信。 这条红线针对的做法是:用同族数据集的分数外推到没跑过的规模上。 外推在工程上是合理估算,在基准表里是伪造。 因为读者无法区分「实测」与「外推」,而两者的可信度差一个数量级。
第二,陌生人 git clone 加一条命令必须跑通。 这条红线把「可复现」从意图变成可执行的验收条件。 它同时要求仓库自带小规模样例数据。 否则陌生人在下载完数 GB 数据集之前无法确认环境是否正常。
第三,适配器开放,谁都能加系统。 一个只接受特定厂商接入的基准,无论分数多客观,本质上是该厂商的测试集。 开放适配器意味着竞争对手也能被公平测量,也意味着自己的分数可能被第三方推翻。 这是基准与广告的区别:广告不能被第三方修改,基准必须能。
三条红线都有代价。 第一条让公布的数字看起来更少。 第二条把大量时间花在环境适配上而不是算法上。 第三条放弃了用基准构建壁垒的可能。 愿意付这三个代价的团队,才是在做基准而不是做宣传。
16.4 我们自己也没做到满分
这一节交代我们自己的状态,包括尚未做到的部分。 它是本章可信度的支点。 如果只对别人的分数苛刻、对自己的分数宽容,前面的三层可信度就只是话术。
仓库 README 公布四项结果:LongMemEval 96.2、LoCoMo 94.8、BEAM 1M 68.5、BEAM 10M 53.9。 并对照 Mem0 公布的同口径数值 94.4、92.5、64.1、48.6。 这些数值来自真实运行,但它们的可复算状态需要单独说明。 不能与数值大小混为一谈。
对照 Mem0 时必须带三项口径,缺任何一项这个对比就没有意义。 部署形态:Platform 还是开源版,两者组件默认配置不同,不可互换。 检索深度:Top 50 还是 Top 200,候选池越大命中概率越高。 指标类型:准确率还是通过率,两者分母不同,不可并列。
按 Mem0 官方基准仓库 mem0ai/memory-benchmarks 的 README,其公布值为: LongMemEval 94.4%(Top 200)与 94.8%(Top 50)、LoCoMo 92.5% 与 91.8%、 BEAM 1M 70.1% 与 67.1%、BEAM 10M 50.5% 与 45.5%。 其中 BEAM 两项标注为通过率,而非准确率。 这里出现一个必须指出的不一致:我们 README 引用的 Mem0 对照值是 64.1 与 48.6。 与该仓库当前给出的 BEAM 数字不同,差异原因未在两处任一方说明。 这本身就是一个证据——对照表不写口径,两个团队引用同一家数据也会得出不同结论。 此外,Mem0 官网研究页的图表由客户端渲染,直接抓取得到的是 0.0,不能作为引用来源。 引用一律取基准仓库。
| 基准 | 仓库公布值 | README 引用的对照值 | 仓库内独立复算状态 |
|---|---|---|---|
| LongMemEval | 96.2 | 94.4 | 尚未由仓库内完整运行复算 |
| LoCoMo | 94.8 | 92.5 | 尚未由仓库内完整运行复算 |
| BEAM 1M | 68.5 | 64.1 | 尚未由仓库内完整运行复算 |
| BEAM 10M | 53.9 | 48.6 | 尚未由仓库内完整运行复算 |
表 16-3 公布分数与其在仓库内的可复算状态
需要说明的细节有三处。
第一,仓库中 results/2026-10-24.md 自我标注为 seed result table。 它明确说明自己承载的是主仓库 README 已公布的分数,目的是让两处不漂移。 这份文件的作用是防止数字分叉,而不是提供一次新的运行证据。
第二,仓库中目前唯一提交的机器可读运行记录,是一次 naive 基线在中文数据集 (CN-Scenarios)上的运行:hit@5 为 0.0,题目数 6。 这个记录证明管线能端到端跑通并产出符合 schema 的 JSON。 但它不构成对任何一项公布分数的支持。
第三,harness 的骨架是完整的。 统一 scorer 不含 per-system 逻辑、适配器为三方法接口、naive 基线在内、 输出为机器可读 JSON、数据下载脚本与校验和齐备、scripts/verify_harness.py 可自检。 五件必需品都在。 缺的是「用这套东西把四个分数重跑一遍并把记录提交进仓库」这一步。
因此正确的结论是:
基准骨架已可复现,但 README 中的分数尚未由仓库内一次完整运行独立复算。
这句话不体面,但它准确。写清楚它有三重作用。 对读者,它把「96.2 分」从事实降级为待验证的主张。 避免读者按第三层可信度对待一个实际处于第二层的结果。 对团队,它把缺口变成具体待办——不是「改进算法」,而是「提交一次完整运行记录」。 对本章的论证,它提供了一个自指检验。 如果连基准的维护者都会在「骨架完成」与「数字可复算」之间留出空档, 那么任何只给结论的厂商材料都值得同样对待。
代价必须说清楚。 公开这个状态会让读者对我们的分数打折扣,也会让竞争对手引用这句话作为攻击点。 这是应当付的代价。 一个基准如果只在自己有利时讲可复现,它就不是基准。
16.5 一个诚实的分数该带哪些元数据
分数是一个数,元数据决定这个数能回答什么问题。 以下六项缺任何一项,读者都需要做额外假设才能使用这个分数。
judge 类型。 严格匹配(strict)与宽松语义判定(flexible)在同一份预测上可以差出十几个百分点。 前者要求答案字符串命中标注,后者允许判定模型认定「语义等价」。 两者的差别不是精度高低,而是评测对象不同。 strict 测的是系统能否产出精确形式,flexible 测的是系统能否表达正确意思。 不给 judge 类型的分数无法被解释。
是否完整运行。 全量题集与子集抽样的结果不可比,尤其当抽样不是随机时。 一个常见做法是在「模型表现较好」的题目子集上跑全量、在其余题目上抽样。 这会产生乐观偏差而不易被发现。
是否含沿用行。 一张表里若部分格子来自本次运行、部分来自上次运行或他人公布值,必须逐格标注。 混填是基准表最常见的失真来源,而且往往是善意的——作者只是没重跑。
数据规模。 题数与平均轮数都要给。 500 题与 50 题的分数方差完全不同;十几轮对话与两百轮对话测的是不同能力。 只给题数不给轮数,等于隐藏了最关键的难度变量。
并发度。 并发会改变时序类题目的结果。 高并发下写入乱序,某些依赖「后写覆盖」语义的题目会得到不同答案。 这一项很少被公布,因此复算时出现差异往往归因不到它。
硬件与运行时长。 它同时约束成本与可比性。 同一份 harness 在不同硬件上分数应当一致。 若不一致,说明存在未声明的随机性。 而运行时长决定了这个基准能否被纳入日常回归。
前三项决定分数「在测什么」,后三项决定分数「能不能被重现」。 一份只给分值的材料通常六项全缺。 一份只给分值加数据集名称的材料,通常缺后三项。
16.6 自测清单:拿到任何一家厂商的分数,问五个问题
以下五个问题按回答成本从低到高排列。 前两个问题若对方无法回答,后续问题不必再问。
- 这个分数跑在哪个版本的数据集上,数据集能否通过公开脚本下载并校验?
- judge 是 strict 还是 flexible,判定 prompt 能否看到?
- 是全量运行还是子集抽样,表格里有没有沿用行?
- 平均对话轮数是多少,是否允许跨会话多跳检索?
- harness 能否
git clone之后用一条命令跑通,包括 naive 基线?
五个问题全部得到肯定回答时,这个分数可以按第三层可信度使用。 只得到前四个时,它属于第二层:可以判断场景是否同构,不能用于精确排序。 只得到第一个甚至零个时,它属于第一层,只能用于排除明显不合格者。
这套清单同样适用于自评。 团队在内部公布自己的分数时,如果回答不了第 3 和第 5 个问题, 说明内部评测还没有达到可复算标准。 此时把分数写进汇报材料,与对外部厂商的苛求不对称。
harness 的实际调用方式应当简单到不需要文档。
bash
python -m harness.run --systems mnemosyne,naive --datasets longmemeval--systems 与 --datasets 是两个正交参数,前者决定被测对象、后者决定题集。 输出落在 results/<date>/ 下的 JSON 文件。 逐题记录 system、dataset、question_id、检索到的 id 列表、黄金 id 列表与 hit@k。 两次运行可以直接 diff 出哪些题目变了。
仓库自检则用一个独立入口。
bash
python scripts/verify_harness.py它检查三件事:适配器是否完整实现三个方法、scorer 对同一份预测能否产出稳定结果、 数据校验和是否匹配当前清单。 返回非零说明仓库自身不自洽,此时任何基于它的分数都不应被采信。 这条检查的价值在于把「基准坏了」与「系统退化了」两种失败区分开。 两者的处置方式完全不同。
16.7 本章小结
本章确立了三条判断。
第一,基准的可信度是一个三层结构。 分数本身不携带可信度,附带的过程才携带。 把三层结果放在同一张表里排序,是选型中最常见的错误。
第二,可复现不是态度而是五件具体文件。 缺任何一件,复算链条就断在最容易被做手脚的地方。 统一 scorer、三方法适配器、naive 基线这三件尤其容易被简化掉。
第三,基准与广告的分界在于能否被第三方修改与推翻。 不填没测过的格子、一条命令跑通、适配器开放,这三条红线是这条分界的可操作形式。
三条判断各有成立条件。 三层可信度的判据在读者具备复算能力时最有效。 若读者没有工程能力,第二层与第三层对他没有实际差别。 此时更实际的做法是委托第三方复测。 五件必需品的清单针对「事后无法定位分歧」这一类问题。 如果评测只用于内部排序、不对外公布,可以适度简化,代价是内部分数无法与外部结果对照。 三条红线在团队规模较小时执行成本偏高。 一个三人团队维护开放适配器的投入产出比可能不划算。 此时更合理的选择是直接采用已有开源基准而不是自建。
16.4 的结论需要被当作本章的样板而不是例外。 一个团队对自己分数的诚实程度,应当不低于对他人的要求。 本章目前留下的缺口是「公布分数尚未由仓库内完整运行复算」。 这是一个可以在一到两次完整运行内关闭的缺口。 关闭之前,那些分数应当被引用为待验证主张。
这个缺口同时指向下一章的问题域。 即便四个分数全部被复算,它们仍然不能回答「哪套系统适合你的业务」。 复算解决的是同一个基准内部的可靠性,跨基准的可比性是另一个问题。 数据集版本、判定实现、检索轮数、是否允许多跳,任何一项不同都足以造成十几分的差距。 第 17 章处理这个层面。 不是如何把分数算对,而是为什么两个都算对的分数仍然不能直接比较。