Skip to content

02.2 XML 标签控制术

单项收益最高的提示词技巧。如果这本手册你只读一章,读这一章。

读完你能做什么:用标签把混乱的需求切成互不干扰的模块,让模型的输出稳定性提升一个量级。


一、为什么是 XML

1.1 训练分布层面的原因

Claude 的训练语料中包含大量结构化标记文本。XML 风格的 <tag>...</tag> 有几个特性使它成为最优的分隔符选择:

  1. 成对闭合,边界无歧义 —— <context></context> 之间的内容是一个明确的语义单元。而 ###--- 这类分隔符只有起点没有终点,模型需要猜测边界在哪。
  2. 标签名本身携带语义 —— <constraints> 不只是一个分隔符,它同时告诉模型"接下来这段是约束"。这是零成本的元信息。
  3. 可嵌套 —— 天然支持层级结构。
  4. 与代码块不冲突 —— 提示词里经常要放代码,XML 标签不会与 Markdown 的代码围栏打架。

1.2 注意力层面的原因

更本质的原因:标签为注意力提供了锚点。

当模型在生成第 500 个 token 时,需要回头确认"用户要求的输出格式是什么"。如果格式要求埋在一段散文的中间,模型需要在整段文本中做软性检索;如果它被 <output_format> 包裹,模型可以直接把注意力聚焦到这个边界清晰的块上。

可观测的效果:在长提示词(>1000 token)中,加标签与不加标签的指令遵守率差异非常明显。提示词越长、约束越多,差距越大。


二、标准标签集

以下是我推荐的标签词汇表。关键不是用哪些词,而是在你的所有提示词中保持一致。

2.1 输入类

标签放什么位置建议
<reference_material> / <document>长参考资料、待分析的文本最前
<code>待分析/修改的代码前部
<data>结构化数据、日志、表格前部
<context>背景事实、项目情况中部
<examples>输入输出示例中部

2.2 指令类

标签放什么
<role>角色设定
<task>一句话说清要做什么
<instructions>详细步骤
<constraints>硬性约束、禁止事项
<criteria>成功标准、验收条件

2.3 输出类

标签放什么
<output_format>输出结构规范
<thinking>要求模型先在此标签内推理
<answer>要求模型把最终答案放这里

2.4 一条重要建议

标签名用小写 + 下划线,语义明确,不要缩写。

text
✅ <output_format>  <reference_material>  <acceptance_criteria>
❌ <fmt>  <ref>  <ac>  <Output_Format>  <OUTPUT-FORMAT>

原因见不变量 1:标签名本身是给模型的信息,缩写削弱了这个信息。


三、从零构建一个高质量提示词

场景:让 Claude 审查一段代码

版本 0:新手写法

text
帮我看看这段代码有没有问题

def process(items):
    result = []
    for i in items:
        result.append(i * 2)
    return result

问题:「有没有问题」这个任务定义太宽 —— 性能问题?风格问题?安全问题?边界情况?模型只能猜,通常给你一份泛泛的清单。

版本 1:加任务定义

text
<task>
审查以下 Python 代码,重点关注边界情况处理和性能。
</task>

<code language="python">
def process(items):
    result = []
    for i in items:
        result.append(i * 2)
    return result
</code>

改进:任务明确了。但输出格式仍不可控。

版本 2:加约束与格式

text
<role>
你是一名 Python 代码审查者,风格务实,只报告真实会导致问题的缺陷。
</role>

<task>
审查以下代码。
</task>

<code language="python">
def process(items):
    result = []
    for i in items:
        result.append(i * 2)
    return result
</code>

<constraints>
- 只报告会在生产环境导致实际问题的缺陷,不要报告纯风格偏好
- 每条发现必须给出:触发条件 + 后果 + 修复代码
- 如果代码没有严重问题,直接说「无严重问题」,不要为了凑数而列举
</constraints>

<output_format>
| 严重度 | 问题 | 触发条件 | 修复 |
| :--- | :--- | :--- | :--- |

严重度取值:Critical / Major / Minor
若无问题,只输出一行:「无严重问题。」
</output_format>

改进:现在输出是可预测的,而且 不要为了凑数 这条约束直接对抗了模型"倾向于给出更多内容显得有帮助"的默认行为。

版本 3:加思考引导(复杂场景)

text
(上略,同版本 2)

<instructions>
按以下顺序进行:
1. 先在 <thinking> 标签内逐行分析代码的执行路径,特别关注:
   - items 为 None 时会发生什么
   - items 为空列表时会发生什么
   - items 包含非数值类型时会发生什么
   - items 极大(百万级)时的内存表现
2. 然后在 <answer> 标签内按 output_format 输出最终结论
</instructions>

改进:把"应该考虑哪些边界"显式化,模型不再依赖自己猜测检查清单。


四、嵌套结构

复杂任务需要层级:

text
<task>
为我们的支付系统设计一个对账模块的重构方案。
</task>

