Claude Code Harness 第6章:通过提示词引导行为
Claude Code Harness 第6章:通过提示词引导行为
提示词工程(Prompt Engineering)常被视为一门"黑魔法",但通过对 Claude Code 源码的深入分析,我们发现这实际上是一门精确的工程学科。本章将揭示如何通过结构化的提示词设计,在模型生成的概率空间中竖起精确的围栏,引导 AI 表现出符合期望的行为模式。
6.1 行为引导的概率空间本质
大语言模型的输出是一个在巨大概率分布上的采样过程。每一个 token 的生成都涉及从数千亿可能的选择中进行概率采样。系统提示词中的行为指令,本质上是在这个高维概率空间中设定边界条件——提高期望行为的概率密度,同时压低不期望行为的概率。
然而,围栏的措辞方式决定了它是一堵坚固的墙还是一条模糊的界线。通过分析 Claude Code 的提示词源码,我们可以识别出一套经过实践验证的行为引导模式语言。这些模式之所以有效,不仅因为它们传达了正确的信息,更因为它们的结构设计与模型的注意力机制和指令遵循特性高度契合。
6.2 模式一:极简主义指令
核心原理
大语言模型在训练过程中习得了"乐于助人"的倾向,这种倾向在代码生成场景中往往表现为过度工程——添加不必要的抽象、过度的错误处理、冗余的文档注释。极简主义指令通过明确的反面约束,将这种倾向限制在任务实际需要的范围内。
实现机制
让我们看看 Claude Code 如何在源码中实现这一模式。在 src/constants/prompts.ts 中,我们可以看到这样的代码结构:
const codeStyleGuidelines = [
// 场景化的反面约束
`Don't add features, refactor code, or make "improvements" beyond what was asked.
A bug fix doesn't need surrounding code cleaned up.
A simple feature doesn't need extra configurability.`,
// 具体的数量锚点
`Three similar lines of code is better than a premature abstraction.`,
// 系统边界 vs 内部调用
`Don't add error handling for scenarios that can't happen.
Trust internal code and framework guarantees.
Only validate at system boundaries (user input, external APIs).`
]关键设计要素:
数量锚点:"三行代码优于过早抽象"给出了具体的判断基准,打破了模型默认的 DRY(Don't Repeat Yourself)启发式。
场景化解耦:将"bug 修复"和"代码清理"明确解绑,避免模型在修复 bug 时自动触发重构行为。
边界分类:区分系统边界(需要验证)和内部调用(信任框架保证),减少过度防御性编程。
为什么有效
graph TB
A[抽象指令
保持代码简洁] --> B{模型解读}
B --> C[解读1: 删除注释]
B --> D[解读2: 使用短变量名]
B --> E[解读3: 减少嵌套]
F[反面指令
不要添加超出要求的功能] --> G{模型对照}
G --> H[这是不是在
添加功能?]
H -->|是| I[禁止]
H -->|否| J[允许]
style F fill:#96CEB4
style I fill:#96CEB4反面定义的优势:
- 禁止的边界比允许的边界更清晰
- 模型可以将每个生成的 token 与"是否在做 X"进行对照
- 避免了在无数种"保持简洁"方式中的犹豫
可复用模板
// 极简主义指令模板
{
principle: "不要在任务范围外添加 {功能/重构/改进}",
scenarios: [
"{任务类型A} 不需要 {常见过度工程行为A}",
"{任务类型B} 不需要 {常见过度工程行为B}"
],
threshold: "{N} 行重复代码优于过早抽象",
boundary: "只在 {明确边界条件} 时才 {额外行动}"
}6.3 模式二:渐进式升级
核心原理
LLM 在面对失败时有两个极端倾向:要么立即放弃求助,要么无限重试相同操作。渐进式升级模式通过定义一个三阶段协议——诊断、调整、求助——在两个极端之间开辟出一条合理的中间路径。
实现机制
在 src/constants/prompts.ts 中,我们可以看到这样的失败处理协议:
const failureHandlingProtocol = `
If an approach fails, diagnose why before switching tactics —
read the error, check your assumptions, try a focused fix.
Don't retry the identical action blindly,
but don't abandon a viable approach after a single failure either.
Escalate to the user only when you're genuinely stuck after investigation,
not as a first response to friction.
`三阶段协议的流程:
graph TB
A[操作失败] --> B{阶段1: 诊断}
B --> C[读取错误信息]
B --> D[检查假设]
B --> E[尝试有针对性的修复]
C --> F{阶段2: 调整}
D --> F
E --> F
F --> G[基于诊断的修改]
G --> H{仍然失败?}
H -->|否| I[任务完成]
H -->|是| J{阶段3: 求助}
J --> K{真正困住?}
K -->|是| L[升级到用户]
K -->|否| M[返回阶段1]
style A fill:#FF6B6B
style I fill:#96CEB4
style L fill:#FFEAA7双边约束的张力:
const bidirectionalConstraints = {
lowerBound: "不要盲目重试相同操作",
upperBound: "不要一次失败后就放弃可行方案",
// 迫使模型在两个极端之间寻找中间路径
}为什么有效
阶段映射 Chain-of-Thought:诊断 → 调整 → 求助的顺序与模型的推理链自然对齐
求助作为最后手段:将"问用户"设定为最后选项,减少了不必要的交互中断
显式推理要求:迫使模型在每次失败后进行推理,而非机械地重试或放弃
Git 操作的安全协议
在 src/tools/BashTool/prompt.ts 中,我们可以看到针对高风险操作的特殊处理:
const gitSafetyProtocol = `
Before running destructive operations
(e.g., git reset --hard, git push --force),
consider whether there is a safer alternative that achieves the same goal.
Only use destructive operations when they are truly the best approach.
`这种设计要求模型在执行高风险操作前完成一个安全性评估推理步骤,而非简单地禁止这些操作。
可复用模板
// 渐进式升级模板
{
phase1_diagnosis: "当 {操作} 失败时,先 {诊断步骤}",
phase2_adjustment: "不要盲目重试,也不要一次失败就放弃。基于诊断结果 {调整动作}",
phase3_escalation: "只在 {升级条件} 时才 {求助动作}",
note: "升级是最后手段,而非遇到阻力的第一反应"
}6.4 模式三:可逆性意识
核心原理
这是 Claude Code 提示词工程中最复杂的模式之一。它不是简单列举"危险操作",而是建立一个风险评估框架,教会模型使用"可逆性"和"影响范围"两个维度自主评估操作风险。
实现机制
在 src/constants/prompts.ts 中,我们可以看到这样的框架设计:
const riskAssessmentFramework = `
Carefully consider the reversibility and blast radius of actions.
Generally you can freely take local, reversible actions like editing files or running tests.
But for actions that are hard to reverse, affect shared systems beyond your local environment,
or could otherwise be risky or destructive, check with the user before proceeding.
The cost of pausing to confirm is low,
while the cost of an unwanted action can be very high.
`2x2 决策矩阵:
quadrantChart
title Risk Assessment Matrix
x-axis Small Impact --> Large Impact
y-axis Hard to Reverse --> Reversible
quadrant-1 Inform and Execute
quadrant-2 Free to Execute
quadrant-3 Confirm then Execute
quadrant-4 Must Confirm
Edit files: [0.25, 0.85]
Run tests: [0.30, 0.80]
Create branch: [0.65, 0.75]
Delete files: [0.25, 0.25]
Force push: [0.85, 0.15]
Drop table: [0.90, 0.10]Git 安全协议的精致设计
在 src/tools/BashTool/prompt.ts 中,Git 操作的安全协议展现了可逆性意识模式的最高水准:
const gitSafetyProtocol = `
Git Safety Protocol:
- NEVER update the git config
- NEVER run destructive git commands (push --force, reset --hard)
unless the user explicitly requests these actions.
- NEVER skip hooks (--no-verify) unless the user explicitly requests it
- CRITICAL: Always create NEW commits rather than amending, unless the user
explicitly requests a git amend. When a pre-commit hook fails, the commit
did NOT happen — so --amend would modify the PREVIOUS commit.
`设计要点:
NEVER 大写:使用全大写强调绝对禁止,提高注意力权重
精确豁免:每条 NEVER 规则都附带明确的
unless the user explicitly requests豁免条件CRITICAL + 因果解释:对于最微妙的 amend 规则,不仅标记 CRITICAL,还解释了为什么(hook 失败时 commit 尚未发生,amend 会修改前一个 commit)
授权作用域
const authorizationScope = `
A user approving an action once does NOT mean that they approve it in all contexts.
Authorization stands for the scope specified, not beyond.
Match the scope of your actions to what was actually requested.
`这防止了 LLM 的危险倾向:从单次许可推广到通用许可。
graph TB
A[用户批准 git push
在 feature 分支] --> B{模型推理}
B --> C[错误泛化]
C --> D[所有上下文
都批准了 push]
D --> E[不经确认就
push to main]
B --> F[正确作用域限定]
F --> G[授权仅限于
指定范围]
G --> H[push to main
仍需确认]
style E fill:#FF6B6B
style H fill:#96CEB4为什么有效
维度分析替代枚举:教会模型使用两个维度自主评估,而非列举所有危险操作
NEVER + unless 精确豁免:避免了模型在模糊地带的"创造性解读"
因果解释促进泛化:解释"为什么"让模型能在新场景中推导规则精神
可复用模板
// 可逆性意识模板
{
framework: {
dimensions: ["可逆性", "影响范围"],
matrix: {
reversible_lowImpact: "自由执行",
reversible_highImpact: "告知后执行",
irreversible_lowImpact: "确认后执行",
irreversible_highImpact: "强制确认"
}
},
prohibitions: {
format: "NEVER {危险操作} unless {明确豁免条件}",
critical_rules: [
"{操作1}, because {因果解释}",
"{操作2}, because {因果解释}"
]
},
authorization: {
principle: "一次批准不代表在所有上下文批准",
scope: "授权仅限于指定的范围"
}
}6.5 模式四:工具偏好引导
核心原理
Claude Code 提供了丰富的专用工具(Read、Edit、Write、Glob、Grep),但模型的训练数据中充斥着 Unix 命令。工具偏好引导模式在工具描述的最早位置插入重定向指令,截获模型的默认工具选择路径。
实现机制
在 src/tools/BashTool/prompt.ts 中,我们可以看到这样的前置拦截设计:
export function getSimplePrompt(): string {
const toolPreferenceItems = [
`File search: Use Glob (NOT find or ls)`,
`Content search: Use Grep (NOT grep or rg)`,
`Read files: Use Read (NOT cat/head/tail)`,
`Edit files: Use Edit (NOT sed/awk)`,
`Write files: Use Write (NOT echo redirection)`,
]
return `
IMPORTANT: Avoid using this tool to run find, grep, cat, head, tail, sed, awk, or echo commands,
unless explicitly instructed or after you have verified that a dedicated tool cannot accomplish your task.
Instead, use the appropriate dedicated tool:
${toolPreferenceItems.map(item => `- ${item}`).join('\n')}
`
}三层设计:
graph TB
A[模型考虑使用 Bash] --> B[读取工具描述]
B --> C[首先看到 IMPORTANT]
C --> D[看到 Use Grep NOT grep]
D --> E[看到用户体验论证]
E --> F{重新决策}
F -->|应该用专用工具| G[使用 Grep 工具]
F -->|确实需要 Bash| H[使用 Bash 工具]
style C fill:#FFEAA7
style D fill:#4ECDC4
style G fill:#96CEB4位置优先:重定向指令出现在 Bash 工具描述的开头,是模型首先遇到的约束
NOT 括号对照:
Use Grep (NOT grep or rg)创造直接二选一对照用户体验论证:
this will provide a much better experience给出遵循理由
冗余强化策略
同样的映射表在两个位置重复出现:
- 系统提示词(
src/constants/prompts.ts) - Bash 工具描述(
src/tools/BashTool/prompt.ts)
graph TB
A[模型生成响应] --> B{需要读取文件}
B --> C[路径1: 系统提示词]
B --> D[路径2: 工具描述]
C --> E[看到: Use Read NOT cat]
D --> F[看到: Use Read NOT cat]
E --> G[使用 Read 工具]
F --> G
style E fill:#4ECDC4
style F fill:#45B7D1
style G fill:#96CEB4这种冗余强化确保无论模型的注意力路径从哪里经过,都会遇到这个约束。
条件适配
const embedded = hasEmbeddedSearchTools()
const toolPreferenceItems = [
...(embedded ? [] : [
`File search: Use Glob (NOT find or ls)`,
`Content search: Use Grep (NOT grep or rg)`,
]),
// ...
]当内部构建版本将 find/grep 映射到嵌入式 bfs/ugrep 时,跳过相关重定向,避免自相矛盾。
为什么有效
截获决策路径最早点:在模型选择工具前进行干预
二选一消除歧义:将开放性选择转化为二元判断
冗余覆盖注意力盲区:对抗模型在长上下文中的注意力衰减
可复用模板
// 工具偏好引导模板
{
interception: {
location: "工具描述开头",
format: "IMPORTANT: Avoid using {通用工具} to run {命令列表}",
redirection: "Instead, use the appropriate dedicated tool"
},
mapping: {
format: "{操作}: Use {专用工具} (NOT {通用命令})",
items: [
"File search: Use Glob (NOT find)",
"Content search: Use Grep (NOT grep)"
]
},
reinforcement: {
strategy: "在多个位置冗余放置相同约束",
locations: ["系统提示词", "工具描述"]
}
}6.6 模式五:Agent 委托指引
核心原理
当 AI 系统可以派生子 Agent 时,新的失败模式随之出现:无限递归派生、上下文污染、结果捏造。Agent 委托指引模式通过精确的委托规则防止这些失败。
Fork vs Fresh Agent
在 src/tools/AgentTool/prompt.ts 中,我们可以看到这样的选择框架:
const whenToForkSection = `
## When to fork
Fork yourself (omit subagent_type) when the intermediate tool output isn't
worth keeping in your context. The criterion is qualitative —
"will I need this output again" — not task size.
- Research: fork open-ended questions
- Implementation: fork work that requires more than a couple of edits
`关键洞察:判断标准不是任务大小,而是"我以后还需要看这些输出吗"。
"Don't Peek" 规则
const dontPeekRule = `
**Don't peek.** The tool result includes an output_file path — do not Read
or tail it unless the user explicitly asks for a progress check.
Reading the transcript mid-flight pulls the fork's tool noise into your
context, which defeats the point of forking.
`graph TB
A[主 Agent 派出 fork] --> B[fork 写入输出文件]
B --> C[主 Agent 看到文件路径]
C --> D{temptation}
D -->|Don't peek| E[等待完成通知]
D -->|读取中间结果| F[工具噪声进入上下文]
E --> G[保持上下文干净]
F --> H[浪费 token 预算]
style G fill:#96CEB4
style F fill:#FF6B6B"Don't peek" 用两个日常词汇精确描述了复杂的技术约束。
"Don't Race" 规则
const dontRaceRule = `
**Don't race.** After launching, you know nothing about what the fork found.
Never fabricate or predict fork results in any format —
not as prose, summary, or structured output.
`防止主 Agent 在 fork 返回前"预测"结果(这种预测本质上是幻觉)。
身份覆盖机制
const forkIdentityOverride = `
STOP. READ THIS FIRST.
You are a forked worker process. You are NOT the main agent.
RULES (non-negotiable):
1. Your system prompt says "default to forking." IGNORE IT — that's for the parent.
2. Do NOT spawn sub-agents; execute directly.
3. Do NOT converse, ask questions, or suggest next steps
`graph TB
A[fork 子 Agent 启动] --> B[继承父系统提示词]
B --> C[看到: default to forking]
C --> D{temptation}
D -->|无干预| E[尝试再次 fork]
E --> F[无限递归]
D -->|身份覆盖| G[STOP. READ THIS FIRST]
G --> H[You ARE the fork]
H --> I[IGNORE default to forking]
I --> J[直接执行任务]
style F fill:#FF6B6B
style J fill:#96CEB4承认矛盾并显式覆盖比假设模型会正确处理矛盾更可靠。
为什么有效
拟人化动词建立直觉:"Don't peek"和"Don't race"比技术描述更易记忆
穷举式格式禁止:
not as prose, summary, or structured output封堵规避路径显式矛盾解决:直接指出并覆盖父提示词中的矛盾指令
可复用模板
// Agent 委托指引模板
{
forkCriteria: {
standard: "中间输出不值得保留在上下文",
question: "我以后还需要看这些输出吗?",
scenarios: ["研究: fork 开放性问题", "实现: fork 多编辑任务"]
},
postForkBehavior: {
dontPeek: {
rule: "不要读取子 Agent 中间输出",
reason: "避免上下文污染"
},
dontRace: {
rule: "不要在结果返回前预测",
formats: ["prose", "summary", "structured output"],
response: "给出状态信息,而非猜测"
}
},
identityOverride: {
marker: "STOP. READ THIS FIRST",
declaration: "你是 fork 工作进程,不是主 Agent",
override: "父提示词中的 {指令} 不适用于你"
}
}6.7 模式六:数值锚定
核心原理
"简洁一些"、"保持简短"这类定性指令几乎没有约束力,因为模型对"简洁"的理解依赖训练数据分布。数值锚定通过给出具体数字,将主观判断转化为可度量的约束。
实现机制
在 src/constants/prompts.ts 中,我们可以看到这样的实验性设计:
// 数值锚定 - 研究显示相比定性 "be concise" 减少了约 1.2% output token
const numericLengthAnchors = process.env.USER_TYPE === 'ant' ? [
systemPromptSection(
'numeric_length_anchors',
() => `
Length limits: keep text between tool calls to ≤25 words.
Keep final responses to ≤100 words unless the task requires more detail.
`
)
] : []1.2% token 削减——这个数字听起来不多,但考虑到每天的处理量,成本节约相当可观。更重要的是,这是纯提示词优化(零代码变更)获得的。
硬约束 vs 软约束:
const anchorTypes = {
hard: "≤25 词(工具调用间)", // 硬性约束
soft: "≤100 词(最终响应)unless..." // 带豁免条件的软约束
}A/B 测试方法论
// ant-only 灰度部署
const rollout = {
condition: "USER_TYPE === 'ant'",
metric: "-1.2% output tokens",
next_step: "测量质量影响后决定是否推广"
}graph TB
A[新的提示词优化] --> B[ant-only 灰度]
B --> C[收集数据]
C --> D{质量影响可接受?}
D -->|是| E[推广到外部用户]
D -->|否| F[回滚或修改]
F --> A
style B fill:#FFEAA7
style C fill:#4ECDC4
style E fill:#96CEB4
style F fill:#FF6B6B这种模式在整个提示词中反复出现:先对内部用户开放,收集数据后再决定是否推广。
为什么有效
消除主观解释空间:"25 words"无歧义,"concise"有歧义
锚定效应:提示词中的数字成为输出长度的参考点
硬约束 + 软豁免:默认遵守数字,但在合理情况下可突破
可复用模板
// 数值锚定模板
{
anchors: {
hard: {
constraint: "{输出类型} 保持在 ≤{N} 词以内",
example: "工具调用间文字 ≤25 词"
},
soft: {
constraint: "{输出类型} 保持在 ≤{N} 词以内,除非 {豁免条件}",
example: "最终响应 ≤100 词,除非任务需要更多细节"
}
},
abTesting: {
strategy: "先对小范围用户启用",
metrics: ["token 消耗", "质量影响", "用户满意度"],
decision: "基于数据决定是否推广"
}
}6.8 六种模式的对比与总结
| 模式 | 核心思想 | 关键技术 | 效果度量 |
|---|---|---|---|
| 极简主义指令 | 反面定义 > 正面描述 | 数量锚点、场景化解耦 | 减少过度工程 |
| 渐进式升级 | 双边约束创造中间路径 | 三阶段协议、诊断优先 | 避免盲目重试或过早放弃 |
| 可逆性意识 | 维度分析替代枚举 | 2x2 矩阵、NEVER+unless | 精确风险评估 |
| 工具偏好引导 | 截获决策路径最早点 | NOT 括号对照、冗余强化 | 提升专用工具使用率 |
| Agent 委托指引 | 拟人化动词建立直觉 | 穷举式禁止、身份覆盖 | 防止递归和污染 |
| 数值锚定 | 精确数字消除模糊 | 硬约束+软豁免、A/B 测试 | 1.2% token 削减 |
跨模式设计原则
mindmap root((行为引导
模式)) 精确性 具体数字 反面案例 因果解释 结构化 总则+例子 三阶段协议 2x2 矩阵 冗余强化 多位置重复 双边约束 显式矛盾解决 灰度部署 ant-only 实验 数据驱动决策 A/B 测试
核心原则:
- 反面定义 > 正面描述:"不要做 X"比"做 Y"更易遵循
- 具体例子校准抽象规则:例子是规则的校准点
- 因果解释促进泛化:解释"为什么"让规则超越字面意思
- 冗余是刻意的:对抗注意力衰减的工程手段
- 灰度部署是工程的一部分:提示词修改也需要 A/B 测试
6.9 实践建议
基于这六种模式,以下是可直接应用的实践建议:
建议 1:反面约束优先
// ❌ 效果差
"生成简洁的代码"
// ✅ 效果好
"不要添加超出要求的功能。Bug 修复不需要清理周围代码。"建议 2:三阶段失败处理
const failureProtocol = `
诊断 → 调整 → 求助
1. 先理解错误原因
2. 基于诊断尝试有针对性的修复
3. 只在真正困住时才升级到用户
`建议 3:数字替代形容词
// ❌ 模糊
"保持简洁"
// ✅ 精确
"保持在 ≤25 词以内"
// 效果:Claude Code 数据显示 1.2% token 削减建议 4:工具重定向表
// 在通用工具描述开头插入
const toolRedirect = `
IMPORTANT: Avoid using {通用工具} to run {命令列表}.
Instead, use {专用工具列表}.
`建议 5:可逆性评估框架
// 不列举所有危险操作,而是教会评估维度
const riskFramework = {
dimensions: ["可逆性", "影响范围"],
matrix: {
"可逆+小影响": "自由执行",
"不可逆+大影响": "强制确认"
}
}建议 6:灰度验证
// 新指令先小范围测试
const rollout = {
phase1: "内部用户(ant-only)",
phase2: "收集效果数据",
phase3: "决定是否推广"
}6.10 小结
本章从 Claude Code 源码中提炼了六种经过验证的行为引导模式。这些模式的共同特征是精确性:用具体数字替代模糊形容词、用反面案例替代正面描述、用因果解释替代无条件命令。
这种精确性反映了一个基本事实:大语言模型遵循指令的可靠性,与指令的精确度正相关。
graph TB
A[提示词精确度] -->|正相关| B[指令遵循可靠性]
A1[模糊指令
"保持简洁"] --> C1[模型解读空间大]
C1 --> D1[遵循可靠性低]
A2[精确指令
"≤25 词"] --> C2[模型解读空间小]
C2 --> D2[遵循可靠性高]
style A2 fill:#96CEB4
style D2 fill:#96CEB4关键要点
- 6 种行为引导模式形成了一套完整的提示词工程方法论
- 反面定义比正面描述更有效
- 具体数字打破默认启发式
- 双边约束创造中间路径
- 维度分析替代不可能的枚举
- 拟人化表达建立行为直觉
- 精确锚定消除模糊解释
- 冗余强化覆盖注意力盲区
- 因果解释促进规则泛化
- A/B 测试是提示词工程的一部分
6.11 展望
提示词工程不是一次性的设计任务,而是一个持续的迭代优化过程。通过理解这六种行为引导模式,我们不仅能够更好地使用 Claude Code,更能够将这些原则应用到自己的 AI 系统设计中。
下一章将转向运行时行为的观察与调试:当这些精心设计的提示词在实际对话中遇到意外情况时,系统如何检测、记录和应对。
参考资料
本文基于 harness-engineering-from-cc-to-ai-coding 开源项目。
源码参考:
- Claude Code 提示词:
src/constants/prompts.ts - Bash 工具提示词:
src/tools/BashTool/prompt.ts - Agent 工具提示词:
src/tools/AgentTool/prompt.ts - Fork 子 Agent 配置:
src/tools/AgentTool/forkSubagent.ts
延伸阅读:
- 第5章:系统提示词架构
- 第7章:模型特定调优与 A/B 测试
- 第20章:Agent 集群与多 Agent 编排
作者注:本章深入分析了 Claude Code 的提示词工程实践,提炼出的六种行为引导模式不仅适用于 Claude Code,更可以推广到任何基于 LLM 的 AI 系统设计中。通过精确的、结构化的、可度量的提示词设计,我们可以将 AI 模型的行为引导到期望的范围内。