系列回顾:主循环 · 代码库工具 · 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

  1. cwd 开始,向上最多 5 层(或碰到 git root 就停)
  2. 某一层若有文件:
    • 只有 AGENTS.md → 用它
    • 只有 CLAUDE.md → 用它
    • 两个都有 → 合并:AGENTS 在前 + 分隔线 + CLAUDE
  3. 找到第一层有文件的目录就返回(不整棵树全合并)
  4. 合并后超过 64KB → 截断,末尾注明 [project context truncated at 64KB]
  5. 什么都没有 → 返回 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 })

无文件时 systemPromptundefined,行为和加上下文之前完全一样——无破坏性变更


和前几篇怎么拼在一起?

篇章 补的能力 一句话
主循环 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,避免一路爬到用户家目录把无关说明书吃进来。


你可以从这里带走什么?

  1. 项目约定 ≠ 对话历史:一个走 system,一个走 messages/clear 只清后者。
  2. 注入通道比文件格式更重要:先打通 load → systemPrompt → adapter,以后 Skills / MCP 才有地方挂。
  3. 无文件时静默跳过:可选增强不应变成启动失败。

到这里,react-agent-mini 最小故事又多了一块砖——Agent 不只会「动手」,还会先读你的仓库说明书


仓库与相关文档

欢迎 Star、Issue 和 PR。


本文基于 react-agent-mini 变更 v2-project-context(AGENTS.md / CLAUDE.md → system prompt)撰写。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