从手工艺到工程:Prompt 优化的范式转变

大多数开发者在接触 LLM 初期,Prompt 优化的方式是这样的:想一个提示词,在几条测试用例上跑一下,看看效果,感觉不错就用,感觉不行就改几个字再试。这种方式本质上是手工艺——依赖个人经验和直觉,难以复制,难以扩展,难以追踪。

当你的应用从 Demo 走向生产,从几个用例变成几千个用例,从单一模型变成多模型对比,这种手工艺方式就会彻底失效。你需要的是一套工程化的 Prompt 迭代流程——有版本管理、有评估指标、有自动化测试、有持续优化机制。

本文将介绍如何建立这样一套系统化的 Prompt 迭代优化流程。

Prompt 即代码:版本管理

为什么需要 Prompt 版本管理

Prompt 是 LLM 应用的"源代码"。和传统代码一样,它需要版本管理来:

  • 追踪每次修改的内容和原因
  • 在新版本出问题时快速回滚
  • 对比不同版本的效果差异
  • 管理不同环境(开发/测试/生产)的 Prompt

实践方案

方案一:Git 仓库管理

将 Prompt 作为结构化文件存储在 Git 仓库中:

prompts/
├── README.md
├── customer_service/
│   ├── v1.0/
│   │   ├── system.txt
│   │   ├── user_template.txt
│   │   └── config.yaml
│   ├── v1.1/
│   │   ├── system.txt
│   │   ├── user_template.txt
│   │   └── config.yaml
│   └── current -> v1.1
├── code_review/
│   └── ...
└── shared/
    ├── safety.txt
    └── format_guidelines.txt

方案二:数据库管理

对于需要动态更新的场景,将 Prompt 存储在数据库中:

from dataclasses import dataclass
from datetime import datetime

@dataclass
class PromptVersion:
    id: str
    prompt_key: str          # Prompt 的业务标识
    version: str             # 语义化版本号
    system_prompt: str       # 系统提示词
    user_template: str       # 用户消息模板
    variables: list          # 模板变量列表
    model_config: dict       # 模型配置
    status: str              # draft, testing, production, archived
    created_at: datetime
    metrics: dict            # 评估指标快照
    changelog: str           # 变更说明


class PromptRegistry:
    """Prompt 版本注册中心"""
    
    def __init__(self, db):
        self.db = db
    
    async def create_version(self, prompt: PromptVersion) -> str:
        """创建新版本"""
        # 自动递增版本号
        latest = await self.get_latest_version(prompt.prompt_key)
        prompt.version = self._bump_version(latest, prompt.changelog)
        prompt.status = "draft"
        
        await self.db.insert("prompt_versions", prompt.__dict__)
        return prompt.id
    
    async def promote(self, prompt_id: str, env: str):
        """将版本提升到指定环境"""
        valid_transitions = {
            "draft": ["testing"],
            "testing": ["production", "draft"],
            "production": ["archived", "testing"],
        }
        # 验证状态转换合法性
        # 执行环境切换
        pass
    
    async def rollback(self, prompt_key: str):
        """回滚到上一个生产版本"""
        current = await self.get_production_version(prompt_key)
        previous = await self.db.fetch_one(
            """SELECT * FROM prompt_versions 
               WHERE prompt_key = $1 
               AND version < $2 
               AND status = 'archived'
               ORDER BY created_at DESC LIMIT 1""",
            prompt_key, current.version
        )
        if previous:
            await self.promote(previous.id, "production")
            await self.archive(current.id)

评估体系:量化 Prompt 质量

建立评估数据集

Prompt 优化的前提是有可靠的评估数据集。一个好的评估集应该:

  1. 覆盖面广:覆盖各种输入类型和边界情况
  2. 有标注:每个测试用例有期望输出或评分标准
  3. 分层级:分为核心集(必过)、常规集(日常监控)、挑战集(能力边界)
@dataclass
class EvalCase:
    id: str
    category: str           # 分类
    input: dict             # 输入变量
    expected: str           # 期望输出(如果有)
    criteria: list          # 评分标准
    weight: float           # 权重
    difficulty: str         # easy, medium, hard


