调研对象:DynamicMem: A Long-Horizon Memory Benchmark in Real-World Settings,arXiv:2606.22877v1(2026-06)。 材料来源:论文全文、当前仓库的生成/任务包/评测代码、Hugging Face 公开数据集的
001_user_001样本(app_log_large.json与task_packs.json)。 贯穿全文的例子:真实公开样本001_user_001。它在第一个 checkpoint 有一个每周日 09:30 的budget_review习惯;14 条历史日志共同构成该状态的标准证据,服务题只给“周日 09:15,刚喝完咖啡”的场景,要求系统主动写出正确提醒。
摘要
DynamicMem 面向个人助理的长期记忆场景。它不把记忆简化为从聊天记录中找回一句旧话,而是要求系统从购物、健身、金融、邮件、社交等 App 的长期行为里,维护用户在某个时间点的当前状态:有哪些属性、形成了什么习惯、偏好又发生了哪些变化。
论文构造了 10 名合成用户,每人覆盖 15 个月、平均约 220 万 token,总计包含 17,715 条 App 日志、16 个应用和 5 个季度 checkpoint。每个 checkpoint 只开放此前历史,因此能检查旧信息是否保留、新信息是否覆盖,也能观察历史变长后当前画像的恢复质量如何变化。论文 报告的主要困难集中在长历史和隐含偏好上。
数据先从会随季节与生活事件变化的用户状态出发,再生成跨 App 事件链,最后落为带请求与响应的 App API 日志。评测在五个时间点截断历史,分别检查系统能否恢复当前状态,以及能否把这些状态用于当下服务。

图 1 全局架构:动态状态被转成跨 App 行为,在多个 checkpoint 交给 memory system;两类任务共享 memory,但由 evaluator 用不同视角评分。
这套基准可以接入 RAG、向量库、知识图谱、摘要式记忆或 MemoryOS 一类系统,但要求严格的时间边界:日志按时间写入,checkpoint 不得看到未来,查询不能污染公共记忆,输出还要能与 checkpoint 对齐。完全黑盒、无法控制时间切片的系统,只能做受限测试。
复现时还要注意两个问题。其一,公开的 task_packs.json 同时包含题目、gold state、evidence 和服务评分点,当前实现主要依靠协议而不是数据隔离来防止真值泄漏。其二,README 的 point_score、默认配置和发布任务包并未完全统一。本文会分别说明这些边界,并给出更稳妥的输入投影与版本记录方式。
1. 它在解决什么问题
个人助理真正难的部分,不是记住用户曾说过“我喜欢运动”,而是数月后仍知道:这项偏好是否还成立、它是不是已经变成固定习惯、用户是不是因为搬家而换了健身地点。真实世界里,这些信息很少由用户直接宣布,而是散落在多个应用的行为里。
以 001_user_001 为例,系统看到的并不是一段“我每周日做预算复盘”的聊天,而是多次发生在金融 App 中的交易/查询记录;benchmark 再将其标为一个每周日 09:30 的 budget_review 习惯。也就是说,被测系统要完成从“行为”到“状态”的归纳,不能仅靠关键词命中。
表 1 DynamicMem 的定位:它改变的是“记忆是什么”的假设,而不只是把上下文拉长。
| 问题 | 传统长对话记忆 benchmark 常见做法 | DynamicMem 的做法 | 带来的额外能力要求 |
|---|---|---|---|
| 证据来源 | 用户与助手的对话 | 跨 App 的 API 行为日志 | 从行为推断状态 |
| 用户状态 | 常作为已说出的事实 | 属性、习惯、偏好三类状态 | 区分不同变化速度 |
| 时间 | 整段历史后问答,或有限更新题 | 五个 checkpoint 都只看历史前缀 | 避免未来信息泄漏 |
| 输出 | 回答一个问题 | 恢复状态 + 完成个性化服务 | 区分“知道”与“会用” |
| 诊断 | 最终准确率为主 | 长度、保留、更新、证据与错误类型分析 | 定位 memory 的失败模式 |
论文将状态拆成三类:属性是“有什么 / 是什么”的离散事实,例如工作、设备、住处;习惯是会重复、可能暂时改变的行为;偏好是长期、隐含、渐进的选择倾向。三个类别不是命名游戏:属性往往要覆盖旧实体,习惯要从重复中归纳,偏好则要从许多弱信号中聚合。论文的创新重点,是把这三种不同难题放进同一条长时间线,要求系统输出“此刻”的状态,而不是历史的平均值。
2. 一个完整的例子:数据怎么造、长什么样
公开数据不是一个大聊天文件,而是每名用户两份并列文件:app_log_large.json 是系统可见的活动流,task_packs.json 是裁判持有的 checkpoint、状态真值、题目和评分规则。数据集目录正是 outputs/<user_id>/ 的形状。数据集说明
2.1 跟着 001_user_001 看一次
001_user_001/app_log_large.json 有 1,455 条日志;task_packs.json 有 5 个季度 checkpoint。它们的关系不是“每条日志配一题”,而是“很多日志共同支持一项状态,再由该状态生成一题或一项服务”。
表 2 真实样本 `001_user_001` 的五次考试。可见历史和可考状态都随时间增长。
| checkpoint | 截止时间 | 最后可见日志 | State Completion 状态项 | Personalized Service 题数 |
|---|---|---|---|---|
cal_quarterly_001 | 2023-12-31 19:30 | log_00180 | 30 | 30 |
cal_quarterly_002 | 2024-03-31 14:30 | log_00466 | 37 | 37 |
cal_quarterly_003 | 2024-06-30 20:00 | log_00716 | 37 | 37 |
cal_quarterly_004 | 2024-10-01 05:45 | log_01087 | 45 | 45 |
cal_quarterly_005 | 2024-12-31 18:00 | log_01455 | 40 | 40 |

