系统指令 (System Instructions)
上下文视角:系统指令是注入每个 API 请求的“行为基准”——它为 Agent 设定了身份、规则和目标,是 LLM 收到的第一份、也是优先级最高的上下文。
它是如何工作的
上一节说了,Agent 负责编排上下文。系统指令就是 Agent 在每次 API 请求中最先注入的那份上下文。
你打字之前,它就已经在了。
── 第 1 轮 ──
// → REQUEST(agent → LLM API)
{
"system": "你是一个 AI 编程助手。遵循 Google TypeScript 风格指南。只编写或修改代码,不回答与编程无关的问题。",
"messages": [{ "role": "user", "content": "帮我写一个斐波那契函数" }]
}// ← RESPONSE(LLM API → agent,SSE 流)
{
"role": "assistant",
"content": "function fibonacci(n: number): number { ... }"
}── 第 2 轮 ──
// → REQUEST(agent → LLM API)
{
"system": "你是一个 AI 编程助手。遵循 Google TypeScript 风格指南。只编写或修改代码,不回答与编程无关的问题。",
"messages": [
{ "role": "user", "content": "帮我写一个斐波那契函数" },
{ "role": "assistant", "content": "function fibonacci(n: number)..." },
{ "role": "user", "content": "加上 JSDoc 注释和输入验证" }
]
}messages 变长了,但 system 一字不变。
对话跑 50 轮,它还是一模一样。系统指令不是对话的一部分——是对话的规则。
它包含什么
- 身份:告诉 LLM 它扮演什么角色
- 能力:告诉 LLM 它能做什么操作
- 规则:必须遵守的约束和规范
- 目标:它的工作目标是什么
- 输出格式:结果用什么格式呈现
这些把通用 LLM 变成你的专属工具。
谁写的?
系统指令有三个来源:
Agent 开发者:他们编写了大部分基础指令,定义了 Agent 的核心能力和行为模式。大多数情况下你改不了这部分。
你 (用户):大多数 Agent 工具都提供了自定义指令的入口,例如
CLAUDE.md、AGENTS.md或工具内的配置选项。Agent 在对话中生成/更新:Agent 和 LLM 自己也能成为作者。这通常在两种情况下发生:
- 用户直接指示:“把这个约定加到规则文件里去。”
- 系统指令本身授权 Agent 在特定条件下更新自己:“如果发现新的代码约定,就把它追加到指令文件。”
这让系统指令从一份静态配置,演变成一份可以随着项目进展而“活”起来的文档。
用户自定义指令是你手里最有效的手段
用户自定义的系统指令,是你能用来影响 Agent 行为的最有效手段。
你不需要改 agent 的内核代码,也不需要理解它的内部实现。
用自然语言写清楚你的规则和期望,就能全局改变 Agent 的行为。
想让它遵循团队独特的代码风格?写进系统指令。 想让它避免接触某个敏感模块?写进系统指令。 想让它在每次提交前都执行特定检查?写进系统指令。
比如你的指令文件里可以这么写:
## 代码风格
- 所有函数必须有 JSDoc 注释
- 变量命名用 camelCase,常量用 UPPER_SNAKE_CASE
## 安全边界
- 不要读写 .env 和 credentials/ 目录
## 工作流
- 修改代码后必须执行测试,全部通过才算完成这些用户层面的指令通常会被追加到 Agent 的基础指令之后,或者通过特定语法覆盖默认行为。
精心打磨的几十行指令,投入产出比极高——远超你花大量时间去摸索 Agent 的默认行为。
项目里已有的工程约束——tsconfig strict、lint 规则、pre-commit hooks——对人和 Agent 都有价值,只是方式不同。人写代码时,这些检查帮你守住底线;Agent 生成代码量更大,每一行都过同一套关卡,效果更显著。你编码一次的规则,每一行生成的代码都受益。
写好指令的两个方向
想让 Agent 好用,别总想着怎么"管住"它,多想想怎么让它"融入"你的工作流。
方向一:画路,别砌墙
想禁止 Agent 删文件,别跟它说"不要删文件"。否定式指令容易被模型忽略,正面描述期望行为效果更好。
- ❌ "不准用
delete_file。" - ✅ "只在用户明确要求'删除'时,才用
delete_file。" - ✅✅ "调用
delete_file前,必须检查用户输入是否包含'删除'二字。不含则拒绝。"
给它画一条清晰的路,而不是把所有的坑都围起来。
还有一个很实用的拆法:别把所有规则揉成一句话。
| 你在写什么 | 它解决什么问题 | 典型写法 |
|---|---|---|
| 边界 | 圈定什么不能碰 | “只有用户明确要求删除时,才允许删文件。” |
| 判断标准 | 说明什么才算做对 | “修改完成后,必须跑测试;测试全过才算完成。” |
| 示例 | 让它看到结果长什么样 | “按这个 JSON 结构返回,不要自由发挥字段名。” |
边界管范围,判断标准管验收,示例管结果形状。
三种东西分开写,Agent 更不容易把约束、目标和输出格式混成一团。
方向二:把它当新同事,不是新 linter
如果你的指令文件里只有"用 tabs 不用 spaces"这种代码风格规则,那你只是多了个 linter。
把它当成一个新来的同事,告诉他"我们是怎么做事的":
- 我们修 bug 的流程是"复现 -> 测试 -> 提交",不是"看日志 -> 瞎猜 -> 等用户反馈"。
- 我们希望你是个能提出异议的队友,不是只会执行命令的机器。如果我的要求可能搞坏架构,直接拒绝,然后给出你的替代方案。
指令是份对齐预期的协议。
指令是可维护资产
别把你的系统指令当成一次性的便条。它是一份活的文档,是需要像代码一样维护的资产。
你可以把项目级指令文件理解成"组织教训库"——每一条规则背后都是一次真实的踩坑教训。
但指令文件不只是踩坑记录。那些经过反复验证、沉淀下来的成功做法——同样写在里面。
踩坑教训告诉 Agent 什么路走不通,验证过的好做法告诉它应该怎么走。 两者合起来,才是完整的团队智慧。
"画路别砌墙"针对的是指令的写法——用正面描述替代否定句。这里说的"踩坑教训"是指令的内容来源——记录团队真实犯过的错,把经验固化成规则。两者不矛盾:内容来自踩坑,写法要正向引导。
- 版本化:用 Git 管理你的指令文件,追踪每次变更。
- Review:让同事 review 你的指令,就像 review 代码一样。
- 迭代:根据 Agent 的行为反馈,持续优化和追加新规则。
一份好的系统指令集,是你所在项目知识沉淀的一部分。
团队级指令治理
一个人用,指令迭代就行。一个团队用,就需要上升到治理。
- 指令文件必须进 Git。 后来回看时,
git blame能告诉你是谁、为什么加了这条规则。不在版本控制里的指令,出了问题没人能追溯。 - 变更必须 Review。 一行指令的变更,威力不亚于改一行核心代码。一个人加了"永远用 tabs",另一个人加了"永远用 spaces",冲突要到下个会话才炸。指令变更应该走 PR,让团队知道"这条规则会影响谁"。
- 项目变,指令跟着变。 技术栈升级了、架构调整了、依赖换了——指令里相关的规则也得同步更新。这不是"有空再说",这是"下一个会话就会基于过时的规则做决策"。
失败记录什么时候该写进 system instructions
Agent 跑完一个任务,留下失败记录——删错了文件、用了弃用 API、没跑测试就提交。哪些该固化进 system instructions,哪些当次处理完就行?
判断前,先确认失败原因:这是指令缺口,还是工具、权限、流程设计的问题。权限不够、hook 缺失、流程没有审批,单靠 system instructions 很难兜住。
然后问三个问题:
- 根因是指令缺口吗? 如果真正问题是工具不可用、权限不对、流程没设计好,先修工具和流程。
- 这类失败会重复出现吗? 换个任务、换个人、过一段时间,同样的坑还在吗?
- 规则能泛化吗? 能写成一条对所有类似任务都成立的指令,而不是「这次任务里的特殊处理」?
三个问题都是「是」,通常就值得考虑写进 system instructions。如果需要强制执行,更适合交给 hook、审批或校验机制。否则,先在对话里纠正,不必急着固化。
| 失败类别 | 是否进 system instructions | 原因 |
|---|---|---|
| Agent 删了不该删的配置文件 | ✅ 是 | 属于高风险通用错误;system instructions 只能当第一层提醒,涉及不可逆操作时必须配合 hook 或审批 |
| 某次任务里的临时格式偏差 | ❌ 否 | 一次性偏差,在当次对话纠正即可 |
| 用了已弃用的 API | ✅ 是 | 整个代码库都受影响,适合用全局规则统一提醒;如果能自动识别,也应配合 lint 或测试 |
| 推理链走偏、分析方向偏了 | 看情况 | 具体那次推理过程不适合固化;只有能提炼成稳定做法时,才写成规则 |
| 总是忘记跑 lint 就提交 | ✅ 是 | 属于工作流缺口,适合沉淀为全局规则;同时应落到 pre-commit 或 CI,能自动拦截更可靠 |
还有一条实操原则:进 system instructions 的规则要精炼。好规则应该短、可执行、能在不同任务复用。每条规则都会占用上下文窗口,塞多了会挤占当前任务真正需要的信息;规则越多,也越容易互相冲突。
失败日志里能提炼出一条准确的行为指令,才值得写进去。如果只能描述「上次出了问题」,先留在失败日志、issue 或 postmortem 里,等 trace 足够清楚、模式足够稳定,再固化。
这就是 system instructions 和会话内纠错的边界:system instructions 管「以后每次都该怎么做」;会话内纠错管「这次任务现在怎么改回来」。前者主要处理可预测、可重复的系统性风险;一次性、任务特有的偏差,通常先在会话里纠正。不该进去的硬塞进去,指令文件会越来越臃肿,规则越来越难维护。
本节小结
- 上下文流动:系统指令是上下文的"静态层"——每轮请求都原样存在,为后续所有动态上下文(用户输入、工具返回)提供稳定的行为基准。
- 风险:Agent 基础指令和你的自定义指令冲突时,LLM 行为会变得不可预测。Agent 表现怪异?先查指令冲突。
- 可审计性:系统指令完全可读——打开 Agent 的默认指令和你自己的自定义指令,就能精确知道什么在驱动它的行为。
下一节看工具——工具定义和返回值如何进入同一个上下文回路。