agent-memory LoCoMo 评测数据集实践:数据放置、mini 冒烟样本截取与 SHA-256 校验

原创2026-10-13 15:33:381,997 阅读
文章标签:人工智能Agent 记忆AI AgentRAGMCP 服务

agent-memory LoCoMo 评测数据集实践:数据放置、mini 冒烟样本截取与 SHA-256 校验

本篇聚焦 agent-memory(jiuwen_memory 内核)本地端到端评测框架中的 LoCoMo 数据集约定:官方 locomo10.json 的标准放置位置、locomo_mini.json 冒烟样本的机械截取规则、SHA-256 校验口径,以及如何通过 evaluation/config.yml 与 runner 参数在 mini 与标准全量数据之间切换。读完本篇,你可以独立完成 LoCoMo 冒烟评测的跑批、复现验证与结果溯源,并理解 mini 样本"只减规模、不换链路"的设计边界。

LoCoMo 数据集在评测框架中的位置

agent-memory 仓库的 evaluation/ 目录提供本地一键端到端评测能力,支持 LongMemEval 与 LoCoMo 两套相互独立的 runner(分别位于 evaluation/longmemeval/ 与 evaluation/locomo/),通过官方 assemble_runtime() 装配记忆内核,覆盖"写入 → 抽取 → 存储 → 检索 → 回答生成 → 判分"全链路。

数据侧的目录约定由 输入数据目录说明 明确:

  • evaluation/datasets/ 只存放评测输入,不存放运行结果、日志、审计包或缓存;
  • locomo/ 子目录存放 LoCoMo 原始数据和可提交的微型样本,longmemeval/ 子目录同理;
  • 完整数据文件(locomo10.json、longmemeval_oracle.json)默认被 .gitignore 排除;两个 mini 样本作为本地冒烟输入保留并提交。

这一边界在 evaluation/README.md 的"PR 提交边界"中进一步落实:可提交的是两个数据目录的 README 与 locomo_mini.json、longmemeval_mini.json;不得提交完整标准数据与 evaluation/environment/.env 中的真实密钥。

数据文件放置约定

按 LoCoMo 数据说明 的约定,官方数据集与冒烟样本的放置位置固定为:

evaluation/datasets/locomo/locomo10.json    # 官方完整数据(不提交,.gitignore 排除)
evaluation/datasets/locomo/locomo_mini.json # 本地冒烟样本(提交入库)

locomo10.json 是 LoCoMo benchmark 的完整 10 段双人对话样本(数组下标 0~9,共 10 个 conversation)。完整文件获取后放入上述路径即可,evaluation/config.yml 中 locomo.data 默认指向 mini 文件,切换全量只需改数据路径与范围(见后文"切换到标准数据集")。

真实连接串与模型配置经 environment/.env.example 复制为 evaluation/environment/.env 后注入;配置文件本身不落密。LoCoMo 相关的关键变量包括:

API_BASE=            # LLM OpenAI 兼容端点
MODEL_NAME=GLM-5.2
API_KEY=
EMBED_MODEL_NAME=BAAI/bge-m3
REDIS_URL=redis://127.0.0.1:6379/0
MILVUS_URI=http://127.0.0.1:19530
MILVUS_COLLECTION=agent_memory_vectors
ES_HOSTS=http://127.0.0.1:9200
ES_FULLTEXT_INDEX=agent_memory_fulltext

这些变量在 locomo/config.yml 中以 ${VAR} / ${VAR:-默认值} 形式被 shared/config_loader.py 的 expand_env() 递归展开,实现"配置文件不落密、连接串经环境变量注入"。

locomo_mini.json 冒烟样本:机械截取规则

locomo_mini.json 的定位是本地冒烟输入,它只用于验证端到端链路是否打通,不代表正式分数。文档对其生成方式的描述非常严格——由当前标准 locomo10.json 机械截取得到,而非人工改写或 Mock:

  • 来源:标准 locoma10.json(SHA-256:79FA87E90F04081343B8C8DEBECB80A9A6842B76A7AA537DC9FDF651EA698FF4);
  • 截取内容:原始数组下标 1、sample_id=conv-30、session_1 的 D1:1 和 D1:2、原始 qa[0];
  • 对话、日期、问题、答案、证据、category 均未改写。

当前仓库中 locomo_mini.json 的完整内容如下,可直接对照上述规则验证截取范围:

