关于 memory-collector
memory-collector 是一款 Agent 技能,可从编码 Agent 会话记录中提取长期知识,并写入当前可用的记忆存储。它通过来源适配的读取器、预算控制、来源标记和可重试检查点管理采集过程。
主要功能
- 01读取 Claude Code、Pi / OMP 和 Codex 会话
- 02按运行预算筛选合格会话
- 03提取主题、人物和事件
- 04为存储条目添加来源信息
- 05跟踪进度并协调重试
适用场景
- 01从编码会话收集决策和技术记录
- 02将选定的会话历史采集到记忆存储
- 03在写入中断后安全恢复采集
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ thorinside/ skills --skill memory-collector安装后在 Claude Code 中输入/memory-collector确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
prompts/events.mdfacts.mdpeople.mdREADME.mdtopics.mdreaders/claude_code.mjscodex.mjscollector-state.mjscommon.mjspi.mjsREADME.mdselect.mjsreaders/tests/collector.test.mjsfixtures.mjsreader-safety.test.mjs详细介绍
记忆收集器
你最丰富的记忆来源就在磁盘上,一直无人触及:这台机器上运行过的每一次编码 Agent 会话。记录着决策、经验教训、未决问题、人物,以及偶尔值得引用的激烈发言的完整转录——这些内容无法查询,全都在 JSONL 文件中逐渐腐朽。
这个技能就是收集器:它会按预算、借助游标跟踪,挖掘这些转录,并将持久知识写入当前环境提供的任意记忆存储。它与存储无关(在运行时发现可用能力,类似于 memory-gardener),并且了解来源(知道编码工具会将会话存放在哪里)。提取提示词随附于 prompts/,由 Jeremy Scherer 从 EI 的提取流水线中带来(MIT)。
分工方式:收集器负责播种,园丁负责修剪。收集过程有意容忍近似重复和过度生长——园丁的验证关卡、去重整理器和臃肿拆分机制,正是为整理收集器产出的内容而存在的。不要让收集器变得完美;要让两者协同收敛。
基本规则——安全约定
- 来源只读。 切勿修改、移动或删除转录文件。
- 存储只增不减。 收集器会创建和更新记忆条目;绝不删除。任何看起来应该删除的内容都应由园丁处理。
- 每次运行都要设置预算。 默认:每次运行 3 个会话(或约 150 条消息),从主机上所有受支持的来源中优先选取符合条件的最新会话,按
lastMessageAt降序排列,其次按工具和会话 ID 升序排列(按码点顺序;ID 重复时按路径区分)。在消耗预算前,先筛除活跃、已完成和琐碎的会话。达到预算后停止;保留待处理队列。 - 跳过活跃会话。 最近约 30 分钟内修改过的转录(或工具显然仍在会话中运行)都应跳过——提取未完成的会话效果很差。下次运行时它还会在那里。
- 绝不存储机密。 编码转录中包含令牌、连接字符串、ARN 和密钥。如果提取出的值看起来像凭据,就将其丢弃。随附的提示词已将这些内容排除在引用之外;存储任何字段时都应采用同样的标准。
- 保守是铁律。 随附的提示词经过调优,因此最常见的响应应该是空结果。遵守这一点——噪声比缺失更糟。
- 来源信息不可或缺。 每个已存储条目都必须包含其来源 ID(见阶段 4)。无法追溯到具体会话的条目只是传闻。
阶段 0 — 调查
转录来源。 该技能附带无需依赖的 Node 读取器
(readers/)。使用只读跨来源选择器:
node readers/select.mjs --cursor <local-cursor-snapshot.json> --host <host> --budget 3。
首次运行时省略 --cursor。将主机游标的完整分页内容复制到
本地私有快照;绝不要用截断的响应代替。检查来源时,使用
node readers/<tool>.mjs --list --limit 100 --offset 0,并按需分页。不要使用 --since highWater 进行采集:它不仅会排除积压内容,还会排除较早且尚未解决的缺口。选择器会在内部盘点所有历史记录,但返回的是有界元数据,而非转录文本。不要手动解析会话存储:读取器已经处理了格式陷阱(sidechain 文件、伪装成用户消息的工具结果记录、有损的 cwd 编码)。
| 工具 | 读取器 | 会话存储位置 |
|---|---|---|
| Claude Code | readers/claude_code.mjs | ~/.claude/projects/<encoded>/<uuid>.jsonl |
| Pi / OMP | readers/pi.mjs | ~/.pi/agent/sessions/(以及 ~/.omp/…) |
| Codex | readers/codex.mjs | ~/.codex/state_<N>.sqlite + rollout JSONL |
| OpenCode、Cursor | 暂无 — 请参阅 readers/README.md 以添加读取器 | 本地应用数据 |
记忆存储。 按照园丁在阶段 0 中的做法,准确从工具界面发现功能 — 记忆搜索/修改、知识图谱、日记、统计信息。 不要假定工具名称。
游标。 查找上一次采集状态:标记为 collector-cursor 的记忆条目或工件。保留所有现有字段、已处理/已跳过的 ID、
时间戳、已存储条目的引用、来源信息和未知来源。readers/collector-state.mjs 同时支持嵌套的 Mac sources 映射,以及扁平的 dev 映射和 skipped_trivial 数组。旧版 highWater/high_water_mark 值仅供历史参考:保持不变,绝不要将其用作遍历边界。
新增的每会话修订和部分进度存储在附加的
collectorV2.sources.<tool>.sessions.<id> 旁侧数据中;旁侧数据加上旧版已解决 ID(而非全局时间戳)共同决定是否完成。
没有游标 → 首次运行:选择最近符合条件的少数会话;所有更早的会话仍可发现。今后的每次运行都从最新到达的内容开始发现,并向更早的未解决历史记录推进。在持续有新输入的情况下,优先处理最新内容仍可能导致历史记录饥饿;这并不意味着存在公平性配额或不同策略。
阶段 1 — 选择
使用 readers/select.mjs 在计算预算前合并来源,而不是针对每个工具分别循环预算或来源顺序。它会应用 30 分钟的修改时间和消息时间保护机制、已解析版本检查以及已知的简单过滤器。对于已知处于活动状态的任何会话,传入 --live-id <tool>:<id>(可重复);当前的 PI_SESSION_ID 会自动排除。活动或不稳定的会话绝不会被标记为简单会话/已完成。无法读取或格式错误的来源仍会保持未解决状态并报告,不会被悄悄归类为空。
返回的 selected 条目是候选项,不代表已消耗预算。提取前,使用捆绑的 --session 读取器确认其中包含真实的人类对话;未知的自动化模板也需要进行此检查。将转换后的版本和 fileVersion 与所选条目核对。如果明显由机器生成,则记录真实、稳定的 skipTrivial 状态转换,并使用更新后的快照和剩余预算重新从选择器补充候选项。绝不要用额外会话替换失败或仅部分分析的会话,从而超出本次运行的预算。
优先选择真实对话。 Agent 自动化也会生成会话——一个 Pi 存储中,每个真人会话可能对应一千个机械化运行任务会话。跳过篇幅很短(少于约 4 条消息)的会话,或开场消息明显是机器生成的任务提示、且之后没有实质性人工跟进的会话,并在游标中将这些会话针对相应版本记录为 skipped-trivial。会话发生变化或重新打开后,必须等其稳定再重新评估。把预算花在噪声上会让采集器无法正常工作。
阶段 2 — 转换
node readers/<tool>.mjs --session <id> 会直接返回已整理干净的会话——仅包含人类文本和助手文本;思考块、工具调用/结果、系统噪声和子 Agent 对话都会由读取器剥离。根据输出执行以下操作:
- 构建完全限定的消息 ID:
<tool>:<machine>:<session>:<reader msg id>(例如claudecode:mbp:0a1f…:42)。引用和来源信息都指向这些 ID。 - 以窗口(约 20–40 条消息)为单位处理会话。每个窗口本身就是“最近的消息”,而此前内容的精简尾部则是“较早的对话”——随附提示词正是围绕这一划分构建的,并且只会分析最近的窗口。
- 使用
readers/collector-state.mjs中的pendingMessages(cursor, session),在重试/重新打开时排除已确认的消息指纹。保留完整对话以供参考,而非用于重新植入。旧版已处理记录没有指纹:只能分析超出其记录的最后一条消息时间戳(或其仅保留的解决时间)的后缀。任何旧版回放之前,先协调现有的来源标签条目。未知的旧版时间应报告为legacy-review,绝不可猜测。首次稳定的 V2 状态转换会为已解决的旧版前缀生成指纹(legacyBaseline:true),但不会重新植入;之后的编辑使用哈希,而非旧的时间戳截止值。基线建立之前,无法可靠检测时间戳相同的重写;这是旧版限制,不代表可以重新摄取所有内容。
阶段 3 — 提取
对每个窗口运行随附的流水线;对于编码工具会话,设置 technical_context: true(这会将 Technical 设为优先类别):
- 主题 —
prompts/topics.md:扫描标记候选主题 → 匹配会逐一检查其是否已存在于记忆中(采取保守策略:不确定 ⇒“新建”)→ 更新会将记录写入相应学科(Event 叙事;Technical 累积,不综合;其他所有内容均综合,不累积)。引用会一并保留。 - 人物 —
prompts/people.md:扫描标记人物(置信度 1–5、捕获标识符、自我指代/假设情形防护)→ 优先按标识符匹配,然后按姓名匹配 → 在对应的人物学科下更新。编码会话中的大多数窗口不会得到任何人物结果;这是正常的。 - 事件 —
prompts/events.md:每个会话运行一次,使用活动回顾测试(“我们调试 CPU 的那个夜晚”)。通常结果为空。 - 事实 —
prompts/facts.md:仅当你维护缺失事实列表时运行(该列表与游标放在一起)。没有列表,就不运行。
阶段 4 — 存储
将提取结果写入已发现的存储,并将字段映射到存储的架构(置信度/曝光影响 → 类似重要性的字段;类别 → 标签/容器;丢弃存储无法容纳的字段,而不是凭空创建归属位置)。为所有内容添加标签 source:<tool>:<machine>:<session> 和 collected:<ISO date>。
如果存储会区分近期/未审核项目,请让新项目明显保持为新项目——园丁的验证关卡(其阶段 1)就是这些新项目应当经过的入口。如果同时存在快速存储和结构化知识存储,就将摘要放在执行检索的位置,将结构(实体、链接)放在图谱所在的位置。
重试安全性不同于语义去重。 在写入任何窗口之前,将其净化后的提取计划、稳定的 windowKey(host, session, messages, cursor) 以及每个存储的操作键/目标,持久化到游标旁的待处理文件中,并读回确认。然后使用该计划 ID 调用 beginWindow,并在任何存储写入之前持久化并读回其游标意图。此操作会使受影响的旧消息确认失效,并记录新的修订代次,即使源之后在写入回复/检查点之前恢复原状也是如此。仅有外部计划并不足够:选择时必须能看到待处理意图。重试时复用该计划。为每项操作添加其键,并通过精确搜索/读回进行协调,然后再创建;plantOnce 说明了所需的存储适配器契约。写入超时并不能证明没有写入。仅当所有计划操作的结果/缺失都已验证后,才使用 reconcileWindow;它会保留收据和计划来源信息,但不会确认消息。源发生变化后,切勿重放已被取代的计划;应先协调其旧写入,然后分析当前待处理消息。如果存储无法协调不确定的写入,就让它保持待处理状态并报告失败,而不是盲目重试。
只有在验证完窗口的所有流水线和必需的存储写入(包括空结果)之后,才为该窗口创建检查点;保留收据 ID 和指纹。针对每个消息身份,单独追踪当前已确认的指纹,不要与历史收据混为一谈。窗口键中的源修订代次会使 A→B→A 的恢复成为新工作,而不是对原始 A 的重试。在同一修订内,键在各检查点/重试之间保持稳定。新消息或恢复的消息应在适当时更新同一个匹配项;新的操作键并不意味着要创建新实体。完成重新打开的会话之前,先协调所有保留的待处理计划,即使写入仍处于不确定状态时源已发生变化。
阶段 5 — 保存游标并报告
使用 readers/collector-state.mjs 中的纯辅助函数构建新的游标快照;这些函数本身不会写入生产状态。每次检查点或完成操作之前,都通过对应的读取器重新读取会话,并通过转换辅助函数使用 assertStable,拒绝已变更或仍处于活动状态的源。每个已验证窗口处理后,持久化并读回 checkpointWindow 进度。部分会话尚未完成;它会保留回执,并在下次符合条件的选择中继续处理。只有在所有待处理消息和会话事件扫描均已验证后,才能标记 completeSession;skipTrivial 必须提供诚实的理由。绝不要将延后处理的 ID 一概标记,也不要删除旧的映射条目。保持所有旧版高水位字段不变;更新的完成状态不能掩盖较早的工作。
每个主机游标只允许有一个采集器。持久化前立即重新读取生产游标,并将其与开始时的快照比较;若发生并发变更,则停止并协调状态,而不是覆盖。分页读取并验证整个已保存游标和报告,包括保留的映射及来源信息。然后报告:
# Collection report — <ISO timestamp>
Sources: <tool: sessions found / processed / skipped-live>
Windows analyzed: N · budget used: <sessions>/<max>
Planted: topics N (new X, updated Y) · people N · events N · facts N · quotes N
Dropped: secrets-shaped values N · low-confidence extractions N
Cursor: resolved <IDs/revisions>; partial <IDs>; legacy high-water unchanged
Backlog: <eligible deferred count>; oldest discoverable <ID/date>; blocked <IDs/reasons>
Handoff: <n> new items awaiting the gardener's validate gate
Substrate 语义完成
当此技能作为 Substrate runner 或工作流作业运行时,保留采集报告作为面向用户的输出,然后确保最后一条非空的助手消息严格为一条 SUBSTRATE_OUTCOME_V1= 声明。生命周期完成状态不是成功信号。
只有在每个计数会话都已完整处理或如实跳过、只有已解决的修订被标记为完成、种入的条目保留了来源信息,且报告和游标都已通过可用存储进行持久化并读回时,才能声明 outcome: "succeeded"。只有在已验证的进度和待写入内容均得到保留并报告为恢复点时,才允许会话处于部分完成状态;来源信息丢失、用时间戳掩盖未解决的工作、持久化机密,或未能持久化所需报告,均应将 outcome 设为 "failed"。内存存储写入不属于 Git 更改,因此使用 changes.status: "notApplicable"。
成功行示例(将占位符替换为真实证据):
SUBSTRATE_OUTCOME_V1={"version":1,"outcome":"succeeded","summary":"Collected <n> sessions and persisted report <artifact-id>","evidence":{"changes":{"status":"notApplicable","reason":"Memory-store mutations are not Git changes"},"verification":{"status":"passed","commands":["read back collector cursor","read back report artifact <artifact-id>"]}}}
不要输出第二条声明,也不要在其后输出任何内容。在 Substrate 作业之外,不要添加这条特定于平台的声明。
定期运行
托管方式与 gardener 相同:任何能够使用此技能调用 Agent 的调度器都可以。建议的频率是——采集器每天运行,gardener 随后每晚运行——这样每次采集都会在一天内得到整理。两者都有预算上限;最坏情况下,报告只会说“没有新内容”。
来源与致谢
提取流水线(扫描 → 匹配 → 更新)、保守的默认设置、三项描述规范、引文测试,以及 prompts/ 中的提示词均来自 Jeremy Scherer 的 Flare576/ei(MIT,© 2026 Jeremy Scherer)——EI 将此流水线作为导入层,用于五种编码工具。此技能将其推广到存储侧,并与 memory-gardener 配套使用。