系统指令 (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前,必须检查用户输入是否包含'删除'二字。不含则拒绝。"
给它画一条清晰的路,而不是把所有的坑都围起来。
方向二:把它当新同事,不是新 linter
如果你的指令文件里只有"用 tabs 不用 spaces"这种代码风格规则,那你只是多了个 linter。
把它当成一个新来的同事,告诉他"我们是怎么做事的":
- 我们修 bug 的流程是"复现 -> 测试 -> 提交",不是"看日志 -> 瞎猜 -> 等用户反馈"。
- 我们希望你是个能提出异议的队友,不是只会执行命令的机器。如果我的要求可能搞坏架构,直接拒绝,然后给出你的替代方案。
指令是份对齐预期的协议。
指令是可维护资产
别把你的系统指令当成一次性的便条。它是一份活的文档,是需要像代码一样维护的资产。
你可以把项目级指令文件理解成"组织教训库"——每一条规则背后都是一次真实的踩坑教训。
但指令文件不只是踩坑记录。那些经过反复验证、沉淀下来的成功做法——同样写在里面。
踩坑教训告诉 Agent 什么路走不通,验证过的好做法告诉它应该怎么走。 两者合起来,才是完整的团队智慧。
"画路别砌墙"针对的是指令的写法——用正面描述替代否定句。这里说的"踩坑教训"是指令的内容来源——记录团队真实犯过的错,把经验固化成规则。两者不矛盾:内容来自踩坑,写法要正向引导。
- 版本化:用 Git 管理你的指令文件,追踪每次变更。
- Review:让同事 review 你的指令,就像 review 代码一样。
- 迭代:根据 Agent 的行为反馈,持续优化和追加新规则。
一份好的系统指令集,是你所在项目知识沉淀的一部分。
团队级指令治理
一个人用,指令迭代就行。一个团队用,就需要上升到治理。
- 指令文件必须进 Git。 后来回看时,
git blame能告诉你是谁、为什么加了这条规则。不在版本控制里的指令,出了问题没人能追溯。 - 变更必须 Review。 一行指令的变更,威力不亚于改一行核心代码。一个人加了"永远用 tabs",另一个人加了"永远用 spaces",冲突要到下个会话才炸。指令变更应该走 PR,让团队知道"这条规则会影响谁"。
- 项目变,指令跟着变。 技术栈升级了、架构调整了、依赖换了——指令里相关的规则也得同步更新。这不是"有空再说",这是"下一个会话就会基于过时的规则做决策"。
本节小结
- 上下文流动:系统指令是上下文的"静态层"——每轮请求都原样存在,为后续所有动态上下文(用户输入、工具返回)提供稳定的行为基准。
- 风险:Agent 基础指令和你的自定义指令冲突时,LLM 行为会变得不可预测。Agent 表现怪异?先查指令冲突。
- 可审计性:系统指令完全可读——打开 Agent 的默认指令和你自己的自定义指令,就能精确知道什么在驱动它的行为。
下一节看工具——工具定义和返回值如何进入同一个上下文回路。