03:Prompt System:Claude Code 如何让 Agent 按协议行动
03:Prompt System:Claude Code 如何让 Agent 按协议行动
上一篇把工具系统当成 Claude Code 的执行层来看。
工具让 agent 有了行动能力:它可以读文件、搜代码、改内容、跑命令、拿到验证结果。但工具一旦能接触真实项目,新的问题马上出现:
模型为什么知道自己该怎样使用这些工具、遵守哪些项目规则、用什么方式和用户协作?
这篇就接着看 Prompt System。
我现在更愿意把它叫作“行为协议层”。它把系统提示、项目规则、工具描述、用户指令和运行时补充信息装配在一起,让 agent 每一轮行动前都有一套默认规则:自己是谁,能做什么,先看什么,怎样使用工具,哪些边界交给权限和运行时。

这篇文章要拆的主线:Agent 的行为协议从哪里来
这一篇回答的问题是:
Claude Code 如何把系统提示、项目规则、工具描述、用户指令和运行时注入,组织成 agent 每轮行动前看到的行为协议?
这个问题容易被压缩成“prompt 怎么写”。但 Claude Code 这类 coding agent 面对的是更大的工程问题。
普通聊天里,prompt 主要影响回答方式。Coding agent 里,prompt 会影响整个任务推进方式:
- 它会决定 agent 是先解释,还是先观察项目。
- 它会决定 agent 什么时候读文件,什么时候搜索,什么时候跑验证。
- 它会决定 agent 怎样理解工具描述、项目规则和用户当前目标。
- 它会决定 agent 遇到风险时先说明、先请求确认,还是换一条低风险路径。
所以提示词系统的重点,已经从“让模型答得更好”升级成“让 agent 行为更稳定”。
用一句话概括:
Prompt System 是 coding agent 的行为协议装配层。
它给工具系统套上行动规则,也给后面的记忆、上下文、计划、权限、hook 和 subagent 留出接口。记忆系统会在第 04 篇展开,上下文管理放到第 05 篇,权限第 07 篇讲,hook 和扩展第 10 篇讲,subagent 第 11 篇讲。
提示词系统解决的工程问题:让 agent 行为稳定
LLM 会顺着当前上下文调整行为,这正是它好用的地方。用户说“解释这段代码”,它可以像讲解者;用户说“帮我修构建错误”,它可以像开发者;用户说“审一下这段 diff”,它可以像 reviewer。
放到 agent 里,这种适配能力需要一套稳定边界托住。
没有稳定的行为协议,容易出现几类问题:
-
身份漂移。
同一个 agent 一会儿像教程老师,一会儿像命令执行器,一会儿像产品助手。用户很难形成稳定预期。 -
工具误用。
模型知道有工具,还需要知道工具适合什么场景、参数怎么填、结果回来后怎样理解。 -
项目规则被冲淡。
真实仓库里有测试命令、目录约定、生成文件禁区、代码风格和团队习惯。这些规则靠用户每轮重复,任务会很快变散。 -
软规则盖过硬边界。
“避免读取密钥文件”写进 prompt 可以提醒模型,但敏感路径、危险命令和审批策略更适合交给权限系统。 -
协作方式忽左忽右。
同样是修 bug,有时需要先列计划,有时可以直接最小修改,有时要先问用户业务选择。协议层要把这些分歧变成可判断的入口。
提示词系统要做的,就是把这些默认行为收束起来:基础身份由系统提示承载,项目约定由 CLAUDE.md 和 rules 承载,工具使用由工具描述承载,当前任务由用户输入承载,硬边界由运行时机制承载。
这样后面要调整行为时,才知道该改哪里。
提示词不是一段话,而是一组可组合 section
几篇源码分析文章都提到一个共同点:Claude Code 的系统提示更像一条组装管线,而非一段静态长文本。[1],[2],[3]
这对我理解 Prompt System 很关键。
如果 system prompt 只是一段手写文本,那维护方式很简单:改文字、调措辞、加规则。但 Claude Code 这种工具要面对不同项目、不同工具集合、不同 output style、不同 MCP 状态、不同用户任务。它需要按当前运行环境装配出一轮可用的提示上下文。
可以把它想成几类 section:
| section 类型 | 主要放什么 | 变化频率 |
|---|---|---|
| 基础身份 | agent 是软件工程任务助手,怎样和用户沟通 | 低 |
| 行为规则 | 读文件、最小改动、验证、输出方式 | 低 |
| 工具说明 | 可用工具、参数、使用边界 | 中 |
| 项目规则 | CLAUDE.md、目录规则、项目命令 |
中 |
| 动态环境 | 当前目录、shell、git 状态、日期、MCP 状态 | 高 |
| 当前任务 | 用户本轮目标、临时限制、工具结果反馈 | 高 |
Siddhant Khare 的分析里提到,Claude Code 有稳定区和动态区的边界,也有用于构造 section 的内部接口;其中有一类显眼命名的动态 section,会提示开发者它会破坏缓存,需要给出理由。[1] 这类函数名本身不是本文重点,真正值得学的是背后的工程原则:
每一段提示都应该知道自己的来源、作用域、稳定性和成本。
这让 prompt 从“一锅文本”变成了可维护的配置系统。
稳定前缀和动态注入:哪些规则适合长期存在
提示词系统里有一条很重要的边界:哪些内容适合长期稳定,哪些内容应该按需注入。
稳定内容适合放在前面,例如基础身份、通用行为规则、常驻工具说明和输出约定。它们变化少,模型每轮都需要看到,适合形成稳定前缀。
动态内容更适合放在后面,例如当前目录、git 状态、临时 MCP 信息、用户本轮目标、工具结果和会话反馈。它们变化快,放进稳定前缀会增加缓存成本,也会让行为边界跟着环境频繁抖动。
David Breunig 的文章把 Claude Code 的 system prompt 拆成很多组件:有些始终进入,有些按条件进入,有些随 output style 或工具集合变化。[2]
Piebald-AI 的提示词仓库也能看到类似现象:Claude Code 相关提示并非只有一份,工具描述、subagent 提示、utility prompts、compact prompt 等都会随版本演进。[5]
所以 prompt system 的工程问题可以这样理解:
稳定规则要稳定,动态事实要动态,临时信息要有明确注入位置。
这条边界后面会在上下文管理里继续展开。这里先记住一件事:提示词系统既影响行为,也影响成本和缓存命中。
多来源提示:system、CLAUDE.md、output style、command、skill、hook
Agiflow 通过抓取 API payload,把 Claude Code 里几类提示增强机制分成了不同注入点:output style 改系统层,CLAUDE.md、slash command、skill 进入消息层,hook 作用在工具生命周期,subagent 开新会话。[4]
这张地图很有价值,因为它说明这些入口是分工关系。它们各自适合解决不同问题。
| 来源 | 注入位置 | 适合放什么 | 后文展开 |
|---|---|---|---|
| system prompt | 系统层 | 基础身份、通用行为、工具使用总规则 | 本篇 |
| output style | 系统层补充 | 表达风格、解释深度、角色口吻 | 本篇 |
CLAUDE.md / rules |
消息层或项目上下文 | 项目约定、命令、架构说明、目录规则 | 第 05 篇连到上下文 |
| slash command | 单轮消息注入 | 可复用流程、检查清单、固定任务模板 | 第 10 篇 |
| skill | 按需注入 | 专门领域流程、复杂能力说明 | 第 10 篇 |
| hook | 工具生命周期 | 工具前后补充、拦截、审计、自动化 | 第 10 篇 |
| subagent | 独立会话 | 大范围探索、专业任务、上下文隔离 | 第 11 篇 |
这张表也解释了为什么 CLAUDE.md 很重要,但它不适合承载所有规则。
项目级规则适合写进 CLAUDE.md:测试命令、目录结构、代码风格、生成文件说明、常见坑。临时任务指令适合放在用户当前 prompt。持续表达偏好适合 output style。需要自动执行或拦截的规则更适合 hook。需要隔离大块上下文的任务更适合 subagent。
这样分开以后,prompt system 就像一个带作用域的配置系统。每类规则都有自己的入口,行为异常时也更容易排查。
工具描述也是行为协议
02 里已经讲过,工具系统是一条从工具请求到结果回流的执行链。
到了 Prompt System 这里,要换个角度看工具:工具描述本身也是提示词。
模型会根据工具说明判断该不该用工具,根据参数 schema 判断怎么填输入,根据输出格式理解结果。工具描述写得清楚,agent 更容易选对工具;工具描述含糊,agent 就会靠猜。
比如搜索工具的描述,如果只说“搜索文件”,模型很难判断它和 shell 里的 grep 有什么区别。更好的说明应该让模型知道:
- 这个工具适合找路径、函数名、错误文本和调用点。
- 返回值包含路径、行号和命中片段。
- 命中很多时先缩小关键词,再读关键文件。
- 搜索结果只是候选证据,最终判断要回到文件内容。
命令工具也是一样。它需要说明工作目录、超时、退出码、stdout/stderr、可能副作用和失败格式。这样主循环拿到结果以后,才知道下一轮该继续定位、重新验证,还是停止总结。
所以工具协议和提示词系统之间有一条很直接的连接:
工具描述决定模型如何理解行动选项。
这也是为什么工具越多,提示词系统越需要治理。工具说明本身会占上下文,工具集合变化也会影响稳定前缀。工具膨胀和延迟加载在第 02 篇已经点到,后面第 05 篇会从上下文成本继续讲。
Prompt 和 runtime 的边界:软规则与硬边界
提示词系统很容易被写成万能开关:把规则写进去,模型就会照做。
工程上更稳的划分是:
Prompt 负责表达意图和协作方式,runtime 负责动作许可和硬边界。
有些规则天然适合 prompt:
- 回答要简洁。
- 修改前先读相关文件。
- 尽量做最小改动。
- 总结时说明验证结果。
- 遇到不确定业务选择时询问用户。
这些规则影响行为风格和工作方式,属于软约束。
另一些规则适合 runtime:
- 禁止读取
.env、密钥、证书。 - 删除文件前需要确认。
- 危险 shell 命令需要审批。
- 每次工具调用要记录审计。
- 某些 hook 必须在编辑前后运行。
这些规则涉及副作用、安全和组织策略,需要硬边界。Prompt 可以提醒 agent 如何解释这些规则,但最终放行、拒绝、拦截和记录,应该交给权限、settings、hook 或工具层。
Agiflow 对 hook 的观察也能支撑这个边界:hook 发生在工具生命周期里,可以确定性地注入信息、拦截动作或补充结果。[4]
这类能力适合做运行时控制,第 10 篇再展开。
冲突和优先级:多条规则同时出现时怎么办
提示词系统真正麻烦的地方,是多来源规则会同时出现。
用户当前要求可能是“直接修掉”。项目 CLAUDE.md 可能要求“涉及多文件先列计划”。output style 可能偏向详细讲解。工具描述可能建议先搜索再读文件。权限系统又可能拦住某个写入或命令。
这时需要一套简单的分层判断。
| 冲突类型 | 更稳的处理方式 |
|---|---|
| 软偏好冲突 | 让更具体、更近期的指令优先 |
| 项目规则冲突 | 在项目规则里写清覆盖关系 |
| 工具使用冲突 | 按工具描述和当前证据选择低成本路径 |
| 安全边界冲突 | 交给权限、hook、managed policy |
| 输出风格冲突 | 当前任务需要优先,例如修 bug 总结可以短,教学场景可以长 |
比如用户说“直接改”,而项目规则说“多文件改动先列计划”。如果只改一个 Markdown frontmatter,agent 可以直接做最小修改;如果牵涉多个模块,就应该先说明修改范围。
再比如用户让 agent “顺手清理一下所有旧文件”,这属于高风险范围扩张。Prompt 可以让 agent 解释风险,权限系统决定实际动作能否执行。
这个分层能减少模型猜测,也能让用户更容易理解 agent 为什么这样做。
案例分析:构建失败时行为协议如何生效
继续沿用前两篇的例子:用户说“博客页面构建失败了,帮我修一下”。
工具系统会负责读 package.json、跑构建、搜索报错、读文件、编辑和验证。提示词系统负责约束这些动作怎样发生。
可以把这次任务里的行为协议写成一条事件流:
用户目标:修复博客构建失败
-> 基础行为:先观察项目,再做最小范围修改
-> 项目规则:遵守博客目录、frontmatter 和写作约定
-> 工具契约:先用搜索和读取定位证据,再编辑目标文件
-> 运行时边界:写文件看 diff,高风险命令交给权限
-> 验收方式:重新运行构建,返回错误原因、改动和验证结果
这个例子里,用户只给了一个目标。真正让 agent 稳定行动的,是多层协议一起生效:
- 系统提示让它保持软件工程任务助手的身份。
- 项目规则告诉它博客文章和构建约定。
- 工具描述告诉它怎样读文件、搜索、编辑、运行命令。
- 用户当前指令给出任务目标和完成方向。
- 权限与运行时边界控制有副作用的动作。
这样一来,“帮我修一下”就不再是一个模糊请求,而是被组织成一条可执行、可验证、可解释的行动路径。
对我写 Agent 的启发:把 prompt 当成可维护配置
如果以后自己写一个 coding agent,我会先把 prompt system 当成配置系统来设计,而不是当成一段长字符串。
我会拆出几个区域:
| 区域 | 放什么 | 谁维护 |
|---|---|---|
| base prompt | agent 身份、协作方式、默认工程原则 | 产品或平台 |
| tool contracts | 工具说明、参数、返回格式、错误含义 | 工具开发者 |
| project rules | 项目命令、目录约定、代码风格 | 项目团队 |
| user preferences | 长期表达偏好、协作偏好 | 用户 |
| task prompt | 当前目标、限制、验收标准 | 当前用户 |
| runtime guardrails | 权限、hook、敏感路径、审批策略 | 平台或管理员 |
| feedback context | 工具结果、测试失败、用户纠正 | agent loop |
每条规则进入系统前,都要问五个问题:
- 它的作用域是什么:全局、项目、目录、任务,还是单次工具调用?
- 它的 owner 是谁:平台、项目、用户,还是工具作者?
- 它的稳定性如何:适合长期前缀,还是适合动态注入?
- 它是软偏好,还是硬边界?
- 它有没有验证方式:能否通过日志、测试、权限结果或 hook 复盘?
这样做的好处是,agent 行为变差时可以定位原因。是 output style 太啰嗦,还是工具描述让模型误解,还是 CLAUDE.md 写了过期命令,还是 runtime guardrail 没有兜住风险。
我也会让提示词版本可追踪。Piebald-AI 的仓库把 Claude Code 的提示片段、工具描述、subagent prompt 和 utility prompt 按版本整理出来,这一点很有启发。[5] 对自己的 agent 来说,prompt 变化也应该像代码变化一样能 diff、能回滚、能解释。
最后是规则写法。
“请谨慎”这类空泛表达帮助有限。更好的规则应该能落到行为:
- 修改前先读目标文件和相邻测试。
- 多文件改动先列范围。
- 写入后运行最小相关验证。
- 验证失败时保留失败摘要和下一步假设。
- 涉及删除、迁移、批量替换时先说明风险。
这种规则才能真正改变 agent loop。
参考资料
Claude Code 源码与提示词分析
[1] The plumbing behind Claude Code - Siddhant Khare
本文主要参考它关于 system prompt 分段组装、稳定区和动态区边界、动态 section 影响缓存的分析。
[2] How Claude Code Builds a System Prompt - David Breunig
本文主要参考它对 Claude Code system prompt 组件化装配方式的可视化拆解。
[3] Inside the Claude Code source - Haseeb Qureshi
本文主要参考它关于可组合 prompt function、缓存边界和权限反馈链路的整理。
[4] Claude Code Internals: Reverse Engineering Prompt Augmentation Mechanisms - Agiflow
本文主要参考它关于 CLAUDE.md、output style、slash command、skill、hook、subagent 不同注入位置的分析。
[5] Piebald-AI/claude-code-system-prompts
本文主要参考它对 Claude Code 多类提示片段、工具描述、subagent prompt 和 utility prompt 的版本化整理。
下一篇:Memory System
这一篇先把提示词系统理解成 Claude Code 的行为协议装配层。
核心结论是:
工具让 agent 能行动,提示词系统决定 agent 按什么身份、项目规则、工具契约和协作方式行动。
但行为协议解决的是“本轮怎么行动”。任务一旦跨越多轮、多天、多个项目,agent 还需要沉淀长期偏好、项目经验和反复出现的用户要求。
所以下一篇继续追的问题是:
Claude Code 如何把一次任务里的经验,变成后续任务可以召回的记忆?
更多推荐

所有评论(0)