首页
/ DeerMem 容量驱逐策略基准:用 LongMemEval 确定性复现 confidence 与 hybrid-v1 的差异

DeerMem 容量驱逐策略基准:用 LongMemEval 确定性复现 confidence 与 hybrid-v1 的差异

2026-09-07 10:04:43作者:史锋燃Gardner

本文围绕 deer-flow 中 DeerMem 记忆后端“容量驱逐(capacity eviction)”评测目录 backend/scripts/benchmark/deermem_eviction 展开,讲解如何把可选的 hybrid-v1 容量策略与历史 confidence 策略放在完全受控、可复现的协议下做离线对比与现场 QA 验证。读完本文,你将掌握数据集固定、确定性事实池重建、命令执行、盲评分与配对统计全链路的操作方法和其底层源码依据,并能自行复跑出已发布的结果。

DeerMem 容量驱逐问题与评测动机

DeerMem 是 deer-flow 中负责长期记忆的事实后端,代码位于 deermem/core/eviction.py。当某个快照中的事实数量超过容量上限 max_facts 时,需要一个确定、可解释的评分策略来选出被保留的事实与被驱逐的事实。该模块定义了两种策略常量:

  • confidenceEVICTION_POLICY_CONFIDENCE):完全沿用历史排名,仅以事实的 confidence 字段作为评分;
  • hybrid-v1EVICTION_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_helpaccess_helpconfidence_controlnoisy_signal_controlscenario_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 个策略(confidencehybrid-v1)执行 evaluate_casecli.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):

  1. 拒绝空预测或精确的 INSUFFICIENT 哨兵;
  2. 归一化 token 序列完全相等即接受;
  3. 一方 token 序列作为连续子序列包含于另一方即接受(token 级,因此 5 永远不会匹配进 25 内部);
  4. 预测的整数 token 全部落在参考答案显式 ranging from X ... to Y 区间内即接受;
  5. 双方都含整数且冲突则拒绝;
  6. 否则要求双向去停用词后的唯一 token 重叠率 ≥ 60%(OVERLAP_THRESHOLD)。

停用词表是这一评分器版本的固定组成部分(#4789 披露未公开精确停用词表):以常见英文功能词为主,且有意排除 yesnonot——因为否定可以是整个答案。修改停用词表或任何一条规则都必须发布新的 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-v1confidence),统计参数由 config 钉住(bootstrap_seed: 4789bootstrap_iterations: 10000alpha: 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.jsonqa.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/4523/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 且重新调用。因此后续现场运行必须满足五项要求:

  1. 两个策略都重新运行,而非复用历史基线;
  2. 使用相同的模型、提示词、2048 token 预算、重试策略与并发度;
  3. 评分器对策略身份保持盲态;
  4. 保存不含上游数据集文本的公开行级输出;
  5. 官方与合成统计分开报告。

测试:离线、仅用合成数据的回归保障

全部默认测试都是离线的,只用合成的 LongMemEval 形态数据行:

PYTHONPATH=. uv run pytest tests/test_bench_deermem_eviction_*.py -q

对应仓库中的一组测试文件:test_bench_deermem_eviction_contracts.pytest_bench_deermem_eviction_policy.pytest_bench_deermem_eviction_qa.pytest_bench_deermem_eviction_report.pytest_bench_deermem_eviction_results.pytest_bench_deermem_eviction_published_results.py。覆盖范围包括:config 与 manifest 契约、提示词哈希、数据集完整性拒绝、证据抽取、干扰过滤、确定性池构造、生产选择器行为、纠错预留、公开结果脱敏、覆盖保护,以及每一条评分规则及其边界情况

总结:为什么这套协议值得复用

把整套评测串起来看,deermem_eviction 目录的价值不在算法新颖,而在方法论的严谨:固定数据集哈希、固定评测时钟、事实 ID 顺序作为并列裁决键、评分器对策略身份盲态、输出目录与协议身份强绑定、公开产物只含元数据且可被离线重算。对任何想验证 DeerMem hybrid-v1 容量策略、或者计划在 deer-flow 上开展类似受控记忆评测的开发者,这套 README.md配置源码测试 的组合既是一份可直接照跑的说明书,也是一份可审计、可复算的实验记录。

登录后查看全文
热门项目推荐
相关项目推荐