AI Evolution

6.3 可控生成

在模型对齐的基础上,可控生成技术旨在精确驾驭模型的输出。从基础的提示词工程(如角色设定、格式约束)到引导模型思考的高级技巧(如思维链),再到强制性的结构化输出控制和参数级微调(如温度、Top-p),这些方法共同构成了一个工具箱,让开发者能够确保模型生成的内容在格式、风格和内容上都符合预期。

经过指令微调和对齐优化后,语言模型已经能听懂人话,且大致按照人类期望行为。然而,这些还远远不够。实际应用中,我们希望能够更加精确地控制模型输出的格式、内容、风格甚至是思维过程。这就是 可控生成(Controllable Generation) 技术所要解决的核心问题。

6.3.1 提示词工程

可控生成中,最基础也是最重要的技术是 提示词工程(Prompt Engineering)。简单地说,就是对模型提问时,输入经过精心设计和组合的词句,使其产生更准确、更有用、更给力的输出。

提示词工程有很多技巧,我们先简单介绍三个最基础也是最核心的技巧。

1. 清晰、明确的指令

清晰、明确的指令是控制语言模型输出的第一道防线。

大模型虽然聪明,但它并不能“读心”。模糊的提示会让模型陷入猜测、主观臆断、语义漂移,导致结果不准确、不完整、风格飘忽不定。

比如,当你只说“写一个函数”,模型并不确定你想用什么语言、解决什么问题。但如果你改成:

请用Python编写一个函数,接收两个整数参数,返回它们的最大公约数,使用欧几里得算法实现。

模型的输出就变得更加准确、有用、对齐。

语言模型是通过上下文预测下一个词的,因此越是具体、明确的上下文,越容易收敛出符合预期的输出。尤其在包含多个潜在选择(语言/算法/风格)的场景下,模糊提示等于放任模型“自由发挥”。

除了清晰描述“做什么”,还可以补充“不做什么”,用负面约束增强准确性。

2. 角色设定

大模型的语言风格、知识重点、表达方式会因“身份”设定而发生显著变化。提示词中加入角色设定,可以让模型在语气、知识水平、专业术语、写作风格等方面更贴近你期望的输出风格。比如:

你是一位经验丰富的Python导师。现在需要向初学者解释列表推导式的概念,请用简单易懂的语言,并提供具体例子。

你也可以换一种角色对比查看下同一问题不同角色下的输出对比。

一般模型聊天系统中通常会把角色设定常放在 系统提示(system prompt) 中,这样能影响整个对话过程中的风格,而不仅限于单轮。

3. 输出格式约束

很多时候,我们不仅想让模型“说得对”,还想它“说得整齐”。通过在提示词中明确说明期望的输出格式,我们可以让模型生成结构化内容,如 Markdown、JSON、表格、段落、甚至 LaTeX 公式等。比如:

请分析下面代码的时间复杂度,按照以下格式回答:
算法分析:[简要描述算法逻辑]
时间复杂度:[Big O表示法]
解释:[详细解释为什么是这个复杂度]

6.3.2 引导模型“思考”

初级提示词只是详细说明“做什么”,而高级提示技巧则尝试教模型“怎么做”,从而进一步提高可控性。这里我们介绍三种典型的技巧:

思维链提示(Chain-of-Thought Prompting)

通过引导模型按步骤思考,提升其在推理、计算、规划等复杂任务中的表现。

比如当用户输入“请使用 Python 解决这个编程问题,并展示你的思考过程:给定一个数组,找到其中缺失的最小正整数”,模型的回答如下:

让我一步步思考这个问题:

步骤1:理解问题
- 需要找到数组中缺失的最小正整数
- 正整数指1, 2, 3, 4...
- 如果数组是[1, 2, 4],那么缺失的最小正整数是3

步骤2:分析方法
- 可以使用哈希集合来记录存在的数字
- 从1开始遍历,找到第一个不在集合中的数字

步骤3:实现代码
def find_missing_positive(nums):
    num_set = set(nums)
    i = 1
    while i in num_set:
        i += 1
    return i

这种技巧可以显著提升数学题、逻辑题、代码调试类问题的正确率。

少样本提示(Few-shot Prompting)

