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 里,这种适配能力需要一套稳定边界托住。

没有稳定的行为协议,容易出现几类问题:

  1. 身份漂移。
    同一个 agent 一会儿像教程老师,一会儿像命令执行器,一会儿像产品助手。用户很难形成稳定预期。

  2. 工具误用。
    模型知道有工具,还需要知道工具适合什么场景、参数怎么填、结果回来后怎样理解。

  3. 项目规则被冲淡。
    真实仓库里有测试命令、目录约定、生成文件禁区、代码风格和团队习惯。这些规则靠用户每轮重复,任务会很快变散。

  4. 软规则盖过硬边界。
    “避免读取密钥文件”写进 prompt 可以提醒模型,但敏感路径、危险命令和审批策略更适合交给权限系统。

  5. 协作方式忽左忽右。
    同样是修 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

每条规则进入系统前,都要问五个问题:

  1. 它的作用域是什么:全局、项目、目录、任务,还是单次工具调用?
  2. 它的 owner 是谁:平台、项目、用户,还是工具作者?
  3. 它的稳定性如何:适合长期前缀,还是适合动态注入?
  4. 它是软偏好,还是硬边界?
  5. 它有没有验证方式:能否通过日志、测试、权限结果或 hook 复盘?

这样做的好处是,agent 行为变差时可以定位原因。是 output style 太啰嗦,还是工具描述让模型误解,还是 CLAUDE.md 写了过期命令,还是 runtime guardrail 没有兜住风险。

我也会让提示词版本可追踪。Piebald-AI 的仓库把 Claude Code 的提示片段、工具描述、subagent prompt 和 utility prompt 按版本整理出来,这一点很有启发。[5] 对自己的 agent 来说,prompt 变化也应该像代码变化一样能 diff、能回滚、能解释。

最后是规则写法。

“请谨慎”这类空泛表达帮助有限。更好的规则应该能落到行为:

  1. 修改前先读目标文件和相邻测试。
  2. 多文件改动先列范围。
  3. 写入后运行最小相关验证。
  4. 验证失败时保留失败摘要和下一步假设。
  5. 涉及删除、迁移、批量替换时先说明风险。

这种规则才能真正改变 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 如何把一次任务里的经验,变成后续任务可以召回的记忆?

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