库满了,删哪条
DeerFlow 的记忆后端 DeerMem 给每个 agent 的 fact 库设了上限,默认 100 条。新 fact 进来,超了上限就得挤掉一条。假设现在有两个候选:一条 confidence 0.9,180 天没人再提过;另一条 confidence 0.7,用户七天前刚确认过"对,就是这样"。再换一组:一条 correction 类别的 fact,记录的是用户纠正过 agent 的某个错误做法,confidence 0.7,对面是十条 confidence 0.9 的 preference。
2026 年 8 月之前,DeerMem 的答案是删 0.7 那条,也删那条 correction。
这个问题我在 issue #4641 里提出,AnnaSuSu 在 PR #4789 里用一个需要显式打开(opt-in)的 hybrid-v1 策略修掉了它,PR #4810 把他的评测协议做成了仓库里可以独立重跑的评测。两个 PR 都在 2.1.0 里。
问题:只看 confidence 的淘汰(issue #4641)
#4641 提出时 main 在 8234370a,容量控制全部集中在 _trim_facts_to_max 一个函数里:所有 fact 按 confidence 降序排,保留前 max_facts 条,updater 在追加新 fact 之后调用它。排序不看 updatedAt,没有"最近一次确认"的时间戳,没有类别配额,也没有任何多样性信号。fact 本身有 createdAt 和 updatedAt 两个字段,淘汰路径不用它们。
单看这一个函数,问题只是排序键太单一。放回整个系统里看,问题是三套治理互不认识:
- DeerMem 在注入侧对
correction有专门保护:它是默认的 guaranteed 类别,拥有独立的 500 token 预算,prompt 格式化时和普通 fact 分开挑选,目的是让"别用 pip,用 uv"这种纠正在预算紧张时也能进 prompt。 - 按 fact 年龄触发的 staleness review 也默认保护
correction。(解决"这条 fact 还是真的吗"的问题) - 但容量裁剪两个设置都不读。(解决"库满了,牺牲哪几条"的问题)
correction 一旦被高 confidence 的 preference 挤出库,注入侧那 500 token 保障的是一个已经不存在的东西。#4641 里的这张图是当时的容量路径,和它绕开的两项保护:
第三层是删除方式。fact 的 schema 只接受 status: "active",删除是物理的:持久化时把快照里缺失的 fact ID 转成 delete,storage 直接 unlink 对应的 Markdown 文件。没有 archived 状态,没有审计记录,没有恢复路径。淘汰错了,看不到删了什么,也看不到它为什么输了排序。
issue 里 AnnaSuSu 先后提了两个要求:有没有具体测试,以及效果要有具体的测试集。前一个我在
upstream/main@e16ef296上用三个本地离线测试回答,结果和上面的推演一致:0.9@180 天赢了 0.7@7 天;一条 0.7 的 correction 对十条 0.9 的 preference,correction 出局;重新加载后这条 correction 不在持久化数据里,Markdown 文件已删,没有archived状态。后一个要求划定了后面两个 PR 的边界:修复的参数要来自受控对比,评测要能被别人重跑。
修复:hybrid-v1(PR #4789,AnnaSuSu)
#4789 由 AnnaSuSu 设计并实现,PR 里对 #4641 的发现和方向做了致谢。我本地那版方向重合的实现没有再提,转去做评测。
评分:三个有界信号
hybrid-v1 的容量评分是三个 [0, 1] 区间信号的加权和:
1 | score = 0.65 * confidence |
- confidence 是现成字段,解析失败时退回 0.5,一条脏数据不会让整个淘汰路径抛异常。
- confirmation_freshness 用半衰期衰减:有
lastConfirmedAt就按它算,半衰期 90 天,七天前确认过的 fact 拿到约 0.95;从来没确认过的 fact 退回createdAt,再乘 0.5,创建这件事本身是比用户确认弱得多的证据;两个时间戳都解析不了就记 0。 - access_heat 来自一个单独的 usage sidecar,先按 30 天半衰期衰减,再做
log1p(heat) / log(9)的对数饱和,截到 1。对数饱和的意思是热度 8 就到顶,被搜到一百次和八次没有区别,这条信号只占 0.10 的权重,不会因为某个 fact 反复命中就把别的都挤掉。
三个权重在 config 里可调,加起来必须是 1,不然启动就报错。
回到开头那两条 fact,两条都没有访问热度,access 项为 0。记 confirmation_freshness 为 ,总分为 。0.9@180 天、从未确认的那条:
0.7@7 天、七天前确认过的那条:
七天前的确认赢了。
确认信号从哪来:不加 LLM 调用
难点在 confirmation 信号。"用户确认过这条 fact"不是现成字段,要有人判断。Anna 的做法是不加新的 LLM 调用,复用 memory-update 那次已有的调用:响应 schema 里新增可选的 factsToReinforce 数组,让模型在做记忆更新时顺便报告这轮对话明确确认了哪条已有 fact。模型说了不算,lastConfirmedAt 只有在三个条件同时满足时才会更新:
- 确定性的消息处理在最近六条过滤后的人类消息里匹配到了 reinforcement 模式,“对,就是这样”“完全正确”“that’s exactly right” 这类正则,pattern 文件可以扩展;
- 模型给出的条目 scope 是
user;reason 非空。 - 重复提取、自动注入、搜索命中都不算确认,prompt 里专门写了这条。
pattern 文件的注释里记了一条取舍:“你说的对”“没错”"对的"这类单独的肯定故意不在默认列表里,“对"在"对不起”“对方”"对啊"里都会误命中,需要时自行扩展。有效确认同时重置那条 fact 的 staleness review 时钟,不然一条
createdAt很老的 fact 刚被确认,转头就被年龄清理掉。
id / scope / reason"] --> Cond["模型条目:scope == user,reason 非空"] Det --> Gate{"两边同时成立?"} Cond --> Gate Gate -- "是" --> Upd["按 id 找到已有 fact
lastConfirmedAt = now
confirmationCount + 1"] Gate -- "否" --> Skip["不更新"] Upd --> Clock["staleness review 时钟重置"] Excl["重复提取 / 自动注入 / 搜索命中"] -. "都不算确认" .-> Skip
correction 保留槽和其他
对开头第二组 fact,评分改了也不够,十条 0.9 的 preference 在任何加权下都能压过一条 0.7 的 correction。所以 hybrid 模式先给 correction 留 min(10, ceil(max_facts × 10%)) 个槽,按分数最高的 correction 先占,占不满的槽立即释放给普通竞争,超出保留数的 correction 也正常竞争。默认 100 条的库,保留 10 个槽。整条选择路径合起来是这样:
字段值,解析失败取 0.5"] F["confirmation_freshness
lastConfirmedAt 按 90 天半衰期
未确认则 createdAt 衰减再乘 0.5"] A["access_heat
usage sidecar,30 天半衰期
log1p(h) / log(9) 截到 1"] end C --> S["score = 0.65 C + 0.25 F + 0.10 A"] F --> S A --> S S --> R["correction 保留槽
min(10, ceil(max_facts × 10%))
按分数先占,占不满即释放"] R --> Fill["剩余名额按 score 降序填满"] Fill --> Kept["kept"] Fill --> Evicted["evicted"] Evicted --> Audit["审计 sidecar
只记 ID、类别、分数分量、原因"]
另外几处设计一起说:
- 自动提取、手工或工具创建、导入三条写入路径原来各走各的容量逻辑,现在统一过
select_facts_for_capacity()。 - 淘汰仍是物理删除,软删除涉及存储迁移,但每次容量淘汰会追加一条只含元数据的审计记录:fact ID、类别、策略分数、各分量、原因,不复制内容,默认保留 200 条。
- usage 和审计 sidecar 与 canonical Markdown 解耦,搜索命中不会改写 fact 文件和它的
updatedAt;sidecar 只在 hybrid-v1 或 shadow mode 打开时才读写,默认部署不多一次文件锁。 - 默认策略仍是
confidence,hybrid-v1要显式打开;还有一个 shadow mode,用 hybrid 算一遍但不改实际保留结果,只记录两种策略的分歧。
opt-in 的原因 Anna 在 PR 里写得很直接:权重是在受控的、强制到容量上限的对比里选出来的,“They remain opt-in because the evaluation used synthetic metadata and is not sufficient to justify a default switch.”
评测:把一段文字协议做成可以重跑的东西(PR #4810)
#4810 目标是任何人拿到仓库和数据集,能跑出一样的池子、一样的 prompt、一样的判分,再用同一套东西跑一次两边同预算的 QA 对比。"一样"有好几层,每层要单独设计。整条链路如下,后面几小节各对应其中一段:
本地文件,pin revision 与 SHA-256"] --> V["validate
校验 hash,重算 40 题筛选规则
构建 bank,准备 45 个 case"] M["仓库内:config、两个 manifest、prompt
只有题目 ID,没有题目"] --> V V --> Pool["确定性池子重建
sha256 offset 取 9 条干扰项
gold_ / d_ 命名,按 ID 排序"] Pool --> Sel["生产 select_facts_for_capacity()
confidence 与 hybrid-v1
容量 5 / 7 / 9"] Sel --> RP["run-policy
270 行元数据
support retention"] Sel --> RQ["run-qa,容量 7
90 次调用,两边同参数
每行存 request_fingerprint"] RQ --> Rows["responses/ 行文件
预测与非敏感元数据"] Rows --> G["grade-qa
核对五个 hash,重算任务与 fingerprint
盲判分,按 row ID join 回策略"] G --> Out["qa.rows.jsonl
qa.summary.json
qa.stats.json"]
评什么:四个 scenario 各控制一件事
40 道题来自 Hugging Face 数据集 xiaowu0162/longmemeval-cleaned 的 longmemeval_oracle.json,只取 knowledge-update 和 temporal-reasoning 两类,排除 8 个 pilot ID、_abs 结尾的 ID、答案为空或超过 100 字符、答案含 “not enough” 或 “only mentioned”、证据为空或超过 2000 字符的行,每类按 question_id 排序取前 20,切成四组各五题,两类对应的组合并成一个 10 题的 scenario。每道题的证据从 haystack_sessions 里抽 has_answer 的轮次拼成一条支持事实,另配 9 条干扰项。干扰项来自一个 40 条的 bank,取 single-session-user 和 single-session-preference 两类、渲染后证据长度在 40 到 700 字符之间的记录。
四个 scenario 的区别全在支持事实和干扰项的元数据上:
| Scenario | 支持事实 | 干扰项 | 想验证什么 |
|---|---|---|---|
confirmation_help |
confidence 0.70,7 天前确认 | 前 N 条 0.94 起每条减 0.02,其余 0.68 起递减,都无信号 | 确认信号能否救回低 confidence 的事实 |
access_help |
confidence 0.70,访问热度 8 | 同上 | 访问热度能否救回 |
confidence_control |
confidence 0.95,无信号 | 前五条 0.94 起递减;其余 0.70,交替带 7 天前确认或热度 8 | 0.25 + 0.10 的权重会不会反过来掀翻高 confidence 的事实 |
noisy_signal_control |
confidence 0.90,无信号 | 前 N 条 0.70 且交替带信号,其余 0.70 无信号 | 干扰项上的噪声信号会不会把稳的事实挤出去 |
N 由每题的 loss_rank 决定,序列固定为 (6, 6, 6, 8, 8, 8, 10, 10, 10, 10),N = loss_rank − 1。loss_rank 8 的意思是按 confidence 排支持事实排第八,容量 7 时刚好出局。所以纯 confidence 策略在两个 help scenario 各只能保住 3 条,也就是 loss_rank 6 的那三题;两个 control scenario 全保住;5 个 correction case 只保住 1 条。加起来 27/45。这个数字不经过任何模型,是协议对齐的第一道关卡。QA accuracy 是第二层指标:保留下来的 fact 按固定 prompt 交给模型,要求只用给出的 stored memory 回答,不够就输出 INSUFFICIENT,YES/NO 题只答 YES 或 NO,其他题给最短的直接答案。
5 个 synthetic correction case 由 Anna 编写,比如 “USER CORRECTION: I am allergic to peanuts. The earlier note saying I enjoy peanut snacks was wrong.”,问题是 YES/NO,支持事实类别 correction、confidence 0.65,干扰项从 0.90 起每条减 0.03。它测的是保留槽,不是评分。
数据边界:pin 住,不下载,不提交
LongMemEval 的题目、答案和对话不能进仓库,评测又必须能确认大家跑的是同一份数据。做法是把数据集 pin 到仓库 revision 98d7416c 和文件 SHA-256 821a2034…,CLI 从不下载,调用者自己把文件放到本地,任何命令在读之前先校验 hash,不匹配就拒绝。仓库里只提交 40 个 question ID 和 scenario 分组,以及 Anna 原创的 5 个 synthetic case 全文。所有输出行只含 ID、kept 和 evicted 的 fact ID、分数分量、保留标记、模型预测和判分,不含事实内容、题目和参考答案。
池子重建没有随机数。每题的 9 条干扰项从 bank 里连续取,起点是 sha256("deermem-medium-v1:{case_id}") 前四个字节对 bank 长度取模,取到尾部就绕回开头;支持事实和干扰项的元数据按上面的规则赋值,确认时间和访问时间都相对于固定的评测时钟 2026-08-13T00:00:00Z,半衰期衰减用这个时钟算,隔一年重跑结果也不会漂。
第二条边界是不抄评分实现。评测直接 import 生产代码里的 select_facts_for_capacity(),两种策略都通过它跑;config 里 required_policy_version: hybrid-v1 会和生产常量 EVICTION_POLICY_HYBRID_V1 对比,生产策略版本变了,validate-contracts 直接失败。
grader:把文字规则变成可冻结的判分器
QA accuracy 表里的每一个"对"都要有人判。模型输出是自由文本,参考答案是一句话,不能直接比字符串。Anna 用的是确定性规则,不是人工,也不是再叫一个模型当裁判,零成本,每次跑结果一样。要复现他的数字,我的判分器必须在每一行上和他做出同样的决定。
他披露的规则是一段文字,实现成 deterministic-overlap-v1 后是这样:先归一化,两边全小写、非字母数字字符换成空格、英文数字词 one 到 ten 和 fifteen 换成数字;然后按顺序过六条规则:
- 输出为空或等于
INSUFFICIENT,判错; - 归一化后完全相同,判对;
- 一边是另一边的连续片段,判对,比如参考答案 Paris、模型答 in Paris;
- 参考答案是 “ranging from X to Y” 的区间、模型答的整数都落在区间里,判对;
- 两边都有整数但对不上,判错;
- 以上都没命中,去掉 the、of 这类功能词后,剩下的词双向都有 60% 以上重合才判对。
有两处光靠文字定不下实现。第 3 条必须按词匹配:参考答案 5、模型答 25,按字符 “5” 是 “25” 的子串会误判成对,按词就不会。第 6 条要先剔掉功能词,Anna 没有给词表,我提交了一份固定的英文功能词表,特意把 yes、no、not 留在外面,YES/NO 题的整个答案就是这一个词,剔掉就没法判了。
冻结的依据是 Anna 公开的 90 行历史记录:每行有模型当时的回答、参考答案和他判的对错,两种策略各 45 行。把这 90 对回答和参考答案喂给新判分器,90 个对错逐条一致,才把版本号写进 config,validate-contracts 校验这个版本号,之后改任何规则都必须换版本号。新的 live run 跑完后没有回头改过规则。
判分函数 grade_answer(prediction, reference) 只收两个字符串,不知道这条回答来自哪个策略,判完再按行 ID 拼回策略和 scenario。判分逻辑没有机会偏向任何一边。
协议等价的三层:聚合、逐行、逐字节
"复现 Anna 的评测"可以停在三个深度:
- 聚合指标一致,比如容量 7 下的 27/45 对 45/45;
- 每题的池子和保留集逐行一致;
- 发给模型的请求逐字节一致。
三层之间不能互相推出。这个协议里,聚合保留率只由每条干扰项的序号决定,第 i 条给什么 confidence、带不带信号,都和它是哪条记录无关;换掉 bank 里的记录,序号不变,保留率一个数都不动。27/45 对 45/45 只能证明元数据规则对了,证明不了池子对了。Anna 逐行比对第一版实现时的结论就是这句:
The matching aggregate alone does not establish row-level reproduction.
逐字节一致最终取决于三个披露里没有写的协议细节。
证据渲染格式,每个 session 的证据前缀是 SESSION {id} AT {date}。它决定渲染后的长度,而 bank 的准入条件是 40 到 700 字符。记录 35a27287 用这个前缀是 697 字符,换成 [session_id={id} date={date}] 是 704,跨过上限被 60d45044 顶替。bank 变了一条,每题的 offset 不变但指向的记录变了,33 个池子、264 条元数据分配、65/90 个容量 7 保留集、全部 90 条 prompt 随之变化,保留率仍是 27/45 对 45/45。
SESSION id AT date 写成了另一种前缀"] --> Len["记录 35a27287 的渲染长度
697 字符变成 704 字符"] Len --> Bound["跨过 bank 的 700 字符上限
被 60d45044 顶替"] Bound --> Bank["bank 成员变了,offset 不变"] Bank --> Pools["33/45 个池子变化
264 条元数据分配变化"] Pools --> Kept["65/90 个容量 7 保留集变化"] Pools --> Prompts["90/90 条 prompt 变化"] Pools -. "元数据只看干扰项序号" .-> Agg["支持事实保留 27/45 对 45/45
不变"]
fact ID 命名与排序,支持事实是 gold_{case},干扰项是 d_{case}_{index}_{source},进入选择器前按 ID 排序。生产选择器按分数降序、同分按输入顺序,所以 ID 的字典序就是 tie-break。用数据集原始 ID 时,题 41698283 在容量 7 下保留的是 001be529,历史保留的是 58bf7951,2/90 个保留集不同。
prompt 序列化,STORED MEMORY 里 fact 块之间是空行,块的顺序就是排序后的 ID 顺序。这一项不改变任何保留集,只改变请求字节。
三次 live run 对应这三项逐步对齐的协议,每次协议改动后旧运行整体作废,不部分复用:
| run | 协议状态 | 官方 40 题 confidence / hybrid-v1 |
exact McNemar p |
|---|---|---|---|
| 1 | 自定义前缀、原始 ID、单换行 | 23/40 / 35/40 | 0.0042 |
| 2 | 历史前缀 | 23/40 / 33/40 | 0.0129 |
| 3 | 历史前缀、历史 ID、空行 | 23/40 / 35/40 | 0.0018 |
三次运行的确定性保留率都是 27/45 对 45/45。前两层可以自验,第三层只有持有历史产物的人能验,所以"复现"的结论由 Anna 给出。他在最终版 e481a2e7 上的比对结果:45/45 个池子的 fact ID、内容和元数据一致,90/90 个容量 7 保留集一致,90/90 条请求消息逐字节一致,冻结的 grader 复现 90 个历史判分。
运行身份与完整性校验
设计目标是一份发布的 qa.rows.jsonl 能自己证明每一行是在被判分的那个协议下产生的,不靠作者保证。协议身份由五个文件的 SHA-256 定义:config、官方 manifest、synthetic manifest、answer prompt、数据集。run-qa 在输出目录写 qa_run.json,记下这五个 hash 和 git 状态。每一行回答文件除了预测和用量,还存 row_id、case_id、source、scenario、policy、capacity、kept_fact_ids,以及 request_fingerprint,也就是发给模型的完整消息的 SHA-256。
校验分布在两个阶段:
| 阶段 | 校验内容 | 不通过时 |
|---|---|---|
run-qa 续跑 |
五个 hash 与当前协议一致;每条已存在的行用当前协议重算任务,row_id、case_id、source、scenario、policy、capacity、保留集、fingerprint 全部一致 |
hash 不一致:拒绝在该目录续跑,指出变的是哪个文件;行不一致:重新调用,不复用 |
grade-qa 发布 |
只读核对五个 hash;重建全部 90 个任务;逐行核对 fingerprint 和身份字段;参考答案取自重算的 PolicyResult,不取自行内的 case_id |
任一行不匹配或缺失:整个判分拒绝执行 |
回归测试逐个篡改每个身份字段,每一种都必须被拒绝;另一组测试从发布的行重算 McNemar 和 bootstrap,与 qa.stats.json 逐字节一致。
这些检查有三条来自 review。续跑最初只绑 config 一个 hash,已存在的行不核对 fingerprint 就复用,改一条消息后第二次运行报 1 reused, 0 called,于是有了五个 hash 的 marker 和逐行重算。发布路径当时没有同样的校验,一条在旧序列化下产生的行可以直接进入发布结果,这正是前一节三次重跑遇到的漂移类型,此前只靠 Anna 的带外比对才发现。判分最初从行里存的 case_id 取参考答案,把一条合法行的 case_id 改成另一个合法 case,fingerprint、保留集、容量、策略都不动,所有校验通过而判分从对变错,续跑的匹配函数同样没有比对 case_id、source、scenario。三处都在合入前补上,已发布的行没有改动,判分结果不变。
统计:两边同预算
Anna 的历史运行里,confidence 基线用的是 max_tokens=1024,hybrid 候选是 2048 的新调用,基线行被复用。他在披露里主动说了这一点,也建议后续两边重跑。#4810 里两种策略都是新调用,deepseek-v4-flash,temperature 0,max_tokens=2048,三次重试,三个 worker,走 DeepSeek 官方 API,每行记录 API 返回的 response_model。90 次调用一共消耗 86,342 input 和 12,340 output token,大约 0.03 美元。
配对统计用 exact McNemar 加 seed 固定的 paired bootstrap,seed 4789,10000 次,α=0.05,官方 40 例、synthetic 5 例、45 例 overall 三个 suite 分开报,qa.summary.json 里永远不把官方题和 synthetic 折成一个数。离线测试从发布的行重新计算统计量,和 qa.stats.json 逐字节一致。
怎么跑
从 backend/ 下执行,四条命令对应四层。validate-contracts 不需要数据集,校验提交的 config、manifest 和 prompt 的 hash;validate 校验数据集 hash、重新计算 40 题的筛选规则、构建 bank、准备 45 个 case;run-policy 在容量 5、7、9 下跑确定性选择,输出 270 行元数据,不调任何模型;run-qa 和 grade-qa 才需要 API key。前三条完全离线,不需要 API key 就能复现 27/45 对 45/45 那张表。
1 | export LONGMEMEVAL_ORACLE_PATH=/absolute/path/to/longmemeval_oracle.json |
合并前最后一处改动是文档。维护者试跑时发现 README 只写了数据集名和 hash,没写它是公开、无需登录的 Hugging Face 数据集,也没给下载命令,有些网络里 huggingface.co 本身就访问不了。69eecfa5 补上了直接下载的 curl 命令、期望的 hash 和 hf-mirror.com 的替代主机。
工具方面按 PR 里的披露:评测脚手架和协议校验用了 Codex,rebase、grader、runner、统计和 live run 用了 Claude Code;协议、代码和结果我逐一审过并负责。
结果怎么读
发布在 results/pr4789-reproduction-v1/ 的最终运行,容量 7,两种策略同参数:
| Suite | confidence |
hybrid-v1 |
Exact McNemar p | 准确率差(95% CI) |
|---|---|---|---|---|
| 40 官方题 | 23/40 | 35/40 | 0.0018 | +0.300 [+0.150, +0.450] |
| 5 synthetic corrections | 1/5 | 5/5 | 0.1250 | +0.800 [+0.400, +1.000] |
| 45 overall | 24/45 | 40/45 | 0.0001 | +0.356 [+0.200, +0.511] |
按 scenario 拆: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,synthetic 1/5 对 5/5。两个 help scenario 的差距就是保留率的差距,支持事实不在池子里,模型按 prompt 的要求输出 INSUFFICIENT。
confidence_control 是 hybrid 唯一低于基线的格子,要单独看。40 道官方题里 hybrid 只输了一道,1cea1afa,它的支持事实在两种策略下都被保留了,confidence 策略下模型答对,hybrid 下模型输出了 INSUFFICIENT。问题不在淘汰。两种策略保留的干扰项集合不同,同一支持事实旁边放着不同的干扰项,模型的措辞和弃答倾向会变。grader 是冻结的,这个格子按原样报告。
两边都比历史数字高不少,历史是 14/45 对 23/45。confidence 基线从 14 涨到 24,和输出预算从 1024 翻到 2048 是一致的,披露里记录过一次 hidden reasoning 耗尽预算后重试到 2048 的情况。hybrid 的历史行本来就是 2048,从 23 涨到 40 不能用预算解释;这次走的是 DeepSeek 官方 API,历史记录里是聚合命名空间 deepseek/deepseek-v4-flash,底层模型相同但服务路径不同,我没有证据把差异归到某一个原因上。两种策略之间的差距在三次运行里都在,官方 40 题上 hybrid 分别多答对 12、10、12 道。
这些数字不构成把 hybrid-v1 改成默认的论据。评测用的元数据是构造的,confidence_control 的那一行说明信号权重和干扰项集合会影响模型的弃答行为,真实部署里 confirmation 信号还依赖前面说的 batch-level 门。#4789 自己也把 confidence 留作默认,切默认是另一个需要社区讨论的问题。
回到那两条 fact
开头那条 0.7、七天前确认过的 fact,在 hybrid-v1 下总分约 0.69,赢过 0.62 的 0.9@180 天。那条 0.7 的 correction,在 100 条的库里有 10 个保留槽,十条 0.9 的 preference 挤不掉它。这两件事在 issue 里是文字推演,现在是生产代码里的行为,也是评测里 confirmation_help 的 10 行和 correction_reserve 的 5 行。
评测这边只有一条结论:聚合指标对协议漂移不敏感,逐字节一致才算复现。仓库里现在的五个 hash、每行的 request fingerprint 和 grade-qa 的发布前自证,都是为这一条服务的。
参考
- issue:bytedance/deer-flow#4641,Improve DeerMem fact-cap eviction policy
- 修复:bytedance/deer-flow#4789,feat(memory): add hybrid fact eviction policy,作者 AnnaSuSu,2026-08-17 合入,squash
5ffaa09f,milestone 2.1.0 - 评测:bytedance/deer-flow#4810,eval(memory): add a reproducible hybrid eviction evaluation,2026-08-29 合入,squash
2eba6544,milestone 2.1.0 - 评测代码与发布结果:
backend/scripts/benchmark/deermem_eviction/,README 里有数据集下载方式和四条命令 - 数据集:xiaowu0162/longmemeval,清洗版 Hugging Face 数据集
xiaowu0162/longmemeval-cleaned