oh-my-pi 的 learn 工具:把一次性调试经验沉淀为长期记忆与可复用技能

发布时间:2026/9/11 21:28:14
oh-my-pi 的 learn 工具:把一次性调试经验沉淀为长期记忆与可复用技能 oh-my-pi 的 learn 工具把一次性调试经验沉淀为长期记忆与可复用技能【免费下载链接】oh-my-pi⌥ Coding agent with the IDE wired in项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-pi导读在长会话式 Coding Agent 的日常工作中最难积累的资产不是代码而是吃过一次亏才知道的经验一个非显而易见的修复、一条项目约定、一套最终跑通的工作流。oh-my-pioh-my-pi/pi-coding-agent提供的learn工具正是为此设计的——它在一次调用内把可复用的经验写入长期记忆并在需要时同步铸造或增强一个受管managed技能。读完本文你将掌握learn工具的触发时机、memory/context/skill参数的完整语义、三种记忆后端local / mnemopi / hindsight的落盘差异以及技能命名、隔离与安全边界背后的源码实现原理。1.learn工具是什么从解决完问题到沉淀下答案learn工具在 oh-my-pi 中扮演经验捕获器的角色。它的官方定义见 learn.md只有一句话但信息密度极高Capture reusable lessons in long-term memory; optionally mint/enhance a managed skill in the same call.即捕获可复用的经验到长期记忆在同一调用中可选地铸造或增强一个受管技能。关键在reusable可复用一词。learn不是随便记录任何对话而是要求在解决了一个很可能再次带来回报的问题之后使用文档给出了三类典型场景非显而易见的修复non-obvious fix比如某个第三方库的坑、某个平台特定的行为下次遇到同样的错误可以少走弯路发现的项目约定discovered project convention例如本仓库的测试必须用bun test而不是jest提交前需要跑gen-clippy-bazelrc这类代码里不会明说、但违反就会出问题的规则最终跑通的工作流workflow that worked一套多步骤的操作序列例如升级依赖后必须同时更新 Cargo.lock 与 bun.lock。从实现上看learn是一个标准的 AgentTool其注册与门控逻辑位于 tools/learn.tsstatic createIf(session: ToolSession): LearnTool | null { if (!session.settings.get(autolearn.enabled)) return null; const backend session.settings.get(memory.backend); if (backend ! hindsight backend ! mnemopi backend ! local) return null; return new LearnTool(session); }这意味着工具并非总是可用它要求同时满足两个前提——autolearn.enabled开启且memory.backend处于local、mnemopi或hindsight三者之一。默认配置下autolearn.enabled为false见 settings-schema.ts因此learn属于按需启用、零默认足迹的实验性能力。2. 调用时机什么时候该用什么时候不该用原文档给出了清晰的使用判据在解决了一个很可能再次带来回报的洞见之后使用。反过来说learn不该被当作普通日志或聊天记录来用。判断是否值得捕获可以自问三个问题这个经验会不会在另一个任务、另一个项目阶段再次出现不会复用的不值得占用记忆预算它是否足够具体记得多用异步是模糊的建议而在 A 包大于 1GB 时改用流式 API 避免 OOM才是可执行的教训它是事实还是流程事实服务器地址是 x直接进记忆即可只有值得固化成SKILL.md的可重复过程才需要附带skill参数。原文档最后一句给出了取舍原则也是全文最核心的纪律Capture sparingly, specifically: one strong reusable lesson several vague ones.少而精地捕获一条强而具体的可复用经验胜过好几条模糊的经验。这不仅是提示词层面的建议也反映在实现上——本地后端对learned.md的条目数量有硬性上限见下文第 5 节模糊、重复的捕获会被去重和淘汰机制自然过滤。3. 参数详解memory、context与可选的skilllearn工具的 JSON Schema 定义在 tools/learn.ts共三个参数参数类型必填语义memorystring是需要记住的、自包含durable, self-contained的经验应说明 what / when / whycontextstring否该经验的可选来源上下文项目、场景、命令等skillobject否在同一调用中创建或更新一个受管技能3.1memory写出自包含的经验memory的约束是self-contained自包含——即脱离了当前会话上下文单独读起来依然成立。原文档要求它涵盖 what / when / why 三个维度what发现了什么、怎么做when在什么情况下适用why为什么这样做是对的避免后人把结论当成教条。例如一条合格的memory长这样当修改本仓库 Rust crate 的公共 API 时必须同步更新Cargo.lock与bun.lock否则 CI 的 bazel 构建会因版本漂移失败bazel 与 cargo 的依赖解析相互独立。3.2context轻量来源标注context用于记录这条经验从哪里来如某个模块、某次事故、某个构建脚本方便日后回溯。在本地后端中它会被内联进条目渲染为_(context: ...)_后缀详见第 5 节。3.3skill把流程固化为SKILL.mdskill是可选参数且文档明确限定只为值得固化为SKILL.md的可重复过程提供而不是为事实提供。一条事实公司代理端口是 8080用memory就够了一套设置序列 / 调试配方 / 项目专属工作流才需要技能化。skill对象包含四个字段字段类型说明actioncreate \| update新建或覆盖更新namestringkebab-case 技能名小写字母、数字、连字符descriptionstring一行说明何时使用该技能用于技能发现bodystringSKILL.md正文不含 frontmatter注意body的约束frontmatter 由系统根据name与description自动生成调用方只需提供纯 Markdown 正文。这一设计保证了机器生成的技能文件格式一致也避免了调用方注入任意 frontmatter 字段。4. 一次调用的完整执行链路从 tools/learn.ts 的实现看LearnTool.execute分两个阶段第一阶段持久化经验到长期记忆必然执行。根据当前memory.backend走不同分支mnemopi本地 SQLite 后端调用state.rememberScoped以importance: 0.8、source: coding-agent-learn、memoryType: fact写入并携带session_id、cwd、context等元数据若后端未初始化或写入失败返回空 id工具会显式抛错而不是静默丢弃local文件后端调用localBackend.save走saveLearnedLesson管道详见第 5 节若清洗后内容为空stored 0同样抛错hindsight远程记忆服务调用state.enqueueRetain将经验排队交给后台保留管道返回信息为 Lesson queued for retention。第二阶段可选地铸造/增强受管技能失败不致命但会如实报告。当调用带skill时先做两道前置校验名称清洗sanitizeSkillName校验名称是否符合^[a-z0-9][a-z0-9-]{0,63}$小写字母/数字/连字符1~64 字符非法名称直接拒绝作者技能冲突检查isNameClaimedByAuthoredSkill检查该名称是否已被用户手写的技能占用。由于受管技能在发现时的优先级低于作者技能若强行创建同名受管技能写出的文件永远不会被呈现——工具此时会返回错误提示换个名字而不是谎报成功tools/learn.ts。随后调用共享原语writeManagedSkill完成写入并按action返回 Created 或 Updated 的确认信息。若技能写入失败错误消息会同时说明经验已存储/排队但技能未能写入保证调用方不会误以为整个操作失败。此外LearnTool的审批分级值得注意tools/learn.ts带skill载荷、或后端为local时审批级别为write否则为read。这是因为纯远程后端的记忆写入是排队性质的轻操作而写文件技能或learned.md需要显式写权限。5. 本地后端learned.md的存储与读回机制当memory.backend为local时经验被写入项目记忆根目录下的learned.md文件memories/index.ts。该文件刻意与后台汇总产物memory_summary.md、MEMORY.md、skills/分离保证汇总管线永远不会覆盖手动捕获的经验。5.1 写入时的规范化管道每条经验落盘前要经过 normalizeLearnedText 的三步处理注入中和neutralizeInjection剔除控制/格式字符、尖括号/skills、system-directive、反引号和~~~围栏。因为learned.md的内容会原样渲染进后续会话的系统提示词必须防止经验文本里夹带提示词注入载荷密钥脱敏redactSecrets替换疑似 token含ghp_等供应商令牌前缀为[REDACTED]。顺序上有讲究——先中和再脱敏避免分隔符被剥离后把 token 重新拼起来绕过正则长度封顶boundCharsmemory内容上限2000 字符、context上限400 字符截断时会处理末位未配对的 Unicode 高代理项避免产生损坏字符。5.2 文件级约束最新在前 精确去重同一经验重复捕获不会产生重复条目100 条上限MAX_LEARNED_LESSONS超出时淘汰最旧的条目控制文件按条数增长并发安全learnedWriteChains按文件路径串行化读-改-写同一轮次内并行调用如多个子代理同时learn不会互相覆盖memories/index.ts保留手工结构对已经存在头部、散文、分节标题的learned.md追加操作只触碰-开头的列表条目新条目进入第一个列表段的头部标题与正文的相对位置不变——你可以放心手工维护这个文件。测试用例对这些行为有完整覆盖例如 autolearn-learn-local.test.ts 验证了空白归一化与 context 内联、L47-L64 验证了密钥脱敏、L74-L83 验证了 100 条上限、L261-L298 验证了手工结构保持与字节幂等性。5.3 读回与注入预算读回时buildMemoryToolDeveloperInstructionslearned.md的条目会与memory_summary.md合并注入系统提示词且两者共享summaryInjectionTokenLimit预算memories/index.ts先按 token 预算截断汇总剩余预算才分配给经验列表。若汇总已耗尽预算经验条目会被丢弃而非撑爆上下文——这正是少而精原则在系统层面的强制保证。读回时还会再次执行中和与脱敏因此手工编辑过的learned.md即使夹带危险内容也不会泄漏进提示词autolearn-learn-local.test.ts。6. 受管技能隔离目录、命名与安全边界learn的skill参数写入的是受管技能managed skill其全部文件操作被限制在独立目录~/.omp/agent/managed-skills与用户手写技能目录~/.omp/agent/skills严格隔离。原文档强调了两条铁律Managed skills: isolated~/.omp/agent/managed-skills; surfaced as normal skills next session; NEVER touch user-authored skills.即受管技能下一次会话会像普通技能一样被发现和呈现但 Agent永远不得触碰用户手写的技能。这一隔离体现在 autolearn/managed-skills.ts 的多层防护上提供者标记受管技能被标记为omp-managed提供者与作者技能区分名称白名单SKILL_NAME_PATTERN /^[a-z0-9][a-z0-9-]{0,63}$/禁止..、斜杠、空名和大写从源头杜绝路径逃逸描述消毒sanitizeManagedDescription在写入和读取两个方向都剥离控制字符、、反引号与~~~防止机器生成的描述在未来的会话中破坏skills列表结构frontmatter 自动生成toSkillFrontmatter用 YAML 序列化name与消毒后的description形成标准---\n...\n---头部64KB 大小上限MAX_MANAGED_SKILL_BYTES 64_000按最终文件 UTF-8 字节数含 frontmatter校验防止一次生成把技能文件写爆写入防伪create使用wx标志O_CREAT|O_EXCL原子创建、文件已存在即失败update使用O_NOFOLLOW打开且拒绝符号链接文件与硬链接数 1 的文件——防止把写入重定向到用户技能或其他文件managed-skills.ts根目录防符号链接写入前lstat检查managed-skills根与技能子目录不是符号链接杜绝合法名称经符号链接写到目录外同名单次串行化serializeSkillMutation让同一技能名的多次变更如同一轮里 create 与 update 并发按提交顺序执行不同技能名仍可并行。6.1create与update的语义差异action行为失败条件create原子新建SKILL.md技能已存在EEXIST 显式报错update覆盖正文frontmatter 由 name/description 重新生成技能不存在两个动作都要求description与body非空空的 description 会被发现扫描静默丢弃工具因此会在写入前直接拒绝。这与manage_skill工具manage-skill.md共享同一套writeManagedSkill原语行为完全一致manage_skill还额外支持delete。7. 与 Auto-Learn 体系的关系及配置learn是 oh-my-pi Auto-Learn实验性体系中的一个工具。会话停止后系统会根据 autolearn-guidance.md 的指引提示 Agent 捕获经验——learn是其中的手动、即时通道manage_skill则是构建可复用技能库的通道。相关配置项集中在 settings-schema.ts配置类型默认说明autolearn.enabledbooleanfalse总开关关闭时learn/manage_skill均不可用createIf返回 nullautolearn.autoContinuebooleanfalse停止时自动运行一次私有捕获回合额外消耗 token关闭则仅保留常驻 Auto-Learn 提示autolearn.minToolCallsnumber5触发捕获提示所需的最少工具调用次数仅配置文件可设memory.backendenumoffoff/local/hindsight/mnemopi/sharpshooter仅前三者支持learn一个实用的组合是memory.backend: localautolearn.enabled: true。此时无需任何外部服务经验会落到项目记忆根的learned.md技能落到~/.omp/agent/managed-skills全程本地文件、零网络依赖也便于直接查看和手工维护。8. 最佳实践清单综合原文档与实现使用learn工具时应遵循以下纪律只在洞见可能再次变现时调用——非显而易见修复、项目约定、跑通的工作流一条强经验胜过多条弱经验——memory写自包含what/when/why宁缺毋滥事实进memory流程进skill——只有可重复过程才值得SKILL.md化skill.name用 kebab-case小写字母/数字/连字符1~64 字符description写清何时使用body不带 frontmatter不要与用户手写技能重名——受管技能永远无法覆盖作者技能冲突时换个名字local后端可放心查看与手工编辑learned.md——读回时会再次消毒结构也能被保留但内容最终会渲染进未来会话的提示词注意别写入不需要长期保留的信息。掌握了这几点你就拥有了让 Coding Agent 越用越懂你的项目的完整闭环解决问题 →learn捕获 → 跨会话注入 → 技能化复用。【免费下载链接】oh-my-pi⌥ Coding agent with the IDE wired in项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-pi创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考