图 2 真实样本的五次 checkpoint:每个点都只开放此前日志,并以当时的用户状态重新出题;题数不会随日志数机械单调增加。
2.2 一个完整的可评分数据闭环:budget_review
app_log_large.json(本样本约 3.75 MB)和 task_packs.json(约 4.17 MB)不能全文贴进报告;下面不是“缩写过的 JSON 骨架”,而是一条可独立走通评测的完整闭环:一条原始证据日志、它所在的 checkpoint、当前状态、全部 gold evidence ID、两类任务的输入和服务题的全部评分点。除了与这道题无关的顶层元数据,字段没有用 … 省略。
这条原始日志来自 app_log_large.json;它是该状态 14 条证据中的第一条,系统在输入侧能看到它:
{
"app_log_id": "log_00003",
"timestamp": "2023-10-01 09:30:00",
"app_name": "Chase",
"api_name": "GetTransactions",
"request": {
"account_id": "Checking-9214",
"start_date": "2023-09-24",
"end_date": "2023-10-01"
},
"response": {
"transactions": [
{
"transaction_id": "CHASE-TX-10012023-8842-192",
"transaction_date": "2023-10-01",
"merchant": "Vanguard Brokerage",
"amount": 7500.0,
"transaction_type": "debit",
"category": "other"
},
{
"transaction_id": "CHASE-TX-09302023-7733-102",
"transaction_date": "2023-09-30",
"merchant": "Primanti Bros.",
"amount": 64.15,
"transaction_type": "debit",
"category": "dining"
},
{
"transaction_id": "CHASE-TX-09292023-2222-001",
"transaction_date": "2023-09-29",
"merchant": "PPG Industries Payroll",
"amount": 4850.0,
"transaction_type": "credit",
"category": "other"
},
{
"transaction_id": "CHASE-TX-09292023-1122-451",
"transaction_date": "2023-09-29",
"merchant": "Giant Eagle",
"amount": 218.42,
"transaction_type": "debit",
"category": "groceries"
},
{
"transaction_id": "CHASE-TX-09282023-4411-998",
"transaction_date": "2023-09-28",
"merchant": "Duquesne Light Company",
"amount": 142.5,
"transaction_type": "debit",
"category": "utilities"
},
{
"transaction_id": "CHASE-TX-09262023-4411-998",
"transaction_date": "2023-09-26",
"merchant": "Home Depot",
"amount": 89.12,
"transaction_type": "debit",
"category": "shopping"
}
],
"account_balance": 13750.75
}
}
App 日志的统一核心正是 timestamp、app_name、api_name、request、response;生成器也按这五类信息保存 API 调用。trajectory_synthesis/app_system.py:61-79 值得注意的是,数据中有 Gmail、WhatsApp、LLM Assistant 等带自然语言正文的应用,所以“不是对话 benchmark”不等于“没有文本”;准确说,它把文本也作为某个 App 上的一次行为来处理。
同一条 budget_review 状态在 task_packs.json 的 cal_quarterly_001 中,横跨下列对象:
表 3 一个完整评测闭环中的字段归属。读者应看清:日志是输入,状态和 evidence 是裁判真值,题目只是对状态的不同读法。
| 对象与实际字段 | 真实值 / 内容 | 作用 | 按公平协议谁应使用 |
|---|---|---|---|
as_of | log_index: 179、app_log_id: log_00180、timestamp: 2023-12-31 19:30:00 | 定义 C1 的历史截断点 | 系统与裁判 |
validated_snapshot_state.habits_state.budget_review | weekly、days_of_week: [6]、start_time: 09:30 | 这道题的当前状态真值 | 裁判 |
state_observability | evidence_count: 14、last_app_log_id: log_00177、last_change_type: unchanged | 说明真值可由哪些历史行为支持 | 裁判 |
state_completion_pack | 状态 key + 待填 JSON 模板 | 要求系统恢复当前状态 | 系统 |
rq3_apply_service_qa | 周日 09:15 场景 + 提醒指令 | 要求系统在场景中主动使用状态 | 系统 |
answer_scoring_points | identity gate、weekly、Sunday、09:30 共 4 点 | 服务答案的评分依据 | 裁判 |
下面是该闭环的 task-pack 内容,改用 JSONC 在同一处标注字段作用;它保留了所有直接决定这道题输入、真值与评分的值:
{
"checkpoint_id": "cal_quarterly_001",
"as_of": {
"log_index": 179,
"app_log_id": "log_00180",
"timestamp": "2023-12-31 19:30:00",
"window_id": "w0",
"domain": "Leisure & Media Consumption",
"completed_chain_ids": ["leisure_media_consumption_w0_3"]
},
// evaluator 的当前状态真值,不应进入被测 memory
"validated_snapshot_state": {
"habits_state": {
"budget_review": {
"schedule": { "frequency_type": "weekly", "days_of_week": [6] },
"timing": { "start_time": "09:30" }
}
}
},
// 真值的可追溯证据;14 条日志共同支持“这是习惯”
"state_observability": {
"habits_state": {
"budget_review": {
"evidence_count": 14,
"last_timestamp": "2023-12-31 09:30:00",
"last_app_log_id": "log_00177",
"last_change_type": "unchanged",
"evidence_app_log_ids": [
"log_00003", "log_00017", "log_00033", "log_00045",
"log_00057", "log_00071", "log_00085", "log_00103",
"log_00120", "log_00130", "log_00141", "log_00154",
"log_00168", "log_00177"
],
"is_valid": true,
"provenance_chain_id": "finances_material_living_w0_6",
"provenance_evidenced_fields": [
"current_value.location",
"current_value.schedule",
"current_value.timing"
]
}
}
},
// Task A:系统只应得到 key、问题和模板,而非上面的当前值
"state_completion_pack": {
"keys": {
"habits_state:budget_review": {
"item_id": "scp_1e4b7dd64238",
"state_key": "habits_state:budget_review",
"question_text": "Infer the user's current state for habits budget review (habits_state:budget_review) using this template: {\"habits_state:budget_review\": {\"schedule\": {\"days_of_week\": [\"<fill the blank>\"], \"frequency_type\": \"<fill the blank>\"}, \"timing\": {\"start_time\": \"<fill the blank>\"}}}. Schedule date encoding: `day_of_week` and `days_of_week` use zero-based weekday indexes: 0=Monday, 1=Tuesday, 2=Wednesday, 3=Thursday, 4=Friday, 5=Saturday, 6=Sunday. `day_of_month` and `days_of_month` use calendar day numbers 1-31; they are not zero-based. `week_of_month` uses ordinal week numbers within the month: 1=first, 2=second, 3=third, 4=fourth, 5=fifth.",
"answer_template": {
"schedule": {
"frequency_type": "<fill the blank>",
"days_of_week": ["<fill the blank>"]
},
"timing": { "start_time": "<fill the blank>" }
},
"retrieval_query": "Infer the user's current state for habits budget review (habits_state:budget_review) using this template: {\"habits_state:budget_review\": {\"schedule\": {\"days_of_week\": [\"<fill the blank>\"], \"frequency_type\": \"<fill the blank>\"}, \"timing\": {\"start_time\": \"<fill the blank>\"}}}. Schedule date encoding: `day_of_week` and `days_of_week` use zero-based weekday indexes: 0=Monday, 1=Tuesday, 2=Wednesday, 3=Thursday, 4=Friday, 5=Saturday, 6=Sunday. `day_of_month` and `days_of_month` use calendar day numbers 1-31; they are not zero-based. `week_of_month` uses ordinal week numbers within the month: 1=first, 2=second, 3=third, 4=fourth, 5=fifth.",
"scoring_points": []
}
}
},
// Task C:系统得到场景和指令,不能从场景本身读出预算复盘
"rq3_apply_service_qa": {
"keys": {
"habits_state:budget_review": {
"items": [
{
"qa_id": "q1",
"service_family": "user_communication",
"scenario": "It is Sunday at 09:15. The morning coffee has just been poured.",
"task_instruction": "Draft a specific reminder message for the user in this scenario.",
"retrieval_query": "[Scenario] It is Sunday at 09:15. The morning coffee has just been poured. [Task Instruction] Draft a specific reminder message for the user in this scenario.",
"answer_scoring_points": [
{
"point_id": "aqp_habits_state_budget_review_q1_identity",
"point_type": "micro",
"point_role": "identity_gate",
"point_text": "The message is clearly about the budget review routine itself, not a different routine or unrelated task."
},
{
"point_id": "aqp_habits_state_budget_review_q1_p1",
"point_type": "micro",
"point_text": "The message correctly uses the state field schedule.frequency_type with value \"weekly\".",
"source_field_path": "schedule.frequency_type",
"reference_value": "weekly"
},
{
"point_id": "aqp_habits_state_budget_review_q1_p2",
"point_type": "micro",
"point_text": "The message correctly uses the state field schedule.days_of_week with value [6 (Sunday)].",
"source_field_path": "schedule.days_of_week",
"reference_value": [6]
},
{
"point_id": "aqp_habits_state_budget_review_q1_p3",
"point_type": "micro",
"point_text": "The message correctly uses the state field timing.start_time with value \"09:30\".",
"source_field_path": "timing.start_time",
"reference_value": "09:30"
}
],
"reference_anchors": [],
"gold_memory_evidence_app_log_ids": [
"log_00003", "log_00017", "log_00033", "log_00045",
"log_00057", "log_00071", "log_00085", "log_00103",
"log_00120", "log_00130", "log_00141", "log_00154",
"log_00168", "log_00177"
]
}
]
}
}
}
}
这里有一个必须写明的安全边界:发布的 task_packs.json 是“评测包”,物理文件中同时带有题目和上面的 gold 字段;它不是已脱敏的“选手输入包”。当前 pipeline 会读取完整 benchmark,并把 checkpoint 对象传给 prepare_checkpoint_state(...)(tce_core/pipeline.py:1059-1064、tce_core/pipeline.py:1247-1250)。仓库协议要求 adapter 不使用真值字段,但代码路径本身并未把它们从对象中剥离。因此,正规实验必须把 adapter 约束、审计或数据投影当成强制步骤;否则自定义系统有能力读取答案,任何分数都不可信。这个问题会在 §7.1 继续讨论。

