Agent 还不懂你的仓库约定?给它注入 AGENTS.md / CLAUDE.md
系列回顾:主循环 · 代码库工具 · REPL 会话
Agent 会转、会逛仓库、能开着聊了——但模型仍然不认识你的项目规矩。你在AGENTS.md里写了「issue 用某套标签」「提交用 Conventional Commits」,它下一轮照样按通用习惯答。这篇讲 项目上下文(project context):启动时发现 Markdown → 当成 system prompt 注入每次
callModel。Skills / MCP 以后都要走这条「把外部知识塞进会话」的通道;先把底座打通。
缺的不是工具,是「说明书」
对话历史(messages[])记的是这一次聊了什么。
项目约定记的是这个仓库平时怎么干活——和某次用户输入无关,也不该被 /clear 清掉。
| 东西 | 放哪 | /clear 后 |
|---|---|---|
| 用户问「我叫 Alice」 | messages |
没了 |
| 「PR 标题用中文」写在 AGENTS.md | system prompt | 还在 |
没有这条通道时,你只能:
- 每轮手动把规矩贴进用户消息(烦、费 token)
- 或者指望模型「自己去 Read AGENTS.md」(不可靠,且浪费一轮工具)
所以 v2 加的是:启动加载一次,每轮透传,不进对话历史。
一张图看清注入路径
cwd 向上找 AGENTS.md / CLAUDE.md
↓
loadProjectContext() → string | undefined
↓
CLI / QueryEngine(构造时拿住)
↓
query({ systemPrompt })
↓
callModel({ systemPrompt })
↓
adapter:messages 首位插入 { role: 'system', content }
要点:发现在 L1/入口,透传穿过 L2,真正变成 API 字段在 Provider 适配层。
工具流水线、权限策略都不动。
发现规则:简单到能背下来
实现集中在 src/utils/projectContext.ts:
- 从 cwd 开始,向上最多 5 层(或碰到 git root 就停)
- 某一层若有文件:
- 只有
AGENTS.md→ 用它 - 只有
CLAUDE.md→ 用它 - 两个都有 → 合并:
AGENTS在前 + 分隔线 +CLAUDE
- 只有
- 找到第一层有文件的目录就返回(不整棵树全合并)
- 合并后超过 64KB → 截断,末尾注明
[project context truncated at 64KB] - 什么都没有 → 返回
undefined,静默跳过,进程不挂
export async function loadProjectContext(
startCwd = process.cwd(),
): Promise<string | undefined>
兼容两套生态惯例:本仓库用 AGENTS.md,Claude 系常用 CLAUDE.md——同目录就一起喂给模型。
为什么不塞进 messages[]?
设计上刻意选了 systemPrompt?: string,而不是 messages.unshift(systemUserMsg):
| 做法 | 问题 |
|---|---|
| 当第一条 user / assistant | 污染会话语义;/clear 容易误清或难清 |
| 独立 systemPrompt | 历史只管对话;system 由引擎持有,每轮透传 |
QueryEngine:
constructor({ …, systemPrompt? })
// runTurn → query({ …, systemPrompt: this.#systemPrompt })
// clear() 只清空 messages,不动 systemPrompt
REPL 里 /clear 之后,项目规矩仍然生效——这是验收场景里特意写明的行为。
adapter 侧一行就够:
if (systemPrompt) {
result.push({ role: 'system', content: systemPrompt })
}
内部循环继续只认 Anthropic 形态的 user/assistant;system 是出站时加上的,和「内部协议 / 外部协议」分层一致。
CLI 三种模式一起接线
启动时加载一次,三种路径共用:
const systemPrompt = await loadProjectContext()
// pipe / headless
query({ messages, tools, toolUseContext, systemPrompt })
// REPL
new QueryEngine({ tools, toolUseContext, systemPrompt })
无文件时 systemPrompt 为 undefined,行为和加上下文之前完全一样——无破坏性变更。
和前几篇怎么拼在一起?
| 篇章 | 补的能力 | 一句话 |
|---|---|---|
| 主循环 | L2 会转 | 模型 ↔ 工具直到停 |
| 代码库工具 | L3 会逛 | Glob → Grep → Read |
| REPL | L1 能聊 | messages 跨 turn 累积 |
| 本篇 | 项目说明书 | AGENTS/CLAUDE → system,每轮都在 |
Harness「八大能力柱」里,这对应 Context 的最小切片:还不是 compact / git status / 日期拼盘,只是「把仓库 Markdown 说明喂给模型」。
但 Skills、MCP、更多上下文源,都可以复用同一条 systemPrompt(或以后扩展成「上下文管道」)。
30 秒验证
在仓库根目录放(或已有)AGENTS.md,写一句可检验的规矩,例如:
# 测试约定
回答时先说一句:项目暗号是 pineapple。
然后:
bun run dev -- "随便打个招呼"
# 或 REPL:bun run dev
模型回复里应能看出它读到了这份说明。再试:
> /clear
> 再打个招呼
暗号约定应仍然在——因为清的是对话,不是 system。
没有 AGENTS.md / CLAUDE.md 时,行为与旧版一致,不会报错退出。
刻意没做什么
| 没做 | 为什么 |
|---|---|
| 整棵目录树多层 CLAUDE.md 全合并 | v2 只取「向上找到的第一层」 |
| Memory / git status / 日期 | 完整 context 管道留后 |
| token budget / compact | 先用 64KB 硬顶 |
| 把 Skills 本体做完 | 依赖本 change 的注入通道;见后续 v2-skills |
向上爬升的护栏:最多 5 层或停在 .git,避免一路爬到用户家目录把无关说明书吃进来。
你可以从这里带走什么?
- 项目约定 ≠ 对话历史:一个走 system,一个走
messages;/clear只清后者。 - 注入通道比文件格式更重要:先打通
load → systemPrompt → adapter,以后 Skills / MCP 才有地方挂。 - 无文件时静默跳过:可选增强不应变成启动失败。
到这里,react-agent-mini 最小故事又多了一块砖——Agent 不只会「动手」,还会先读你的仓库说明书。
仓库与相关文档
- GitHub:https://github.com/jimchou-h/react-agent-mini
- 主循环 · 代码库工具 · REPL
- 架构导读:docs/architecture.md
欢迎 Star、Issue 和 PR。
本文基于 react-agent-mini 变更 v2-project-context(AGENTS.md / CLAUDE.md → system prompt)撰写。
更多推荐


所有评论(0)