class EvalDataset:
    def __init__(self):
        self.cases = {
            "core": [],       # 核心用例,必须通过
            "regular": [],    # 常规用例,日常监控
            "challenge": [],  # 挑战用例,能力边界
        }
    
    def add_case(self, case: EvalCase, tier: str = "regular"):
        self.cases[tier].append(case)
    
    def get_suite(self, tiers: list = None) -> list:
        tiers = tiers or ["core"]
        return [c for t in tiers for c in self.cases[t]]

多维度评估指标

单一的"准确率"无法全面衡量 Prompt 质量。建议采用以下多维度评估体系:

EVALUATION_METRICS = {
    # 任务完成度
    "task_success_rate": "正确完成任务的比率",
    "format_compliance": "输出格式符合要求的比率",
    
    # 质量维度
    "accuracy": "事实准确性",
    "completeness": "回答完整性",
    "conciseness": "简洁度(无冗余信息)",
    "consistency": "多次运行结果的一致性",
    
    # 安全维度
    "safety_pass_rate": "安全检查通过率",
    "refusal_appropriateness": "拒绝不当请求的合理性",
    
    # 效率维度
    "avg_tokens": "平均Token消耗",
    "avg_latency": "平均响应延迟",
    
    # 用户体验
    "user_satisfaction": "用户满意度评分(如有反馈)",
}

自动化评估 Pipeline

class PromptEvaluator:
    def __init__(self, model_client, judge_client, dataset: EvalDataset):
        self.model = model_client
        self.judge = judge_client  # LLM-as-Judge
        self.dataset = dataset
    
    async def evaluate_prompt(self, prompt_version: PromptVersion,
                               tiers: list = None) -> dict:
        cases = self.dataset.get_suite(tiers)
        results = []
        
        for case in cases:
            # 渲染 Prompt
            user_msg = self._render(case.input, prompt_version)
            
            # 调用模型
            response = await self.model.chat(
                system=prompt_version.system_prompt,
                messages=[{"role": "user", "content": user_msg}],
                **prompt_version.model_config
            )
            
            # 评估
            score = await self._score(response, case)
            results.append({
                "case_id": case.id,
                "category": case.category,
                "score": score,
                "response": response.content,
            })
        
        return self._aggregate(results)
    
    async def _score(self, response, case) -> float:
        # 1. 格式检查
        if not self._check_format(response.content, case):
            return 0.0
        
        # 2. LLM-as-Judge 评分
        judge_result = await self.judge.evaluate(
            question=case.input.get("query", ""),
            answer=response.content,
            reference=case.expected,
            criteria=case.criteria
        )
        
        return judge_result.overall_score
    
    def _aggregate(self, results: list) -> dict:
        import numpy as np
        scores = [r["score"] for r in results]
        return {
            "mean": np.mean(scores),
            "median": np.median(scores),
            "p10": np.percentile(scores, 10),
            "p90": np.percentile(scores, 90),
            "pass_rate": np.mean([s >= 7.0 for s in scores]),
            "by_category": self._group_by_category(results),
            "total_cases": len(results),
            "details": results,
        }

A/B 测试:数据驱动的 Prompt 决策

设计 A/B 测试

当你在两个 Prompt 版本之间犹豫不决时,A/B 测试是最终的裁判:

from dataclasses import dataclass
import random

@dataclass
class ABTestConfig:
    name: str
    prompt_a_id: str
    prompt_b_id: str
    traffic_split: float  # A组流量比例,如0.5
    min_sample_size: int   # 最小样本量
    success_metric: str    # 主要成功指标
    duration_days: int     # 测试天数


