第 4 章

服务层级与速率限制:Priority / Standard / Batch 三层 SLA 完全指南

第四章:Prompt 设计基础:角色、指令、上下文的黄金结构

4.1 为什么 Prompt 工程值得系统学习

Prompt 不是"写几句话让 AI 干活"那么简单。一个设计良好的 prompt 可以让 Haiku 的表现接近 Sonnet,让 Sonnet 的输出质量接近 Opus。反之,一个糟糕的 prompt 会让即使是 Opus 也输出不稳定、不可靠的结果。

Anthropic 的内部研究表明,prompt 质量对输出结果的影响可以超过模型选择的影响。在许多任务上,花 2 小时优化 prompt 带来的收益,比花更多钱升级模型更显著。

本章介绍 Claude 最重要的 prompt 设计模式,重点是黄金三角结构:角色(Role)、指令(Instructions)、上下文(Context)。

4.2 System Prompt 的作用与优先级

System Prompt vs User Message

Claude 的对话结构包含两个主要输入位置:

[System Prompt]
    ↓ 更高优先级,定义全局行为
[User Messages]
    ↓ 较低优先级,具体任务请求

System prompt 的优先级高于 user message。如果 system prompt 说"只用中文回复",而用户用英文提问,Claude 会用中文回答。这个机制允许开发者建立不可被用户覆盖的行为边界。

System Prompt 的典型内容

一个结构良好的 system prompt 通常包含:

1. 角色定义(你是谁)
2. 核心指令(你应该做什么 / 不应该做什么)
3. 输出格式要求
4. 处理边界情况的规则
5. 示例(可选但有效)

System Prompt 的长度考量

System prompt 会被计入每次请求的输入 token。对于高频请求,一个 2000 token 的 system prompt 意味着每天额外消耗大量 token。

示例:
  system prompt = 2000 tokens
  日请求量 = 10,000 次
  每日额外成本(Sonnet)= 2000 * 10000 / 1,000,000 * $3 = $60/天

建议:system prompt 应该足够完整,但不要包含冗余信息。
通常 500-1500 tokens 是合理范围,复杂系统可到 3000 tokens。

4.3 角色设定(Role):最被低估的技巧

为什么角色设定有效

角色设定(Persona Engineering)是最简单但常被忽视的 prompt 技巧。它的效果来自于模型训练中的关联性:当 Claude 被告知"你是一位资深安全工程师",它会调用训练数据中与安全工程师相关的知识模式、表达风格和思维框架。

这不是魔法,而是概率激活:描述一个专家的特征,会让模型输出在那个专家的"分布"上采样。

有效角色 vs 无效角色

无效角色(过于泛泛)

你是一个有帮助的 AI 助手。

这等于没说,因为这是 Claude 的默认行为。

有效角色(具体且有行为导向)

你是 TechCorp 的高级后端工程师,有 10 年分布式系统经验。
你专注于 Python 和 Go 生态。当分析代码问题时,你总是:
- 先识别根本原因,而不仅仅是症状
- 考虑生产环境中的扩展性影响
- 给出具体的、可执行的建议,而不是泛泛的原则
- 当有多种解决方案时,解释每种方案的权衡取舍

角色模板:不同场景的参考

技术助手模板

你是 [公司名] 的技术文档工程师,专长是 [技术领域]。
你的目标用户是 [目标受众描述]。
你总是提供 [具体行为特征]。
你从不 [禁止行为]。

内容分析师模板

你是一位专业的 [领域] 分析师,有 [经验特征]。
你的分析风格是 [风格描述:如 数据驱动、简洁直接]。
你擅长 [特定能力],在 [特定情况] 下会 [特定行为]。

客服代表模板

你是 [品牌名] 的客服专家 [名字]。
你友善、专业,对 [产品/服务] 有深入了解。
你总是先理解客户的核心问题,然后提供精确的解决方案。
当无法解决时,你会明确说明并给出升级路径。

4.4 指令设计(Instructions):精确胜于冗长

三种指令表达方式的对比

方式一:描述性指令(效果一般)

"用专业的语气回答问题,提供有帮助的信息。"

太模糊,Claude 会按自己的理解执行,结果不一致。

方式二:禁止列表(效果中等)

