历史聊天记录检索
术-流程 · 术层 skill 全文
本页是 <code>rules/skills/bestpractice_chat_history_retrieval.md</code> 的逐字投影(仅隐私清洗,零改写)。
时点提示:本页是仓内文件 rules/skills/bestpractice_chat_history_retrieval.md 的逐字投影(仅做隐私清洗:仓库根绝对路径→相对路径、家目录→~/;除此零改写)。若源文件后续有修订,以仓内真源为准。
Skill: 历史聊天记录检索
元数据
- 类型: BestPractice
- 适用场景: 用户要求定位、回溯、提取之前的原始对话记录(跨 Claude Code、Codex、Antigravity、Cursor 等 IDE / AI 工具)
- 触发词: "找到那段对话"、"之前聊过"、"上一个 session"、"那个设计文档"、"之前讨论的那个东西"
- 创建日期: 2026-04-03
- 重大修订: 2026-04-26(补入 Codex harness 入口、Codex SQLite/rollout 检索路径、统一
chat_history_search工具;明确与每日 Observation 提取分离) - 相关 skill:
bestpractice_antigravity_pb_decoding(draft,只当目标在 Antigravity 存储里时触发)
目标与边界
做:在本地文件系统中定位用户描述的历史对话,返回对话所在的 session ID、时间、关键内容摘录或完整提取。
不做:
- 解码 Antigravity
.pb文件的工具细节:委托给bestpractice_antigravity_pb_decoding。本 skill 只说"什么时候需要解码"。 - 对话内容的加工分析:如果用户要总结、重写、比较多份对话,那是独立任务。本 skill 只负责找出原文。
- 长上下文全量覆盖:检索到 raw session 后,如果需要分片阅读、coverage audit、二层交叉验证,转
workflow_long_context_scale_up。 - 完成声明或 reviewer 审查:如果目标是判断任务是否真的完成,转
workflow_post_loop_critical_review;如果目标是审查 reviewer 是否真的审查,转workflow_recursive_agent_review。 - 每日 Observation 提取:Observation 是定时摘要/记忆建设;本 skill 是按用户问题即时检索原始聊天记录。不要为了找某段聊天去改
observer.py、推进 marker、或写OBSERVATIONS.md。 - 跨用户聊天记录搜索:只在当前本机用户的数据里找。
验收标准
完成搜索后必须能回答:
- 命中位置:目标对话的 session ID、所在文件路径、关键时间戳。
- 搜索覆盖:扫了哪些数据源、用了哪些关键词、命中多少次。
- 遗漏判断:是否存在未扫到的数据源;如果扫不到,给出下一步建议(例如"可能在 Antigravity 存储,需要解码")。
- 产出物(如果用户要求落盘):提取的文本写到约定位置,格式自洽。
- Loop 位置(如果用户问已运行 Loop):返回
loop_home、状态文件、Final Report 路径和关联 raw session / rollout;找不到loop_home时说明它可能是 report-only session,不把 raw rollout 误称为 sustained Loop 记录。
核心原则
- 绝不说"无法访问对话历史"。 所有 IDE / AI 工具的对话都落在本机文件系统,只是格式各异。
- 用户回忆常有偏差。 时间可能偏后或偏前 1-3 天;工具名可能记错(说"CC 里的对话"实为 Antigravity);结构描述可能与实际分节不一致。对用户提供的元信息保留 30% 不确定度。
- 模糊搜索是常态。 "那个 session 里有个完整设计文档"比"session ID 是 xxx"更常见。本 skill 的核心是把模糊描述转为高置信度的命中。
- 检索和观察分离。
contexts/daily_records/{claude,codex}/是可读索引层,Observation 是下游消费者;检索任务可以读取 daily records,但不调用 Observation pipeline,也不把检索结果写成 Observation。
快速入口
需求/任务类检索的第 0 步:目标是「用户提过的需求 / 任务的 prompt 原文」时,先查 contexts/survey_sessions/session_task_registry/REGISTRY.md(187 需求条目,按本质×领域两轴,覆盖 2026-05-10~06-10 窗口);已知条目 ID 直接 python3 tools/req_lookup/req_lookup.py show <SR-xxx|RO-xx> --full 一跳取原文全文(带溯源链与数据边界警示),命中后再按需回 raw JSONL。窗口外或非需求类目标再走下面的通用检索。
优先用 chat_history_search 工具做第一轮候选定位,再按需读原文:
# 同时搜索 Claude Code + Codex 的可读 daily records
python3 tools/chat_history/search.py "关键词或短语" --source all --store daily --limit 20
# Codex 专项:先搜 daily records + state_5.sqlite thread metadata
python3 tools/chat_history/search.py "关键词或短语" --source codex --store all --limit 20
# 多关键词同时满足,适合用户记忆模糊时收窄候选
python3 tools/chat_history/search.py "Library" "Dissolution" --source all --all-terms --since 2026-04-20工具输出只做候选定位。命中后用 sed -n / jq / rg 读对应 daily record 或 raw JSONL 的上下文,最终回答必须包含路径和行号。
查 sustained Loop 运行记录时,先定位 Loop home:在 ~/context-infra/{adhoc_jobs,contexts/survey_sessions}/ 下搜索 CONTROL.md、.loop_trace/state.json、loop_state.codex.json 或 reports/FINAL_REPORT.html,最近的共同父目录就是该 Loop 的本地运行目录。命中 raw session 但不知道 Loop home 时,用 session id、首条用户 prompt、task slug 搜 USER_PROMPTS/、SOURCE_MANIFEST*、PROGRESS.md;若仍无命中,按 report-only session 处理。
数据源(按检索成本 / 可读性优先级)
| 优先级 | 数据源 | 路径 | 特点 |
|---|---|---|---|
| 1 | Claude Code 提取日志 | ~/context-infra/contexts/daily_records/claude/*.md | 结构化、可 grep、已去噪,按天一个文件 |
| 2 | Codex 提取日志 | ~/context-infra/contexts/daily_records/codex/*.md | 结构化、可 grep,保留截断工具输出,文件末尾含 Codex memory 变更 |
| 3 | Claude Code 原始 JSONL | [session-path] | 完整原始,按项目分目录 |
| 4 | Codex 原始 rollout JSONL | [codex-path];索引见 [codex-path] 的 threads.rollout_path | Codex Desktop / CLI 原始事件流 |
| 5 | Loop homes | ~/context-infra/adhoc_jobs/**/{CONTROL.md,.loop_trace/state.json,loop_state.codex.json,reports/FINAL_REPORT.html};审查/报告型目录也可能在 ~/context-infra/contexts/survey_sessions/ | 已运行 Loop 的本地控制面;用于从聊天命中反查执行过程 |
| 6 | Antigravity 对话 | ~/.gemini/antigravity/conversations/*.pb | protobuf 格式,需解码 → 见独立 skill |
| 7 | Cursor 对话 | (待补,Cursor 加入后由该 skill 维护者扩展此表) | — |
| 8 | Claude Code Transcripts | ~/.claude/transcripts/*.jsonl | 备份存储 |
| 9 | OpenCode 活跃库 | ~/.local/share/opencode/opencode.db | 近 7 天 session,OpenCode server 运行时用;SQLite,按 session_id/时间/title 索引查 |
| 10 | OpenCode 归档库 | ~/.local/share/opencode/opencode_archive.db | 7 天前的 session(含巨型 message.data 行),只检索用、不被 server 加载;见下方「OpenCode 聊天记录归档与检索」 |
默认从 1-2 开始。当用户线索指向非 Claude Code / Codex 工具,或 1-4 都找不到时,扩展到 Antigravity / Cursor / OpenCode(活跃库 + 归档库)。
识别提示:看到 originator: Codex Desktop、rollout-*.jsonl、[codex-path]、Codex Desktop,考虑 Codex;看到 Planner、cascadeId、"step N(步骤 N)"这类说法,考虑 Antigravity;看到 .cursor/rules/ 路径,考虑 Cursor;看到 .claude/ 或 claude CLI 命令,是 Claude Code。
OpenCode 聊天记录归档与检索
OpenCode 的聊天记录存在 SQLite(~/.local/share/opencode/opencode.db)。活跃库曾因 17G 巨型 message.data 行(单条粘贴的大文件/工具结果)拖慢 server,已按时长归档拆成活跃库 + 归档库。本节说清实际情况 + 策略 + 持续更新 + 检索方式。
实际情况
- 活跃库
opencode.db:只留近 7 天的 session,OpenCode server 运行时只加载这个,保持轻量、不卡。 - 归档库
opencode_archive.db(同目录、同 schema、单文件):7 天前的 session 整闭包搬过来(session + message + part + todo + session_share + session_context_epoch + session_input + session_message + event_sequence + event),含全部巨型行。不被 server 加载,专给你检索用。数据只搬不删。
切分策略
- 切分线 =
last_activity < now - 7天(last_activity = session/message/part 的 max time_updated,session 子树闭包内取最大值)。 - 7 天是消除全部巨型行的最大窗口(巨型行集中在 07-02~07-07 的闲置 session)。更短更激进,更长(10/14 天)会留下巨型行。
- 共享表(project/workspace/account/credential/migration)留活跃库;为保持原 schema 的
session.project_idFK 完整,归档库只额外复制被归档 session 引用的 project 检索元数据,credential 不进归档。
持续更新机制
归档逻辑合并进 tools/opencode_maintenance/opencode_event_cleanup.py + 它的 launchd cron(com.context-infra.opencode-event-cleanup),不另造平行 archiver:
- cron 定期把新老化(last_activity < now-7天)的 session 闭包移入归档库 + 从活跃库移除(搬,不删);活跃 session 的 event 保留。
- 活跃库 db >10G 时触发完整双目标离线重建(回收 freelist;event/event_sequence 仍按 aggregate 随 session 分流),一次产出新活跃库 + 归档库,原子替换、留 rollback 副本。
- 随天数增加、聊天累加,旧数据持续进归档、新数据留活跃库,活跃库始终轻量。
检索归档
检索用只读方式跨活跃库 + 归档库查,不让 OpenCode server attach 归档库(避免 server 加载 17G):
# ATTACH 归档库,跨活跃+归档按 title 搜(走索引,快)
sqlite3 -readonly ~/.local/share/opencode/opencode.db <<'SQL'
ATTACH DATABASE '~/.local/share/opencode/opencode_archive.db' AS arc;
SELECT 'active' AS src, id, title, datetime(time_created/1000,'unixepoch','+2 hours') FROM session WHERE title LIKE '%关键词%'
UNION ALL
SELECT 'archive', id, title, datetime(time_created/1000,'unixepoch','+2 hours') FROM arc.session WHERE title LIKE '%关键词%'
ORDER BY 4 DESC;
SQL检索原则:
- 先按 session_id / 时间 / title 缩范围(走索引,快),只在用户要看具体 session 时才读 message.data(巨型行读慢,偶发)。
- 不要对归档库做
json_extract(message.data, ...)全扫(会触发巨型行 overflow 链全读)。 - 要恢复某个归档 session 回活跃库:从归档库按闭包复制回,或
opencode export <sid>导出 JSON 再opencode import(注意 export 只含 transcript,不含 todo/event/share)。
已知边界
- 上次 event 清理(07-07,
opencode_event_cleanup.py重建时跳过 event 行)清掉的是老 event 的 durable 重放历史,不是聊天记录(message/part 独立且完整)。当前 UI/检索不依赖 event,但跨端 sync/replay/审计缺旧事件——归档机制已把 event 随 session 一起搬,不再无条件清零,避免再留 gap。
标准搜索流程
小范围(用户给了精确关键词或 session ID,或时间窗口 ≤ 3 天)
直接 Grep → Read 命中上下文 → 汇报。不需要 sub-agent。
中范围(> 3 天,或跨多个项目)
- 先在
daily_records/claude/.md和daily_records/codex/.mdgrep 定位候选日期和行号 - 读命中区域前后 50-100 行确认相关性
- 如需原文细节,去对应 JSONL 文件深入提取
Codex 额外步骤:如果 daily record 命中不足,查 state_5.sqlite 的 threads 表(title、first_user_message、cwd、model、rollout_path),再打开对应 rollout-*.jsonl。chat_history_search --source codex --store metadata 已封装这一步。
大范围(三 Agent 架构)
本架构只用于候选 session discovery。它产出已审查的候选 session 清单、路径和短摘录,不做全文 coverage,不裁决任务完成状态。需要全量阅读候选原文时,把 raw session manifest 交给 workflow_long_context_scale_up。
启用条件(满足任一):
- 时间跨度 > 5 天
- 累计数据 > 500MB
- 相关度判定需要跨多维度(结构 + 内容 + 时间 + 工具)
- 预期关键词虚报率高(同一主题多个 session 互相类似)
三波并行架构:
| 波次 | 职责 | 输出 |
|---|---|---|
| Extract | 按日分配 sub-agent,按四级相关度(HIGH/MEDIUM/LOW/NO)打标,只保留 HIGH/MEDIUM | 每天一份候选清单 |
| Review | 独立读原始日志 + Extract 结果交叉验证,修正虚报、去重、加补漏 | 每天一份已审查清单 |
| Summary | 对已审查的每个 session 生成 3-5 条 bullet 摘要 | 每天一份摘要 |
| Audit(可选补遗波) | 给每天已确认 session IDs 作为排除列表,扫剩余 session 找遗漏 | 补漏清单 |
并行模式:同一波次内 N 天任务并行(run_in_background=true)。波次间串行(Review 依赖 Extract 结果,Summary 依赖 Review)。
虚报率目标:经过 Review 后应降到 < 20%。如果 Review 砍掉的比例超过 50%,说明 Extract 关键词过宽,需要在 Extract 提示词里加结构约束(例如要求同时命中 ## 需求 + ## 设计 才打 MEDIUM)。
模型选择:默认 Haiku。三 Agent 架构的价值在于并行和独立验证,不在于单次推理的深度。
Audit 波什么时候加:如果有某几天初次扫描结果数量远低于预期(例如 1-2 个 session),且这些天确实有重要讨论,加 Audit 波专门查漏。2026-04-20 的 Git session 扫描里,Audit 波在 04-05 和 04-17 各补回 1 个真漏(6f0564a4 和 0de65789)。
模糊搜索方法学
用户只有模糊记忆时使用。2026-04-20 跨 CC+Antigravity 搜索用这套方法 3 分钟定位到 session 18f72295。
三层启发式
| 层 | 作用 | 关键词模式 | 命中准则 |
|---|---|---|---|
| L1 粗过滤 | 缩减到 ~10% 候选 | 用户说的主题词 + 中英同义词 | 任意命中即保留 |
| L2 语义过滤 | 缩减到 ~2-3 候选 | 项目域专有概念(如 "Brain Layer"、"orchestration") | 至少 2 个并现 |
| L3 深度匹配 | 锁定唯一目标 | 结构标记(分节 / 列表特征)+ 边界标记("落盘意图"短语) | 两种标记共同确认 |
L3 最关键:多 session 可能在 L1、L2 都命中(类似话题的多次讨论),但"聊天 → 落盘"的显式标记往往只出现在那个真正生成了完整产物的 session 里。
决定性信号库
按领域收集。用户说的是什么场景,取对应信号:
系统设计 / 架构文档:
- "你确认这个设计后,我就把它写入..."
- "设计先在聊天里成型,然后才落盘"
- "下面是完整方案"、"N 个核心问题需要解决"
- "以下是最终设计"
代码审查 / 功能实现:
- "我把实现提交到..." / "测试通过了,记录在..."
- "完整 diff 如下"
创意内容 / 文案:
- "最终版本在..." / "修改历史见..."
跨工具识别:
- Antigravity 特征:
step N、planner trajectory、cascadeId - Cursor 特征:
.cursor/rules/路径引用 - CC 特征:
.claude/目录引用、claudeCLI 命令
边界标记的使用:在 L3 用这些短语作 grep 关键词,配合时间窗口收敛。
时间 + 结构双约束
不要只用时间窗口筛:
- 时间:用户估计的窗口 ±3 天(同时向前和向后扩展,用户记忆可能偏任一方向)
- 结构:该 session 的 assistant 回复数 > 20(深度讨论),或某单条回复 > 5000 字(综合产物)
- 话题连贯:same topic 的连续对话,而非多话题跳跃
排除法
L2 层出现 2-3 个看似都匹配的 session 时:
- 各候选提取代表性 step 的内容片段
- 对比结构特征:问题列表 + 方案分节 vs 平铺讨论?
- 对比上下文功能:最终 deliverable vs 中间讨论?
2026-04-20 案例:03-26 有 3 个平行会话(L5/L6/L7 conflict-resolution),prompt 明确"只列冲突不给建议",回复是平铺列表。而目标 session 18f72295 是"核心需求 + 设计方案"的分节文档。结构差异一目了然。
已知陷阱(从实战总结)
陷阱 1:虚报率控制失败(2026-04-20)
Extract 阶段仅按关键词过滤,虚报率达到 67%(174 候选 → 58 确认)。
根因:MEDIUM 判定过宽,只要关键词命中就标 MEDIUM,忽略了结构特征。
应对:Extract 提示词里强制要求同时命中"内容关键词 + 结构特征"(如 ## 需求 + ## 设计 分节标签同时存在)。Review 波兜底兜不住的话,考虑引入补遗 Audit 波。
陷阱 2:时间窗口双向偏差(2026-04-20)
用户回忆"03-26 或 03-27",实际目标在 03-24 → 03-25。只向后扩展窗口就会错过。
应对:默认同时向前和向后扩展 ±3 天。关键:问用户"记忆的时间是大约还是很确定",确定度低的话窗口扩到 ±5 天。
陷阱 3:多工具混淆
用户说"那个对话"默认假设在 CC JSONL 里,漏掉 Antigravity 存储。
应对:听到"Planner"、"step N"、"cascadeId"等特征词,立即怀疑不是 CC。同时扫 CC + Antigravity 两边的数据,不预设在哪。
陷阱 4:长 session 尾部发起大规模扫描
context 已经 80%+ 时发起 JSONL 全量扫描会爆主上下文。
应对:派 sub-agent 处理,主 session 只接收汇总。大文件(> 500KB 单个)必派 sub-agent。
陷阱 5:单次精确搜索失败就放弃
错:grep "现在是这样子因为之前没有好好管理版本" → 没找到 → 放弃
对:拆分关键词:grep "版本" + grep "管理" → 缩小范围 → 确认陷阱 6:声称"无法访问对话历史"
所有聊天记录都在本机,只是格式可能需要解码。永远不说"访问不到"。
跨源整合(CC + Antigravity 等)
当目标跨越多个工具时:
- 各数据源独立搜索,再合并:不要试图统一格式搜索,各家 grep 各家的
- 时间顺序合并:按时间戳把两边命中串成一条时间线
- 边界清晰:每条命中标注来源(
[CC]/[Antigravity]/[Cursor])
典型合并格式:
# 2026-03-24 ~ 2026-03-28 设计文档演化链
## [Antigravity] 03-24 23:53 ~ 03-25 17:34 — session 18f72295
<关键讨论 / 最终产出摘录>
## [Claude Code] 03-28 ~ — session xxx
<承接上面 Antigravity 设计的落盘实施>反模式
- 声称"无法访问" → 所有记录都在本地
- 单关键词精确匹配失败即放弃 → 拆关键词、走三层启发式
- 只扫单一数据源 → 用户线索暧昧时同时扫多源
- 盲目相信用户的时间 / 工具描述 → 双向扩窗口、交叉验证工具
- Context 高位启动大规模扫描 → 派 sub-agent
- 凭空写 SOP → 本 skill 的每条指令都有实战触发过的场景
输出规范
搜索完成的汇报模板:
命中:<session ID>(<数据源>,<时间>)
覆盖:扫了 N 个文件 / 共 M 行 / 关键词 {kw1, kw2, ...}
产出:<如有落盘>写到 <path>
遗漏风险:<如果存在可能未扫到的数据源,列出>落盘文件推荐命名:session_<id_prefix>_<topic>_<YYYYMMDD>.md,放约定位置(如 <project>/.external-inputs/、contexts/survey_sessions/)。
JSONL 快速参考(Claude Code)
- 每行一个 JSON 对象
"type": "user"/"type": "assistant"(不是 "human" / "ai")content可能是字符串或数组(含 text / tool_use)- 跳过
tool_use和tool_result的详细内容可以省大量 token thinking通常也可跳过
用 grep -n <keyword> file.jsonl 定位行号,sed -n '<line>p' file.jsonl | jq . 解析单行。
JSONL / SQLite 快速参考(Codex)
- 可读索引优先:
contexts/daily_records/codex/YYYY-MM-DD.md - 原始事件流:
[codex-path] - thread 索引:
[codex-path]的threads表 - 常用字段:
id、title、cwd、rollout_path、source、originator、model、reasoning_effort、first_user_message - Codex reasoning 常见为 encrypted;检索时不要试图解密,也不要凭 encrypted_content 编造 thinking
常用命令:
sqlite3 [codex-path] \
"select id,title,cwd,rollout_path,model,reasoning_effort from threads where title like '%关键词%' or first_user_message like '%关键词%' order by updated_at desc limit 20;"
rg -n "关键词" [codex-path] contexts/daily_records/codexCodex daily record 的 header 已包含 rollout: [codex-path],这是从可读摘要回到原始记录的主路径。