今天打算简单分享一下我对 AGENTS.md 的一些思考,起因是最近学习了开源 Agent 框架 Pi,对它的 AGENTS.md 眼前一亮,它解答了我最近使用 Code Agent 的一些困扰。
为什么需要 AGENTS.md?
在正式进入话题之前,先简单解释一下为什么需要 AGENTS.md。
在使用 Coding Harness,比如 Codex、Claude Code、DSH 等编程智能体来辅助开发时,为了使得
- AGENTS.md 的回答与行为符合我们的预期
- 避免反复对齐基本需求并缩减沟通成本
- 满足自定义的需求
往往我们会编写一个名为 AGENTS.md 的 markdown 文件,来约束其行为。
为什么 AGENTS.md 有效?
AGENTS.md 有效的原因很简单,因为其中的内容是作为 system prompt 输入到 LLM 中的,一个常见的输入大致长这样:
{
"system_prompt": "你是一个……",
"user_prompt": "请帮我……",
"assistant": "……"
}而且重要的是,每当你发起一个请求,Harness 都会将之前所有的对话内容与当前请求一起打包发送给 LLM 进行分析处理。 因此,AGENTS.md 的内容永远是最先被处理的文本,但 LLM 是不是对越前面的文本越敏感,我暂时还不清楚。
除此之外,现在的 LLM 训练都是包含 system prompt 的,因此 system prompt 对 LLM 行为的约束最强。
为什么需要自己编写 AGENTS.md
现在的 agent Harness 基本都有一个 /init 命令,用来根据代码库内容初始化一个新的 AGENTS.md 或者更新现有的 AGENTS.md,
虽然这种方式很方便,但有几个缺陷:
- 内容是根据代码库生成的,代码库的质量决定 AGENTS.md 的质量。
- 随着代码库的增长,因 AI 习惯于增量更新,AGENTS.md 的内容也会不断增长,而我们应该尽可能保持 AGENTS.md 的简洁。
- 每个人都有自己的编码习惯,比如使用什么框架、包管理器、项目配置等,这些自定义内容无法保证。
- 每个人也有自己与 AI 的交互习惯,比如有的人喜欢简洁的回答,有的喜欢全面的回答,这些交互规则应该自己定义。
总而言之,用 /init 生成和更新的 AGENTS.md,一方面难以满足自定义需求,另一方面难以管理。因此,我们应该尽量自己管理 AGENTS.md。
如何编写适合自己的 AGENTS.md 呢?
想要编写一份自定义的高质量的 AGENTS.md 有以下几个原则:
- 内容分层:根据需求分层描述,将规则划分为:交互规则、编码规则等几个独立的规则。
- 规则简洁:应该保持每条规则描述清晰简洁。
- 避免耦合:每条规则应该保持相对独立,避免与其他规则耦合。
为了具体一点,下面举一个我觉得优秀的 AGENTS.md 的案例:
# Development Rules
## Conversational Style
- Keep answers short and concise
- No emojis in commits, issues, PR comments, or code
- No fluff or cheerful filler text (e.g., "Thanks @user" not "Thanks so much @user!")
- Technical prose only, be direct
- Use concise, clear, simple language. Define unavoidable jargon before using it.
- Explain non-trivial designs and problems as: problem, concrete example or short trace, then solution. State why the solution is necessary and distinguish it from optional complexity.
- Prefer concrete behavior and small illustrations over abstract summaries, dense terminology, or unexplained lists of changes.
- When the user asks a question, answer it first before making edits or running implementation commands.
- When responding to user feedback or an analysis, explicitly say whether you agree or disagree before saying what you changed.
## Code Quality
- Read files in full before wide-ranging changes, before editing files you have not fully inspected, and when asked to investigate or audit. Do not rely on search snippets for broad changes.
- No `any` unless absolutely necessary.
- Inline single-line helpers that have only one call site.
- Check node_modules for external API types; don't guess.
- **No inline imports** (`await import()`, `import("pkg").Type`, dynamic type imports). Top-level imports only.
- Never remove or downgrade code to fix type errors from outdated deps; upgrade the dep instead.
- Use only erasable TypeScript syntax (Node strip-only mode) in code checked by the root config (`packages/*/src`, `packages/*/test`, `packages/coding-agent/examples`): no parameter properties, `enum`, `namespace`/`module`, `import =`, `export =`, or other constructs needing JS emit. Use explicit fields with constructor assignments.
- Always ask before removing functionality or code that appears intentional.
- Do not preserve backward compatibility unless the user asks for it.
- Never hardcode key checks (e.g. `matchesKey(keyData, "ctrl+x")`). Add defaults to `DEFAULT_EDITOR_KEYBINDINGS` or `DEFAULT_APP_KEYBINDINGS` so they stay configurable.
- Never modify `packages/ai/src/models.generated.ts` directly; update `packages/ai/scripts/generate-models.ts` instead, then regenerate. Including the resulting `models.generated.ts` diff is always OK, even if regeneration includes unrelated upstream model metadata changes.
## Commands
- After code changes (not docs): `npm run check` (full output, no tail). Fix all errors, warnings, and infos before committing. Does not run tests.
- Never run `npm run build` or `npm test` unless requested by the user.
- Never run the full vitest suite directly: it includes e2e tests that activate when endpoint/auth env vars are present. For all non-e2e tests, run `./test.sh` from the repo root. Otherwise run specific tests from the package root:
- Vitest: `node "$(git rev-parse --show-toplevel)/node_modules/vitest/dist/cli.js" --run test/specific.test.ts`
- `packages/tui` (`node:test`): `node --test test/specific.test.ts`
- If you create or modify a test file, run it and iterate on test or implementation until it passes.
- For `packages/coding-agent/test/suite/`, use `test/suite/harness.ts` + the faux provider. No real provider APIs, keys, or paid tokens.
- When regressions tests for fixing a github issue, add a comment with the github issue number next to the test.
- For ad-hoc scripts, `write` them to a temp file (e.g. `/tmp`), run, edit if needed, remove when done. Don't embed multi-line scripts in `bash` commands.
- Never commit unless the user asks.
## Dependency and Install Security
- Treat npm dep and lockfile changes as reviewed code. Direct external deps stay pinned to exact versions.
- When updating `undici`, you MUST read its changelog/release notes for the target version and evaluate whether any changes may affect functionality before applying the update.
- Hydrate/update locally with `npm install --ignore-scripts`; clean/CI-style with `npm ci --ignore-scripts`. Don't run lifecycle scripts unless the user asks.
- If dep metadata changes, refresh `package-lock.json` with `npm install --package-lock-only --ignore-scripts`.
- If `packages/coding-agent/npm-shrinkwrap.json` needs regen, run `node scripts/generate-coding-agent-shrinkwrap.mjs` (verify with `--check` or `npm run check`). New deps with lifecycle scripts require review and an explicit allowlist entry in that script; never add one silently.
- Pre-commit blocks lockfile commits unless `PI_ALLOW_LOCKFILE_CHANGE=1`. Don't bypass unless the user wants the lockfile change committed.
## Git
Multiple pi sessions may be running in this cwd at the same time, each modifying different files. Git operations that touch unstaged, staged, or untracked files outside your own changes will stomp on other sessions' work. Follow these rules:
Committing:
- Only commit files YOU changed in THIS session.
- Stage explicit paths (`git add <path1> <path2>`); never `git add -A` / `git add .`.
- Before committing, run `git status` and verify you are only staging your files.
- `packages/ai/src/models.generated.ts` may always be included alongside your files.
- Message format: `{feat,fix,docs}[(ai,tui,agent,coding-agent)]: <commit message> (optionally multiple lines)`. Message is informative and concise.
Never run (destroys other agents' work or bypasses checks):
- `git reset --hard`, `git checkout .`, `git clean -fd`, `git stash`, `git add -A`, `git add .`, `git commit --no-verify`.
If rebase conflicts occur:
- Resolve conflicts only in files you modified.
- If a conflict is in a file you did not modify, abort and ask the user.
- Never force push.
## Issues and PRs
See `CONTRIBUTING.md` for the contributor gate (auto-close workflows, `lgtm`/`lgtmi`, quality bar).
When reviewing PRs:
- Do not run `gh pr checkout`, `git switch`, or otherwise move the worktree to the PR branch unless the user explicitly asks.
- Use `gh pr view`, `gh pr diff`, `gh api`, and local `git show`/`git diff` against fetched refs to inspect PR metadata, commits, and patches without changing branches.
- If you need PR file contents, fetch/read them into temporary files or use `git show <ref>:<path>` without switching branches.
When creating issues:
- Add `pkg:*` labels for affected packages (`pkg:agent`, `pkg:ai`, `pkg:coding-agent`, `pkg:tui`); use all that apply.
When posting issue/PR comments:
- Write the comment to a temp file and post with `gh issue/pr comment --body-file` (never multi-line markdown via `--body`).
- Keep comments concise, technical, in the user's tone.
- End every AI-posted comment with the AI-generated disclaimer line specified by the originating prompt (e.g. `This comment is AI-generated by `/wr``).
When closing issues via commit:
- Include `fixes #<number>` or `closes #<number>` in the message so merging auto-closes the issue. For multiple issues, repeat the keyword per issue (`closes #1, closes #2`); a shared keyword (`closes #1, #2`) only closes the first.
## Testing pi Interactive Mode with tmux
For testing pi's interactive mode, load and follow [.pi/skills/interactive-testing.md](.pi/skills/interactive-testing.md).
## Changelog
Location: `packages/*/CHANGELOG.md` (one per package).
Sections under `## [Unreleased]`: `### Breaking Changes` (API changes requiring migration), `### Added`, `### Changed`, `### Fixed`, `### Removed`.
Rules:
- All new entries go under `## [Unreleased]`. Read the full section first and append to existing subsections; never duplicate them.
- Released version sections (e.g. `## [0.12.2]`) are immutable; never modify them.
- Do not create changelog entries when working on a branch other than `main` or pull request
Attribution:
- Internal (from issues): `Fixed foo bar ([#123](https://github.com/earendil-works/pi/issues/123))`
- External contributions: `Added feature X ([#456](https://github.com/earendil-works/pi/pull/456) by [@username](https://github.com/username))`
## Releasing
For release preparation, publishing, verification, or recovery, load and follow [.pi/skills/release.md](.pi/skills/release.md).
## User Override
If the user's instructions conflict with any rule in this document, ask for explicit confirmation before overriding. Only then execute their instructions.这是 Pi 的代码仓库提示词,它将整个规则划分成了交互规则、代码规范、版本管理、命令管理等多个独立的层次,每条规则都简洁清晰。
下面分享一套我最近常用的 AGENTS.md(持续更新),使用体感不错。
# 规则
## 交互规则
- 回答保持简洁
- 回答优于行动
- 推荐最佳实践,并给出原因
- 当用户要求解释一个术语时,务必使用通俗易懂的方式解释清楚
- 每当修复一个问题后,向用户具体地讲解修复思路和方法,让用户能了解具体过程,同时也能从中学习
- 当执行某个后台命令时,如果预估时间过长,优先向用户询问是否进行,或者有没有等价的短时策略
## 编码规则
- 代码必须要有清晰的类型注释和方法注释虽然根据上面的方法,可以写出一个还不错的 AGENTS.md,但是你完全不用一次性写完美,一个好的 AGENTS.md 不是一次性创造出来的,它是在你与 Agent 交互过程中长出来的:每次与 Agent 的交互过程中你觉得哪里不好,比如回答不够简洁, 代码缺少注释等,这些基础的,需重复约束的东西,都可以不断沉淀进你的 AGENTS.md,最终它就会变成你自己的形状。
小结
简单分享并总结一下我对 AGENTS.md 的一些思考,希望能帮到你。