图 3 同一组历史日志在 checkpoint 被汇成“当前状态”;这个状态再被改写成补全题和服务题。图中橙色路径是裁判应持有、系统不应读取的 gold 信息。
2.3 budget_review 这条线怎么贯穿数据、题目与证据
在第一个 checkpoint,标准状态中有:
{
"habits_state:budget_review": {
"schedule": { "frequency_type": "weekly", "days_of_week": [6] },
"timing": { "start_time": "09:30" }
}
}
这里的 [6] 不是六号,而是零基星期编码中的 Sunday。其 state_observability 列出了 14 个证据日志:从 log_00003 一直到 log_00177;最后一次发生在 2023-12-31 09:30。单次记录不足以证明“习惯”,但 14 次按周重复的记录可以。
同一状态在服务题中被改写为:
Scenario: It is Sunday at 09:15. The morning coffee has just been poured.
Task: Draft a specific reminder message for the user in this scenario.
场景没有提“预算复盘”“每周”“09:30”,这些都应从 memory 中找回。服务题的标准检查表会逐项看:提醒的是不是 budget review 本身、是否体现 weekly、是否识别 Sunday、是否给出 09:30。这就是 DynamicMem 试图区分“能填空”和“能在正确时机主动帮忙”的地方。
2.4 数据是怎么造出来的
数据完全合成,论文提供了三段流水线:
-
为用户建立基础画像和按季度变化的动态画像;季度背景包含季节、生活事件与外部条件。
-
把每项变化转成带动机的跨 App 事件链,例如法规变化可同时引发读邮件、更新 LinkedIn、联系客户。
-
把事件链落为带 App 状态的 API 请求/响应日志;之后从验证过的状态构建 checkpoint、证据和任务包。

