把同样的提示词跑五遍,只会得到五份同样的错误

N 版本编程默认多样性来自不同的作者。对 LLM 来说,这意味着不同的模型、不同的提供商,甚至不同的训练批次。但这个假设是错的。你可以通过改变提问的方式,而不是提问的内容,从同一个模型中获得有意义的多样性。

问题在于:把 temperature 调到 1.0 然后跑五次提示词算不上策略。你只会得到表层的变化:变量名变了,注释换了位置,但结构完全一致,bug 也完全一样。

如果你希望实现彼此独立地出错,你需要引导的是不同的思维模式,而不是不同的输出。

N 版本编程对 LLM 的真正需求是什么

N 版本编程是一种容错技术:并行执行同一规格的多个独立实现,比较输出结果,通过多数投票决定正确答案。其核心思路是,不同的开发者独立工作时会引入不同的 bug,这些 bug 不会相互关联,因此多数投票可以抑制它们。

这是个老想法,也很昂贵——你要付 N 个团队的钱来做同一件事。

LLM 让这件事便宜到可以尝试。不用 N 个团队,只需 N 次 API 调用。但问题是:用同样的提示词调用同一个模型 N 次,会得到 N 份几乎完全一致的实现。bug 完美相关,多数投票也就失去了意义。

解决方法是把提示词当作开发者,而不是把模型当作开发者。不同的提示词会催生出不同的开发者。

为什么仅靠 Temperature 只能产生表面上的多样性

Temperature 控制的是 token 上的概率分布。高温下,模型会选择概率较低的下一个 token,这会带来措辞、变量命名和表面结构上的变化。

但它不会在算法路径上产生变化。如果你要求写一个寻找最长回文子串的函数,temperature 只会影响你用 left/right 还是 l/r,而不会改变你到底是选择中心扩展法还是动态规划。

对 N 版本编程来说,这毫无用处。你需要的是用不同方式解决问题的实现,而不是看起来不同、实则解法相同的实现。

四种强制算法多样性的提示策略

下面介绍四种能够改变模型思考问题方式的方法。

变化问题框架

同样的任务,用”写一个解析器”和”写一个识别该语法的状态机”来框定,会产生不同的代码。前者可能用递归下降,后者可能用表驱动的方法。

你可以通过让模型在解题前先采纳特定框架来自动化这一过程:

import os
from openai import OpenAI

client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])

def generate_with_framing(task: str, framing: str) -> str:
    prompt = f"""{framing}

Task: {task}

Write a complete, correct implementation. Do not explain your approach."""
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": prompt}],
        temperature=0.7,
    )
    return response.choices[0].message.content

task = "Parse a CSV string into a list of dictionaries, handling quoted fields and newlines within quotes."

framings = [
    "Approach this as a finite state machine with explicit state transitions.",
    "Approach this using recursive descent parsing with a lexer and parser.",
    "Approach this by splitting on delimiters and post-processing edge cases.",
]

for framing in framings:
    print(f"=== {framing} ===")
    print(generate_with_framing(task, framing))

在 GPT-4o 上运行这段代码,状态机框架始终会生成逐字符解析器,并带有显式的状态枚举。递归下降框架会生成词法分析器和独立的解析函数。分割-修复框架则会生成更紧凑但更脆弱的方案。

切换角色

不同的角色会激活不同的知识。系统程序员写的代码与数据科学家或竞赛程序员写的代码截然不同。

personas = [
    "You are a systems programmer who prioritizes memory efficiency and avoids unnecessary allocations.",
    "You are a Pythonic developer who prefers concise, idiomatic code using standard library features.",
    "You are an algorithms researcher who reaches for theoretically optimal solutions even if the code is longer.",
]

角色提示在结构多样性方面出奇地有效。系统程序员会倾向于数组和下标;Pythonic 开发者会倾向于 itertools 和推导式;算法研究员可能会引入某个库,或写出更形式化的解法。

限制可用工具

限制或扩展可用的工具集会迫使模型采用不同的方法。

constraints = [
    "You may only use the Python standard library. No external dependencies.",
    "You may use numpy and pandas. Optimize for vectorized operations.",
    "You must implement this without using regular expressions.",
]

当你知道某种方法存在盲区时,这尤其有用。如果你的正则解析器总是处理不好嵌套引号,那就强制要求一个不使用正则的版本。

思维链配合发散推理

不要直接要求代码,而是让模型先列出多种解决策略,然后选择其中最不明显的一种。

cot_prompt = f"""Task: {task}

First, list three different algorithms or approaches to solve this problem.
Then, pick the one that is most different from the others and implement it.
Do not pick the most obvious approach."""

response = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": cot_prompt}],
    temperature=0.7,
)

思维链迫使模型显式呈现推理过程,“最不同”的约束则把它推向默认解法之外。在实践中,这是单一技巧中能产生最高结构多样性的方法。

局限性:多样性的天花板

同模型多样性有其极限,而且你迟早会碰到。

基础知识的盲区是共享的。如果训练数据中对浮点数比较存在系统性的误解,那么无论怎么换框架、换角色,模型都会复现这种误解。模型只有一套权重,提示词绕不过这一点。

收益递减也同样存在。前三种框架可能分别给你状态机、递归解析器和基于分割的方法;第四种框架可能只给你一个变量名不同的状态机。三到五种真正不同的方法之后,你就是在刮桶底了。

有些技巧会降低质量。“最不同”的约束偶尔会产生一些之所以不同,是因为它们错了的解法。为了不同而不同没有价值。你需要投票或测试机制来筛掉这些糟糕的方案。

今天就能部署的实用方案

如果你要把这套方案集成进系统,不要随机化。要设计你的多样性。

从上面的列表中挑选三到五种技巧,每种技巧生成一个实现。用测试套件或基于属性的测试对所有实现进行验证,保留通过测试的。最终输出采用简单的多数投票。

from collections import Counter

def majority_vote(outputs: list[str], test_fn) -> str:
    passing = [o for o in outputs if test_fn(o)]
    if not passing:
        raise RuntimeError("No implementation passed tests")

    # Exact match voting; swap for AST comparison if needed
    return Counter(passing).most_common(1)[0][0]

测试过滤这一步不可妥协。没有正确性保障的多样性只是噪音。

常见问题

这对更小的模型有效吗?

有效,但多样性天花板更低。小模型的训练数据中包含的不同解决策略更少。你可能只能得到两种真正不同的方法,而不是四种。这些技巧依然有效,只是产生的变化更少。

我到底需要多少个实现?

多数投票的实际最低数量是三。五个能提供更好的覆盖,但成本线性增长。超过五个之后,同模型多样性会退化为表面变化。如果你需要超过五个,就切换到跨模型多样性。

同模型多样性能达到跨模型多样性的水平吗?

不能。不同模型拥有不同的训练数据、架构和微调方式,它们会以真正不同的方式失败。同模型多样性是成本和运维便利性之间的权衡。当你需要快速获得不错的容错能力时用它;当你需要完美的容错能力时,不要用它。

我可以组合这些技术吗?

完全可以。角色提示加上工具约束和思维链步骤,会比任何单一技巧产生更多的多样性。代价是更长的提示词和每次生成消耗更多 token。对于关键代码路径,这些额外的 token 是值得的。

先尝试什么

从变化问题框架开始。这是最容易实现的,也能产生最稳定的结构多样性。如果还需要更多,再加入角色切换。把跨模型多样性留给同模型多样性触顶的场景。

在让它们投票之前,先把所有实现跑一遍相同的测试套件。一个未经测试的多样性实现,不过是你还没碰上的 buggy 实现而已。