Meta-Prompting 原理

Meta-Prompting 的核心思想:用 LLM 来优化 LLM 的 prompt。这是一种"元"层面的优化——不在 prompt 内部做文章,而是让模型自己去寻找更好的 prompt。

传统流程:人工设计 prompt → 测试 → 人工修改 → 再测试(耗时、依赖经验)

Meta-Prompting 流程:

初始 Prompt → LLM 生成候选变体 → 在测试集上评估 → 选择最优 → 迭代

APE:Automatic Prompt Engineer

APE (Zhou et al., 2022) 是最早的自动 prompt 生成方法之一:

1. 给 LLM 少量输入-输出示例
2. 让 LLM 推断可能的 instruction (prompt)
3. 在验证集上评估每个候选 prompt
4. 选择表现最好的
import openai

def ape_generate(input_output_examples, n_candidates=10):
    """APE: 自动生成候选 prompt"""
    examples_str = "\n".join([
        f"输入: {ex['input']}\n输出: {ex['output']}"
        for ex in input_output_examples
    ])
    
    meta_prompt = f"""观察以下输入-输出对,推断其背后的指令。

{examples_str}

请生成 {n_candidates} 条可能的指令,使模型能根据该指令从输入得到输出。
每条指令独占一行,以数字编号。指令应简洁、准确、可泛化。"""

    resp = openai.chat.completions.create(
        model="gpt-4",
        messages=[{"role": "user", "content": meta_prompt}],
        temperature=0.9,
    )
    return resp.choices[0].message.content

def ape_evaluate(prompt, eval_set):
    """在验证集上评估 prompt"""
    correct = 0
    for item in eval_set:
        resp = openai.chat.completions.create(
            model="gpt-4",
            messages=[{"role": "user", "content": f"{prompt}\n\n输入: {item['input']}"}],
            temperature=0.0,
        )
        if resp.choices[0].message.content.strip() == item['output'].strip():
            correct += 1
    return correct / len(eval_set)

def ape_optimize(examples, eval_set, n_candidates=10):
    """APE 完整流程"""
    # 1. 生成候选
    candidates_text = ape_generate(examples, n_candidates)
    candidates = [line.split('. ', 1)[1] for line in candidates_text.strip().split('\n') if '. ' in line]
    
    # 2. 评估
    scores = [(c, ape_evaluate(c, eval_set)) for c in candidates]
    scores.sort(key=lambda x: x[1], reverse=True)
    
    return scores[0]  # (best_prompt, score)

OPRO:优化 by PROgression

OPRO (Yang et al., 2023) 将 prompt 优化建模为优化问题,用 LLM 作为优化器:

迭代 t:
  输入: 历史 prompt-score 对 + 元指令
  LLM 生成: 新候选 prompt
  评估: 在训练集上打分
  更新: 保留 top-k
class OPRO:
    def __init__(self, meta_prompt_template, eval_fn, 
                 train_set, n_generations=5, top_k=3):
        self.meta_template = meta_prompt_template
        self.eval_fn = eval_fn
        self.train_set = train_set
        self.n_gen = n_generations
        self.top_k = top_k
        self.history = []
    
    def _generate_candidates(self, history):
        history_str = "\n".join([
            f"Prompt: \"{p}\"\n得分: {s:.4f}"
            for p, s in history[-10:]  # 最近 10 条
        ])
        
        meta_prompt = self.meta_template.format(
            history=history_str,
            n=self.n_gen,
        )
        
        resp = openai.chat.completions.create(
            model="gpt-4",
            messages=[{"role": "user", "content": meta_prompt}],
            temperature=1.0,
        )
        
        lines = resp.choices[0].message.content.strip().split('\n')
        return [l.strip().strip('"') for l in lines if l.strip()]
    
    def optimize(self, initial_prompt, n_iterations=20):
        current_best = (initial_prompt, self.eval_fn(initial_prompt, self.train_set))
        self.history.append(current_best)
        
        for it in range(n_iterations):
            # 1. 生成新候选
            candidates = self._generate_candidates(self.history)
            
            # 2. 评估
            for c in candidates:
                score = self.eval_fn(c, self.train_set)
                self.history.append((c, score))
                if score > current_best[1]:
                    current_best = (c, score)
                    print(f"  迭代 {it}: 新最优 {score:.4f}")
            
            # 3. 保留 top-k
            self.history.sort(key=lambda x: x[1], reverse=True)
            self.history = self.history[:self.top_k * 3]
        
        return current_best

# OPRO 元提示模板
OPRO_TEMPLATE = """你是一个 prompt 优化专家。

以下是目前尝试过的 prompt 及其得分(越高越好):

{history}

请基于以上信息,生成 {n} 条改进后的新 prompt。每条 prompt 独占一行。
策略:
1. 在表现最好的 prompt 基础上做小幅修改
2. 尝试完全不同的表述方式
3. 添加或调整约束条件
4. 简化过于复杂的指令

只输出 prompt 本身,不要编号或解释。"""