"不要使用技术术语,不要太长,不要跑题。"

告诉模型"不做什么"比"做什么"更难执行。

方式三:正向、具体的行为指令(效果最佳)

"每个回复必须包含:
1. 一句话直接回答问题
2. 最多 3 个要点的解释
3. 一个实际应用的例子
总长度不超过 300 字。"

指令优先级排序技巧

当有多条指令时,Claude 需要知道哪条优先级更高。显式的优先级声明比隐式期望更有效:

你的响应规则(按优先级排序):

最高优先级:
- 永远不提供可能造成安全风险的信息
- 永远不在未经请求时收集用户个人信息

高优先级:
- 响应必须与用户问题直接相关
- 使用用户的语言(中文提问用中文回答)

普通优先级:
- 使用 Markdown 格式化长响应
- 在适当时提供代码示例

处理边界情况

好的 prompt 预见到边界情况并给出明确指导:

SYSTEM_PROMPT = """你是一个代码审查助手。

对于每个代码片段,你应该:
1. 识别 bugs 和逻辑错误
2. 指出性能问题
3. 提供改进建议

边界情况处理:
- 如果代码少于 5 行且明显正确:直接确认,不需要过度分析
- 如果代码包含安全漏洞(SQL 注入、XSS 等):在响应开头用 [安全警告] 标注
- 如果代码语言无法识别:询问用户是什么语言
- 如果提交的不是代码:礼貌地告知你只处理代码审查请求

响应格式:

问题概述: [一句话总结]

发现的问题:

  1. [问题描述 + 所在行号]

改进后的代码: [代码块]

解释: [改进说明]

"""

4.5 上下文提供(Context):给 Claude 它需要的信息

信息完整性原则

Claude 只知道你告诉它的。如果你的请求依赖于某个背景信息,但你没有提供,Claude 要么会猜测(风险),要么会询问(效率低)。

低效的上下文提供

"修复这个函数的 bug"

Claude 不知道这个函数的预期行为是什么,也不知道现有的 test case 是什么。

高效的上下文提供

"修复以下函数的 bug。

预期行为:将一个嵌套字典扁平化,key 用点号分隔。
例如:{"a": {"b": 1}} → {"a.b": 1}

当前代码:
[代码]

失败的测试用例:
flatten({"a": {"b": {"c": 1}}}) 返回 {"a.b.c": 1},但实际返回 {"a": {"b.c": 1}}
"

上下文位置策略

在超长 prompt 中,信息放置位置影响 Claude 的注意力分配:

最佳实践:
┌────────────────────────────────────┐
│  System Prompt(全局规则)          │  ← 始终被关注
├────────────────────────────────────┤
│  关键约束 / 最重要信息              │  ← 放在消息开头
├────────────────────────────────────┤
│  背景信息 / 参考资料                │  ← 中间位置(可能被弱化)
├────────────────────────────────────┤
│  具体任务要求                      │  ← 放在消息结尾(最近记忆)
└────────────────────────────────────┘

结构化上下文的标签技巧

使用 XML 标签来组织长上下文,让 Claude 更容易区分不同类型的信息:

def build_analysis_prompt(
    document: str,
    requirements: list[str],
    examples: list[dict]
) -> str:
    examples_text = "\n".join(
        f"<example>\n<input>{e['input']}</input>\n<output>{e['output']}</output>\n</example>"
        for e in examples
    )
    
    requirements_text = "\n".join(f"- {r}" for r in requirements)
    
    return f"""请分析以下文档并提取关键信息。

<requirements>
{requirements_text}
</requirements>

<examples>
{examples_text}
</examples>

<document>
{document}
</document>

根据以上要求和示例,请提取文档中的关键信息。"""

4.6 Few-Shot 示例:让模型"看见"你期望的结果

为什么 Few-Shot 有效

Few-shot 示例比抽象指令更有效,因为它通过演示而不是描述来传达期望。对于格式要求复杂的任务,几个好的示例往往抵得上数百字的格式说明。

Few-Shot 示例的构建原则

  1. 代表性:示例应覆盖任务的典型场景,不只是最简单的情况
  2. 多样性:包含边缘情况和不同类型的输入
  3. 正确性:示例中的输出必须是你期望的准确格式
  4. 数量:3-5 个通常足够;过多示例会占用大量 context 窗口
