在原型中,提示可能只是你某天下午写下的一个字符串。到了生产环境,这种做法通常在第一个月左右就会瓦解。
你会想只修改指令的一部分,而不影响其他部分;想让不同客户等级获得不同的行为;想对不同版本进行A/B测试;出现故障时想要回滚;还想知道提示上次何时更改、为何更改。
生产提示系统能够处理这一切。它不是“写一个字符串”,而是一套架构。
本文介绍这套架构的样貌——三层结构、模板化纪律、版本控制、评估,以及将提示从一份制品转变为基础设施的运维实践。
生产提示工作包含两类独立制品:可复用模板和每次请求的数据。像代码一样对模板进行版本控制。只要渲染后的提示和模型响应包含用户、客户或内部数据,就应将其视为敏感日志。
三层结构
生产提示分为三个不同的层次,每层关注的问题都不同:
系统层。 稳定的行为、身份和约束。很少更改。由设计AI行为的团队负责。
开发者层。 特定功能的指令、工具描述和输出格式要求。功能变化时随之更改。由功能团队负责。
用户层。 用户的具体请求,以及动态上下文(用户数据、对话历史、检索到的知识)。每次调用都不同。
混合这些层次是最常见的生产提示错误。系统提示膨胀到5,000字,身份、功能指令和动态上下文混杂其中,此时修改一处就会破坏其他部分。
分离这些层次是基础:
┌─────────────────────────────────────┐
│ 系统提示(稳定) │ 身份、行为、硬性约束
├─────────────────────────────────────┤
│ 开发者提示(按功能) │ 功能指令、工具、格式
├─────────────────────────────────────┤
│ 用户提示(按调用) │ 用户查询、上下文、对话
└─────────────────────────────────────┘
模型API明确支持这种区分:
- OpenAI:
system、developer、user角色。 - Anthropic:
system,以及带有user和assistant角色的messages。工具描述是单独的参数。 - Gemini:
systemInstruction,以及带有角色的contents。
请有意识地使用这些区别。
第1层:系统提示
系统提示定义AI的身份和行为方式。它很少更改。
良好的系统提示包括:
身份。 AI是谁。“你是 [公司] 的AI助手,专注于 [领域]。”
语气和风格。 它说话的方式。使用具体特征,而不是含糊的形容词。
硬性约束。 它绝不能做的事情,例如输出某些内容、作出某些决定、忽略某些指令。
行为模式。 它如何处理常见情况,例如拒绝、升级和不确定性。
安全与合规。 必需的披露、监管规则和内容政策。
不应包含:
- 特定功能的指令(“销售邮件要做X”)。
- 动态上下文(“用户的订单历史是……”)。
- 工具描述(应放在其他位置)。
- 经常变化的内容。
一份良好的系统提示通常为300–1000字。更长会难以管理;更短则可能无法充分规定行为。
一种行之有效的模板:
你是 [名称],是 [公司/场景] 的AI助手。
## 你的职责
[用2-3句话说明你做什么]
## 语气和风格
- [具体特征1]
- [具体特征2]
- [具体特征3]
- 不要 [反模式1]
- 不要 [反模式2]
## 硬性约束
- 绝不 [硬性规则1]
- 绝不 [硬性规则2]
- 始终 [硬性规则3]
## 如何处理不确定性
- 如果你不知道某项事实:明确说明。
- 如果用户请求超出范围:说明你能提供哪些帮助。
- 如果请求可能造成伤害:拒绝并解释原因。
## 格式要求
- 默认使用纯文本
- 展示代码或结构化数据时使用Markdown
- 保持简洁;不要用空话填充响应
这是整个系统的主干。每次交互都会经过它。对它的更改应审慎且少见。
第2层:开发者提示
开发者提示针对具体功能。不同功能使用不同的开发者提示。
摘要功能的开发者提示:
任务:为下方文档生成摘要。
要求:
- 3-5个要点
- 每个要点都是一个完整句子
- 关注事实和具体主张,不要描述印象
- 如果文档包含数字,请列出最重要的数字
- 不要包含营销用语或推测
- 如果文档在重要事项上含糊不清,请指出
格式:仅使用普通Markdown项目符号,不要前言。
代码审查功能的开发者提示:
任务:审查下方代码差异。
输出一个JSON对象,其中包含:
- summary:用1-2句话概述变更
- concerns:具体问题数组(每项包含:file、line、severity、description)
- suggestions:改进建议数组(每项包含:file、line、suggestion)
- approved:布尔值(没有阻塞性问题时为true)
严重级别:
- "blocker":合并前必须修复
- "warning":应该处理,但不阻塞合并
- "nit":风格问题,可选
重点关注:
- 逻辑错误
- 安全问题
- 性能问题
- 缺少测试覆盖
- 命名或结构不清晰
忽略:
- 格式(由格式化工具处理)
- 主观风格偏好
每项功能都有自己的开发者提示。它们分别存储、分别进行版本控制、分别评估。
第3层:用户提示
用户层是动态的。它通常包含:
用户的实际请求。 “请帮我总结这份文档。”
系统检索到的上下文。 来自RAG的文档、客户历史、对话历史。
每次调用的变量。 用户姓名、时区、语言偏好、账户等级。
这一层在调用时通过程序构建。结构通常如下:
{conversation_history_summary}
{retrieved_context}
用户请求:{user_query}
其他上下文:
- 用户姓名:{name}
- 用户时区:{timezone}
- 用户等级:{tier}
具体结构取决于功能。原则是:数据放在这里,而不是放在系统或开发者提示中。
模板化纪律
生产提示由模板构建。在代码中内联拼接字符串是原型做法,无法扩展。
一个简单的模板系统:
from string import Template
SUMMARIZE_TEMPLATE = Template("""
$conversation_summary
待总结文档:
$document
用户的具体指令:$user_instructions
""")
prompt = SUMMARIZE_TEMPLATE.substitute(
conversation_summary=summarize_conversation(history),
document=document_text,
user_instructions=user_query,
)
更复杂的方案是使用支持条件和局部模板的模板库(Jinja2、Handlebars)。
{% if user_tier == "enterprise" %}
你可以使用高级分析功能。
{% endif %}
{% if retrieved_context %}
知识库中的相关上下文:
{{ retrieved_context }}
{% endif %}
用户请求:{{ user_query }}
模板化可以防止通过变量实施提示注入(在适当情况下转义用户输入),支持条件逻辑,并保持提示结构一致。
版本控制
提示就是代码。请将它们存入源代码管理系统。
一种行之有效的模式是在仓库中设置prompts/目录,每个提示一个文件:
prompts/
system/
main.txt
customer-support.txt
code-assistant.txt
features/
summarize.txt
classify-ticket.txt
generate-email.txt
templates/
base.j2
每个文件都是独立提示,拥有自己的提交历史。变更通过PR审查。生产部署引用特定版本。
这很重要,原因如下:
- 差异可见。 提示更改时,差异会出现在PR中。审查者能准确看到变更内容。
- 回滚。 变更破坏功能时可以还原。
- 历史记录。 “我们何时更改了提示中的退款政策?”“为什么会有这一段?”——可以通过git blame找到答案。
- 工具集成。 检查工具、验证器和评估套件都能与文件形式的提示集成。
应避免:将提示作为字符串存储在代码中(难以查找、难以比较差异)、存储在界面工具中(版本控制属于工具,而不属于你)、从个人聊天窗口复制粘贴提示(无跟踪、无法测试)。
不要提交生产对话、客户记录、支持工单、内部文档,或包含敏感变量的渲染后提示。源代码管理系统用于保存可复用模板、测试夹具和经过清理的评估示例。真实跟踪记录应保存在具备保留策略、访问控制和脱敏能力的可观测性存储中。
将提示作为数据:外部存储
对于频繁变化的提示——A/B测试、用户等级变体、特定语言区域的提示——基于文件的源代码管理速度太慢。
可以使用数据库或服务存储提示版本及其元数据。
prompt = prompt_service.get(
name="summarize",
version="v3",
locale="en",
user_tier="enterprise",
)
该服务维护:
- 每个提示的当前版本和历史版本。
- 元数据:何时添加、由谁添加、为何添加。
- 每个版本附带的评估分数。
- 回滚能力。
工具包括PromptLayer、Helicone或内部自建方案。对于大多数团队,使用简单数据库模式自行构建就足够了。
界面至关重要。工程师和非工程人员(产品、内容)都应该能够编辑提示,但变更上线前必须经过审查并通过评估。
以评估作为变更门禁
每次提示变更都要在部署前经过评估。对于严肃的生产系统,这是不可妥协的要求。
流程如下:
- 工程师或非工程人员起草提示变更。
- 使用评估套件运行该变更。
- 在审查变更的同时审查评估结果。
- 如果评估通过(没有回归,最好还有改进),变更就可以获批。
- 部署已批准的变更。
- 部署后监控捕获评估遗漏的问题。
在实践中,这意味着每个提示都有评估套件,并且提示变更时会在CI中运行该套件。
没有这道门禁,提示变更会以不可预测的方式破坏功能。有了它,你就能快速且有信心地推进。
实用发布检查清单
提示版本上线前,应要求完成一份简短的检查清单:
| 检查项 | 要求 |
|---|---|
| 归属 | 提示有明确指定的负责人和审查者。 |
| 指令层 | 系统、开发者和用户/上下文数据相互分离。 |
| 模式 | 结构化输出具有模式定义和失败处理路径。 |
| 注入处理 | 用户提供的内容有清晰边界,绝不被视为指令。 |
| 评估 | 候选提示通过回归集和安全用例。 |
| 日志 | 模板版本、模型、延迟、成本及脱敏后的输入/输出均可观测。 |
| 回滚 | 无需大幅修改代码即可恢复之前已知良好的版本。 |
本文所链接的配套检查清单会将这些检查项转化为可重复执行的发布审查。
生产环境中的A/B测试
对于新提示,可以将其与现有版本一起用于少量生产流量的A/B测试,从而获得评估之外的真实世界信号。
模式如下:
- 95%的流量使用生产提示v3。
- 5%使用新的候选版本v4。
- 衡量:用户反馈、下游指标、真实流量上的评估分数。
- 获得足够数据后作出决定:将v4推广到100%,或保留v3。
工具包括功能开关(LaunchDarkly、内部自建)、提示版本控制服务(PromptLayer)和自定义路由。
注意事项:
- A/B测试只能捕获你所衡量的信号。如果没有用户反馈或下游转化指标,A/B测试提供的信息很少。
- 统计显著性需要足够的流量。对于低流量功能,A/B测试很难实施。
- 不要同时运行过多A/B测试,否则交互影响会变得混乱。
提示的可观测性
每次生产大语言模型调用都应记录:
- 使用了哪个提示模板(名称、版本)。
- 替换了哪些变量;变量名称采用白名单,必要时对值进行脱敏。
- 仅在政策允许时记录最终渲染的提示;否则存储脱敏、抽样或哈希后的表示。
- 模型响应;敏感工作流中应进行脱敏或抽样。
- 延迟、令牌、成本。
- 下游信号(用户反馈、成功指标)。
你需要依靠这些数据来调试“为什么模型给这位用户的回答如此奇怪?”没有它们,就只能猜测。
存储位置可以是数据库表或可观测性工具。如果记录所有内容,成本是真实存在的(调用量 × 令牌数 × 存储);隐私风险同样真实存在。应按工作流决定哪些字段可以安全存储,默认对秘密和个人数据进行脱敏,并保持较短的保留期,除非出于合规原因需要保留更长时间。部分团队会进行抽样。
审查方面,可以每周定期阅读一批真实生产提示和响应样本。这能捕获评估遗漏的问题。
提示反模式
以下几种模式应避免:
反模式1:超级提示。 一份10,000字的系统提示,试图处理所有情况。难以更改、难以调试,而且其中较靠后的指令往往会被模型忽略。
修复:拆分成分层且聚焦的提示。每项功能一个提示。
反模式2:内联字符串拼接。
prompt = "你是一位乐于助人的助手。" + (
"用户是付费客户。" if user.tier == "paid" else ""
) + f"用户姓名是 {user.name}。" + ...
脆弱、难以阅读,而且容易遭受注入。
修复:使用模板系统。
反模式3:过多用例共用同一提示。
一个“通用助手”提示同时用于撰写邮件、代码审查、客户支持和研究。这些任务彼此不同;同一提示无法针对其中任何一项进行优化。
修复:在共享系统提示之上,为每项功能使用专门的开发者提示。
反模式4:硬编码提示。
response = openai.chat.completions.create(
messages=[
{"role": "system", "content": "你是一位乐于助人的助手……"},
{"role": "user", "content": query}
]
)
提示埋在代码中。无法在不部署的情况下编辑,无法进行A/B测试,也无法独立进行版本控制。
修复:提取到提示文件或服务中。
反模式5:没有评估覆盖。
某项功能随从未经过系统测试的提示一起发布。质量完全“凭感觉”,也无法检测漂移。
修复:每个提示都配有评估套件。
反模式6:将数据混入系统提示。
你是John的助手。他是一名premium客户,于2023年加入,居住在Tallinn,有47个未结工单。
现在系统提示每次调用都会变化。缓存失效,混乱随之而来。
修复:动态数据应放在用户/上下文层,而不是系统提示中。
反模式7:指令埋在中间。
帮助用户处理请求。保持礼貌。将输出格式设为JSON。不要使用Markdown。用户正在询问定价,因此引用数字时要谨慎。输出应为1-2句话。现在帮助用户。
重要指令会被淹没,模型可能遗漏。
修复:使用清晰的分节结构,将关键指令放在开头和结尾(近因效应会有所帮助)。
常见功能的具体模式
下面是一些针对特定功能的模式:
分类
任务:将以下文本归入下列类别之一:
- billing:付款、退款、订阅
- technical:缺陷、错误、集成问题
- account:登录、密码、个人资料变更
- feature_request:新功能请求
- complaint:没有具体可执行问题的一般性不满
输出一个JSON对象:{"category": "<one of above>", "confidence": "<high|medium|low>", "reasoning": "<1 sentence>"}
待分类文本:
{text}
模式:带定义的枚举类别、结构化输出、置信度和推理字段。
提取
任务:从下方文档中提取结构化数据。
模式:
- vendor_name:开具发票的公司
- invoice_number:文档上印刷的编号
- date:ISO 8601格式
- line_items:由 {description, quantity, unit_price, total} 组成的数组
- subtotal、tax、total:数字
规则:
- 如果字段不存在,使用null
- 数字应使用数值类型,而不是字符串
- 对于含糊情况,将 "needs_review" 设为true并作出解释
文档:
{document}
模式:明确的模式定义、类型要求、缺失数据处理、含糊情况升级。
带风格要求的生成
任务:围绕 {topic} 主题,面向 {audience} 撰写一篇 {format}。
风格:
- {具体风格特征1}
- {具体风格特征2}
- 避免:{反模式1}、{反模式2}
约束:
- 长度:{N} 字
- 包含:{必需元素}
- 排除:{禁止元素}
语气参考:
[提供符合所需语气的样例]
输出:仅输出 {format},不要前言或后记。
模式:具体风格特征(而不是泛泛描述)、明确约束、使用参考样例锚定语气。
代理循环
你可以使用以下工具:
{tool_descriptions}
每轮执行以下步骤:
1. 思考需要做什么。
2. 判断是否需要工具。如果需要,则调用工具。
3. 观察结果后,判断是否还需要更多工具,或是否可以回答。
4. 获得足够信息后,生成最终答案。
约束:
- 每个请求最多调用5次工具。
- 如果5次调用后仍无法完成,请说明缺少什么。
- 绝不编造工具名称或参数。
- 根据工具结果采取行动前先进行验证。
用户请求:
{user_query}
模式:明确的推理步骤、工具预算、反幻觉、反思。
团队协作
生产提示通常涉及多类人员:
- 工程师 将提示接入系统、维护模板、管理部署。
- 产品人员 定义提示应实现的目标。
- 内容/营销人员 负责语气和风格指南。
- 领域专家 知道具体用例中的正确做法(法律措辞、医学术语等)。
一种实用模式是建立类似代码审查的“提示审查”流程,并让每个领域的适当人员参与审查。语气变更由内容人员审查,逻辑变更由工程人员审查,特定领域内容由领域专家审查。
对于法律、医疗、金融等敏感用例,提示可能需要正式审查和签字批准。应据此建立流程。
90天提示成熟度计划
对于希望从“提示是代码中的字符串”转向“提示是受管理的基础设施”的团队:
第1–30天:奠定基础。
- 将所有提示提取到源代码管理系统中的专用文件。
- 建立3层模式(系统/开发者/用户)。
- 构建一个简单的模板层。
- 设置提示和响应的基本日志记录。
第31–60天:评估。
- 为最重要的5个提示构建评估套件。
- 提示更改时运行评估(起初可手动运行)。
- 设置评估的CI集成(在PR上自动运行)。
第61–90天:运维。
- 实现提示版本控制(数据库或服务)。
- 至少为一个关键提示添加A/B测试能力。
- 构建生产提示质量仪表板。
- 建立提示变更审查流程。
90天后,提示将成为受管理的基础设施。变更审慎、可测试、可审查、可回滚。质量可以衡量,漂移可以检测。
将提示作为基础设施
生产提示不是字符串,而是一套分层系统,并围绕版本控制、模板化、评估和可观测性建立工程纪律。
三层架构(系统/开发者/用户)可以分离关注点,使提示保持可维护。模板化可以防止脆弱性。源代码管理或提示服务提供版本历史。评估充当变更门禁。可观测性则捕获评估遗漏的问题。
对于严肃的生产工作,这些并非可选项。跳过这些步骤的团队最终会陷入提示混乱——字符串散落在代码各处、不知道生产环境运行哪个版本、无法衡量质量,而且行为不断发生无法解释的变化。
投资提示基础设施的团队则能获得可控制、可衡量、可改进的AI行为。这就是一项历久弥新的功能与最终沦为技术债务的功能之间的区别。
从架构开始,其他一切都会变得更容易。



