第 05 章:系统提示词与上下文文件
前面四章的系统提示词只有一句 "You are a coding assistant…"。这一章我们认真写它——不是写长,而是写准。pi 的系统提示词加工具定义合计不到 1000 token,却在 Terminal-Bench 上打出了第一梯队的成绩(论证见设计思想:极简核心)。这一章把 pi 的提示词"抄"过来,逐句理解,再加上 pi 的第二个上下文武器:AGENTS.md 分层加载。
pi 的系统提示词,逐句拆解
以下是 mini-pi 采用的全文(对照 pi system-prompt.ts 的默认版本,去掉 pi 专属的文档指引段):
You are an expert coding assistant operating inside mini-pi, a coding agent harness.
You help users by reading files, executing commands, editing code, and writing new files.
Available tools:
- read: Read file contents
- bash: Execute bash commands
- edit: Make surgical edits to files
- write: Create or overwrite files
Guidelines:
- Use bash for file operations like ls, rg, find
- Use read to examine files before editing
- Use edit for precise changes (old text must match exactly)
- Use write only for new files or complete rewrites
- Be concise in your responses
- Show file paths clearly when working with files
每一句都有用:
- "operating inside … a coding agent harness":一句话锚定角色和运行环境。前沿模型被 RL 训练得"天生知道 coding agent 是什么",不需要上万 token 去教。
- Available tools 清单:与真正的工具 schema 互为补充——schema 管"怎么调",这里管"什么场合用哪个"。
- "Use bash for file operations like ls, rg, find":因为我们故意不提供
ls/grep/find 工具,必须告诉模型去哪找这些能力。
- "Use read to examine files before editing":配合
edit 的精确匹配——不先读,oldText 一定对不上。
- "Use write only for new files or complete rewrites":约束 token 使用方式,防止模型动不动全量重写。
注意整段提示词里没有任何"工作流程"层面的规定(什么时候该 plan、怎么组织 commit、代码风格……)。这类内容 pi 认为不属于核心,应该由用户通过 AGENTS.md 自行注入——这就是我们接下来要做的。
AGENTS.md:项目级上下文的分层加载
pi 启动时按 全局 → 父目录逐级 → 当前项目 的顺序收集 AGENTS.md(或 CLAUDE.md),全部拼接进系统提示词。这样:
- 个人偏好(如"永远用中文回答")放
~/.pi/agent/AGENTS.md,全局生效;
- 项目约定(技术栈、命令、禁区)放项目根的
AGENTS.md,跟着仓库走;
- 单仓多项目(monorepo)时,父级目录的约定自然级联。
pi 用 <project_context> 标签把这段内容和核心提示词隔开,并在末尾附上当前工作目录。mini-pi 照搬这个结构:
src/main.ts(新增)
const BASE_SYSTEM_PROMPT = `You are an expert coding assistant operating inside mini-pi, a coding agent harness. You help users by reading files, executing commands, editing code, and writing new files.
Available tools:
- read: Read file contents
- bash: Execute bash commands
- edit: Make surgical edits to files
- write: Create or overwrite files
Guidelines:
- Use bash for file operations like ls, rg, find
- Use read to examine files before editing
- Use edit for precise changes (old text must match exactly)
- Use write only for new files or complete rewrites
- Be concise in your responses
- Show file paths clearly when working with files`;
/** 加载 AGENTS.md:~/.mini-pi/AGENTS.md(全局)+ 从根目录一路向下到 cwd 的每一级 */
function loadContextFiles(): { path: string; content: string }[] {
const files: { path: string; content: string }[] = [];
const globalFile = join(homedir(), ".mini-pi", "AGENTS.md");
if (existsSync(globalFile)) files.push({ path: globalFile, content: readFileSync(globalFile, "utf8") });
const dirs: string[] = [];
let dir = process.cwd();
while (true) {
dirs.push(dir);
const parent = dirname(dir);
if (parent === dir) break;
dir = parent;
}
for (const d of dirs.reverse()) {
const p = join(d, "AGENTS.md");
if (existsSync(p)) files.push({ path: p, content: readFileSync(p, "utf8") });
}
return files;
}
function buildSystemPrompt(): string {
let prompt = BASE_SYSTEM_PROMPT;
const contextFiles = loadContextFiles();
if (contextFiles.length > 0) {
prompt += "\n\n<project_context>\n\nProject-specific instructions and guidelines:\n\n";
for (const f of contextFiles) {
prompt += `<project_instructions path="${f.path}">\n${f.content}\n</project_instructions>\n\n`;
}
prompt += "</project_context>\n";
}
prompt += `\nCurrent working directory: ${process.cwd()}`;
return prompt;
}
(join/homedir/existsSync/readFileSync 等 import 见本章完整代码,或对照最终版 code/mini-pi-vanilla/src/main.ts。)
然后把系统提示词接进 runAgent——注意它不作为普通消息存进历史,而是每次调用时在边界上拼进 wire messages:
src/main.ts(runAgent 开头)
async function runAgent(systemPrompt: string, session: Session, messages: Message[], signal: AbortSignal): Promise<void> {
while (true) {
const wire: Message[] = [{ role: "system", content: systemPrompt }, ...messages];
// … streamChat(wire, signal, onText)
}
}
"系统提示词在调用边界注入,而不是塞进消息历史"是个有意的选择:提示词是当前配置,历史是事实记录,两者分开,换提示词不用改历史。pi 的 Context { systemPrompt, messages, tools } 也是同样的切分。
关于 system role 的兼容性
一些新型号(如 OpenAI 的推理模型)期望 developer role 而不是 system,某些推理引擎又不认识 developer。pi-ai 用 compat.supportsDeveloperRole 之类的开关逐 provider 处理这类怪癖(见设计思想:LLM 抽象层)。mini-pi 用 system,在主流 OpenAI 兼容端点上都没问题。
验收
在项目根放一个 AGENTS.md:
AGENTS.md
# 项目约定
- 所有回复使用中文。
- 本项目使用 pnpm,不要使用 npm 命令。
- 修改 src/ 下的文件前先列出不涉及的测试文件。
重启 mini-pi,问 "这个项目用什么包管理器?"——它会按 AGENTS.md 回答 pnpm。再观察它的行为:pnpm 出现在它构造的 bash 命令里。你没有写一行代码,就改变了 agent 的行为——这就是 pi 语境下的"上下文工程":把定制放到用户可控的文件里,而不是塞进 harness 的代码里。
本章要点
- 系统提示词的任务是"锚定角色 + 工具分工 + 少量行为约束",不是百科全书;前沿模型不需要被教怎么做 agent。
AGENTS.md 分层加载把定制权交给用户和项目,是 pi "adapt pi to your workflows" 哲学的直接体现。
- 系统提示词在调用边界注入,与消息历史分离。
- pi 还支持
.pi/SYSTEM.md 整体替换系统提示词、APPEND_SYSTEM.md 追加——mini-pi 留作练习。
下一章:第 06 章:会话持久化 —— JSONL 树。