SYSTEM_PROMPT = """你是一个情感分类器。分析文本的情感并返回结构化结果。

示例:

输入: "这家餐厅的服务太慢了,等了40分钟才上菜,非常失望。"
输出: {"sentiment": "negative", "intensity": 0.8, "aspects": ["service", "wait_time"], "confidence": 0.95}

输入: "还行吧,没什么特别的,但也没有太大问题。"
输出: {"sentiment": "neutral", "intensity": 0.3, "aspects": ["overall"], "confidence": 0.85}

输入: "食物非常美味!特别是他们的招牌菜,强烈推荐!"
输出: {"sentiment": "positive", "intensity": 0.9, "aspects": ["food", "recommendation"], "confidence": 0.97}

规则:
- sentiment: "positive" | "negative" | "neutral" | "mixed"
- intensity: 0.0-1.0(情感强度)
- aspects: 文本涉及的方面列表
- confidence: 0.0-1.0(分类置信度)

只返回 JSON,不要其他内容。"""

动态 Few-Shot:相似性检索

对于复杂任务,可以动态选择与当前输入最相似的示例:

from anthropic import Anthropic

# 简化版本:实际应用中会用向量相似性检索
def select_relevant_examples(
    query: str, 
    example_pool: list[dict], 
    n: int = 3
) -> list[dict]:
    """
    从示例池中选择最相关的 n 个示例
    简化实现:按关键词匹配;生产中应使用向量检索
    """
    query_words = set(query.lower().split())
    
    scored = []
    for example in example_pool:
        example_words = set(example["input"].lower().split())
        overlap = len(query_words & example_words)
        scored.append((overlap, example))
    
    scored.sort(key=lambda x: x[0], reverse=True)
    return [ex for _, ex in scored[:n]]


def dynamic_few_shot_prompt(
    task_description: str,
    example_pool: list[dict],
    current_input: str
) -> str:
    relevant_examples = select_relevant_examples(current_input, example_pool)
    
    examples_text = "\n\n".join(
        f"输入: {ex['input']}\n输出: {ex['output']}"
        for ex in relevant_examples
    )
    
    return f"""{task_description}

{examples_text}

输入: {current_input}
输出:"""

4.7 思维链提示(Chain-of-Thought)

何时需要 CoT

思维链提示(CoT)通过让模型"展示推理过程"来提高复杂任务的准确性。在以下场景中显著有效:

CoT 的几种实现方式

方式一:直接指令(最简单)

"请逐步思考,解释你的推理过程,然后给出答案。"

方式二:结构化推理框架

"使用以下步骤分析问题:
1. 问题分解:将问题拆分为子问题
2. 信息识别:找出已知条件和未知量
3. 方法选择:确定解决路径
4. 执行与验证:逐步执行并检验
5. 结论:给出最终答案"

方式三:XML 标签结构(最清晰)

def create_reasoning_prompt(problem: str) -> list[dict]:
    """创建带思维链的对话"""
    return [
        {
            "role": "user",
            "content": f"""请分析以下问题。在 <thinking> 标签中展示你的推理过程,
在 <answer> 标签中给出最终答案。

<problem>
{problem}
</problem>"""
        }
    ]

# 使用示例
client = Anthropic()
messages = create_reasoning_prompt(
    "一个班有 32 名学生,其中 3/8 是女生。"
    "如果又转来了 4 名女生,现在女生占全班的百分比是多少?"
)

response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    messages=messages
)

# Claude 会在 <thinking> 中展示计算过程,在 <answer> 中给出答案
print(response.content[0].text)

CoT 与 Extended Thinking 的区别

CoT(Prompt 层面):
- 通过 prompt 指令让模型在响应中展示推理
- 推理内容在输出中可见
- 适用于所有模型

Extended Thinking(API 层面):
- 模型在生成最终响应前进行内部推理
- 推理 tokens 单独计费(输出价格)
- 仅 Opus 和 Sonnet 支持
- 通常效果更好,但成本更高

4.8 黄金结构完整模板

将前述所有要素组合成一个完整的 system prompt 模板:

def build_production_system_prompt(
    role_name: str,
    expertise: str,
    audience: str,
    core_behaviors: list[str],
    output_format: str,
    constraints: list[str],
    examples: list[dict] = None
) -> str:
    """
    生产级 system prompt 构建器
    """
    behaviors_text = "\n".join(f"{i+1}. {b}" for i, b in enumerate(core_behaviors))
    constraints_text = "\n".join(f"- {c}" for c in constraints)
    
    examples_section = ""
    if examples:
        examples_lines = []
        for ex in examples:
            examples_lines.append(f"输入: {ex['input']}\n期望输出: {ex['output']}")
        examples_section = f"\n## 示例\n\n" + "\n\n".join(examples_lines)
    
    return f"""## 角色

你是 {role_name},{expertise}。你的用户是 {audience}。

## 核心行为

{behaviors_text}

## 输出格式

{output_format}

## 约束

{constraints_text}{examples_section}
"""


# 实际使用示例
system = build_production_system_prompt(
    role_name="CodeReviewBot",
    expertise="专注于 Python 和 TypeScript 的代码质量分析师",
    audience="中级到高级的软件工程师",
    core_behaviors=[
        "识别 bugs、性能问题和安全漏洞",
        "提供具体的、可执行的改进建议",
        "解释每个问题的潜在影响",
        "区分必须修复的问题和建议性改进"
    ],
    output_format="""使用以下结构:
**严重问题** [需要立即修复]
**建议改进** [最佳实践,非强制]
**改进后代码** [如适用]""",
    constraints=[
        "只评审代码,不评论开发者的技术水平",
        "如果代码没有问题,直接说'代码看起来没有问题,以下是一些可选的优化'",
        "每次响应不超过 600 字"
    ]
)

import anthropic
client = anthropic.Anthropic()

response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    system=system,
    messages=[{
        "role": "user",
        "content": """请审查以下 Python 函数:

```python
def get_user(user_id):
    conn = sqlite3.connect('users.db')
    cursor = conn.cursor()
    query = f"SELECT * FROM users WHERE id = {user_id}"
    cursor.execute(query)
    return cursor.fetchone()
```"""
    }]
)

print(response.content[0].text)

4.9 常见 Prompt 反模式

反模式一:模糊的期望

❌ 不好: "生成一些营销文案"
✅ 好: "为 XX 产品(面向 25-35 岁都市白领)生成 3 个微信朋友圈文案,
     每条 50-80 字,强调省时省力的核心价值,语气活泼不做作"

反模式二:矛盾的指令

❌ 不好: "回答要详细,但要简短,要专业,但要通俗易懂"
✅ 好: "回答面向非技术人员,使用日常比喻解释技术概念,
     不超过 150 字,确保关键术语有解释"

反模式三:依赖 Claude 的默认行为

❌ 不好: "帮我写一封道歉邮件"(没有指定对象、场景、语气)
✅ 好: "帮我写一封道歉邮件,对象是我的客户(正式商务关系),
     原因是项目延期 2 周,语气诚恳但不过度卑微,
     需要包含补救措施,200-300 字"

反模式四:过长的"条款和条件"式 prompt

每条指令都需要 Claude 的注意力资源。超过 30 条规则的 system prompt 通常会导致规则互相干扰,总体遵循率反而下降。

最佳实践:识别最关键的 5-10 条规则,把其余的整合或删除。


小结

本章系统梳理了 Claude prompt 设计的黄金结构:

  1. 角色(Role):具体的专家描述激活相关的知识模式,比"有帮助的助手"有效得多
  2. 指令(Instructions):正向、具体的行为指令优于模糊描述和禁止列表;显式的优先级排序减少冲突
  3. 上下文(Context):提供足够的背景信息;重要信息放在开头和结尾;用 XML 标签组织长上下文
  4. Few-Shot 示例:演示比描述更有效,3-5 个覆盖典型情况的示例往往就足够
  5. 思维链(CoT):对复杂推理任务显著有效;结合 XML 标签获得更清晰的结构

下一章将转向 token 的经济学:计费机制、上下文窗口的限制,以及如何在超长文本任务中保持质量和控制成本。

本章评分
4.5  / 5  (109 评分)

💬 留言讨论