claude-mem LoCoMo 评估 Phase 03:构建「检索 + Opus 4.6」的 QA 答题管道全解
【免费下载链接】claude-memPersistent Context Across Sessions for Every Agent – Captures everything your agent does during sessions, compresses it with AI, and injects relevant context back into future sessions. Works with Claude Code, OpenClaw, Codex, Gemini, Hermes, Copilot, OpenCode + More项目地址: https://gitcode.com/GitHub_Trending/cl/claude-mem
本文基于 claude-mem 仓库中的 LoCoMo 评估阶段文档 Phase 03: QA Answer Pipeline,完整拆解该 QA 答题管道的三个核心模块——检索上下文构建、分类别提示词模板、带延迟与 Token 计费的回答生成器——并结合同仓库中 claude-mem worker 搜索子系统的真实实现(路由、编排器、搜索常量)说明其底层调用链。读完后你将掌握:如何用 claude-mem 的检索 API 为每个 LoCoMo QA 问题构建受限上下文窗口、如何用 Opus 4.6 以 temperature=0 生成短抽取式答案,以及如何用 p95 延迟与 tokens/query 指标与 Mem0 的公开基准(p95 延迟 1.44s、约 1,764 tokens/query)做生产就绪度对比。
1. Phase 03 在 LoCoMo 评估中的位置与目标
LoCoMo 是一套长程对话记忆基准:给定一段跨多个会话的对话(conversation),先通过 claude-mem 的摄取流程将其压缩为可检索的 observations(观察记录),再用 QA 问题检验"记忆系统 + LLM"能否回答正确。整个评估按阶段推进,Phase 03 是其中的答题环节,前序阶段文档可在 Phase 01 与 Phase 02 查看。
Phase 03 要解决的核心问题是:
- 检索:对每个 LoCoMo QA 问题,从已摄取的对话记忆中搜索相关 observations,并按会话所属 project 作用域隔离;
- 格式化:把检索结果拼接为单一上下文字符串,并在字符预算内做"观察边界感知"的截断;
- 答题:调用 Opus 4.6(
claude-opus-4-6)生成短抽取式答案,上下文信息不足时输出unanswerable; - 计量:对每次 API 调用记录延迟与 input/output token 数,用于与 Mem0 公开指标(p95 延迟 1.44s、约 1,764 tokens/query)做对比,验证 claude-mem 管道的生产就绪度。
阶段文档记录该阶段已于 2026-02-26 完成:在 conv-26 对话的 20 个问题上跑通端到端原型,平均搜索延迟 1165ms、平均答题延迟 2032ms、每题约 455 tokens,结果落盘为qa-prototype-results.json,7 个测试文件共 80 个测试全部通过。
2. 检索模块:searcher.ts 的三个函数
阶段文档定义了evals/locomo/src/qa/searcher.ts中的三个函数,它们构成从"问题"到"可用上下文"的完整链路。
2.1 searchForContext:带项目作用域的检索
searchForContext(question: string, project: string, limit: number)的约定:
- 通过 worker 客户端的搜索方法查询 claude-mem 的 observations,scope 到当前对话的 project(即 Phase 02 中按
locomo-eval-{sample_id}命名的项目),确保检索只命中同一对话的记忆,不跨对话污染; - 复用摄取层的 worker 客户端
evals/locomo/src/ingestion/worker-client.ts,避免重复实现 HTTP 通信; - 返回
{ results, search_latency_ms },其中search_latency_ms直接取自 worker 客户端内置计时的搜索方法——延迟指标在检索层就打好点,后续无需再测。
这一设计与当前仓库中 claude-mem worker 的搜索子系统是对齐的。从源码结构看,worker 在 SearchRoutes.ts 中挂载了统一的/api/search及/api/search/observations、/api/search/by-file等端点,并在路由中间件中对所有/api/search*请求打统一遥测点;实际检索由 SearchManager.ts 的search方法编排,支持按project、obs_type、concepts、files过滤,并可走 Chroma 语义检索、SQLite 过滤检索或混合策略。而 search/types.ts 中的SEARCH_CONSTANTS给出默认DEFAULT_LIMIT: 20、Chroma 批量大小 100、90 天近期窗口等常量——QA 管道原型中"limit: 10"的取值正是建立在这类默认约束之上的更保守配置。
2.2 formatSearchResultsAsContext:观察边界感知的上下文拼接
formatSearchResultsAsContext(searchResults)的职责是把多条检索结果拍平成一段可喂给 LLM 的文本:
- 依次拼接每条 observation 的title、facts、narrative三类字段;
- 在相邻 observation 之间插入清晰的分节分隔符,让模型能区分"证据来自哪一条记忆"——这对 multi-hop(需要跨会话组合信息)类别尤其重要;
- 对字段缺失或结果结构不完整的情况有容错(测试覆盖"raw text fallback"与"partial fields"两种退化输入)。
2.3 buildContextWindow:字符预算内的边界截断
buildContextWindow(formattedContext: string, maxChars: number)负责把上下文裁剪到预算内:
- 默认预算12000 字符,约合 3000 tokens(按 1 token ≈ 4 字符的常见估算);
- 截断点选在最后一条完整 observation 的边界上,而不是句子中间——被丢弃的一定是整条记忆,不会留下半截叙述干扰模型判断;
- 返回值除最终文本外还携带元数据:实际使用的 observation 条数与总字符数,供日志与结果文件记录。
这一"整条进、整条出"的截断策略是典型的上下文工程取舍:宁可少一条记忆,也不引入噪声。
3. 提示词模板与五类问题分类
evals/locomo/src/qa/prompts.ts包含系统提示词与用户提示词构建器两部分。
3.1 系统提示词:抽取式、受上下文约束
QA_SYSTEM_PROMPT给模型的三条硬性指令:
- 只能基于所提供的对话上下文作答(open-domain 类别除外,见下);
- 答案要短、抽取式,不要写成完整句子——这与 max_tokens=256 的预算互为约束;
- 上下文信息不足时,原样输出
unanswerable,不得猜测。
unanswerable约定让"答不出来"成为可机器判定的合法输出,避免模型硬编答案污染准确率统计。
3.2 buildUserPrompt:五个类别各有专属提示
buildUserPrompt(question: string, context: string, category: string)将"上下文块 + 问题"组织为用户消息,并按 LoCoMo 的 5 个问题类别注入不同指令提示:
| 类别 | 注入的指令提示(原文) |
|---|---|
| single-hop | "Answer using a specific piece of evidence from the context." |
| multi-hop | "This may require combining information from multiple conversation sessions." |
| temporal | "Pay careful attention to dates and the temporal ordering of events." |
| open-domain | "You may use both the provided context and general knowledge." |
| adversarial | "Be careful — verify claims against the context before answering. The question may contain false premises." |
| 未知类别 | 回退到无类别提示的通用格式 |
五类提示的设计意图很清晰:temporal 类考察记忆系统对时间顺序的保持能力(这正是跨会话记忆的难点);adversarial 类问题自带错误前提,考察系统是否会盲从问题表述;open-domain 类是唯一允许使用上下文外通用知识的类别。未知类别的 fallback 分支保证了管道遇到新类别时不会崩溃。
4. 回答生成器 answerer.ts:模型配置与双指标埋点
evals/locomo/src/qa/answerer.ts的answerQuestion(question: string, context: string, category: string)调用 Anthropic API,关键配置与埋点如下(阶段文档记录当时通过bun add @anthropic-ai/sdk安装了@anthropic-ai/sdk@0.78.0):
| 配置项 | 取值 | 目的 |
|---|---|---|
| model | claude-opus-4-6 | 统一答题模型,便于与基准对比 |
| max_tokens | 256 | 答案应短而抽取式,硬截断预算 |
| temperature | 0 | 确定性输出,保证同一问题可复现 |
| 计时 | answer_latency_ms | 从 API 调用发起到收到响应 |
| 计量 | input_tokens/output_tokens | 取自响应usage字段 |
返回值结构为:
{ predicted_answer: string, // 从响应 content blocks 提取文本 input_tokens: number, output_tokens: number, answer_latency_ms: number }答案文本从响应内容块中提取(测试覆盖了多 content block 场景下的拼接逻辑,以及空内容时回退为unanswerable的边界情况)。
为什么必须同时记录延迟和 token?因为 Mem0 的公开指标是p95 延迟 1.44s + 约 1,764 tokens/query——单看准确率无法回答"这套管道能否生产化"。检索延迟、答题延迟、每问 token 三项分开计量后,才能与基准逐项对齐,也才能定位瓶颈在检索侧还是 LLM 侧。
5. 单对话原型运行 run-qa-one.ts
端到端验证由evals/locomo/scripts/run-qa-one.ts完成,流程与产物:
- 选对话:加载 Phase 01 已摄取的第一个对话(conv-26),用
getQuestionsForConversation取全部 QA 问题(该函数默认排除 adversarial 类别); - 限流:原型阶段只取前 20 个问题,保持类别混合(实际跑出 10 条 temporal、8 条 single-hop、2 条 multi-hop);
- 逐题执行四步:
- 用该对话的 project 名检索上下文(limit: 10);
- 构建上下文窗口;
- 调 answerer 用 Opus 4.6 生成预测答案;
- 每题打印一行日志:
"{category} | Q: {question_truncated} | Pred: {answer} | Truth: {ground_truth} | search: {search_latency_ms}ms | answer: {answer_latency_ms}ms"
- 落盘:全部结果写入
evals/locomo/results/qa-prototype-results.json,每个对象含question, category, predicted_answer, ground_truth, search_results_count, search_latency_ms, answer_latency_ms, answer_input_tokens, answer_output_tokens九个字段; - 汇总打印:总题数、按类别分布、3 组"预测 vs 标准答案"样本、延迟摘要(搜索与答题各自的 mean/p95、每问平均 token 数);
- 限速保护:API 调用之间加 500ms 延迟,规避速率限制。
运行方式:
bun evals/locomo/scripts/run-qa-one.ts阶段文档记录的实测基线:平均搜索延迟 1165ms、平均答题延迟 2032ms、约 455 tokens/题。对比 Mem0 的约 1,764 tokens/query,claude-mem 管道在 token 消耗上低了一个量级——这直接来自"检索 limit 10 + 12000 字符上下文窗口"的双重收紧,验证了 2.3 节截断策略的计量意义。
6. 测试套件 qa-pipeline.test.ts:23 个测试的覆盖矩阵
evals/locomo/tests/qa-pipeline.test.ts共 23 个测试,按函数分组:
- formatSearchResultsAsContext(5 个):多观察的分节分隔、空数组、raw text 回退、字段部分缺失、3 条观察的分隔符正确性;
- buildContextWindow(6 个):预算内透传、边界截断、紧预算、零预算、空上下文、默认 maxChars 取值;
- buildUserPrompt(7 个):5 个类别各自的提示词命中、上下文块与问题均被包含、未知类别回退;
- answerQuestion(5 个,mock Anthropic 客户端):发送的 model/system prompt/max_tokens 正确、文本提取并 trim、空内容回退
unanswerable、token 与延迟指标取自 usage、多 content block 拼接。
其中"mock SDK 客户端"的做法值得注意:不发起真实 API 调用即可断言请求参数(模型名、系统提示、max_tokens),既省钱又让参数回归可被 CI 捕获。运行方式:
bun test evals/locomo/tests/qa-pipeline.test.ts阶段文档记录:该文件与其余 6 个评估测试文件合计80 个测试全部通过。
7. 与仓库搜索子系统的对应关系
虽然本阶段文档描述的evals/locomo/评估代码未包含在当前仓库快照中(其存在与实现细节以阶段文档 LOCOMO-EVAL-03.md 的完成记录为准),但 QA 管道所依赖的 claude-mem 搜索能力在主代码中是真实可查的:
- API 入口:SearchRoutes.ts 挂载
/api/search统一端点与/api/search/observations等专用端点,路由中间件为全部搜索请求打统一遥测; - 检索编排:SearchManager.ts 的
search方法按searchType/obs_type/concepts/files等选项分发到 Chroma、SQLite 或混合策略,并在project维度上做作用域过滤——正是searchForContext(question, project, limit)第二参数的落点; - 结果模型与常量:search/types.ts 定义了
SearchResults(observations/sessions/prompts 三类结果)、SearchStrategyHint与SEARCH_CONSTANTS(默认 limit 20、90 天近期窗口); - 策略层:
src/services/worker/search/strategies/下提供 Chroma、SQLite、Hybrid 三种策略实现。
从源码结构看,QA 管道的"project 作用域 + limit 10 + 计时搜索"完全落在上述接口的能力范围内:searchForContext是 worker 搜索 API 面向评估场景的一层薄封装,而非另起炉灶的检索实现。这保证了评估测量的是 claude-mem 真实生产代码路径的延迟与成本,而不是为评测特化的捷径。
8. 小结
Phase 03 交付了一条可复用的 QA 答题管道:searchForContext(项目作用域检索 + 延迟埋点)→formatSearchResultsAsContext(带分节的证据拼接)→buildContextWindow(12000 字符/约 3000 token 预算、观察边界截断)→answerQuestion(claude-opus-4-6、temperature=0、max_tokens=256、token 与延迟计量)。23 个单测 + mock SDK 的参数断言保证了行为回归可检测;conv-26 上的 20 题原型给出 1165ms / 2032ms / 455 tokens 的实测基线,为后续与 Mem0 公开指标的 p95 与 tokens/query 逐项对比、以及扩展到全部 10 个对话的完整评估(见 Phase 04 起的后续阶段)打下了计量基础。
【免费下载链接】claude-memPersistent Context Across Sessions for Every Agent – Captures everything your agent does during sessions, compresses it with AI, and injects relevant context back into future sessions. Works with Claude Code, OpenClaw, Codex, Gemini, Hermes, Copilot, OpenCode + More项目地址: https://gitcode.com/GitHub_Trending/cl/claude-mem
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考