提示词散落在代码字符串里,是 LLM 应用最常见的技术债:改一个字没人知道影响面,线上出问题无法回滚,测试和生产的 prompt 悄悄不一致。提示词是配置更是代码——它需要版本、schema、回归测试和发布流程。本文把 prompt 模板纳入与代码同等的工程化管理。

前置准备

模板独立成文件入库,渲染用 Jinja2,校验用 pydantic:

mkdir prompts && pip install jinja2 pydantic

目录约定:

prompts/
answer_with_rag.v2.j2 # 模板文件名带语义版本号
answer_with_rag.schema.json # 该模板需要的变量 schema

步骤一:模板入库与版本化

模板从代码里抽出来,用 Jinja2 语法声明变量:

你是客服助手。依据以下资料回答,资料不足时说明无法确认。
资料:
{% for doc in docs %}- {{ doc.title }}: {{ doc.snippet }}
{% endfor %}
用户问题:{{ question }}
  • 文件名语义版本(v2/v3),不覆盖旧文件——回滚就是换文件名
  • git 管历史,PR 管评审:prompt 改动与代码改动走同一套 review 流程
  • 代码里只引用 name + version,禁止再出现裸字符串 prompt

步骤二:变量 schema 校验

渲染前校验入参,把”模板要 docs 却传了 doc”这类错误挡在运行前:

from pydantic import BaseModel, ValidationError
import json, jinja2

class RagVars(BaseModel):
question: str
docs: list[dict]

env = jinja2.Environment(autoescape=False)

def render(name_ver: str, variables: dict) -> str:
schema = json.load(open(f"prompts/{name_ver.split('.')[0]}.schema.json"))
RagVars.model_validate(variables) # 缺字段/类型错直接抛错
tpl = env.from_string(open(f"prompts/{name_ver}.j2").read())
return tpl.render(**variables)

schema 与模板同 PR 修改,保证”模板-变量”契约始终一致。

步骤三:离线回归评估

维护 30+ 条输入样例与期望要点,每次模板变更跑全量对比:

import pytest

CASES = [("订单到哪了", ["物流", "单号"]), ("能退款吗", ["退款政策"])]

@pytest.mark.parametrize("q,expects", CASES)
def test_template_regression(q, expects):
prompt = render("answer_with_rag.v2", {"question": q, "docs": load_docs(q)})
answer = llm.chat(prompt)
assert all(e in answer for e in expects)
  • 关键场景再加”禁语断言”(如不得承诺赔偿金额)
  • 对比基线:新版本与旧版本在同批 case 上的得分差,写进 PR 描述

步骤四:灰度发布与回滚

prompt 版本做成运行时配置,而非发版常量:

ver = config.get("prompt.answer_with_rag", default="v2")  # 配置中心可热更
if hash(request_id) % 100 < 10:
ver = config.get("prompt.answer_with_rag.canary", default=ver)
  • 灰度期对比两组指标:拒答率、人工转接率、用户负反馈率
  • 回滚 = 配置改回旧版本号,秒级生效,无需重新部署服务

常见问题

  • 模板里要不要写示例(few-shot):要,但示例也是回归对象——改示例等同改逻辑,必须跑评估
  • 多模型共用一套模板:避免;不同模型对格式敏感度不同,按模型族分模板分支
  • 版本爆炸:废弃模板季度归档删除,保留最近 3 个版本即可
  • 评估成本高:全量 case nightly 跑,PR 门禁只跑核心 10 条 + 变更涉及的场景

小结

把提示词当代码管,核心是四件事:文件化与语义版本(可回滚)、schema 契约(可校验)、回归评估(可度量)、灰度与热回滚(可发布)。做到这四件,prompt 迭代就从”改字符串碰运气”变成有门禁、有灰度、有回滚的标准变更流程——LLM 应用的可维护性,一大半藏在这个目录里。