提示词工程模式:写出稳定可靠的 AI 提示词
这是一份实用指南,教你写出稳定、可预测的 AI 提示词。
核心原则:降低不确定性
模型每写下一个词,其实都在一堆候选词里挑一个。而你的提示词怎么写,直接决定了模型在关键节点是”心里没底”还是”胸有成竹”。
- 确定性高 → 行为稳定
- 确定性低 → 行为飘忽
接下来的所有技巧,其实都在做同一件事:让模型在关键决策点更有把握。
简单例子
假设你的提示词需要模型看当前环境,决定要不要问用户。
自然语言版本:
如果在 CI 环境中且没有参数,使用默认值。
如果在交互模式中有参数,直接运行。
如果在交互模式中没有参数,询问用户。
模型读完这三条,它们会一起来抢注意力,于是它只能回头一条条扫,才能拼出答案。
决策树版本:
## CI 环境?
├─ 是
│ └─ 有参数?
│ ├─ 是 → 使用参数,执行
│ └─ 否 → 使用默认值,执行
└─ 否(交互模式)
└─ 有参数?
├─ 是 → 使用参数,执行
└─ 否 → 询问用户
当模型判断出 “CI = 是”、”参数 = 否”,注意力自然就落在这一行上:
│ └─ 否 → 使用默认值,执行
↑ 注意力集中在这里
答案就摆在眼前,用不着回头再看,确定性自然就高了。
一句话总结
用决策树,模型瞄一眼旁边几个词就知道该干嘛;用自然语言,它得把一整段扫完才能拼出答案。要找的范围越小,结果就越确定。
模式 1:决策树替代自然语言
碰到带分支逻辑的指令,别用一大段文字描述,改画成可视化的树结构。
不好:自然语言
如果在 CI 环境中且没有参数,使用默认值。如果在交互模式中有参数,
直接运行。如果在交互模式中没有参数,询问用户。
好:决策树
## $ARGUMENTS 非空?
├─ 是 → 解析参数,直接执行,不交互
└─ 否
## $CI 或 $CLAUDE_NONINTERACTIVE 已设置?
├─ 是 → 使用 <defaults> 的值,直接执行
└─ 否 → 询问用户缺少的参数,然后执行
为什么有效: 缩进本身就把层级关系编码进去了。模型在训练里见过海量缩进结构(代码、YAML、目录树),早就学会了”缩进越深,就是越下一层的子条件”——这是自然语言给不了的空间信息。
模式 2:锚定(给起点)
给模型一个具体的起点,别让它从零开始凭空发挥。
不好:无锚定
生成一个部署脚本。
好:用模板锚定
基于这个模板生成部署脚本:
<template>
#!/bin/bash
set -euo pipefail
ENV="${1:?Usage: deploy.sh <env>}"
# ... 你的步骤
</template>
为什么有效: 模板里的内容会直接参与模型的注意力计算,输出就会被”拉向”模板的风格,而不是从”部署脚本”这么笼统的概念里随便生成一个。
模式 3:认知卸载(把思考步骤写出来)
把模型原本得在脑子里默默推理的步骤,一条条明确写出来。
不好:隐式推理
分析这段代码的性能问题并修复。
好:显式步骤
<analysis_steps>
1. 找出所有循环和递归
2. 标注每个的时间复杂度
3. 标记 O(n²) 或更高的
4. 为每个标记的部分提出优化方案
</analysis_steps>
按顺序执行这些步骤。
为什么有效: LLM 其实没有真正的工作记忆。把中间步骤写出来,就等于给它配了一份”外部记忆”——每一步只要看上一步的结果就行,不用从头再推一遍。
决策树,是把分支逻辑的思考卸载出来;思维链,是把推理过程卸载出来。原理一样,只是用在不同地方。
模式 4:注意力局部性(把相关的放在一起)
相关的信息,在文本里就该放得近一些——离得越近,拿到的注意力越强。
不好:规则离目标太远
<rules>永远不要删除生产数据库</rules>
...(中间隔了 500 个词)...
<task>清理过期数据</task>
好:规则紧挨目标
<task>
清理过期数据
<constraint>永远不要删除生产数据库</constraint>
</task>
为什么有效: Transformer 的注意力理论上能顾及全局,但实际上有位置偏好——近的词,注意力就是更强。所以把约束放在它管的那个动作旁边,别丢到老远的”通用规则”里去。
模式 5:指令-动作绑定(一条指令一个动作)
每条指令,最好都能干净地对上一个能直接执行的动作。
不好:一句话多个动作
检查代码风格问题并修复,然后运行测试确保通过。
好:一条指令 = 一个动作
1. 运行:`eslint --fix src/`
2. 运行:`npm test`
3. 如果测试失败 → 读错误输出,修复问题,回到步骤 2
为什么有效: 让模型把一条清楚的指令对应到一次工具调用,可靠性要比让它从一长句里扒出好几个藏着的动作高得多。
模式 6:输出格式预设(给输出一个”形状”)
先给模型一个输出的框架,让它往里填内容就好。
不好:开放式
分析这个 PR 的风险。
好:结构约束
<output_schema>
- risk_level: high | medium | low
- affected_files: [列表]
- rollback_plan: [字符串]
- requires_review: true | false
</output_schema>
为什么有效: 结构定义就像给模型铺好了”铁轨”。它在生成每个字段的值时,注意力都被字段名牢牢牵着走,跑偏的概率就大大降低了。
模式 7:负空间(说”不要”的同时说”要”)
每次告诉模型别做什么,记得顺带告诉它该做什么。
不好:只说不要
不要直接修改数据库。
不要跳过测试。
不要用 sudo。
好:不要 + 替代方案
<boundaries>
- 数据库变更 → 生成迁移文件,不要执行原始 SQL
- 需要验证 → 运行完整测试套件再继续,不要跳过
- 需要提权 → 请求用户确认,不要用 sudo
</boundaries>
为什么有效: “不要做 X” 只是把某些输出压下去,却没给出任何替代方向——模型是知道别往哪走,可它也不知道该往哪走啊。把替代方案一并给出,就能一边堵住错路、一边把它推上对的那条路。
模式 8:XML 标签做语义分区
Claude 的训练数据里就含有 XML 标签,正好拿来给提示词的各个部分分区。
推荐的提示词结构
<context>
模型需要了解的背景信息。
</context>
<parameters>
输入参数,包含类型、默认值、来源。
</parameters>
<decision_tree>
可视化的分支逻辑,每个叶子有明确动作。
</decision_tree>
<examples>
<example>
<input>...</input>
<thinking>模型应该遵循的逐步推理</thinking>
<output>...</output>
</example>
</examples>
<boundaries>
不要做什么 + 应该做什么。
</boundaries>
<output_schema>
期望的输出格式。
</output_schema>
为什么有效: XML 标签能划出实打实的语义边界。模型会把每个标签里的内容当成独立的一块来看,指令、示例、约束之间就不容易互相干扰了。
模式 9:带推理过程的示例
让模型看到你是怎么想的,而不只是最后输出了什么。
不好:只有输入/输出
<example>
<input>deploy staging</input>
<output>已部署到 staging。</output>
</example>
好:输入 + 思考过程 + 输出
<example>
<input>deploy staging</input>
<thinking>
1. 提供了参数:"staging" → 非空 → 跳过用户交互
2. 环境 "staging" 有效(匹配 staging|production)
3. 未检测到 CI 变量 → 但有参数 → 静默执行
4. 执行部署到 staging
</thinking>
<output>已成功部署到 staging。</output>
</example>
为什么有效: 示例里的 <thinking> 写法,会被模型泛化到它自己的推理里去。它学到的是那套思考方式,而不只是输出的格式。
各模式之间的关系
行为稳定性
↑
决策点的确定性高
↑
注意力分布集中
↑
提示词中的词语空间排列
↑
┌──────────┬──────────┬──────────┬──────────┐
│ 决策树 │ 注意力 │ 认知卸载 │ 输出格式 │
│ │ 局部性 │ │ 预设 │
├──────────┼──────────┼──────────┼──────────┤
│ 锚定 │ 指令-动作 │ 负空间 │ XML │
│ │ 绑定 │ │ 标签 │
├──────────┼──────────┼──────────┼──────────┤
│ 带推理的 │ │ │ │
│ 示例 │ │ │ │
└──────────┴──────────┴──────────┴──────────┘
所有技巧都在做同一件事:
改变生成时注意力在各个词上的分布。