图 4 数据生成的因果链:状态变化先被定义,再通过事件链落到日志,最后由 checkpoint 把“此刻真值”和题目固定下来。
这条路径的好处是“标准答案从哪里来”是可追溯的:状态先存在,再生成行为来体现它,而不是事后从混乱日志中人工猜真相。代价也同样明确:它是 LLM 合成世界,行为的连贯性、丰富性和因果关系都可能带有生成模型的风格,不等于真实用户遥测数据。
2.5 数据里有两层可见性,不能混淆
概念上,被测系统只应获得 app_log_large.json 的历史前缀和题目;validated_snapshot_state、state_observability、gold evidence、服务评分点都属于 evaluator 真值。代码的 checkpoint 切片会按 log_index 取前缀,退化情况下才按时间戳过滤。tce_core/pipeline.py:918-934 这条时间边界是能否公平比较的根本。
但 §2.2 的完整样例也显示,发布的 task_packs.json 物理上同时含有这些字段。因此图 5 描述的是必须遵守的协议边界,不是文件系统已经替你实现的访问控制;自定义 adapter 需要主动做安全投影,不能把完整 checkpoint 当成可自由读取的输入。

图 5 协议要求的信息边界:系统只应使用历史日志和当前题目;原始 `task_packs.json` 并未自动剥离右侧真值,接入方必须自行强制这条边界。
3. 评测怎么跑起来
评测可拆成“建记忆、答题、裁判”三段。它比普通 QA 多的地方,不是强制某种 memory 算法,而是要求每个系统都遵守同一条时间线。
按时间读入 app logs
↓
在 cp_i 保存 / 加载当时的 memory snapshot
↓
对 State Completion 与 Personalized Service 分别检索、作答
↓
写出 prediction JSON(checkpoint_id、状态、服务答案、证据)
↓
LLM judge 与结构化指标聚合

