DeerMem 容量驱逐策略基准:用 LongMemEval 确定性复现 confidence 与 hybrid-v1 的差异
本文围绕 deer-flow 中 DeerMem 记忆后端“容量驱逐(capacity eviction)”评测目录 backend/scripts/benchmark/deermem_eviction 展开,讲解如何把可选的 hybrid-v1 容量策略与历史 confidence 策略放在完全受控、可复现的协议下做离线对比与现场 QA 验证。读完本文,你将掌握数据集固定、确定性事实池重建、命令执行、盲评分与配对统计全链路的操作方法和其底层源码依据,并能自行复跑出已发布的结果。
DeerMem 容量驱逐问题与评测动机
DeerMem 是 deer-flow 中负责长期记忆的事实后端,代码位于 deermem/core/eviction.py。当某个快照中的事实数量超过容量上限 max_facts 时,需要一个确定、可解释的评分策略来选出被保留的事实与被驱逐的事实。该模块定义了两种策略常量:
confidence(EVICTION_POLICY_CONFIDENCE):完全沿用历史排名,仅以事实的confidence字段作为评分;hybrid-v1(EVICTION_POLICY_HYBRID_V1):用三个有界信号的加权和confidence * 0.65 + confirmationFreshness * 0.25 + accessHeat * 0.10综合打分,并为category == "correction"的纠错事实预留最少量的保留名额(见 eviction.py)。
仅依赖置信度驱逐存在缺陷:新鲜度高的用户纠错、近期反复访问的冷门事实可能因置信度不高而先被挤出。hybrid-v1 正是针对“confidence-only 驱逐缺陷”提出的修正方案。评测目录的价值在于:它不复制任何评分实现、不引入第三种策略,而是直接调用生产代码中的 select_facts_for_capacity()(见 policy.py),用受控实验验证 hybrid-v1 相对 confidence 的实际增益。
当前评测范围是完全离线、可复现的第一阶段,其目标包括:
- 用仓库修订号和 SHA-256 固定清洗后的 LongMemEval oracle 数据集;
- 只提交 40 个官方问题 ID 与 5 个独立编写的合成纠错守卫,不提交上游题目、参考答案或历史;
- 确定性重建每个 10 事实池;
- 在容量 5、7、9 下对比
confidence与生产hybrid-v1; - 只产出可安全发布的、仅含元数据的逐行结果。
固定的评测输入(Pinned Inputs)
整个评测协议的身份由一组被固定的输入决定,仓库文档用下表明确声明:
| 输入 | 取值 |
|---|---|
| 数据集 | xiaowu0162/longmemeval-cleaned |
| Revision | 98d7416c24c778c2fee6e6f3006e7a073259d48f |
| 文件 | longmemeval_oracle.json |
| SHA-256 | 821a2034d219ab45846873dd14c14f12cfe7776e73527a483f9dac095d38620c |
| 官方样例 | 40 个固定 ID:20 个 knowledge-update、20 个 temporal-reasoning |
| 合成样例 | 5 个纠错守卫 |
| 事实池 | 1 条支撑事实 + 9 条确定性干扰事实 |
| 容量 | 5、7、9;QA 容量为 7 |
| 评测时钟 | 2026-08-13T00:00:00Z(固定,避免访问热度的自然衰减改变复跑结果) |
这些值同时被硬编码在协议配置 configs/pr4789-reproduction-v1.yaml 中,CLI 会以该配置为准逐项校验。配置类 config.py 用 Pydantic 做了大量强约束:数据集 sha256 必须匹配 64 位十六进制、revision 必须是 40 位十六进制;pool.distractors 必须等于 pool.size - 1、干扰池容量必须能覆盖全部干扰项;hybrid 权重必须恰好为 {confidence, confirmation, access}、取值在 [0,1] 且求和为 1.0;evaluation_time 必须带时区并归一到 UTC。
官方与合成样例清单
40 个官方问题分布在四个场景,每个场景 10 个 ID,由官方清单 manifests/longmemeval-pr4789-v1.json 显式声明:confirmation_help、access_help、confidence_control、noisy_signal_control(scenario_order 字段决定离线重建时每连续 5 个样例分配到的场景次序)。同一清单还声明了入选规则:两类可选问题 knowledge-update/temporal-reasoning、8 个被排除的试点 ID、排除 _abs 后缀的弃答样本、参考答案 1–100 字符、证据 1–2000 字符等过滤条件。
5 个合成纠错守卫放在 manifests/synthetic-corrections-pr4789-v1.json,全部属于 correction_reserve 场景、source: synthetic。它们独立编写,用于专门检验“纠错事实必须被保留”这一能力,例如:
correction_peanut_allergy(loss_rank 6):用户纠正“我对花生过敏,之前说我爱吃花生零食是错误的”,问题“你该不该向我推荐花生零食?”,答案NO;correction_language(loss_rank 8):语言偏好纠正为中文;correction_timezone(loss_rank 8):时区纠正为Asia/Shanghai;correction_review_resolution(loss_rank 10):评审线程不可仅因回帖自动关闭;correction_shipping_address(loss_rank 10):收货地址纠正为上海而非北京。
这些纠正事实刻意被放在“纯置信度排序会把它挤出容量上限”的排名位置上(loss_ranks: [6, 6, 6, 8, 8, 8, 10, 10, 10, 10]),用于验证 hybrid-v1 的纠错保留机制而非普通打分高低。
下载并固定数据集
CLI 本身永不下载 LongMemEval。固定文件 longmemeval_oracle.json(约 15 MB)位于公开、免登录的 Hugging Face 数据集仓库,下载一次后以环境变量暴露路径:
export LONGMEMEVAL_ORACLE_PATH=/absolute/path/to/longmemeval_oracle.json
curl -L -o "$LONGMEMEVAL_ORACLE_PATH" \
"https://huggingface.co/datasets/xiaowu0162/longmemeval-cleaned/resolve/98d7416c24c778c2fee6e6f3006e7a073259d48f/longmemeval_oracle.json"
shasum -a 256 "$LONGMEMEVAL_ORACLE_PATH"
# expected: 821a2034d219ab45846873dd14c14f12cfe7776e73527a483f9dac095d38620c
若你的网络无法直连,也可通过 Hugging Face 镜像使用相同的 /datasets/.../resolve/<revision>/... 路径(例如把主机替换为镜像域名),或使用 huggingface-cli download。无论来源如何,任何哈希与固定值不一致的文件都会被所有命令拒绝,因此错误或篡改的下载不可能静默通过;数据集本身与准备好的带文本池都应放在仓库之外或受 gitignore 保护的本地目录中。
四阶段命令全解
所有命令均需在 backend/ 目录下执行,模块入口是 main.py,全部子命令定义在 cli.py。
1. validate-contracts:不依赖数据集校验协议契约
校验已提交的配置、官方/合成清单与回答提示词三者是否一致、哈希是否匹配,无需上游数据集:
PYTHONPATH=. uv run python -m scripts.benchmark.deermem_eviction validate-contracts
对应 cli.py 中的 _load_contracts:强制 config、官方 manifest、合成 manifest 三者的 protocol_id 相同;要求 config 钉住的 qa.grader_version 与已提交评分器版本一致;校验回答提示词文件的实际 SHA-256 与配置中的承诺值一致。
2. validate:校验数据集并重建全部 45 个样例
PYTHONPATH=. uv run python -m scripts.benchmark.deermem_eviction validate \
--dataset "$LONGMEMEVAL_ORACLE_PATH"
该命令校验数据集哈希、按清单重新计算官方样例选择规则、构建干扰事实库并准备全部 45 个样例(40 官方 + 5 合成)。
3. run-policy:零提供商调用的确定性容量选择
PYTHONPATH=. uv run python -m scripts.benchmark.deermem_eviction run-policy \
--dataset "$LONGMEMEVAL_ORACLE_PATH" \
--output-dir /tmp/deermem-eviction-policy-run
命令拒绝覆盖已有运行目录,每次运行必须使用全新的输出目录。CLI 内部会对 45 个样例 × 3 个容量 × 2 个策略(confidence、hybrid-v1)执行 evaluate_case(cli.py)。
4. run-qa:等预算的现场问答(可断点续跑)
export DEERMEM_EVAL_ANSWER_API_KEY=... # 绝不提交或记录日志
export DEERMEM_EVAL_ANSWER_BASE_URL=... # OpenAI 兼容端点
PYTHONPATH=. uv run python -m scripts.benchmark.deermem_eviction run-qa \
--dataset "$LONGMEMEVAL_ORACLE_PATH" \
--output-dir /tmp/deermem-eviction-qa-run
一次全新运行的调用规模为 45 样例 × 2 策略 = 90 次调用(QA 容量 7)。关键设计约束:
- 凭据只在运行前解析:runner 只从配置中声明的两个环境变量名读取凭据,任一缺失就直接失败,不会在触碰数据集之后才报错;
- 两策略完全同配置:模型、温度、
max_tokens、stream、超时、重试次数、并发 worker 全部来自版本化配置,双方设置一致; - 逐行持久化:每次调用成功后立刻把结果写入
responses/<case>__<policy>.json,重跑同一命令可续跑部分运行而不重复已完成调用; - 运行身份绑定:
qa_run.json把输出目录绑定到完整协议身份——config、两个 manifest、回答提示词、数据集四者的 SHA-256——任何一项变更都拒绝续跑; - 行复用受指纹约束:只有行身份、保留事实与
request_fingerprint三者都与当前协议重算结果一致时,存储的行才会被复用;指纹不匹配的行会被重新调用而非静默复用。
行文件只含预测与非机密元数据,绝不包含问题、参考答案、记忆内容、凭据或响应头。
hybrid-v1 底层实现解读
生产策略实现位于 eviction.py,评测目录通过 policy.py 直接调用它。核心评分逻辑:
confidence策略:评分即fact["confidence"],缺失默认 0.5(eviction.py);confirmationFreshness:基于lastConfirmedAt按 90 天半衰期做指数衰减;若无显式确认则退回createdAt,且因“创建弱于显式确认”只取半权重(eviction.py);accessHeat:读取热度经lastAccessedAt衰减后用min(1.0, log1p(heat)/log(9))归一化到 [0,1](eviction.py);- 所有信号先经
_bounded_number收紧到 [0,1],任何非有限值都退回默认值,避免脏数据污染排名; - 最终得分 =
0.65*confidence + 0.25*confirmationFreshness + 0.10*accessHeat(权重与半衰期均为select_facts_for_capacity的默认参数,见 eviction.py)。
排序按 (-score, 原始下标) 执行,因此输入顺序作为稳定的并列裁决键——这正是评测协议刻意按历史事实 ID 顺序输入的原因。hybrid-v1 在 max_facts > 0 时,会先按 correction_reserved_fraction=0.10(上限 correction_reserved_max=10)从排名中预留 category=="correction" 的事实,未用满的名额立刻回归普通竞争(eviction.py)。容量 5、7、9 恰好覆盖了“完全够用 / 需要驱逐少量 / 驱逐较多”三种形态;评测时钟固定为 2026-08-13T00:00:00Z,保证墙上时钟的衰减不会改变复跑结果。
确定性重建(Deterministic Reconstruction)
官方样例不是“检查存在性”,而是从固定数据集独立重算:对两类可选问题按 question_id 排序后取前 20 个,排除清单中声明的 8 个试点 ID 与 _abs 弃答后缀;随后每连续 5 个按清单 scenario_order 分配到四个场景之一。
证据抽取遍历 haystack_sessions:每个会话优先选择标记为 has_answer 的轮次,无标记时回退到用户轮次;每个渲染会话前会加上历史格式的行首 SESSION {id} AT {date}——这一字节级前缀格式很关键,因为证据长度过滤作用于最终渲染值,且 700 字符的干扰库界限也据此判定成员资格(文档注明交叉校验曾正是以这种方式捕获到一次分歧的前缀格式)。
干扰库由按问题 ID 排序的、前 40 条合格的 single-session-user/single-session-preference 记录构成。每个样例的偏移量取自 sha256("deermem-medium-v1:{case_id}") 的前 4 字节,围绕该偏移环形选取 9 条连续记录,用环绕处理越过库尾。事实 ID 沿用历史协议:支撑事实为 gold_{case},第 i 个干扰事实(0 起、按抽取序)为 d_{case}_{i}_{source}。事实在进入生产选择器与提示渲染前都按 ID 排序,使选择器“稳定输入序并列裁决”精确复现历史的并列裁决(干扰按抽取序在前、支撑事实最后),渲染出的 STORED MEMORY 用空行连接各事实块。
在容量 7 下,离线结果精确复现了公开的支撑保留总数:
| 套件 | confidence |
hybrid-v1 |
|---|---|---|
| 40 官方 + 5 合成 | 27/45 | 45/45 |
需要强调的是,这只是确定性选择器结果,并不能证明生产环境下的强化检测或查询访问热度无偏;最终 QA 报告必须将官方、合成纠错与噪声信号结果分开统计。
确定性盲评分器 grading.py
grading.py 实现了公开的评分器,版本号为 deterministic-overlap-v1,由配置 qa.grader_version 钉住,validate-contracts 会在版本不一致时直接拒绝。评分器在构造上就是盲的:grade_answer(prediction, reference) 只接收预测串与参考串两个字符串,绝不接收策略身份;策略归属只能在评分完成后通过稳定的行 ID 回连。
归一化规则:转小写、把每个非字母数字字符替换为空格、把英文数字词 one–ten 与 fifteen 映射为数字。随后按顺序应用 6 条规则(grading.py):
- 拒绝空预测或精确的
INSUFFICIENT哨兵; - 归一化 token 序列完全相等即接受;
- 一方 token 序列作为连续子序列包含于另一方即接受(token 级,因此
5永远不会匹配进25内部); - 预测的整数 token 全部落在参考答案显式
ranging from X ... to Y区间内即接受; - 双方都含整数且冲突则拒绝;
- 否则要求双向去停用词后的唯一 token 重叠率 ≥ 60%(
OVERLAP_THRESHOLD)。
停用词表是这一评分器版本的固定组成部分(#4789 披露未公开精确停用词表):以常见英文功能词为主,且有意排除 yes、no、not——因为否定可以是整个答案。修改停用词表或任何一条规则都必须发布新的 grader_version。冻结前,评分器曾对 #4789 披露的全部 90 个历史 (prediction, reference) 对做本地交叉校验,逐条复现了历史评分且之后未做任何按结果调参。
grade-qa:盲评分与配对统计
PYTHONPATH=. uv run python -m scripts.benchmark.deermem_eviction grade-qa \
--dataset "$LONGMEMEVAL_ORACLE_PATH" \
--output-dir /tmp/deermem-eviction-qa-run
grade-qa 不做任何提供商调用,且拒绝覆盖既有文件。评分前它以只读方式完成五重验证:运行标记中五个协议工件(config、清单 ×2、提示词、数据集)的哈希与当前一致;重算确定性选择器输出与每个任务的请求指纹;拒绝任何保留事实、容量、策略或存储指纹与重算协议不一致的答案行;90 行任缺一行即拒绝继续。因此,一个已发布的 qa.rows.jsonl 可以证明其预测正是在被评分的那份协议下产生的,而非早期序列化产物。
输出契约:run-policy 与 grade-qa 分别产出什么
run-policy 在输出目录创建三个文件:
run.json:记录 git 状态、不可变数据集身份、评测时钟、容量,以及 config、manifest、prompt 的 SHA-256;policy.raw.jsonl:每个样例 × 容量 × 策略一行,含事实 ID、保留/驱逐 ID、评分分量、支撑保留与纠错预留信息;永不包含事实内容、问题或参考答案;summary.json:按来源、场景、容量、策略汇总支撑保留率;合成纠错不会被并入“仅官方”指标。
grade-qa 在已完成的答案运行目录上新增三个可发布文件(同样拒绝覆盖):
qa.rows.jsonl:每个样例 × 策略一行已评分记录——ID、场景/来源、保留事实元数据、模型预测、带判定规则的评分、评分器版本与非机密响应元数据;qa.summary.json:按来源、场景、策略报告准确率,官方与合成纠错永不合并成单一数字;qa.stats.json:分别对官方、合成、整体套件报告精确配对 McNemar 检验与固定种子的配对 bootstrap 差异(hybrid-v1减confidence),统计参数由 config 钉住(bootstrap_seed: 4789、bootstrap_iterations: 10000、alpha: 0.05)。
唯一允许合并的数字是 qa.stats.json 中显式标注的 overall 套件,且它总是与分套件一起报告,而不是取而代之。
完整的提供商请求、数据集文本与构造好的池必须留在被 gitignore 的本地目录;提供商响应头因可能包含敏感或账号级数据而永不持久化。
QA 提供商配置与模型寻址
QA 相关参数全部版本化在 configs/pr4789-reproduction-v1.yaml:
| 配置项 | 取值 |
|---|---|
qa.provider |
openai-compatible |
qa.api_key_env / qa.base_url_env |
DEERMEM_EVAL_ANSWER_API_KEY / DEERMEM_EVAL_ANSWER_BASE_URL |
qa.model |
deepseek-v4-flash |
qa.temperature |
0 |
qa.max_tokens |
2048 |
qa.stream |
false |
qa.timeout_seconds |
120 |
qa.max_attempts |
3 |
qa.workers |
3 |
qa.grader_version |
deterministic-overlap-v1 |
回答提示词模板在 prompts/answer-v1.txt:系统指令要求“只用下方存储记忆作答,无支持则输出 INSUFFICIENT,YES/NO 只输出 YES 或 NO,否则给最短直接答案”;用户模板以 {{CURRENT_DATE_SECTION}}、STORED MEMORY: 与 QUESTION: 占位拼接。该文件内容同样是协议身份的一部分(其 SHA-256 被 config 钉住并由 CLI 校验)。
关于模型命名,README 说明:历史协议披露的答案模型记录为 deepseek/deepseek-v4-flash 这种聚合器式命名空间;本评测改为通过 DeepSeek 官方 OpenAI 兼容 API 直接调用同一底层模型(DeepSeek-V4-Flash-0731,发布于历史运行之前),其官方规范 ID 即 deepseek-v4-flash,配置钉住该 ID。每次调用实际提供服务的模型从提供商响应中记录到每一答案行的 response_model 字段。
已发布结果与解读
results/pr4789-reproduction-v1/ 目录(results)存放了等预算现场运行的公开产物——qa_run.json(溯源)、qa.rows.jsonl(90 行评分)、qa.summary.json、qa.stats.json。该运行执行于仓库修订 01f99d61(2026-08-18,DeepSeek 官方 API,deepseek-v4-flash),且是在证据渲染、历史事实 ID 方案与提示序列化等交叉校验发现的全部问题被采纳之后执行的;早前在分歧序列化下执行的运行被整体丢弃而非部分复用。离线套件验证了已发布统计可由公开行重算。
容量 7、两策略设置完全相同时的 QA 准确率:
| 套件 | confidence |
hybrid-v1 |
精确 McNemar p | 准确率差异(95% CI) |
|---|---|---|---|---|
| 40 官方 | 23/40 | 35/40 | 0.0018 | +0.300 [+0.150, +0.450] |
| 5 合成纠错 | 1/5 | 5/5 | 0.1250 | +0.800 [+0.400, +1.000] |
| 45 整体 | 24/45 | 40/45 | 0.0001 | +0.356 [+0.200, +0.511] |
场景分解:confirmation_help 3/10 对 10/10,access_help 3/10 对 9/10,confidence_control 8/10 对 7/10,noisy_signal_control 9/10 对 9/10,合成纠错 1/5 对 5/5(与 qa.summary.json 逐项一致)。confidence_control 是本次运行中 hybrid-v1 唯一低于 confidence 基线的场景,它被单独报告而不并入任何其他指标。
从不一致单元分解可见:hybrid-v1 相对基线恰好输掉一个样例(1cea1afa,confidence-control),且其支撑事实被保留——因此这不是驱逐失败,而是模型在支撑事实仍在场的情况下仍以 INSUFFICIENT 弃答。两个策略保留了不同的干扰事实集,会轻微影响答案措辞与弃答倾向;评分器保持冻结,该单元按原样报告。
两个总数都远高于历史的 14/45 对 23/45,主因是历史 confidence 基线被限制为 1024 输出 token,而本次运行给两策略相同 2048 token 预算。本次运行 90 次调用共消耗 86,342 输入 token 与 12,340 输出 token。
历史结果警示与后续运行要求
#4789 披露的行级工件修正了 PR 正文中噪声信号 QA 结果(从 5/10 vs 5/10 更正为 5/10 vs 6/10),并揭示历史 confidence 行使用了 1024 token 基线而 hybrid 行使用 2048 token 且重新调用。因此后续现场运行必须满足五项要求:
- 两个策略都重新运行,而非复用历史基线;
- 使用相同的模型、提示词、2048 token 预算、重试策略与并发度;
- 评分器对策略身份保持盲态;
- 保存不含上游数据集文本的公开行级输出;
- 官方与合成统计分开报告。
测试:离线、仅用合成数据的回归保障
全部默认测试都是离线的,只用合成的 LongMemEval 形态数据行:
PYTHONPATH=. uv run pytest tests/test_bench_deermem_eviction_*.py -q
对应仓库中的一组测试文件:test_bench_deermem_eviction_contracts.py、test_bench_deermem_eviction_policy.py、test_bench_deermem_eviction_qa.py、test_bench_deermem_eviction_report.py、test_bench_deermem_eviction_results.py、test_bench_deermem_eviction_published_results.py。覆盖范围包括:config 与 manifest 契约、提示词哈希、数据集完整性拒绝、证据抽取、干扰过滤、确定性池构造、生产选择器行为、纠错预留、公开结果脱敏、覆盖保护,以及每一条评分规则及其边界情况。
总结:为什么这套协议值得复用
把整套评测串起来看,deermem_eviction 目录的价值不在算法新颖,而在方法论的严谨:固定数据集哈希、固定评测时钟、事实 ID 顺序作为并列裁决键、评分器对策略身份盲态、输出目录与协议身份强绑定、公开产物只含元数据且可被离线重算。对任何想验证 DeerMem hybrid-v1 容量策略、或者计划在 deer-flow 上开展类似受控记忆评测的开发者,这套 README.md、配置、源码 与 测试 的组合既是一份可直接照跑的说明书,也是一份可审计、可复算的实验记录。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0626
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00