oh-my-openagent memory-core 源码剖析:Harness-Neutral 的 Git 版 Agent 记忆引擎

发布时间:2026/9/20 19:37:44
oh-my-openagent memory-core 源码剖析:Harness-Neutral 的 Git 版 Agent 记忆引擎 oh-my-openagent memory-core 源码剖析Harness-Neutral 的 Git 版 Agent 记忆引擎【免费下载链接】oh-my-openagentOmO: Just type mass ulw keyword with your prompt. Now you are the master of graph engineering.项目地址: https://gitcode.com/gh_mirrors/oh/oh-my-openagent导读packages/memory-core是 oh-my-openagentOmO项目中负责 Agent 持久化记忆的独立核心包。它以Git 仓库 Markdown 内存文件系统MemFS为存储底座向外提供原子化记忆写入工具、系统提示词编译、反思reflection调度、会话检索、同步与种子内容等能力。本文以 packages/memory-core/AGENTS.md 为主干结合该包的真实源码与测试完整讲解其目录结构、核心不变量、公共 API 与工程约束帮助你理解这套把记忆当作事务化 Git 状态的设计并为接入或扩展记忆能力提供可落地的依据。一、包定位与总体架构1.1 一句话定位根据 packages/memory-core/package.json 中的描述该包是harness-neutral agent memory engine即与具体执行框架harness无关的 Agent 记忆引擎。它服务的对象不是某个单一 CLI而是项目内各种 harness 适配器adapter共享的同一个记忆领域Git 支撑的 Markdown MemFS内存文件系统原子化的记忆写入工具提示词编译prompt compilation反思调度reflection scheduling会话检索transcript search远程同步synchronization种子内容seed content包的公共 API 全部通过 src/index.ts 这个 barrel 文件导出它聚合了git、identity、locks、memfs、tools、journal、facts、personas、reflection、compile、search、recall、soul、sync、people、reminders、seeds共 17 个领域模块。1.2 目录职责总览原文档给出了一张模块—职责对照表是理解整套代码的骨架逐条展开如下目录职责结合源码细读src/git/Git 命令边界clean-tree 检查、commit、merge、remote 以及类型化 Git 错误。核心是GitMemoryRepo见 src/git/repo.ts配套有 src/git/errors.ts、src/git/porcelain.ts、src/git/repo-status.ts 等。src/identity/记忆身份解析与OMO_MEMORY_HOME目录布局。布局常量与路径构建器在 src/identity/layout.ts。src/locks/跨进程锁记忆写入、反思调度、会话状态锁另有机器级的recall-wake计数租约。锁域枚举定义在 src/locks/domains.ts。src/memfs/记忆路径校验、Markdown frontmatter 解析与 hook 脚本安装。frontmatter 严格 YAML 读写核心在 src/memfs/frontmatter.ts。src/tools/memory与memory_apply_patch操作、patch 解析、类型化工具错误与自动提交行为。入口在 src/tools/index.ts。src/journal/每个会话per-conversation的会话游标、反思快照与持久化日志状态。src/facts/持久化事实管道队列 游标水位、失败退避/存储、负载上限、人物路由、恢复、变更规划。src/people/人物卡people-card文法parse/serialize、slug 规则、保留 slug、观察记录。src/soul/Soul 文件路径与身份作用域下的 soul-notice 水位消费。src/reflection/触发求值、运行预留、worktree 执行、完成校验、合并结果、孤儿清理orphan sweep与 park 策略。src/compile/把已提交的记忆版本编译成标记化的系统提示词块并按模板哈希缓存。核心见 src/compile/compile.ts。src/search/查询解析、会话提供者与排序后的记忆/会话检索。src/sync/远程镜像同步与敏感信息脱敏secret redaction。src/reminders/反思与记忆维护类提醒的生成。src/seeds/默认记忆块与首次运行仓库播种。见 src/seeds/seeds.ts。src/concurrency/仅供测试的跨进程子进程 fixture锁与多写者测试不是公共运行时模块。二、Harness-Neutral 边界为什么与框架无关是硬约束原文档把保持 harness 无关列为第一条核心不变量生产代码和包依赖不得导入Senpi、Pi、OpenCode 等 harness 适配器包并声明 src/harness-neutrality.test.ts 强制这一边界。从该测试源码可以看到具体的守护手段禁止依赖清单测试读取package.json的dependencies/devDependencies/peerDependencies断言任何依赖名不得以code-yeongyu/senpi、earendil-works/、mariozechner/pi-、oh-my-opencode/omo-senpi、oh-my-opencode/senpi-task开头。禁止导入扫描递归收集src/下所有.ts文件逐一检查源码中是否出现from forbidden或import forbidden形式的导入。这一设计的意义在于记忆引擎必须能被任何 harness 复用。适配层例如 Senpi 的记忆组件位于 packages/omo-senpi/src/components/memory/负责提供身份、生命周期事件、工具注册和同步编排但这些适配行为只能放在 harness 侧永远不允许反向导入回本包。从源码结构看这正是领域核心 适配器分层架构的典型体现。三、目录布局OMO_MEMORY_HOME与记忆身份src/identity/layout.ts 定义了记忆存储的落盘结构可归纳为一条公式memory-root/agents/safe-id/{repo, runtime/...}其中memory-root的解析规则是若设置了环境变量OMO_MEMORY_HOME则以其值为根resolveMemoryRoot会基于当前工作目录做路径解析否则回退到默认值~/.omo/memorydefaultMemoryRoot。每个 Agent 身份safe-id下分两个大区repo/真正的 Git 记忆仓库即提交可见、可编译的记忆正文runtime/运行时状态目录包含 11 个子目录分别是locks、transcripts、reflection、reflection-sessions、worktrees、viewers、push-queue、facts-queue、facts、notices、recall。把记忆正文repo与运行时瞬态runtime分离是整套设计的关键提交即快照任何未提交的工作区内容都不是权威记忆对应只从已提交状态编译的不变量。四、记忆即事务写路径的原子性契约原文档的第二条核心不变量明确要求把记忆仓库当作事务化状态对待。写工具必须依次完成获取memory-write锁 → 要求仓库干净clean-tree→ 校验路径 → 应用单次操作 → 只提交受影响的路径。禁止绕过GitMemoryRepo、锁域或工具入口直接写 markdown。4.1runMemoryTool六种原子命令src/tools/memory.ts 定义了runMemoryTool()支持 6 种写命令外加 1 个 apply_patch命令必需参数行为要点createfile_path,description目标已存在时报create: block already exists at path写入时用renderMemoryFile渲染 frontmatter 正文str_replacefile_path,old_string,new_string在正文中查找old_string找不到即报错替换后整体重渲染insertfile_path,insert_line,insert_textinsert_line必须是数字行号下限为 1超出末尾则追加deletefile_path支持删除文件或整个目录删目录前会递归校验其中每个.md可编辑并收集受影响的 tracked 文件renameold_path,new_path目标已存在时报错返回新旧两个受影响路径update_descriptionfile_path,description保留其余 frontmatter仅更新description字段apply_patchinput委托给applyMemoryPatch做多文件 Codex 风格补丁所有写操作统一通过commitMemoryWrite包裹见 src/tools/commit-write.ts 的导出最终产出一个提交对象包含sha、subject与affectedPaths。每次工具调用都要求提供非空reason——它既是提交信息也是审计线索。值得注意的防御性校验均可见于 src/tools/memory.tsread_only: true的块禁止任何修改loadEditable直接抛错非 UTF-8 / UTF-16 编码文件会被拒绝要求先转换为 UTF-8readUtf8用fatal: true的 TextDecoderdescription会被规范化换行转空格、去首尾空白并经describeDescriptionViolation校验。4.2runMemoryApplyPatch多文件补丁src/tools/memory-apply-patch.ts 实现runMemoryApplyPatch()在记忆仓库内应用多文件 Codex 风格补丁同样要求非空reason。patch 解析与 hunk 应用逻辑在 src/tools/patch-apply.ts解析失败抛出MemoryPatchParseErrorhunk 级失败抛出MemoryPatchHunkError二者在runMemoryTool中被统一转换为MemoryToolError。五、Markdown 契约严格 frontmatter一处文法原文档用较长篇幅强调一条规则frontmatter 必须是严格 YAML且全局只有一种文法。核心载体是 src/memfs/frontmatter.ts。5.1 字段契约记忆文件要求 YAML frontmatter 中必须存在非空的descriptiondescription必需、非空、单行输出时若裸标量无法通过严格 YAML 原样回读如含:、#、true、前导指示符等会自动加引号。read_only字符串值原样保留不强转成布尔用于阻断写入。kind/aliases人物记录键aliases是 JSON 数组非数组或含空串都会抛FrontmatterError。limit被容忍忽略的遗留键。其余任意键如 SKILL.md 的name、version、deprecated被放入extra原样保留保证编辑不会丢字段。CRLF 在读入时归一化输出只发 LF。5.2 单一写者 往返自检renderMemoryFile被设计为唯一写者它会对任何yaml包无法原样回读的标量加引号并在返回前重新解析自己生成的头部做往返校验assertStrictRoundTrip逐一比对description、read_only、kind、aliases与extra一旦失配立即抛错。这意味着写出来的文件永远能通过 skill loader 的解析。读取侧保留兼容性遇到既有文件时会回退到遗留的首个冒号文法letta parity同时describeFrontmatterViolation是 pre-commit hook 规则、validateCompletion、normalizeMemoryFrontmatter以 common git dir 中的标记为键的一次性遗留修复共享的同一道闸门。文档特别警告永远不要手写不带引号的description:。六、反思Reflection状态机与调度反思是记忆自我维护的核心机制原文档将其约束为三条不变量手动触发优先级高于 compactioncompaction 高于步数触发同一时刻只允许一个活跃运行与一个已合并的 pending 预留。6.1 纯状态机src/reflection/machine.ts 暴露三个纯函数evaluateTransitions(state, event)接收ReflectionEventsettled/compaction_accepted/manual产出EvaluationResult继续等待或发起reserve动作。触发优先级常量可见于源码step-count: 1 compaction: 2 manual: 3。reserveTransition(state, request, runId)无活跃运行时直接激活已有活跃运行时进入 pending 槽。completeTransition(state, runId, outcome, journals, config)按结果merged、no_changes、parent_dirty、merge_conflict、dirty_uncommitted、failed、timed_out推进预留状态决定是否启动下一个运行。共享 pending 槽被显式限界最多 32 个会话、4 MiB UTF-8 JSON见REFLECTION_PENDING_MAX_CONVERSATIONS/REFLECTION_PENDING_MAX_BYTES驱逐按先见先出整会话移除游标保持可重试。触发配置TriggerConfig支持stepCount、onCompaction与snapshotMaxBytes会话积压超过字节预算时即使未达步数阈值也会触发反思。6.2 调度域锁与孤儿清理反思的执行放在独立 worktree 中进行避免污染主工作树配套 src/reflection/worktree.ts 的运行预留、完成校验与合并结果。调度本身受reflection-scheduler与reflection-finalize域锁保护见 src/locks/domains.ts 的LOCK_DOMAINS。orphan-sweepsrc/reflection/orphan-sweep.ts负责回收没有任何活跃运行在认领的 worktree 与分支。此外park策略src/reflection/park.ts在连续失败后停止自动反思每个间隔只放行一次半开探测half-open probe避免反复失败打爆资源。七、锁体系跨进程安全的基石原文档要求锁必须按域区分使用memory-write、reflection-scheduler或会话专用锁不要用一把全局锁替代也不要写依赖时序的测试。src/locks/domains.ts 定义了完整锁域清单memory-write, reflection-scheduler, reflection-finalize, transcript-state, skills-usage, memory-usage, facts-queue, facts-runs, notice每个域对应一个落盘锁文件例如memory-write.lock记忆写入reflection-scheduler.lock反思调度finalize-runId.lock或带 SHA-256 摘要的哈希形式单次反思收尾transcript-state-digest.lock按会话 ID 哈希skills-usage.lock/memory-usage.lock/facts-queue.lock/facts-runs.lock/notice.lock此外recall-wake是机器级计数租约每个 slot 一个recall-wake.slot-n.lock文件配套 FIFO 的recall-wake.tickets/队列默认 2 个 slotslot 与 ticket 都支持基于证据proof-based的过期恢复等待有界超时抛RecallWakeBusyError。这套设计的目的是让多个并发进程可以共享唤醒配额又不至于互相饿死。八、提示词编译从已提交状态到系统提示块compileMemoryBlock()与compileMemoryBlockAtRevision()把已提交的记忆投影渲染为注入 harness 提示词的结构化块实现在 src/compile/compile.ts。关键点compileMemoryBlock(repo, options)以repo.head()作为默认修订版compileMemoryBlockAtRevision可显式指定修订版。编译时通过repo.lsTree(revision)枚举该修订版的路径读取system/persona.md与system/identity.md进入self块其余system/*.md进入memory树非system/且非skills/的外部路径进入外部投影。输出结构包含一段内置的Reminder提醒模型memory是跨会话持久记忆提问前先查阅用 memory 工具即时保存事实、偏好、决策与纠错、projection路径标注、memory_metadata含AGENT_ID。模板按哈希缓存src/compile/cache.ts避免重复编译。只从已提交状态编译这一不变量在这里得到直接体现修订版为空尚无提交时返回空投影脏工作区永不成为权威记忆。九、事实管道、人物卡与 Soul 水位factsFactsQueue、applyFactsBatch()、planFactsMutation()驱动持久化事实生命周期。状态必须持久且失败即关闭fail-closed队列/游标/已消费写入以.tmp rename、模式0o600原子发布到身份作用域锁之下损坏的 JSON 解析为空但绝不阻塞入队/消费水位永不回退排序遵循规范的日志位置/快照边界绝不按消息 ID 字典序。另有负载上限payload cap、失败退避存储、人物路由与恢复逻辑src/facts/。people人物卡文法限制在IDENTITY/ATTRIBUTE/RELATIONSHIP/INSTRUCTION前缀且元数据有界行为模式behavioral patterns永不写入人物卡src/people/。soulconsumeSoulNoticeDelta()消费身份作用域下的 soul-notice 水位src/soul/。十、种子内容首次运行的记忆仓库初始化src/seeds/seeds.ts 提供initMemoryWithSeeds()在全新仓库中播种 5 个默认记忆文件 1 个记忆纪律 skill并交给GitMemoryRepo.init以chore: initialize local memory作为首个提交一次性提交system/persona.mdPersona - who I amsystem/human.mdPerson - Humankind: personsystem/boundaries.mdBoundaries - what the person told me not to dosystem/self-aware.mdSelf-aware - 外部视角下的自我认知reference/self/observations.md自我观察日志供反思提升到 self-aware记忆纪律 skill预渲染的 SKILL.md安全性保证在GitMemoryRepo.init内仓库一旦已有 HEAD 提交初始化直接返回现有 HEADno-overwrite 守卫因此重复调用是安全的 no-op。十一、消费方与适配边界原文档明确harness 适配器提供身份、生命周期事件、工具注册与同步编排Senpi 适配器位于packages/omo-senpi/src/components/memory/。任何适配器特有的行为都必须留在适配器侧永远不要反向导入回本包。这与第二条不变量harness 中立互为表里memory-core只做领域不做绑定。十二、QA 与反模式清单12.1 质量保障命令原文档给出的标准验证命令在当前仓库根目录执行bun test packages/memory-core/src/ bun run --cwd packages/memory-core typecheck局部修改时先跑就近的*.test.ts再跑整个包级套件。并发测试必须以确定的状态或进程事件 有界超时来断言禁止固定 sleep 或自旋重试——这对应不要用时序运气测试跨进程行为的反模式。12.2 反模式速查原文档逐条继承把 harness 包导入memory-core破坏中立性会被harness-neutrality.test.ts拦截。不经过路径校验、frontmatter 解析、加锁与原子 Git 提交直接写记忆 markdown。把脏工作区当作已编译记忆读取。修改read_only块或接受非 UTF-8 记忆文件。吞掉 Git、锁、合并或反思失败。用时序碰运气来测试跨进程行为。结语packages/memory-core的价值不在于多会存东西而在于它把记忆严格建模为可提交、可回滚、可并发、可审计的 Git 状态frontmatter 一处文法保证机器可读锁域与原子提交保证多进程安全反思状态机保证自我维护有界有序harness 中立边界保证可被任意适配器复用。无论是想深入 Agent 记忆实现还是计划在自有 harness 中接入这套记忆引擎packages/memory-core/AGENTS.md、src/index.ts 与上文提到的各模块源码都是最直接的起点。【免费下载链接】oh-my-openagentOmO: Just type mass ulw keyword with your prompt. Now you are the master of graph engineering.项目地址: https://gitcode.com/gh_mirrors/oh/oh-my-openagent创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考