图 6 评测方法图:正式语义分、可选评分点 / 结构化指标、证据诊断与论文额外分析有不同目的,不能混作同一类“分数”。
被接入的 adapter 需要实现三段:prepare_checkpoint_state、retrieve_context_for_query、answer_query。这允许 RAG、图记忆、摘要树和 MemoryOS 使用不同内部表示,但有四条硬约束:顺序 ingest、checkpoint 隔离、查询不污染、可恢复重跑。适配器协议 的 §6 写得很严格:原始日志只能做无损包装,不能在 ingest 前偷偷摘要、删字段或改字段。
表 4 对被测系统的要求。限制的是公平性,而不是记忆算法。
| 要求 | 为什么要有 | 不满足会发生什么 |
|---|---|---|
| 按时间顺序 ingest | 状态变化本身是被测对象 | 新旧信息的先后关系消失 |
| 只读取当前 checkpoint snapshot | 防止未来信息回答过去 | 分数虚高,无法比较 |
| 查询不写回共享 memory | 防止题目和答案污染后续题 | 后面的题被前面的题泄漏 |
| 输出 evidence(尽量) | 支持检索质量诊断 | 仍可测答案,但证据指标失去意义 |
| 保存可恢复的 build 状态 | 长期跑测成本高、允许中断续跑 | 重跑可能改变历史记忆 |
仓库提供六个基线:Oracle 是“直接给真值”的上限,RAG 是对原始日志直接检索的下限;SimpleMem、A-Mem、MemoryOS 和 HippoRAG2 代表四类会建结构化 memory 的方法。README 的基线说明 因此它并不排斥简单系统;普通 RAG 可以测,只是它在“更新”上没有真正改写 memory 的优势。
4. 指标与一遍手算
DynamicMem 的分数层次比 README 一句“Core + Detail”复杂:论文主口径是字段语义分;当前代码还同时保留评分点、结构精确匹配、证据 ID 等诊断指标。理解时先分清“正式语义分”和“辅助诊断分”,不要把后者当排行榜主分。
4.1 论文的 Core + Detail:字段语义分
论文对每个字段让 LLM judge 给两个值:Core ∈ {0, 1} 代表核心含义是否正确,Detail ∈ {0, 1, 2} 代表细节从错误、部分正确到完整正确。单字段分是:
s = 0.8 × Core + 0.2 × (Detail / 2)
还是用 budget_review:标准是“每周日 09:30 预算复盘”。假设系统回答“每周日早上做预算复盘”,则活动、频率和星期的核心正确,但没有精确时间。把它简化为四个字段:
表 5 Core + Detail 的手算。关键事实权重 80%,细节权重 20%。
| 字段 | 系统回答 | Core | Detail | 单字段分 |
|---|---|---|---|---|
| 活动 | 预算复盘 | 1 | 2 | 1.0 |
| 频率 | 每周 | 1 | 2 | 1.0 |
| 星期 | 周日 | 1 | 2 | 1.0 |
| 时间 | “早上”而非 09:30 | 1 | 1 | 0.9 |
该状态分 = (1.0 + 1.0 + 1.0 + 0.9) / 4 = 0.975。若答成“周日晨跑”,核心活动错误,相关字段不能靠“周日”这个细节挽回。这种设计比字符串 exact match 宽容,又比只给整题一个模糊的“对 / 不对”更可解释。当前实现中的同一公式可见 evaluation/eval_tce.py:435-450。

