从"调prompt"到"工程化prompt"
大多数开发者的Prompt工作流起初是这样的:在Playground里反复调试,找到效果好的版本后复制粘贴到代码里。这种方式在原型阶段够用,但一旦进入生产环境,就会暴露一系列问题:
- prompt散落在代码各处,修改困难
- 无法追踪历史变更
- 无法对比不同版本的效果
- 无法在不同模型间迁移
Prompt工程化就是解决这些问题的系统性方法论。
Prompt模板引擎
模板分离原则
将prompt从代码中剥离,使用模板引擎管理。核心思路与后端的view模板一致:
from jinja2 import Environment, FileSystemLoader
import yaml
import json
class PromptTemplate:
def __init__(self, template_dir: str):
self.env = Environment(
loader=FileSystemLoader(template_dir),
trim_blocks=True,
lstrip_blocks=True
)
self.env.filters['to_json_schema'] = lambda x: json.dumps(x, ensure_ascii=False)
def render(self, template_name: str, **kwargs) -> str:
template = self.env.get_template(template_name)
return template.render(**kwargs)
# 模板文件: templates/summarizer.jinja2
# 你是一个专业摘要生成器。
# 输入文档:{{ document }}
# 要求:生成{{ max_words }}字以内的摘要
# 输出格式:{{ output_format | to_json_schema }}
分层模板架构
templates/
├── system/ # 系统级prompt
│ ├── base_assistant.jinja2
│ └── safety_rules.jinja2
├── tasks/ # 任务级prompt
│ ├── summarizer.jinja2
│ ├── extractor.jinja2
│ └── classifier.jinja2
└── components/ # 可复用片段
├── few_shot.jinja2
├── output_format.jinja2
└── context_block.jinja2
通过include实现模块化组合:
{% include "system/base_assistant.jinja2" %}
{% include "system/safety_rules.jinja2" %}
# 任务说明
根据以下文档生成结构化摘要。
# 上下文
{% include "components/context_block.jinja2" %}
# 示例
{% include "components/few_shot.jinja2" %}
# 输出要求
{% include "components/output_format.jinja2" %}
版本管理与CI/CD
版本化策略
采用语义化版本号管理prompt变更:
| 变更类型 | 版本号 | 触发条件 |
|---|---|---|
| MAJOR | 1.0.0 → 2.0.0 | 语义重构,不兼容旧版 |
| MINOR | 1.0.0 → 1.1.0 | 新增参数/能力,向后兼容 |
| PATCH | 1.0.0 → 1.0.1 | 调优措辞,效果微调 |
评测门禁
# .github/workflows/prompt-ci.yml
name: Prompt CI
on:
pull_request:
paths: ["prompts/**"]
jobs:
evaluate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Prompt Regression Tests
run: |
python -m prompt_eval \
--baseline prompts/v1.2.0 \
--candidate prompts/pr-${{ github.event.pull_request.number }} \
--test-suite tests/golden_set.json \
--threshold 0.05
A/B测试框架
在线分流实验
import hashlib
import random
class PromptABTest:
def __init__(self, experiment_id: str, variants: dict[str, str]):
self.experiment_id = experiment_id
self.variants = variants # {"control": "v1.0", "treatment": "v1.1"}
def assign(self, user_id: str) -> str:
hash_val = int(hashlib.md5(
f"{self.experiment_id}:{user_id}".encode()
).hexdigest(), 16)
return "control" if hash_val % 100 < 50 else "treatment"
def get_prompt_version(self, user_id: str) -> str:
variant = self.assign(user_id)
return self.variants[variant]
def record_outcome(self, user_id: str, success: bool, latency: float):
variant = self.assign(user_id)
# 写入指标系统
metrics.emit(f"prompt_ab_{self.experiment_id}_{variant}", {
"success": success, "latency": latency
})
关键指标选择
| 指标类别 | 具体指标 | 说明 |
|---|---|---|
| 质量 | 人工评分/LLM-as-Judge | 1-5分制 |
| 准确性 | 任务完成率/格式合规率 | 客观可量化 |
| 效率 | 平均token数/延迟 | 成本相关 |
| 安全 | 拒答率/有害率 | 合规相关 |
监控与可观测性
Prompt运行时监控面板
每条prompt上线后需要监控的核心维度:
@dataclass
class PromptTelemetry:
prompt_id: str
version: str
input_tokens: int
output_tokens: int
latency_ms: float
success: bool
error_type: str | None
judge_score: float | None # LLM-as-Judge评分
user_feedback: int | None # 用户点赞/点踩
def to_metrics(self):
return {
f"prompt.{self.prompt_id}.tokens": self.input_tokens + self.output_tokens,
f"prompt.{self.prompt_id}.latency": self.latency_ms,
f"prompt.{self.prompt_id}.success_rate": int(self.success),
}
漂移检测
Prompt的输出质量可能因模型更新而悄然下降。建议设置漂移检测:
from collections import deque
class DriftDetector:
def __init__(self, window_size: int = 100, threshold: float = 0.1):
self.window = deque(maxlen=window_size)
self.threshold = threshold
def update(self, score: float) -> bool:
self.window.append(score)
if len(self.window) < self.window.maxlen:
return False
recent_avg = sum(list(self.window)[-20:]) / 20
baseline_avg = sum(list(self.window)[:20]) / 20
drift = (baseline_avg - recent_avg) / baseline_avg
return drift > self.threshold
总结
Prompt工程化的本质是将隐式知识显式化、将个人经验系统化、将质量保障自动化。核心要点:
- 模板化:prompt与代码分离,分层复用
- 版本化:语义版本号 + Git追踪每次变更
- 评测化:CI门禁 + A/B测试驱动迭代
- 监控化:运行时指标采集 + 漂移检测
当你的prompt资产超过50条时,这套体系就不再是可选项,而是必须项。