opencode /learn 命令详解:把会话洞察沉淀进分层 AGENTS.md 知识体系
opencode 仓库自带一个名为 learn 的自定义命令(.opencode/command/learn.md),它的作用是在一次编码会话结束后,让 Agent 回顾整个会话、提取那些"不显而易见"的洞察,并把它们写入正确目录层级的 AGENTS.md 文件。本文完整拆解该命令的调用方式、学习条目的取舍标准与五步工作流程,并基于 指令注入服务源码 说明"读取文件时自动加载父目录 AGENTS.md"这一底层机制,帮助你建立一套可随项目生长、按消息去重、就近注入的仓库知识沉淀方案。
一、/learn 命令的定位与调用方式
learn.md 是一份标准的 opencode 项目级自定义命令定义文件,其 frontmatter 声明了命令用途:
---
description: Extract non-obvious learnings from session to AGENTS.md files to build codebase understanding
---
在 opencode 中,项目级命令通过 .opencode/command/ 目录下的 Markdown 文件提供,本仓库该目录下共包含 8 个命令(learn.md、commit.md、changelog.md 等)。从 命令注册服务 的 Info Schema 可以看出,每个命令最终都会被解析为一条命令记录,包含 name、description、agent、model、source(command / mcp / skill 三种来源)、template 与 hints 字段。
几个值得注意的实现细节:
$ARGUMENTS占位符:learn.md的正文末尾保留了$ARGUMENTS占位符。hints() 函数 会扫描模板中的$ARGUMENTS与$1~$N编号占位符并生成命令提示,因此你可以直接执行/learn,也可以追加参数(如/learn 重点总结构建相关的坑),追加内容会替换掉模板中的$ARGUMENTS。- 命令模板的来源是多通道的:Command 服务初始化逻辑 表明命令不仅来自配置文件
command字段,还可以来自 MCP 服务的 prompt 和 skill 体系,.opencode/command/下的 Markdown 文件则以项目命令的形式参与注册,frontmatter 中的description会展示在命令列表中。 - description 的意义:
learn命令的描述明确点出目标——"build codebase understanding"(构建对代码库的理解)。这不是一个一次性的问答提示词,而是一个以**长期知识资产(AGENTS.md 文件)**为产出物的沉淀流程。
二、AGENTS.md 的分层放置策略:让洞察贴着代码住
learn.md 开篇就给出了一条核心原则:
AGENTS.md 文件可以存在于任意目录层级,而不只是项目根目录。当 Agent 读取某个文件时,其父目录链上所有 AGENTS.md 都会被自动加载进 read 工具的上下文。因此洞察应当尽可能靠近相关代码放置。
文档给出了三级放置建议:
| 洞察作用域 | 放置位置 | 典型内容 |
|---|---|---|
| 项目级(跨模块通用) | 根目录 AGENTS.md |
全局约定、统一的构建/发布流程、跨包约束 |
| 包/模块级 | packages/foo/AGENTS.md |
该包的内部架构、特有的构建命令 |
| 功能级 | src/auth/AGENTS.md |
某一功能目录内的实现细节与坑 |
这种"就近放置"策略的价值在于:它让知识按需加载、按目录隔离,避免把所有项目的细节堆在一个越来越长的根 AGENTS.md 里。opencode 仓库自身就采用了这种分层实践——除了根目录的 AGENTS.md 和已标记弃用的 CONTEXT.md 外,还存在着诸如 packages/opencode/src/session/llm/AGENTS.md 这样的深层目录级指令文件,覆盖 packages/app、packages/ui、packages/llm 等多个包,正好印证了 learn 命令倡导的分层沉淀思路。
三、什么才算一条"学习":只收非显而易见的发现
learn.md 对"learning(学习)"的定义非常克制——只收录非显而易见的发现(non-obvious discoveries only),具体包括八类:
- 文件与模块之间的隐藏关系(Hidden relationships between files or modules)
- 执行路径与代码表象不一致的地方(Execution paths that differ from how code appears)
- 不显而易见的配置项、环境变量或标志位(Non-obvious configuration, env vars, or flags)
- 报错信息具有误导性、最终才找到真相的调试突破点(Debugging breakthroughs when error messages were misleading)
- API/工具的怪癖(quirks)与变通方案(API/tool quirks and workarounds)
- README 中没有记载的构建/测试命令(Build/test commands not in README)
- 架构决策与约束(Architectural decisions and constraints)
- 必须一起修改的文件(Files that must change together)
与之对应,文档明确列出了不应收录的内容:
- 文档中显而易见的常识性事实(Obvious facts from documentation)
- 标准语言/框架行为(Standard language/framework behavior)
- AGENTS.md 中已经写过的内容(Things already in an AGENTS.md)
- 冗长的解释性文字(Verbose explanations)
- 仅与本次会话相关、不具备长期价值的细节(Session-specific details)
这套"白名单 + 黑名单"设计本质上是在控制 AGENTS.md 的信息熵:因为 AGENTS.md 的内容最终会注入模型上下文,每多一行废话都在稀释真正有价值的信号。
四、标准五步流程与输出契约
learn 命令规定的执行流程共五步:
- 复盘会话:Review session for discoveries, errors that took multiple attempts, unexpected connections —— 重点找那些"试了多次才解决的报错"和"意外的关联",这类事件正是高价值洞察的来源;
- 确定作用域:Determine scope —— 判断每条洞察适用于哪个目录,这是分层放置的前提;
- 读取现有文件:Read existing AGENTS.md files at relevant levels —— 先读再写,既避免重复收录,也为增量更新做准备;
- 创建或更新:在相应层级创建或更新 AGENTS.md;
- 控制篇幅:Keep entries to 1-3 lines per insight —— 每条洞察压缩到 1~3 行,保证文件可快速扫读。
流程末尾还有一个输出契约要求:
更新完成后,需总结创建/更新了哪些 AGENTS.md 文件、每个文件新增了多少条洞察。
这使得 /learn 的执行结果是可审计的:你能在回复中直接看到知识的落盘位置与数量,而不是得到一段没有产出的闲聊。
五、源码深挖:AGENTS.md 是如何被"自动加载"的
learn.md 中那句"读取文件时父目录 AGENTS.md 自动进入上下文"并非空话,其实现位于 instruction.ts。理解这段代码,才能真正理解为什么"就近放置"是正确策略。
5.1 指令文件的识别与查找顺序
服务初始化时定义了候选指令文件列表(instruction.ts L60-L68):
const globalFiles = [
path.join(global.config, "AGENTS.md"),
...(!flags.disableClaudeCodePrompt ? [path.join(global.home, ".claude", "CLAUDE.md")] : []),
]
const instructionFiles = [
"AGENTS.md",
...(!flags.disableClaudeCodePrompt ? ["CLAUDE.md"] : []),
"CONTEXT.md", // deprecated
]
可以看出:项目级查找顺序是 AGENTS.md → CLAUDE.md(可通过 disableClaudeCodePrompt 标志关闭)→ CONTEXT.md(源码注释明确标注 deprecated)。find() 函数 在给定目录内按此顺序取第一个存在的文件。
5.2 读取文件时向上遍历目录、按消息去重注入
核心机制在 resolve():当 read 工具读取某个文件时,服务会从该文件所在目录向上逐级遍历(源码注释原话:"Walk upward from the file being read and attach nearby instruction files once per message"),把沿途找到的 AGENTS.md 内容以 Instructions from: <filepath> 为前缀附加进上下文。遍历过程中有三层去重防线:
- 系统级已加载的跳过:已作为系统提示注入的指令文件(
sys.has(found))不会重复附加; - 本消息历史已加载的跳过:extract() 会扫描该消息中所有已完成的 read 工具调用,从
part.state.metadata?.loaded中取出此前已附加过的指令文件路径集合; - claims 记账:服务内部维护
Map<MessageID, Set<string>>(L70-L77),记录每条助手消息已认领过的指令文件,保证同一文件在一条消息内只注入一次。
这套"向上遍历 + 消息级去重"的设计意味着:learn 命令把洞察写到 src/auth/AGENTS.md 后,Agent 今后第一次读取 src/auth/ 下任何文件时就会自动获得这些知识,且不会在同一消息里反复注入同一个文件、撑爆上下文。
5.3 系统提示侧的"首个匹配胜出"策略
在系统提示注入侧,systemPaths() 采用不同策略:全局配置目录下的 AGENTS.md 命中即停;项目级则向上查找,首个命中的项目级指令文件胜出(源码注释:"The first project-level match wins so we don't stack AGENTS.md/CLAUDE.md from every ancestor"),避免把每一级祖先目录的 AGENTS.md 全部堆进系统提示。此外,配置中的 instructions 字段还可以追加额外的本地路径或 HTTP(S) URL 远程指令源(L135-L168),远程内容以 5 秒超时抓取。
把 5.2 与 5.3 对照就能理解分工:系统提示只承载"第一层"的指令文件,而更深层目录的 AGENTS.md 依靠 read 时的向上遍历实现按需注入——这正是 learn 命令鼓励多层级放置的机制基础。
六、实操建议:什么时候跑 /learn
结合命令设计与源码机制,以下场景是运行 /learn 的高价值时机:
- 排查完一个误导性报错之后:错误信息与真实原因不符、试错多次才解决的调试过程,是"黑名单外"最典型的洞察来源,此时执行
/learn让 Agent 把"真正的根因"而不是"表面报错"写进对应目录的 AGENTS.md; - 完成跨模块的关联修改之后:当发现"改 A 必须同时改 B"这类隐藏耦合时,把该约束写到两者公共上级目录(或各自目录)的 AGENTS.md 中;
- 发现 README 与现实的偏差之后:比如实际使用的构建命令、隐藏的环境变量,这些属于文档明确认可的收录范围。
使用时注意两点:其一,参数可以聚焦方向(如 /learn 只沉淀与 pty 模块相关的发现),利用 $ARGUMENTS 占位符收窄复盘范围;其二,执行后务必核对命令要求输出的汇总——哪些文件被创建/更新、每文件几条洞察——并抽查条目是否都满足"1~3 行、非显而易见"的约束,防止 AGENTS.md 被低质量条目污染。
七、小结
opencode 的 /learn 命令是一个小而完整的知识管理闭环:以"非显而易见的发现"为唯一收录标准,以五步流程保证落盘质量,以分层放置让知识贴近代码,最终依托 指令服务 的"系统级首个命中 + 读取时向上遍历 + 消息级去重"三套机制,让每一条沉淀下来的洞察在后续会话中按需、自动、不重复地回到 Agent 的上下文里。AGENTS.md 因此从一份静态的项目说明,变成了随会话持续生长的活文档。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00