通过在提示词中提供几个示例来建立模式,让模型理解期望的输出风格。

比如我们可以这样输入提示词:

请按照下面的风格为函数编写文档注释:

示例1:
def add(a, b):
    """
    计算两个数的和
    
    Args:
        a (int): 第一个数
        b (int): 第二个数
    
    Returns:
        int: 两数之和
    """
    return a + b

现在请为这个函数编写类似风格的文档:
def factorial(n):
    if n <= 1:
        return 1
    return n * factorial(n - 1)

我们在提示词中给定一个或多个示例,再让模型基于示例去输出期望的风格,这种方式有点“举一反三”的意思,模型可以从多个例子中掌握规律后再作答。

自我一致性(Self-Consistency)

让模型多次解决同一个问题,然后选择最一致的答案。比如:

这个编程问题请用三种不同的方法解决,然后比较哪种方法最优:
[问题描述]

方法1:[第一种解法]
方法2:[第二种解法]  
方法3:[第三种解法]

比较分析:[从时间复杂度、空间复杂度、代码可读性等角度比较]
推荐方案:[说明推荐哪种方法及原因]

6.3.3 结构化输出控制

在提示词工程中,我们通过自然语言提示引导模型按照特定格式输出内容,这种方式可以称为“格式引导”或“软约束”。它依赖的是模型对语言指令的理解能力,虽然灵活但也不总是可靠。然而,在真实的生产环境中,模型输出往往需要被其他系统直接解析或用于程序逻辑处理,比如生成符合某种 JSON Schema 的数据,或返回结构化报告。这时,我们就需要一种更强约束、更可靠的生成方式 —— 结构化输出控制(Structured Output Control),也被称为 语法约束生成(Grammar-Constrained Generation)

与提示词引导不同,结构化控制不是“建议模型怎么写”,而是从生成机制层面“硬性规定”模型只能生成满足结构要求的内容。它可以通过语法规则、模板占位符或约束条件,严格限定输出的语法结构与格式范围,从而实现可预测、可校验、可复用的模型输出。

1. 语法引导生成

语法引导(Grammar-Guided Generation) 是通过构建语法规范(如 BNF、JSON Schema、正则等)并将其嵌入到生成过程中的方式。它的原理是在解码过程中动态筛选候选词,只保留那些符合当前语法路径的输出,最终形成一棵合规的“生成树”。

举例来说,如果我们希望模型输出一个特定结构的 JSON,比如用于分析函数性能,我们可以先定义一个 JSON Schema:

{
  "type": "object",
  "properties": {
    "function_analysis": {
      "type": "object",
      "properties": {
        "complexity": {"type": "string"},
        "optimization_suggestions": {"type": "array", "items": {"type": "string"}}
      },
      "required": ["complexity", "optimization_suggestions"]
    }
  }
}

配合语法引导生成的提示词或大模型提供的技术手段,模型将只能生成符合该Schema结构的JSON,否则生成会被拒绝或回退。这对于生成代码分析报告、结构化问答、表单自动填充等任务尤为重要。

2. 约束解码

除了结构上的语法控制,我们还可以对生成内容的语义和行为层面加以约束。这种方式称为 约束解码(Constrained Decoding),通常用于要求模型满足某些功能特征或生成模式。

例如,当我们希望模型生成符合特定编码规范的 Python 函数时,可以设置如下逻辑规则:

constraints = [
    "must_contain_try_except",
    "must_have_docstring",
    "must_use_type_hints"
]

在生成过程中,模型会实时检测是否满足这些语义条件,如未满足则通过惩罚机制回退、重采样,直到符合全部约束。这种方法在代码生成、自然语言计划(NL2Plan)等任务中尤为常见。

3. 模板填充

模板填充(Template Filling) 是一种更直接的结构控制方法。它的思路是预先定义一个格式模板或占位框架,由模型在其中“填空”,避免自由生成带来的不可控性。

例如,一个代码质量评审模板可能如下:

# 代码审查模板
template = """

代码质量评估:
功能正确性:[评分1-10] - [详细说明]
性能效率:[评分1-10] - [详细说明]  
代码风格:[评分1-10] - [详细说明]

改进建议:
1. [具体建议1]
2. [具体建议2]

总体评价:[简短总结]

将这个模板作为生成任务的结构要求,提示模型按模板结构填充内容而非自由写作,可以显著提升输出的可读性、一致性和系统可解析性。这在构建 AI 助手报告、文档生成、结构化写作等场景中非常常见。

6.3.4 安全过滤机制

安全性不仅是在训练模型时靠 RLHF 或 Constitutional AI 就可以完美解决的问题,它还需要在模型实际部署过程中加以运行时控制。

这类控制手段被称为安全过滤机制,可以拦截风险提示、审查模型输出、评估多轮上下文,防止模型生成违法、攻击性或敏感内容。

几种比较典型的方式是:

  • 输入拦截:对用户输入进行关键词检测和语义评估,发现风险提示词时直接拒绝处理;
  • 输出过滤:在模型完成生成后,使用正则、关键词、内容规则等进行拦截;
  • 上下文评估:结合对话上下文、会话历史,识别用户是否试图通过多轮套话绕过安全限制。

虽然它和对齐优化中部分内容目标是相似的,但他们处于不同的阶段,对齐优化属于训练阶段,是“教模型有良知”。而安全过滤属于推理阶段,是“加一道最后的守门人”。从工程角度看,对齐是教育,过滤是监管,两者缺一不可。

6.3.5 参数级控制

除了提示词工程和结构化输出控制,我们还可以通过微调生成参数来影响模型的输出风格和行为。这些参数就像控制台上的旋钮,决定了模型在生成过程中的“胆大”还是“稳重”、“丰富”还是“简洁”、“跳脱”还是“规矩”。

我们介绍三类最常用、最核心的参数控制策略,如果你有使用过 OpenAI、DeepSeek 等大模型的 API,一定会在接口文档中见过这些参数:

温度(Temperature)

温度参数是最直观的行为调节器,它控制的是模型在生成过程中“冒险”的程度,也就是对概率分布的平滑程度。

  • 低温度(如 0.1 ~ 0.3):模型倾向于选择概率最高的词,生成内容稳定、保守、重复度高。适合需要准确、规整输出的场景,如生成代码、填表、问答等。
  • 高温度(如 0.7 ~ 1.0):模型会更愿意尝试低概率词,输出更具创造性、想象力和多样性,但可能伴随逻辑跳跃或偶发性错误。适合写作创意、头脑风暴等场景。

比如我们输入“给我写一首关于雪的短诗”:

Temperature = 0.2:雪落无声,冬夜安静,树影微斜,世界宁明。
Temperature = 0.9:银羽飘舞,思绪滑入月光下的梦,冰凉的孤独被窗灯点燃成诗。

核心采样(Top-p)

Top-p 也叫 nucleus sampling(核心采样),它控制的是模型在每次生成时只从“累计概率前 p 的词”中挑选。与温度不同,Top-p 不是调整分布形状,而是截断低概率选项。

  • 较小的 p 值(如 0.1 ~ 0.3):只考虑最可能的少量词,输出更确定但单一。
  • 较大的 p 值(如 0.8 ~ 0.95):纳入更多可能词汇,输出更丰富、发散,但可能降低精度。

比如我们输入“形容一个程序员的工作状态”:

Top-p = 0.2:安静、专注、持续敲键。
Top-p = 0.9:精神高度集中,像黑夜中的光标,一闪一闪,把思想打磨成代码。

频率惩罚(Frequency Penalty)

频率惩罚参数(在 OpenAI 等 API 中通常为 frequency_penalty)是用于降低模型重复使用同一个词的倾向。这尤其适用于防止生成啰嗦或机械重复的场景。

  • 默认值为 0,表示不惩罚重复。
  • 较高值(如 0.5 ~ 1.0) 会显著降低模型重复使用词语的概率,从而提高表达多样性。

比如我们输入“请描述春天的美好”:

无频率惩罚:春天很美,美在花开,美在阳光,美在绿意,美在希望……
添加频率惩罚:春天是一幅充满活力的画卷,阳光透过枝叶,洒在新生的花蕾上,空气中弥漫着青草的气息。
Edit on GitHub

Last updated on