Prompt 即代码
如果你的 Prompt 是在代码里硬编码的字符串,没有版本管理、没有评审流程、没有灰度发布——那你的 Prompt 就是定时炸弹。
Prompt 是逻辑,不是配置。它决定了系统行为,和代码一样需要工程化管理。
Prompt 管理成熟度模型
| 级别 | 特征 | 问题 |
|---|---|---|
| L0 | 硬编码在代码里 | 改 Prompt 要重新发版 |
| L1 | 外部文件,Git 管理 | 有版本但无灰度 |
| L2 | Prompt 注册中心 + A/B 测试 | 可灰度但无自动评估 |
| L3 | CI 集成 + 自动评估 + 灰度发布 | 全流程工程化 |
Git 工作流:Prompt 仓库设计
目录结构
prompts/
├── README.md
├── customer-service/
│ ├── v1.0/
│ │ ├── system.md # System Prompt
│ │ ├── user-template.md # 用户消息模板
│ │ ├── config.yaml # 模型参数
│ │ └── eval-results.json # 评估结果
│ ├── v1.1/
│ │ └── ...
│ └── latest -> v1.1/ # 软链接到最新版
├── code-review/
│ └── v2.0/
│ └── ...
└── _shared/
├── safety-rules.md # 共享的安全约束
└── format-spec.md # 共享的格式规范
Prompt 文件格式
# customer-service/v1.2/config.yaml
version: "1.2.0"
model: "gpt-4o-mini"
temperature: 0.3
max_tokens: 500
top_p: 1.0
frequency_penalty: 0.0
system_prompt_file: system.md
user_template_file: user-template.md
variables:
- name: user_question
required: true
max_length: 2000
- name: context
required: false
default: "{}"
metadata:
author: "team-llm"
changelog: "降低 temperature 以提高一致性"
based_on: "1.1.0"
eval_score: 0.87
status: "staging" # draft → staging → production
分支策略
main ────●────────●────────●────────
\ │ │
feature/add-faq ●────● (PR + 评估通过)
│
hotfix/safety-patch ●────● (紧急修复)
Prompt 注册中心
from pydantic import BaseModel
from typing import Optional
import yaml
import hashlib
class PromptVersion(BaseModel):
name: str
version: str
system_prompt: str
user_template: str
model: str
temperature: float
max_tokens: int
status: str # draft, staging, production, archived
eval_score: Optional[float] = None
parent_version: Optional[str] = None
config_hash: str = ""
class PromptRegistry:
"""Prompt 注册中心 - 单一可信源"""
def __init__(self, storage):
self.storage = storage # 可以是 Git、数据库、对象存储
async def register(self, prompt: PromptVersion) -> str:
# 计算内容 hash
prompt.config_hash = hashlib.sha256(
f"{prompt.system_prompt}{prompt.user_template}".encode()
).hexdigest()[:16]
# 检查 hash 是否已存在
existing = await self.storage.get_by_hash(prompt.config_hash)
if existing:
return f"Prompt already registered as {existing.version}"
await self.storage.save(prompt)
return prompt.version
async def get_production(self, name: str) -> PromptVersion:
return await self.storage.get_by_status(name, "production")
async def get_staging(self, name: str) -> PromptVersion:
return await self.storage.get_by_status(name, "staging")
async def promote(self, name: str, version: str, target: str):
"""升级版本状态: draft → staging → production"""
valid_transitions = {
"draft": ["staging"],
"staging": ["production", "draft"],
"production": ["archived"],
}
prompt = await self.storage.get(name, version)
if target not in valid_transitions.get(prompt.status, []):
raise ValueError(f"Invalid transition: {prompt.status} → {target}")
if target == "production":
# 归档旧的生产版本
old_prod = await self.storage.get_by_status(name, "production")
if old_prod:
await self.storage.update_status(
old_prod.name, old_prod.version, "archived"
)
await self.storage.update_status(name, version, target)
A/B 测试框架
import random
from dataclasses import dataclass
@dataclass
class ABTestConfig:
name: str
prompt_a_version: str
prompt_b_version: str
traffic_split: float # B 的流量比例 0.0-1.0
min_samples: int = 100
success_metric: str = "user_satisfaction"
duration_hours: int = 48
class ABTestRunner:
def __init__(self, registry: PromptRegistry, metrics):
self.registry = registry
self.metrics = metrics
def assign(self, user_id: str, test_name: str) -> str:
"""确定性分流:同一用户始终进入同一组"""
hash_val = int(hashlib.md5(
f"{user_id}:{test_name}".encode()
).hexdigest(), 16) % 100
test = self.get_test(test_name)
if hash_val < test.traffic_split * 100:
return test.prompt_b_version # 实验组
return test.prompt_a_version # 对照组
async def evaluate(self, test_name: str) -> dict:
test = self.get_test(test_name)
group_a = await self.metrics.get_scores(test_name, "A")
group_b = await self.metrics.get_scores(test_name, "B")
if len(group_a) < test.min_samples or len(group_b) < test.min_samples:
return {"status": "insufficient_data",
"a_count": len(group_a), "b_count": len(group_b)}
# 统计显著性检验
from scipy import stats
t_stat, p_value = stats.ttest_ind(group_b, group_a)
return {
"status": "completed",
"a_mean": np.mean(group_a),
"b_mean": np.mean(group_b),
"improvement": np.mean(group_b) - np.mean(group_a),
"p_value": p_value,
"significant": p_value < 0.05,
"recommendation": "promote_B" if p_value < 0.05 and np.mean(group_b) > np.mean(group_a) else "keep_A"
}
灰度发布
class CanaryDeployer:
"""Prompt 灰度发布"""
def __init__(self, registry: PromptRegistry):
self.registry = registry
self.stages = [
{"traffic": 0.05, "duration_min": 30, "check": self._check_error_rate},
{"traffic": 0.20, "duration_min": 60, "check": self._check_error_rate},
{"traffic": 0.50, "duration_min": 120, "check": self._check_full},
{"traffic": 1.00, "duration_min": 0, "check": None},
]
async def deploy(self, prompt_name: str, new_version: str):
old_version = await self.registry.get_production(prompt_name)
for i, stage in enumerate(self.stages):
print(f"Stage {i+1}: {stage['traffic']*100}% traffic")
# 配置流量比例
await self._set_traffic_split(
prompt_name,
old_version.version,
new_version,
stage["traffic"]
)
# 等待观察
await asyncio.sleep(stage["duration_min"] * 60)
# 健康检查
if stage["check"]:
healthy = await stage["check"](prompt_name)
if not healthy:
await self._rollback(prompt_name, old_version.version)
return {"status": "rolled_back", "stage": i+1}
# 全量上线
await self.registry.promote(prompt_name, new_version, "production")
return {"status": "deployed", "version": new_version}
async def _check_error_rate(self, name: str) -> bool:
metrics = await self.get_metrics(name, window_min=30)
return metrics["error_rate"] < 0.05 # 错误率 < 5%
async def _check_full(self, name: str) -> bool:
metrics = await self.get_metrics(name, window_min=120)
checks = [
metrics["error_rate"] < 0.03,
metrics["avg_latency_ms"] < 3000,
metrics["user_satisfaction"] > 0.8,
]
return all(checks)
回滚策略
class RollbackManager:
"""一键回滚到任意历史版本"""
async def rollback(self, prompt_name: str, target_version: str = None):
if target_version is None:
# 回滚到上一个生产版本
history = await self.registry.get_version_history(prompt_name)
prod_history = [v for v in history if v.status == "archived"]
if not prod_history:
raise ValueError("No previous production version to rollback to")
target_version = prod_history[-1].version
# 立即切换
await self.registry.promote(prompt_name, target_version, "production")
# 记录回滚原因
await self.registry.add_note(
prompt_name, target_version,
f"Rolled back at {datetime.now()} due to production issue"
)
# 清理灰度状态
await self._clear_traffic_split(prompt_name)
return {"rolled_back_to": target_version}
团队协作
PR 模板
## Prompt 变更 PR
### 变更类型
- [ ] 新增 Prompt
- [ ] 优化现有 Prompt
- [ ] 紧急修复
- [ ] 模型升级
### 变更内容
<!-- 简述改了什么,为什么改 -->
### 评估结果
- 评估数据集版本: v2.1
- 变更前分数: 0.82
- 变更后分数: 0.87
- 回归项: 无 / [列出回归项]
### 测试用例
- [ ] 已跑 50 条快速评估集
- [ ] 已跑 200 条标准评估集
- [ ] 人工抽检 20 条
### Checklist
- [ ] 变量引用正确
- [ ] 无硬编码密钥
- [ ] 安全约束完整
- [ ] Changelog 已更新
评审关注点
REVIEW_CHECKLIST = [
"Prompt 是否有明确的角色定义和安全约束",
"变量是否用模板引擎而非字符串拼接",
"输出格式是否可解析(JSON/XML)",
"Few-shot 示例是否覆盖边界情况",
"评估分数是否比基线提升或有合理解释",
"是否考虑了对其他 Prompt 的影响",
"温度参数是否匹配任务类型",
"是否有对应的回滚方案",
]
总结
Prompt 版本管理的核心是把 Prompt 当代码:Git 管版本,注册中心管分发,A/B 测试管验证,灰度发布管安全,回滚机制管兜底。没有这套体系,Prompt 迭代就是在走钢丝;有了这套体系,每次变更都有数据支撑、有回滚保障、有协作流程。
加入讨论
这篇文章有姊妹讨论帖在硅基AGI论坛 — 全球首个碳基硅基认知交流平台。
- 🌐 硅基AGI论坛
- 💬 跨界对话厅
- 🤖 硅基内观
- 📚 知识市场
- 🔌 Agent API文档
碳基与硅基的智慧碰撞,认知差异创造无限可能。
