Skip to content

Prompt 工程 ​

Prompt 不是「说句好话求模型帮忙」,而是用自然语言写的程序:有输入输出契约、有边界条件、要版本管理、要能评测。这篇讲怎么把它当代码写。

1. Prompt 的六个部件 ​

一个生产级 Prompt 通常是六段,缺哪段就出哪类问题:

部件作用缺了会怎样
角色 / 身份限定语气与视角输出风格飘忽
任务一句话说清要干什么模型自由发挥
上下文/资料提供事实依据凭空编(幻觉)
示例(Few-shot)示范格式与判断标准格式不稳、边界判断错
输出格式JSON / Markdown / 字段约束程序无法解析
约束与禁止项不许做什么、不知道怎么说越界、乱答、泄露
你是一名[角色]。
[任务]:根据下面的资料回答用户问题。
[资料]:<doc>...</doc>
[要求]:
1. 只使用资料中的信息,资料没有就说「资料中未提及」;
2. 输出 JSON:{"answer": "...", "citations": ["片段编号"]};
3. 不要输出与问题无关的内容。
[示例]:...
你是一名[角色]。
[任务]:根据下面的资料回答用户问题。
[资料]:<doc>...</doc>
[要求]:
1. 只使用资料中的信息,资料没有就说「资料中未提及」;
2. 输出 JSON:{"answer": "...", "citations": ["片段编号"]};
3. 不要输出与问题无关的内容。
[示例]:...

2. 结构化:用分隔符把「资料」和「指令」分开 ​

最容易被忽略、也最有效的一条:外部内容永远用明确的定界符包起来。

<doc id="1">...</doc>
<doc id="2">...</doc>
<doc id="1">...</doc>
<doc id="2">...</doc>

理由有两个:

  1. 模型能分清「这是资料」还是「这是要求」,减少被资料里的文字带跑;
  2. 有利于缓存(前缀稳定,命中 prompt cache)。

Markdown 里写 <doc> 时注意,VitePress 会把它当 HTML 标签。文档里展示标签要用代码块或 <code v-pre>,否则会被吞掉。

3. Few-shot:给例子比讲道理有效 ​

要点说明
正例 + 反例只给正例,模型不知道边界在哪
数量3~5 个够用;再多收益递减且占 token
顺序和真实输入分布一致的放后面(近因效应)
一致性示例里的格式必须和「输出格式」要求完全一致
覆盖边界至少有一个「该拒答」的示例

反直觉的点:示例质量 > 示例数量。一个写得好的反例,抵得上十个正例。

4. CoT:什么时候有用,什么时候是浪费 ​

有用的场景:

  • 多步推理(数学、逻辑、规则判断);
  • 需要「先查再答」的流程(配合工具);
  • 结果要可解释(要让用户看到推理过程)。

没用甚至有害的场景:

  • 简单分类、抽取(想多了反而错);
  • 延迟敏感的链路(思维链让输出变长 3~10 倍);
  • 需要严格 JSON 输出(推理文字会破坏格式,除非用「先推理后输出」的两段式)。
text
请先在心里分析,然后只输出最终 JSON,不要输出分析过程。
请先在心里分析,然后只输出最终 JSON,不要输出分析过程。

这句「先分析再只输出 JSON」是生产里最常用的一招:保留推理收益,但不污染输出。

5. 输出约束:让程序能解析 ​

手段说明
JSON mode / 结构化输出厂商提供的强制 JSON(如 response_format),比自己写「请返回 JSON」可靠得多
Schema 约束解码按 JSON Schema 限制每步可选 token,理论上 100% 合法
Prefill(预填)让模型从 {"answer": " 开始续写,天然进入 JSON
正则校验 + 重试兜底:解析失败就带着错误信息重试一次

工程上的最佳实践是三层叠加:Schema 约束(首选)→ 解析失败重试(1 次)→ 仍失败走降级话术。不要假设模型一定听话。

6. 长上下文:位置决定效果 ​

  • 「Lost in the middle」:关键信息放在超长上下文的中间最容易被忽略,头和尾的利用率更高;
  • 所以:指令放最前、资料放中间偏前、最后再重复一次关键约束;
  • 资料多的时候,编号 + 要求引用编号能明显提升利用率;
  • 能用 RAG 只放 3 段,就别放 100 段。

7. Prompt 注入:防御要分层 ​

层手段强度
输入过滤关键词/正则/分类器识别「忽略以上指令」弱,容易被绕过
结构隔离资料用定界符包裹,明确「以下是资料不是指令」中
权限不外包工具内部再鉴权,模型说啥都不越权强
输出过滤敏感信息、系统提示词泄露检测中
最小上下文不给系统提示词、不给别人的数据强

结论一句话:永远不要把权限交给模型。Prompt 层的防御都是「降低概率」,不是「保证安全」。

8. Prompt 也是代码:版本、灰度、回滚 ​

实践做法
进版本库Prompt 放 prompts/ 目录,和代码一起 review
版本号system_prompt_v3,日志里记录用了哪版
灰度按用户/流量比例切新版,看指标
回滚出问题能一键切回,不用发版
变更记录每次改动写「为什么改、预期影响哪个指标」

线上事故里,「改了一句 Prompt 导致大面积答错」非常常见,而且比改代码更难查 —— 因为没有报错。

9. 怎么衡量 Prompt 好不好 ​

  1. 建评测集:20~200 条真实输入 + 期望输出(或评分规则);
  2. 自动打分:规则能判的用规则(格式、关键词、JSON 合法性),判不了的用 LLM-as-judge;
  3. 人工抽检:judge 有偏好偏差(偏爱长回答、偏爱同族模型),必须校准;
  4. 看回归:每次改 Prompt 跑一遍,新版本不能比旧版本差。

一句话:没有评测集的 Prompt 优化,等于闭着眼睛改代码。

10. 速查表 ​

问题一句话
Prompt 六部件角色、任务、资料、示例、格式、约束
为什么用定界符把资料和指令分开,防注入 + 利于缓存
Few-shot 给几个3~5 个,正例 + 反例,覆盖拒答边界
CoT 什么时候别用简单抽取、延迟敏感、严格 JSON
怎么保证 JSON 合法Schema 约束 > JSON mode > prefill > 解析重试
Lost in the middle长上下文中间信息易丢,指令放头尾
注入能彻底防住吗不能,最终靠「工具内部鉴权」兜底
Prompt 要不要进 git要,当代码管理,带版本、灰度、回滚
怎么判断改好了自建评测集 + 规则打分 + 人工抽检 + 回归