图 7 “早上”保住了时间字段的核心含义,却少了 09:30 这一完整细节,因此单字段从 1.0 变为 0.9;四字段平均后为 0.975。
4.2 当前任务包中的评分点:服务题的检查表
001_user_001 的上述服务题有四个 answer_scoring_points:一项 identity gate 加三项状态字段检查。设系统输出:
提醒一下:你每周日 09:30 都会做预算复盘,现在可以准备开始了。
四项都通过,分数 4 / 4 = 1.0。如果系统写“周日 09:30 该去晨跑了”,即使星期和时间碰巧正确,identity gate 失败,整题为 0;代码明确定义了这个短路规则。evaluation/eval_tce.py:547-566
若没有 identity gate,则评分点分就是:
point score = 判为 correct 的评分点数 / 评分点总数
它适合服务输出,因为“提醒错了哪件事”比“少写一个细节”严重得多。
4.3 证据 Precision / Recall / F1:系统翻对了哪些日志
对 budget_review,标准证据是 14 个日志 ID。假设系统引用了 5 条日志,其中 4 条在 gold 中:
Recall = 4 / 14 ≈ 28.6%
Precision = 4 / 5 = 80.0%
F1 = 2 × 0.286 × 0.800 / (0.286 + 0.800) ≈ 42.1%
这三个数只评价“拿出的证据 ID 与标注证据的重合”,不是最终服务质量。代码按集合交集逐状态计算,再对状态平均。tce_core/evaluation.py:690-723 论文自己也提醒,gold 通常标的是首次支持记录;同一事实可能后来被重述,因此低 evidence recall 不必然等于系统完全没有相关信息。
4.4 结构化辅助指标:适合排错,不应取代语义分
评测器还会打印 snapshot_value_f1、snapshot_exact_match、key precision/recall 等。它们比较预测 JSON 和标准 JSON 的结构/值,可快速发现字段缺失、类型错误或模板不匹配;但“06:35”与“06:30”这类语义接近答案可能被严格结构比较判错。代码会把这些机械指标和 LLM 语义分一起输出。tce_core/evaluation.py:753-773
5. 两类任务:分别在测什么
两类题都读取同一份 memory,却问两件不同的事;这是 DynamicMem 最值得保留的方法论。
5.1 State Completion:当前状态恢复
输入是一个状态 key 和一个待填 JSON 模板,例如“补全当前 habits_state:budget_review 的频率、星期和时间”。系统检索、归纳后输出结构化状态。它考的是:在当前 checkpoint,系统是否正确维护了一份当前用户画像。
属性、习惯和偏好会分别出题:
-
属性题可能问当前车、岗位、订阅或设备;重点是新旧实体不能混。
-
习惯题可能问频率、星期、时间、地点;重点是从重复行为归纳,不把一次偶发事件当习惯。
-
偏好题可能问偏好的方向、限制和取舍;重点是从分散选择中推断,而不是机械找一句原话。
5.2 Personalized Service:把记忆用到一个当下场景
输入不是用户画像,而是一个不泄漏答案的当前场景和通用任务指令。系统必须自己判断该拿哪条状态出来服务。
对 budget_review,场景是“周日 09:15,刚喝完咖啡”;正确做法是给即将 09:30 开始的预算复盘写提醒。若系统 State Completion 能答出“每周日 09:30 预算复盘”,却面对这个场景想不到要提醒,说明它“知道但不会用”。
服务题按状态族分三种:属性用于实体相关配置,习惯用于场景化提醒,偏好用于搜索或筛选条件。题包在生成时还校验 answerability、service_realism、full_field_dependency、low_leakage 等条件,尽量避免场景本身把答案说出来。
表 6 同一份 memory 的两种读法:一类考恢复,一类考主动应用。
| 任务 | 系统拿到什么 | 系统必须做什么 | 典型失败 |
|---|---|---|---|
| State Completion | 状态 key、JSON 模板 | 从历史重建当前状态 | 把旧公司、旧地点、旧偏好当成当前值 |
| Personalized Service | 当前场景、服务指令 | 识别应调用的状态并给出服务 | 记得习惯,却没有在合适时机主动提起 |
6. 论文的主要发现
以下数字是论文实验结论,不是本文重跑所得。论文使用 RAG、HippoRAG2、A-Mem、MemoryOS、SimpleMem 五类 memory 系统,并以 Oracle 作为“已给真值证据”的上界。
发现一:历史变长时,State Completion 持续下降,Personalized Service 却大致持平。 从 C1 到 C5,五个系统的状态恢复分别下降 4.4、6.6、8.9、14.2、16.7 个点;四个系统的服务分反而小幅上升。作者的解释是:服务场景可从更多相关轨迹中获益,而状态恢复更依赖某条特定、当前有效的状态证据。
发现二:偏好是最脆弱的一类状态。 偏好通常不是直接陈述,而是分散在多个选择里;随着历史变长,无关选择更多,信号被淹没。习惯反而在部分系统中较稳定,因为它会重复出现;属性的难点则集中在“当前到底是哪一个实体”。
发现三:保留与更新是两种不同能力。 对从 C1 起未变化的状态,作者测 retention;对刚发生变化的状态,测 update。没有一个架构在两者上都稳定领先:有些系统容易保留旧事实,也因此难以覆盖新事实;有些系统会积极合并,又会抹掉少见但仍有效的信息。