[
  {
    "qa": [
      {
        "question": "When Jon has lost his job as a banker?",
        "answer": "19 January, 2023",
        "evidence": ["D1:2"],
        "category": 2
      }
    ],
    "conversation": {
      "speaker_a": "Jon",
      "speaker_b": "Gina",
      "session_1_date_time": "4:04 pm on 20 January, 2023",
      "session_1": [
        {
          "speaker": "Gina",
          "dia_id": "D1:1",
          "text": "Hey Jon! Good to see you. What's up? Anything new?"
        },
        {
          "speaker": "Jon",
          "dia_id": "D1:2",
          "text": "Hey Gina! Good to see you too. Lost my job as a banker yesterday, so I'm gonna take a shot at starting my own business."
        }
      ]
    },
    "sample_id": "conv-30"
  }
]

从内容可以直观看到截取规则的自洽性:qa[0] 是一道 category=2(temporal,时间推理)题,证据指向 D1:2——Jon 在 1 月 20 日的会话中说"昨天辞掉了银行的工作",因此标准答案为 19 January, 2023。这道题恰好要求记忆系统结合 session_1_date_time 做相对时间("yesterday")到绝对日期的换算,是冒烟样本中很有代表性的最小单元。

为什么不保留 event_summary、observation、session_summary

标准 LoCoMo 样本中带有 event_summary、observation、session_summary 等派生标注字段,但 mini 中刻意不放,原因有二(见 LoCoMo 数据说明):

  1. 一致性:这些字段会引用被裁掉的 turn 或 session(例如 session 摘要会概括整个 session,而 mini 只保留了 2 个 turn),保留它们会引入自相矛盾的上下文;
  2. 无消费方:SSH LoCoMo v5 runner 也不读取这些派生标注字段。从源码结构可以印证这一点——locomo/run.py 的写入路径只读取 conversation 下的 speaker_a/speaker_b、session_* 对话 turn(含可选 blip_caption)以及 qa 列表;locomo/prompts.py 的判分走 get_judge_prompt()(不带 evidence 参数),数据集中的 evidence 字段同样不直接送入 judge。

mini 与正式链路的等价性

mini 样本"只减规模、不换链路":它仍运行远端 v5 的完整写入、抽取、Redis/Milvus/Elasticsearch 存储、Top-200 检索、多 cutoff 生成与 Judge 链路,不是 Mock。因此 mini 结果可以用来验证环境与配置是否跑通(例如后端是否健康、抽取 prompt 是否零产出、RRF 融合是否生效),但其分数与 LoCoMo 官方口径的正式分数不可比。

SHA-256 校验口径:以 Git blob 原始字节为准

LoCoMo 数据说明 给出了两个文件的 SHA-256 值,并特别说明了校验口径:

文件 SHA-256
locomo10.json(标准数据) 79FA87E90F04081343B8C8DEBECB80A9A6842B76A7AA537DC9FDF651EA698FF4
locomo_mini.json(冒烟样本) C983279B695C284BBE60DB8257DE951C0298D18690F41550100CBEA02E3EB306

关键约束是:以上 SHA-256 均以仓库 raw/Git blob 的原始字节为准。这样做的动机是避免 Windows 检出时的 CRLF 换行符转换影响校验值——如果本地 git config core.autocrlf 在检出时把 LF 改写为 CRLF,直接对磁盘文件做 sha256sum 会得到与记录值不同的结果。校验时应对比仓库 raw 字节(例如 git cat-file blob 或仓库的 raw 文件),而不是直接哈希工作区文件。

这套口径在跑批产物中也得到了延续:locomo/run.py 的 _file_digest() 会对本轮实际加载的数据文件计算 SHA-256 前 16 位,并写入 run_meta.json 的 data_sha256_16 字段,供事后核对"这一轮的 qa_idx 是否仍指向同一批题"。

运行 mini 样本:一键脚本与配置项

一键运行与 benchmark 切换

在仓库根目录执行一键脚本即可(Windows 用 run.ps1,Linux/SSH 用 run.sh):

sh evaluation/run.sh

脚本会自动创建/复用 .venv、按 evaluation/environment/requirements.txt 安装依赖、复用或拉起健康检查通过的 Redis(6379)/Elasticsearch(9200)/Milvus(19530),然后按 evaluation/config.yml 执行评测。切换 benchmark 只需改配置第一项:

benchmark: locomo     # 或 longmemeval

也可以临时覆盖而不改文件(注意:该方式只运行 Python,不会自动拉起后端):

.\.venv\Scripts\python.exe -m evaluation --config evaluation\config.yml --benchmark locomo

run_id 每次启动唯一生成(时间戳 + uuid4 前 8 位)。LoCoMo 用它生成唯一 SCOPE_TAG/scope_org,因此即使复用本机已有的 Redis/Elasticsearch/Milvus,重复运行也不会检索到上一轮留下的记忆。

locomo 配置段与 runner 参数

evaluation/config.yml 的 locomo 段是当前默认指向 mini 数据的运行入口:

locomo:
  data: datasets/locomo/locomo_mini.json     # 相对 evaluation/ 解析
  memory_config: locomo/config.yml           # 内核装配配置
  output_root: outputs/locomo
  conversation_ids: [0]                      # mini 中只有下标 0
  max_sessions: 0                            # 0 = 不限制
  max_turns: 5                               # 每 session 前 N 轮(mini 实际只有 2 轮)
  max_qa: 2                                  # 每 conv 最多 N 道 QA(mini 实际只有 1 道)
  concurrency: 1
  recall_top_k: 200
  cutoffs: [10, 20, 50, 200]
  run_tag: local-smoke
  scope_tag: ""          # 正常运行保持为空
  skip_write: false
  cleanup_after_run: false

evaluation/locomo/entry.py 的 CLI 参数与配置项一一对应,常用项如下:

参数 默认值 含义
--data datasets/locomo/locomo10.json 数据文件路径
--conversation-ids 0 逗号分隔的会话数组下标
--max-sessions / --max-turns / --max-qa 0 / 5 / 2 截取范围,0 表示不限制
--concurrency 1 conversation 级并发(conv 内部 cutoff/QA 串行)
--recall-top-k 200 每题召回条数
--cutoffs 10,20,50,200 多 cutoff 切片,生成 top-k 准确率曲线
--run-tag local-smoke 产物目录标签
--scope-tag 空 仅在复用已写入数据时填写 run_meta.json 中的 SCOPE_TAG
--skip-write 关 跳过写入、复用后端已有记忆(必须与 --scope-tag 同时使用)
--cleanup-after-run 关 成功完成后清理本轮可检索记忆与派生索引

入口对三个易误用参数组合做了硬性校验(见 entry.py):--skip-write 必须同时指定 --scope-tag;--scope-tag 仅能与 --skip-write 一起用于显式复用旧 Scope;复用旧 Scope 时禁止开启 --cleanup-after-run,避免误删对比基线。

mini 默认规模即"1 个 conversation、2 个 turn、1 道 QA"(因为数据中只有这些内容),max_turns: 5、max_qa: 2 等截取上限对 mini 实际不生效。

切换到标准全量数据集

切换到全量只需换数据路径和范围,不换测评实现:

locomo:
  data: datasets/locomo/locomo10.json
  conversation_ids: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  max_sessions: 0
  max_turns: 0
  max_qa: 0

框架建议(见 evaluation/README.md):本阶段是功能验证,先保持 mini 与 concurrency: 1,确认端到端通过后再放大范围。

数据如何进入记忆内核:写入、检索与判分

写入路径

locomo/run.py 中 ConversationProcessor.write_conversation() 把每个 turn 独立写入,关键口径:

  • role 映射:speaker_a → user、speaker_b → assistant,并在正文中做名称替换,与 prompt 中"User = main person"对齐;
  • 时间锚点:occurred_at 取 session 起始时间逐 turn 递增 1 秒;metadata 携带 observation_date(session 日期,%Y-%m-%d)、speaker、session、seed_key(形如 conv_N/D1:2)与 write_id;
  • 抽取触发:metadata 中 infer: "true" 触发 Engine.write 走 Evolver(EXTRACT) → LLM Extractor 抽取派生记忆(否则只走 classifier 打标 + 建索引的默认路径);
  • 默认关掉的兜底开关:RAW_FALLBACK=0(抽取零产出不补写原文,以测纯抽取质量)、RAW_ALWAYS=0、DATE_PREFIX=0(正文不加绝对日期前缀,时间锚点统一取 metadata 的 observation_date 行)。

Scope 隔离方面,run.py 将 SCOPE_TAG 校验后拼为 SCOPE_ORG = "locomo-infer-{SCOPE_TAG}"(默认 SCOPE_TAG 即 {RUN_TAG}-{RUN_ID}),每个 conversation 使用 Scope(org=SCOPE_ORG, user="conv_N"),写入、检索、清理都限定在各自 Scope 内。

检索路径与多 cutoff 评测

每题只召回一次(top_k = RECALL_TOP_K = 200,DisclosureLevel.L2、with_trajectory=True),再按 10 → 20 → 50 → 200 多个 cutoff 对同一批召回结果切片,各自生成答案并判分,得到 top-k 准确率曲线。多 cutoff 复用同一召回,题内串行、conv 间按 concurrency 并行。

召回后端由 locomo/config.yml 定义:rerank_enabled: false(无 rerank 基线)、graph_enabled: false,keyword 路走本地 Elasticsearch(BM25)、vector 路走 Milvus(COSINE,dim=1024,对齐 bge-m3),融合用 weighted RRF(k=20、vector 2.0 / keyword 1.0);检索管线的 min_score、min_score_ratio、min_score_ratio_uncalibrated 均显式置 0,让融合结果不经阈值再砍,对齐基线口径。