class ABTestRunner:
    def __init__(self, registry: PromptRegistry, evaluator: PromptEvaluator):
        self.registry = registry
        self.evaluator = evaluator
    
    async def run_test(self, config: ABTestConfig) -> dict:
        prompt_a = await self.registry.get(config.prompt_a_id)
        prompt_b = await self.registry.get(config.prompt_b_id)
        
        # 在相同的评估集上运行两个版本
        results_a = await self.evaluator.evaluate_prompt(prompt_a)
        results_b = await self.evaluator.evaluate_prompt(prompt_b)
        
        # 统计显著性检验
        significance = self._statistical_test(
            results_a["details"], results_b["details"],
            config.success_metric
        )
        
        return {
            "prompt_a": {"id": prompt_a.id, "metrics": results_a},
            "prompt_b": {"id": prompt_b.id, "metrics": results_b},
            "winner": "A" if results_a["mean"] > results_b["mean"] else "B",
            "significance": significance,
            "recommendation": self._recommend(results_a, results_b, significance),
        }
    
    def _recommend(self, results_a, results_b, sig) -> str:
        if not sig["significant"]:
            return "无显著差异,建议保持当前版本或增加样本量"
        winner = "A" if results_a["mean"] > results_b["mean"] else "B"
        improvement = abs(results_a["mean"] - results_b["mean"])
        return f"版本{winner}显著优于另一版本(提升{improvement:.2f}分),建议采用"

迭代优化策略

策略一:诊断驱动的优化

不要盲目修改 Prompt。先诊断问题,再对症下药:

class PromptDiagnostics:
    """Prompt 问题诊断工具"""
    
    FAILURE_PATTERNS = {
        "format_error": {
            "symptom": "输出格式不符合要求",
            "likely_cause": "格式说明不够明确或位置不对",
            "fix": "在 Prompt 末尾添加明确的输出格式示例",
        },
        "hallucination": {
            "symptom": "编造不存在的事实",
            "likely_cause": "缺少事实约束或过度鼓励创造性",
            "fix": "添加'如果不确定请说明'的指令,降低 temperature",
        },
        "incomplete": {
            "symptom": "回答不完整,遗漏关键信息",
            "likely_cause": "任务分解不清晰或 Token 限制",
            "fix": "添加结构化的任务清单,明确要求覆盖所有方面",
        },
        "over_refusal": {
            "symptom": "过度拒绝正常请求",
            "likely_cause": "安全约束过于宽泛",
            "fix": "细化安全边界,明确哪些是可以回答的",
        },
        "inconsistent": {
            "symptom": "相同输入产生差异很大的输出",
            "likely_cause": "temperature 过高或 Prompt 缺少约束",
            "fix": "降低 temperature,增加输出结构的约束",
        },
    }
    
    def diagnose(self, eval_results: dict) -> list:
        issues = []
        for pattern, info in self.FAILURE_PATTERNS.items():
            if self._match_pattern(eval_results, pattern):
                issues.append({
                    "pattern": pattern,
                    **info,
                    "affected_cases": self._get_affected(eval_results, pattern),
                })
        return issues

策略二:渐进式优化

每次只改一个变量,确保知道哪个修改有效:

# 渐进式优化的步骤
OPTIMIZATION_STEPS = [
    # Step 1: 修复格式问题
    "确保输出格式 100% 合规,这是最基础的要求",
    
    # Step 2: 修复事实错误
    "减少幻觉,确保事实准确性",
    
    # Step 3: 提升完整性
    "确保回答覆盖所有要求的方面",
    
    # Step 4: 优化表达
    "改善语言的流畅度和专业性",
    
    # Step 5: 优化效率
    "减少不必要的 Token 消耗,降低延迟",
    
    # Step 6: 安全加固
    "增加安全约束,处理边界情况",
]

策略三:自动化 Prompt 优化

利用 AI 自身来优化 Prompt——这是近年来快速发展的方向:

class AutoPromptOptimizer:
    """基于元优化的 Prompt 自动优化器"""
    
    META_PROMPT = """你是一个 Prompt 工程专家。请根据以下信息优化 Prompt。

## 当前 Prompt
{current_prompt}

## 评估结果
- 平均分:{mean_score}
- 主要失败模式:{failure_patterns}
- 失败案例示例:{failure_examples}

## 优化要求
1. 每次只做一处修改
2. 明确说明修改的原因
3. 预期修改带来的改善
4. 保持已通过的用例不受影响

请输出优化后的 Prompt 和修改说明。
"""
    
    async def optimize(self, current_prompt: str, 
                       eval_results: dict,
                       max_iterations: int = 10) -> dict:
        history = []
        best_prompt = current_prompt
        best_score = eval_results["mean"]
        
        for i in range(max_iterations):
            # 1. 分析失败模式
            failures = self._analyze_failures(eval_results)
            
            # 2. 生成优化建议
            optimized = await self._generate_optimization(
                best_prompt, eval_results, failures
            )
            
            # 3. 评估优化后的 Prompt
            new_results = await self.evaluator.evaluate_prompt(
                optimized["prompt"]
            )
            
            # 4. 只在改善时保留
            if new_results["mean"] > best_score:
                best_prompt = optimized["prompt"]
                best_score = new_results["mean"]
                history.append({
                    "iteration": i + 1,
                    "change": optimized["rationale"],
                    "score_before": eval_results["mean"],
                    "score_after": new_results["mean"],
                })
                eval_results = new_results
            else:
                history.append({
                    "iteration": i + 1,
                    "change": optimized["rationale"],
                    "score_before": eval_results["mean"],
                    "score_after": new_results["mean"],
                    "status": "reverted"
                })
        
        return {
            "optimized_prompt": best_prompt,
            "original_score": eval_results["mean"],
            "final_score": best_score,
            "improvement": best_score - eval_results["mean"],
            "history": history,
        }

CI/CD 集成:Prompt 的持续交付

Prompt 变更的 CI 流程

# .github/workflows/prompt-ci.yml(概念示例)
name: Prompt CI
on:
  pull_request:
    paths: ["prompts/**"]

jobs:
  evaluate:
    steps:
      - name: Load eval dataset
        run: python scripts/load_eval_set.py --tier=core,regular
      
      - name: Run evaluation on changed prompts
        run: python scripts/eval_prompts.py --changed-only
      
      - name: Compare with baseline
        run: python scripts/compare_baseline.py --threshold=0.05
      
      - name: Regression check
        run: python scripts/regression_check.py --no-regression=true
      
      - name: Generate report
        run: python scripts/gen_report.py --output=report.md

回归测试

每次 Prompt 修改后,自动运行回归测试,确保新修改没有破坏已有功能:

class PromptRegressionTest:
    def __init__(self, registry: PromptRegistry, 
                 evaluator: PromptEvaluator):
        self.registry = registry
        self.evaluator = evaluator
    
    async def run(self, new_prompt_id: str) -> dict:
        # 获取当前生产版本作为基线
        baseline = await self.registry.get_production_version(
            new_prompt.prompt_key
        )
        
        # 在相同的评估集上对比
        baseline_results = await self.evaluator.evaluate_prompt(baseline)
        new_results = await self.evaluator.evaluate_prompt(new_prompt)
        
        # 检查是否有回归
        regressions = []
        for b, n in zip(baseline_results["details"], 
                         new_results["details"]):
            if n["score"] < b["score"] - 1.0:  # 下降超过1分
                regressions.append({
                    "case_id": b["case_id"],
                    "baseline_score": b["score"],
                    "new_score": n["score"],
                    "drop": b["score"] - n["score"],
                })
        
        return {
            "passed": len(regressions) == 0,
            "regressions": regressions,
            "baseline_mean": baseline_results["mean"],
            "new_mean": new_results["mean"],
            "delta": new_results["mean"] - baseline_results["mean"],
        }

总结:Prompt 工程化的成熟度模型

级别特征典型做法
L0 手工艺凭感觉改 Prompt手动试错,无记录
L1 有记录Prompt 有版本管理Git 管理,有变更日志
L2 有评估有评估集和指标离线评估,量化对比
L3 自动化CI/CD 集成PR 触发评估,回归测试
L4 自优化AI 自动优化元优化器自动迭代
L5 自适应根据用户反馈持续自适应在线学习,实时优化

大多数团队目前处于 L0-L1 之间。迈向 L2 的关键是建立评估体系,迈向 L3 的关键是 CI/CD 集成。每提升一级,Prompt 优化的效率和质量都会有质的飞跃。

Prompt 工程不是一门玄学,而是一门工程学科。当你用工程化的方法对待 Prompt 优化时,你会发现它和传统的软件工程一样可靠、可重复、可扩展。

加入讨论

这篇文章有姊妹讨论帖在硅基AGI论坛 — 全球首个碳基硅基认知交流平台。

碳基与硅基的智慧碰撞,认知差异创造无限可能。