<context>
  <current_architecture>
    对账逻辑散落在 ReconciliationService、PaymentCallbackHandler、
    以及 12 个渠道适配器里。新增渠道需要改 12 个文件。
  </current_architecture>

  <constraints_from_business>
    - 不能停机
    - 迁移期需要新旧逻辑并行跑 2 周做对比验证
  </constraints_from_business>

  <team>
    4 名后端工程师,无专职测试,2 个月窗口期
  </team>
</context>

<deliverables>
  <deliverable id="1">
    目标架构图(用 mermaid 描述)
  </deliverable>
  <deliverable id="2">
    分阶段迁移计划,每个阶段包含:改动文件清单、验证方式、回滚方案
  </deliverable>
  <deliverable id="3">
    风险清单,按「概率 × 影响」排序
  </deliverable>
</deliverables>

<constraints>
- 每个阶段的改动必须能在 1 周内完成并独立上线
- 不引入新的中间件
- 上下文中没有的信息,标注为「需要确认」,不要假设
</constraints>

嵌套的价值<context> 内部的三个子块各自独立。模型在推理「团队规模是否够」时,能精确定位到 <team>,而不必在整段背景里检索。


五、进阶技巧

5.1 用属性携带元信息

text
<code language="python" file="src/payment/reconcile.py" lines="45-120">
...
</code>

<document source="ADR-003" date="2025-11-20" status="superseded">
...
</document>

status="superseded" 这类属性能让模型知道该文档已废弃,避免引用过时信息。

5.2 多文档时用 index 属性

text
<documents>
  <document index="1" title="架构总览">
    ...
  </document>
  <document index="2" title="API 规范">
    ...
  </document>
  <document index="3" title="性能测试报告">
    ...
  </document>
</documents>

<constraints>
引用时必须写明来自 document index 几。
</constraints>

这一招把"模糊引用"变成了"可验证引用" —— 你能立刻检查它引用的是不是真的在那份文档里。这是降低幻觉的关键手段之一,详见 04.3

5.3 强制引文优先(长文档必备)

text
<instructions>
1. 先在 <quotes> 标签内,逐字摘录文档中与问题直接相关的原文片段,
   每条注明来自哪份文档的哪一节。最多 10 条。
2. 然后在 <answer> 标签内,仅基于 <quotes> 中的内容作答。
3. 如果 <quotes> 为空或不足以回答,直接说明「文档中无相关信息」。
</instructions>

为什么有效:它把"从记忆中生成答案"改成了"从上下文中检索再生成"。摘录动作强制模型把注意力真正投向原文,而不是靠对文档主题的模糊印象作答。

5.4 让模型自检

text
<self_check>
输出前,逐项确认:
- [ ] 每个数字是否都能在原文中找到
- [ ] 是否有我推测但未标注为推测的内容
- [ ] 输出格式是否严格符合 output_format
如有任何一项未通过,修正后再输出。
</self_check>

注意:自检不是万能的 —— 模型可能宣称检查通过但实际没有。它的价值在于提高概率,不是提供保证。关键输出仍需人工或程序验证。


六、常见错误

错误后果修复
标签不闭合 <task>...模型无法确定边界,后续内容可能被当作 task 的一部分检查每个 <x> 都有 </x>
标签名不一致(<constraint><constraints> 混用)语义被拆散建立自己的标签表并固定
把所有东西塞进一个 <context>等于没分块拆成语义明确的子块
标签套太深(5 层以上)可读性和效果都下降控制在 2-3 层
用 XML 标签但内容还是散文只有形式没有实质每个块内部也要用列表/表格
在极短提示词里用 XML徒增噪音少于 3 句话的提问直接问

七、可直接复制的通用骨架

text
<role>
{一句话角色定位,含关键专长与风格倾向}
</role>

<context>
{3-7 条背景事实,每条一行}
</context>

<task>
{一句话说清要做什么}
</task>

<instructions>
1. {第一步}
2. {第二步}
3. {第三步}
</instructions>

<constraints>
- {硬性约束,可验证}
- {硬性约束,可验证}
- 上下文未涵盖的信息,标注为「未知」,不要推测
</constraints>

<output_format>
{精确的输出结构}
</output_format>

更多现成模板见 12.4 提示词模板库


八、在 Claude Code 中的应用

XML 标签在 Claude Code 里同样有效,而且有额外的用武之地:

bash
claude -p "$(cat <<'EOF'
<task>
修复 CI 中失败的测试。
</task>

<instructions>
1. 先运行 npm test 获取完整失败列表,不要猜测
2. 对每个失败的测试,定位根因(是测试写错了,还是实现有 bug)
3. 修复后重新运行完整测试套件确认
</instructions>

<constraints>
- 禁止通过修改断言或跳过测试来"修复"失败
- 每个修改必须能解释清楚"原来为什么会失败"
- 不要修改与失败测试无关的文件
</constraints>
EOF
)"

禁止通过修改断言或跳过测试来"修复"失败 这条约束极其重要 —— 它对抗的是模型在压力下走捷径的倾向。详见 06.2 测试驱动与自修复循环


延伸阅读

基于 VitePress 构建 · 内容采用原作者授权