回答生成与 J-score 判分

回答与判分 prompt 在 locomo/prompts.py 中,采用业界标准 J-score 方法论(categories 1–4 上的二分类 LLM judge):

  • 类别映射(prompts.py):1 = multi-hop(282 题)、2 = temporal(321 题)、3 = open-domain(96 题)、4 = single-hop(841 题);5 = adversarial(446 题)不参与计分,runner 中 category > 4 or category < 1 的题直接跳过;
  • 回答生成:get_answer_generation_prompt() 取召回结果前 ANSWERER_MEMORY_LIMIT = 200 条,按 created_at 升序(时间叙述式)呈现,不展示排名与分数以避免锚定偏差,并注入 reference_date(由第一个可解析的 session_*_date_time 推断)作为时间推理基准;
  • 判分:统一 judge prompt 输出 {"label": "CORRECT" | "WRONG"},规则包含部分正确即算对(partial credit)、同义改写等价、14 天日期容差、时长 50% 容差等;category 3(open-domain)的标准答案在判分前经 preprocess_answer() 截取分号前的主答案部分。

运行产物与数据清理

每轮产物写入 evaluation/outputs/locomo/<run_id>_<run_tag>/(默认 local-smoke 标签),LoCoMo 侧的关键文件:

evaluation/outputs/locomo/<run_id>_local-smoke/test_result_v5.json   # 各 conv 分 cutoff 准确率
evaluation/outputs/locomo/<run_id>_local-smoke/test_metrics_v5.json  # 阶段耗时/token/分位数
evaluation/outputs/locomo/<run_id>_local-smoke/run_meta.json         # 参数快照/Scope/数据 digest
evaluation/outputs/locomo/<run_id>_local-smoke/run.log               # 脚本日志
evaluation/outputs/locomo/<run_id>_local-smoke/engine.log             # 引擎日志

另有按 conv 分文件的 test_mem_extract_v5<conv>.jsonl(每 turn 的派生记忆)、test_recall_v5<conv>.jsonl(每题召回明细与轨迹)、test_errors_v5<conv>.jsonl(错题详情:召回记忆 + 回答 + judge 输出)。按轮次隔离产物目录是必要的:这几类文件以追加模式写入,共用目录会跨轮混数据。run_meta.json 除记录生效参数与 SCOPE_TAG 外,还通过环境变量白名单 + 密钥标记双保险排除 API Key 类值,配置快照落的是原始 YAML(只含 ${VAR} 变量名,不含展开后的密钥)。

后端数据默认保留以便 bad case 与 evidence 溯源。确认无需复盘后,可在 evaluation/config.yml 中显式开启 locomo.cleanup_after_run: true:程序会先完成写入、检索、Judge 与指标产物采集,再经正式 MemoryAPI 的 DeleteMode.PURGE 删除本轮 Scope 下的 Redis 记忆真源及 Milvus/Elasticsearch 派生索引(见 run.py 的 _purge_run_data(),删除后会分页复核 Scope 内无残留)。/messages/ 原文缓存与审计记录按内核契约保留;清理失败返回非零退出码,不会静默报告成功。

注意事项与自检清单

  1. mini 分数不是正式分数:locomo_mini.json 只有 1 conv / 2 turn / 1 QA,仅验证链路;报告正式指标必须切换 datasets/locomo/locomo10.json 全量范围。
  2. 校验 SHA 用 Git blob 原始字节:跨平台(尤其 Windows CRLF)场景下,对磁盘文件直接哈希可能得到不同值。
  3. 复用旧 Scope 的三件套约束:scope_tag 取自原运行 run_meta.json 的 effective.SCOPE_TAG,必须同时设 skip_write: true,且禁止开启 cleanup_after_run。
  4. 完整数据与密钥不入库:locomo10.json、evaluation/environment/.env 及其真实值均被 .gitignore 排除,提交 PR 时只带数据目录 README 与 mini 文件。
  5. DISABLE_THINKING 默认保持 0:仅自建 vLLM GLM 部署需要时设为 1;托管 OpenAI 兼容端点(如华为云 MaaS DeepSeek)传该参数会触发慢路径,不要开。

综上,agent-memory 的 LoCoMo 数据侧以"标准数据本地放置 + 机械截取的 mini 冒烟样本 + Git blob 字节级 SHA-256 口径"三件套保证了评测输入的可复现性,而 runner 通过 Scope 隔离、多 cutoff 判分与全链路产物落盘,把这一最小样本扩展成了可溯源的完整评测闭环。

登录后查看全文
agent-memory