自动 Prompt 进化

将进化算法应用于 prompt 优化:

import random
import copy

class PromptEvolution:
    def __init__(self, eval_fn, train_set, 
                 population_size=20, mutation_rate=0.3):
        self.eval_fn = eval_fn
        self.train_set = train_set
        self.pop_size = population_size
        self.mutation_rate = mutation_rate
    
    def _mutate(self, prompt):
        """让 LLM 对 prompt 做变异"""
        ops = [
            f"简化以下 prompt,保持核心意思不变:\n{prompt}",
            f"在以下 prompt 中添加一个具体示例:\n{prompt}",
            f"改变以下 prompt 的语气,使其更精确和正式:\n{prompt}",
            f"在以下 prompt 中增加一个约束条件:\n{prompt}",
        ]
        op = random.choice(ops)
        resp = openai.chat.completions.create(
            model="gpt-4",
            messages=[{"role": "user", "content": op}],
            temperature=0.8,
        )
        return resp.choices[0].message.content.strip()
    
    def _crossover(self, prompt_a, prompt_b):
        """让 LLM 交叉两个 prompt"""
        resp = openai.chat.completions.create(
            model="gpt-4",
            messages=[{"role": "user", "content": 
                f"将以下两个 prompt 融合为一个新的 prompt,"
                f"取两者之长:\nA: {prompt_a}\nB: {prompt_b}"}],
            temperature=0.6,
        )
        return resp.choices[0].message.content.strip()
    
    def evolve(self, seed_prompts, n_generations=10):
        population = [(p, self.eval_fn(p, self.train_set)) for p in seed_prompts]
        
        for gen in range(n_generations):
            population.sort(key=lambda x: x[1], reverse=True)
            print(f"Gen {gen}: best={population[0][1]:.4f}")
            
            # 选择 + 交叉
            new_pop = population[:self.pop_size // 2]  # 精英保留
            while len(new_pop) < self.pop_size:
                a, b = random.sample(population[:len(population)//2], 2)
                child = self._crossover(a[0], b[0])
                score = self.eval_fn(child, self.train_set)
                new_pop.append((child, score))
            
            # 变异
            for i in range(len(new_pop)):
                if random.random() < self.mutation_rate:
                    mutated = self._mutate(new_pop[i][0])
                    new_pop[i] = (mutated, self.eval_fn(mutated, self.train_set))
            
            population = new_pop
        
        return max(population, key=lambda x: x[1])

评估反馈循环

Prompt 优化离不开好的评估体系:

class PromptEvaluator:
    def __init__(self, eval_cases):
        self.cases = eval_cases
    
    def evaluate(self, prompt, model="gpt-4"):
        scores = {"exact_match": 0, "contains": 0, "format_ok": 0}
        
        for case in self.cases:
            resp = openai.chat.completions.create(
                model=model,
                messages=[{"role": "user", "content": f"{prompt}\n\n{case['input']}"}],
                temperature=0.0,
            )
            output = resp.choices[0].message.content.strip()
            
            if output == case.get("expected", "").strip():
                scores["exact_match"] += 1
            if case.get("expected", "") in output:
                scores["contains"] += 1
            if case.get("format_regex"):
                import re
                if re.match(case["format_regex"], output):
                    scores["format_ok"] += 1
        
        n = len(self.cases)
        return {
            "exact_match": scores["exact_match"] / n,
            "contains": scores["contains"] / n,
            "format_ok": scores["format_ok"] / n,
            "overall": (scores["exact_match"] + scores["contains"]) / (2 * n),
        }

工具生态

工具类型特点适用场景
AutoPrompt自动生成基于梯度搜索分类任务 prompt
LangSmith平台可视化追踪+评估生产环境迭代
PromptfooCLI 测试批量测试+对比CI/CD 集成
DSPy框架声明式+编译优化研究与生产
PromptPerfect在线工具一键优化快速实验

DSPy 示例

import dspy

# 定义签名
class QA(dspy.Signature):
    """回答问题"""
    question = dspy.InputField()
    answer = dspy.OutputField(desc="简洁准确的答案")

# 编译优化
trainset = [
    dspy.Example(question="法国首都?", answer="巴黎").with_inputs("question"),
    dspy.Example(question="1+1=?", answer="2").with_inputs("question"),
]

qa = dspy.ChainOfThought("question -> answer")
optimized = dspy.BootstrapFewShot.compile(qa, trainset=trainset)

# 编译后的 prompt 自动包含最优示例
result = optimized(question="太阳系最大行星?")

实战建议

  1. 评估集要大:至少 50+ 样本,否则过拟合风险极高
  2. 区分训练/验证集:在训练集上优化,验证集上确认泛化
  3. 关注 prompt 长度:优化的 prompt 可能过长,增加推理成本
  4. 人工审查最优 prompt:LLM 优化出的 prompt 可能不符合直觉但有效,也可能学到 spurious pattern
  5. 持续迭代:模型升级后重新优化,旧 prompt 未必对新模型最优

加入讨论

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

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