图 8 两类评测都在 C4 发问:Retention 希望系统仍答 A,Update 希望系统改答 B。因此“忘记旧的有效状态”和“抱着过期状态不放”是不同错误。
发现四:服务题的习惯分很低,不全是 memory 不会记。 论文报告所有系统在“习惯状态恢复”上约 53%–57%,但在“按场景主动提醒习惯”上只有约 5%–10%。这说明从场景中选择应当调用的 routine,是独立于“能否找回 routine”的推理瓶颈。
发现五:大多数失败来自 memory 提供的证据。 作者抽样分析失败样本,按无关证据、身份缺失、细节缺失、证据冲突、证据齐全但答案仍错分类;后者只占约 2%–7%。这是论文最直接的工程结论:换一个更会写答案的模型,并不能替代改进 memory 的检索和更新机制。
7. 限制与复现注意事项
7.1 最大的公平性坑:gold 真值与题目同包发布,协议边界没有被代码强制
§2.2 的真实样例已经展示:task_packs.json 同时包含 validated_snapshot_state、state_observability、gold_memory_evidence_app_log_ids、answer_scoring_points,其中后几类字段直接或间接给出了正确状态、证据和服务答案所需值。与此同时,通用 pipeline 把整个 checkpoint 字典传给 adapter 的 prepare_checkpoint_state(...)。因此“系统只看历史日志、裁判才看 gold”的边界,目前是研究协议与 adapter 自律,不是数据访问层的强制保证。
这不是说仓库基线一定作弊;RAG 等基线的正常路径会从 pack 中提取题目和模板、从 app_log_large.json 的历史前缀中检索证据。但对第三方自定义系统而言,只要它能读取 benchmark_path,就有机会直接访问这些 gold 字段。发布可比较结果前,至少应做到下面之一:
-
为被测 adapter 生成一份 stripped benchmark,只保留
checkpoint_id、as_of、题目和不含 reference value 的输出模板; -
让 runner 只把受限的
QuerySpec与日志前缀传给 adapter,而不是传完整 checkpoint; -
在 prediction run 的审计日志里记录 adapter 读取的字段,或在独立进程 / 沙箱中阻止读取原始评测包。
在没有这类控制前,DynamicMem 更适合作为“遵守研究协议的实现之间”的 benchmark;把它开放给任意不受审计的黑盒系统,分数的公正性没有技术保证。
7.2 最大的复现坑:README、任务包与默认配置的指标口径没有完全对齐
README 把 snapshot_point_score 和 rq3_apply_answer_point_score 写成 headline scores;但当前默认评测配置只开启 enable_llm_judge: true,没有开启 enable_snapshot_slot_judge 或 enable_apply_slot_judge。评测器的默认值也确实把两类 slot/point judge 设为 false(evaluation/eval_tce.py:66-74)。开启 LLM judge 后,代码无条件运行的是 State Completion 与 Personalized Service 的 holistic judge;point judge 只有显式打开才运行(evaluation/eval_tce.py:2058-2100)。
更具体地说,本文实际检查的 001_user_001 五个 checkpoint 中,State Completion 的 30 / 37 / 37 / 45 / 40 个题项的 scoring_points 全部为空;服务题则分别有 84 / 114 / 100 / 136 / 106 个 answer_scoring_points。因此:
-
直接运行仓库给出的
*_eval.yaml,应重点读取snapshot_holistic和rq3_apply_holistic一类结果; -
想读取服务 point score,需显式启用
enable_apply_slot_judge; -
想让 State Completion 有 point score,不能只改开关,还必须确认所用 task pack 的每题真的生成了
scoring_points; -
论文的 Core + Detail 公式、README 的“headline”、当前数据包与默认配置不是天然同一个口径。
这不是论文声称的发现,而是本文对一份发布任务包和当前代码的独立核对。做横向实验时,务必把 benchmark 文件 hash、task contract、judge 开关、judge model 和最终读取字段写进实验记录。
7.3 合成用户不等于真实用户
DynamicMem 的优势来自可控真值,弱点也来自同一处:用户、生活事件、App 行为、服务题都经由 LLM 合成与验证。它能很好地测“在这个合成世界里,系统是否遵守状态变化”,但不能直接证明系统对真实、嘈杂、异常、跨文化且受隐私约束的用户数据也同样有效。论文仅合成 10 名用户;统计上足够展示方法,但不足以代表所有人群与应用生态。
7.4 LLM judge 是成本、波动与可审计性来源
默认评测配置使用 gpt-5.4-2026-03-05 作为 judge,并保存审计输入输出路径(例如 configs/experiments/tce/rag_eval.yaml:1-18)。这比字符串匹配更能处理语义等价和部分正确,却引入 API 成本、模型版本漂移和裁判一致性问题。至少应保存 judge 输入输出,必要时用固定样本人工复核。
7.5 “跨 App”不等于所有数据都适合开放检索
真实样本中可见金融交易、地址、邮件、消息、健康活动和与 LLM Assistant 的文本对话。虽然全部为合成数据,但这提醒部署者:真实产品若复制这种输入面,必须先处理权限、最小化收集、数据隔离与审计;benchmark 不替你解决这些生产系统问题。
7.6 对被测系统的限制比普通问答更严格
协议要求顺序 ingest、checkpoint isolation 与 query non-pollution;无法快照、无法禁用未来泄漏或查询会写入记忆的系统,不应与遵守协议的系统直接比较。这个门槛提高了实验可信度,也提高了接入成本。黑盒聊天助手最多可做受限测试,不适合据此宣称完整 DynamicMem 成绩。
8. 和 LoCoMo / LongMemEval / AgentBench 怎么选
这四个 benchmark 不能放在一条“谁更难”的尺子上。根本差异是它们把 memory 和 agent 能力分别看成什么。
表 7 选型主线:对“记忆是什么”的假设,决定输入、评分与接入门槛。
| benchmark | 把主要输入当成什么 | 核心问题 | 最适合验证什么 | 不该替代什么 |
|---|---|---|---|---|
| LoCoMo | 多 session 的双人对话 | 能否从长期聊天中回答与总结事件 | 对话检索、长对话 QA | 跨 App 的用户状态更新 |
| LongMemEval | 时间戳化用户-助手对话 | 能否提取、跨会话推理、更新时间并适度拒答 | 聊天助手的长期互动记忆 | 隐含行为偏好与主动服务 |
| DynamicMem | 多 App API 行为流 | 能否维护“当前用户画像”并用于服务 | 个性化 memory、状态演化、更新策略 | 网页/OS 工具操作能力 |
| AgentBench | 可交互任务环境 | 能否规划、调用工具、根据反馈完成任务 | 通用 agent 行动和决策 | 长期个人画像 memory |
LoCoMo 的公开数据由长期对话、问答和事件摘要组成;LongMemEval 明确测信息提取、跨 session 推理、时间推理、知识更新和拒答。LoCoMo 与 LongMemEval 都很适合聊天记忆,但其证据主要是用户说过的话。DynamicMem 的额外难点是:用户经常没有说出结论,系统必须从“做过什么”推断“现在是什么”。
AgentBench 则是另一条路线:它在 OS、数据库、知识图谱、网页购物等多种交互环境中评估决策与执行。AgentBench 的 agent 可能需要比 DynamicMem 更复杂的行动规划;但一个 AgentBench 很强的 agent 仍可能不会长期维护用户偏好。反过来,一个 DynamicMem 很强的 memory module 也不自动具备网页操作能力。
选型可以简化为一句话:
“用户说过什么?” → LoCoMo / LongMemEval
“用户做过什么、现在变成什么?” → DynamicMem
“接到任务后能不能完成操作?” → AgentBench
9. 结论与适用场景
DynamicMem 最有价值的地方,不是“把上下文扩展到 220 万 token”,而是把长期记忆从历史检索题改写成动态用户状态管理题。它迫使 memory system 同时面对四件事:保留仍有效的信息、覆盖已经过期的信息、从重复行为归纳习惯、从弱信号聚合偏好;再进一步,看系统能否在没有直白提示的情况下把这份状态用于服务。
如果你的目标是个人助理、用户画像、长期个性化推荐或跨应用 memory,它是很值得加入的压力测试,尤其适合与 LongMemEval 并用:前者验证“行为驱动的动态用户模型”,后者验证“对话驱动的长期互动记忆”。
如果你的系统只做短期问答、完全黑盒、不能保证 checkpoint 隔离,或产品重点是浏览器/数据库/OS 操作,则不应把它作为唯一主 benchmark。即便接入,也必须先解决 §7.1 的 gold 输入边界和 §7.2 的数据包 / 评测开关口径问题;否则不同实验运行的分数既不一定公平,也不一定可比。
一句话总结:DynamicMem 是一把检查“AI 是否持续理解一个正在变化的人”的尺子;它不是通用 Agent 能力总榜,也不是现实用户数据的替代品。
10. 附录
10.1 术语速查
| 术语 | 一句话解释 |
|---|---|
| App log | 一次有时间、App/API、请求和响应的应用行为记录 |
| checkpoint | 历史被截断并进行一次考试的时点 |
| State Completion | 恢复某时点的用户属性、习惯或偏好 |
| Personalized Service | 在不泄漏用户状态的场景下,用记忆给出服务 |
| attribute / habit / preference | 离散事实 / 重复行为 / 长期选择倾向 |
| TCE | Temporal Checkpoint Evaluation,按时间 checkpoint 的评测协议 |
| evidence | 支持某个状态或服务答案的原始 App 日志 ID / 内容 |
| Oracle | 直接得到相关真值证据的上限,不是实际 memory system |
| identity gate | 服务对象或例行事项本身错了时,整题归零的门槛 |
10.2 最小接入与复现路径
# 1. 下载公开数据(会写入当前仓库的 outputs/)
hf download xiewenya/dynamicmem --repo-type dataset --local-dir outputs/
# 2. 先跑一个基线的预测
python -m baseline_prediction.run_tce \
--config configs/experiments/tce/rag_predict.yaml
# 3. 再跑评测
python -m evaluation.eval_tce \
--config configs/experiments/tce/rag_eval.yaml
运行前检查四件事:数据路径是否指向同一 user;prediction 的 checkpoint_id 是否与题包对齐;使用的 task contract 是否一致;最终读取的是 holistic 还是 point 分数。不要只看控制台中一个叫“score”的数字。
10.3 代码索引
| 看什么 | 位置 |
|---|---|
| 整体任务与基线说明 | README.md:1-41、README.md:149-189 |
| 被测系统的时间隔离与非污染约束 | docs/protocols/tce_generation_and_adapter_contract.md:68-89 |
| checkpoint 的可见日志前缀 | tce_core/pipeline.py:918-934 |
| State Completion 题包字段 | tce_core/task_packs.py:481-507 |
| 服务题 retrieval query | tce_core/task_packs.py:815-845 |
| Core + Detail 公式 | evaluation/eval_tce.py:430-452 |
| point score 与 identity gate | evaluation/eval_tce.py:541-566 |
| 默认 judge 开关和实际运行分支 | evaluation/eval_tce.py:66-74、evaluation/eval_tce.py:2035-2100 |
| 证据 P/R/F1 | tce_core/evaluation.py:690-723 |
| 结构化 snapshot 辅助指标 | tce_core/evaluation.py:753-773 |
10.4 参考资料
-
对照基准:LoCoMo · LongMemEval · AgentBench
调研时间:2026-07-13。本文基于论文、当前代码、公开数据集 001_user_001 的逐字段走查与公开资料;未运行完整预测/评分 pipeline,未声称复现论文结果。§7.1–§7.2 是本文对当前发布样本与当前代码的独立核对,其余设计和结果结论分别来自代码与论文。