系统提示词、AGENTS.md 与 SYSTEM.md
同一个模型、同一套工具,换一份系统提示词就是另一个 agent。Pi 把这层完全交给你改。
学完这节你能做到
- 读懂 Pi 的默认系统提示词分了哪几块、各自解决什么问题
- 为自己的项目写出一份有效的 AGENTS.md,并说明它和 SYSTEM.md 的分工
- 判断某条规则应该写进提示词、写进工具描述、还是做成 skill
同一个模型、同一套工具,换一份系统提示词就是另一个 agent。这一层是你调整 agent 行为 最便宜的手段 —— 也是最容易写砸的一层。
Pi 的三层:默认提示词、上下文文件、系统提示文件
Pi 把这件事拆成三层,各管一件事:
| 层 | 文件 | 作用 | 谁该写 |
|---|---|---|---|
| 默认系统提示词 | Pi 内置(--system-prompt 可整体替换) | 身份、工具约定、行为边界 | 一般不动 |
| 上下文文件 | AGENTS.md / CLAUDE.md | 这个项目的事实与规矩 | 每个项目都该写 |
| 系统提示文件 | SYSTEM.md / APPEND_SYSTEM.md | 换掉或追加系统提示 | 做专用 agent 时 |
上下文文件的层叠规则
AGENTS.md(或 CLAUDE.md)从三处读,叠加生效:
~/.pi/agent/AGENTS.md ← 全局:你个人的偏好,跨项目通用
<从 cwd 往上的每一级父目录> ← 单仓多项目时,公共约定放上层
<cwd>/AGENTS.md ← 当前项目
某一级目录下放了 AGENTS.override.md,这一级就用它替代自己的 AGENTS.md / CLAUDE.md,
其它层照常叠加。--no-context-files(-nc)整体关掉。
系统提示文件
.pi/SYSTEM.md # 项目级:整体替换默认系统提示
~/.pi/agent/SYSTEM.md # 全局:同上
.pi/APPEND_SYSTEM.md # 追加而不是替换
~/.pi/agent/APPEND_SYSTEM.md
默认提示词里有大量关于工具怎么用、输出格式、边界的约定。整体换掉之后,
「它突然不会用 edit 工具了」这类问题会一起来。做专用 agent(只跑一类任务)时才替换,
平时用 APPEND_SYSTEM.md 追加。
AGENTS.md 该写什么
这份文件的读者是模型,判断标准只有一个:它写在这里,能让 agent 少犯一次错吗?
值得写:
# 项目约定
## 构建与验证
- 包管理用 bun,不要用 npm(lockfile 会打架)
- 改完跑 `bun run typecheck`,这是唯一的准入检查
- 测试很慢(约 4 分钟),只在改动涉及 src/core/ 时跑
## 边界
- 不要改 src/generated/ 下的任何文件,那是代码生成产物
- 数据库迁移只能新增文件,不许改历史迁移
## 事实
- 生产环境是 K8s,日志在 Loki,指标在 Prometheus
- API 的 OpenAPI 定义在 api/openapi.yaml,改接口先改它
不值得写:
- 代码结构说明 —— 让它自己
ls和grep,比你写的更新。 - 通用编程规范 —— 「变量名要有意义」这种,模型本来就会。
- 超长的架构文档 —— 每轮都在付这个钱,而且大部分任务用不上。放到 skill 或 普通文档里,让 agent 需要时自己读。
AGENTS.md 里的每一行都在每一轮被重发。如果一条规则十次任务里只有一次用得上, 它就该是 skill 而不是 AGENTS.md。
规则该放哪一层
同一条约束有三个可能的位置,选错了就不生效:
| 约束 | 放哪 | 理由 |
|---|---|---|
| 「不许 force push」 | 工具层(确认门 / 拦截) | 提示词是软的,模型可能忽略;真要拦就拦在执行处 |
| 「用 bun 不用 npm」 | AGENTS.md | 高频、跨任务、说一次就够 |
| 「写 PDF 报表的完整流程」 | skill | 低频、长、只在特定任务需要 |
| 「这个工具超过 400 行会截断」 | 工具的 description | 就近说明,模型用到时才看见 |
Pi 的文档说得很直白:project trust「不是沙箱,也不限制模型能让工具做什么」。 提示词里写一百遍「不要删文件」,也挡不住一次误判。真正的边界只有两种: 在工具执行处拦(L2 的权限门),或者用操作系统 / 容器隔离(L3 的沙箱)。
规则不生效的三个常见原因
- 和默认提示词冲突 —— 你说「回答要简短」,默认提示词说「改动前先解释计划」。
模型在冲突里选一个,通常不是你要的那个。冲突要在同一层解决:用
SYSTEM.md替换, 而不是在 AGENTS.md 里对着喊。 - 位置太靠后、正文太长 —— 三千字的 AGENTS.md 里第 47 条规则,注意力权重很低。 规则超过二十条就该分层。
- 写成了描述而不是指令 —— 「本项目使用 bun」是事实陈述,
「安装依赖必须用
bun install,不要用 npm」是指令。写指令。
用扩展动态改系统提示
Pi 留了钩子:before_agent_start 事件可以注入消息、也可以链式修改系统提示。
用它做「按任务类型切换人格」「注入当前分支/环境信息」这类事:
pi.on("before_agent_start", async (event, ctx) => {
// 注入动态信息 —— 注意放在消息里,别塞进系统提示,否则缓存全废
})
ctx.getSystemPrompt() 能读到当前完整的系统提示,调试时很有用。命令上下文里还有
getSystemPromptOptions() —— 文档特别提醒它可能包含上下文文件全文,属于敏感内容,
别写进日志。
团队要求:agent 绝对不能对 main 分支执行 git push。这条约束最该放在哪?
这一节的结论
- 三层分工:默认提示词(别动)、AGENTS.md(项目事实与规矩)、SYSTEM.md(换人格)。
- AGENTS.md 每轮都在花钱 —— 十次里用一次的规则应该是 skill。
- 写指令不写描述;规则超过二十条就该分层。
- 禁止性约束必须落到工具层或系统隔离,提示词只是第一道软防线。