分子:将提示与示例结合
"整体大于部分之和。" — 亚里士多德
从原子到分子
在上一节中,我们探讨了原子提示——构成 LLM 交互基本单元的单一指令。现在我们将把这些原子组合成分子:包含示例和模式供模型遵循的结构化上下文。
┌─────────────────────────────────────────────────────────────────────────────┐
│ │
│ 分子 = [指令] + [示例] + [上下文] + [新输入] │
│ │
└─────────────────────────────────────────────────────────────────────────────┘这种分子方法利用了 LLM 的一个强大能力:少样本学习。
少样本学习:通过示例教学
少样本学习是指我们提供所需输入-输出模式的示例,让模型识别并延续该模式。
┌───────────────────────────────────────────────────────────────────────┐
│ 输入:"Paris" │
│ 输出:"Paris is the capital of France." │
│ │
│ 输入:"Tokyo" │
│ 输出:"Tokyo is the capital of Japan." │
│ │
│ 输入:"Ottawa" │
│ 输出:? │
└───────────────────────────────────────────────────────────────────────┘模型识别模式并完成它:"Ottawa is the capital of Canada."
分子优势:可测量的改进
让我们比较原子方法与分子方法处理同一任务的效果:
┌───────────────────────────────────────┬──────────────────────────────────────┐
│ 原子方法 │ 分子方法 │
├───────────────────────────────────────┼──────────────────────────────────────┤
│ "将此评论分类为正面或负面: │ "对评论的情绪进行分类。 │
│ │ │
│ '服务糟糕透了,食物是冷的。'" │ 评论:'食物太棒了!' │
│ │ 情绪:正面 │
│ │ │
│ │ 评论:'等了30分钟,食物是冷的。' │
│ │ 情绪:负面 │
│ │ │
│ │ 评论:'服务糟糕透了,食物是冷的。'" │
│ │ 情绪: │
└───────────────────────────────────────┴──────────────────────────────────────┘分子方法通常实现:
- 更高的准确性(在许多任务上提高 10-30%)
- 更好的一致性(输出方差更低)
- 更好的格式遵守
- 更清晰地处理边缘情况
设计有效的分子模板
分子上下文的结构非常重要。以下是常见的模式:
┌─────────────────────────┐ ┌───────────────────┐ ┌───────────────────┐
│ 前缀-后缀 │ │ 输入-输出对 │ │ 思维链 │
├─────────────────────────┤ ├───────────────────┤ ├───────────────────┤
│ <指令> │ │ <指令> │ │ <指令> │
│ │ │ │ │ │
│ <示例1> → <结果1> │ │ 输入:<示例1> │ │ 输入:<示例1> │
│ │ │ 输出:<结果1> │ │ 思考:<步骤1> │
│ <示例2> → <结果2> │ │ │ │ <步骤2> │
│ │ │ 输入:<示例2> │ │ 输出:<结果1> │
│ <新输入> → │ │ 输出:<结果2> │ │ │
└─────────────────────────┘ │ │ │ 输入:<示例2> │
│ 输入:<新输入> │ │ 思考:<步骤1> │
│ 输出: │ │ <步骤2> │
└───────────────────┘ │ 输出:<结果2> │
│ │
│ 输入:<新输入> │
│ 思考: │
└───────────────────┘每个模板针对不同任务都有其优势:
- 前缀-后缀:最简单,适用于直接的任务
- 输入-输出对:界限清晰,适用于结构化数据
- 思维链:展示推理步骤,最适合复杂任务
示例选择的科学
并非所有示例都是平等的。在为分子上下文选择示例时:
┌──────────────────────────────────────────────────────────────┐
│ 示例选择策略 │
├──────────────────────────────────────────────────────────────┤
│ ✓ 涵盖多样化的案例以展示范围 │
│ ✓ 包含边缘案例以明确边界 │
│ ✓ 在可能的情况下从简单到复杂排序 │
│ ✓ 使用最近或常见的示例(近因和频率偏差) │
│ ✓ 包含近似错误以建立精确边界 │
└──────────────────────────────────────────────────────────────┘测量分子效率
随着上下文大小的增长,token 数量也会增加。让我们实证测量这种权衡:
[准确性]
▲
│ ● 4-shot
│ ● 3-shot
│
│ ● 2-shot
│
│
│ ● 1-shot
│
│
│
│ ● 0-shot
└─────────────────────────────────────────────────►
[Tokens]关键洞察:收益递减。每个额外的示例都会消耗 token,但产生的改进比前一个要少。
找到分子的最佳平衡点
对于大多数任务,存在一个最佳的示例数量,可以平衡质量和 token 效率:
┌─────────────────────────────────────────────────────────────────┐
│ 按任务类型的示例数量启发式规则 │
├───────────────────────────┬─────────────────────────────────────┤
│ 分类 │ 每类 1-3 个示例 │
├───────────────────────────┼─────────────────────────────────────┤
│ 生成 │ 2-5 个示例 │
├───────────────────────────┼─────────────────────────────────────┤
│ 结构化提取 │ 2-4 个示例,涵盖所有字段 │
├───────────────────────────┼─────────────────────────────────────┤
│ 推理 │ 2-3 个带有思考步骤的示例 │
├───────────────────────────┼─────────────────────────────────────┤
│ 翻译 │ 3-5 个不同复杂度的示例 │
└───────────────────────────┴─────────────────────────────────────┘动态分子构建
高级上下文工程涉及为每个输入动态选择最相关的示例:
┌───────────────────────────────────────────────────────────────────┐
│ │
│ 用户查询 │
│ │ │
│ ▼ │
│ ┌─────────────┐ ┌─────────────────┐ │
│ │ 查询 │ │ │ │
│ │ 分析 │─────▶│ 示例 │ │
│ │ │ │ 数据库 │ │
│ └─────────────┘ │ │ │
│ └─────────────────┘ │
│ │ │
│ │ 检索最 │
│ │ 相似的示例 │
│ ▼ │
│ ┌─────────────────┐ │
│ │ 动态 │ │
│ │ 分子 │ │
│ │ 上下文 │ │
│ └─────────────────┘ │
│ │ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ │ │
│ │ LLM │ │
│ │ │ │
│ └─────────────────┘ │
│ │
└───────────────────────────────────────────────────────────────────┘这种方法:
- 分析用户查询
- 检索最相关的示例
- 构建定制的分子上下文
- 将优化的上下文发送给 LLM
付诸实践:简单实现
这是一个从示例构建分子上下文的 Python 函数:
python
def create_molecular_context(instruction, examples, new_input,
format_type="input-output"):
"""
从示例构建分子上下文。
参数:
instruction (str): 任务指令
examples (List[Dict]): 示例输入/输出对的列表
new_input (str): 要处理的新输入
format_type (str): 模板类型(input-output、chain-of-thought)
返回:
str: 完整的分子上下文
"""
context = f"{instruction}\n\n"
# 根据格式类型添加示例
if format_type == "input-output":
for example in examples:
context += f"输入:{example['input']}\n"
context += f"输出:{example['output']}\n\n"
elif format_type == "chain-of-thought":
for example in examples:
context += f"输入:{example['input']}\n"
context += f"思考:{example['thinking']}\n"
context += f"输出:{example['output']}\n\n"
# 添加新输入
context += f"输入:{new_input}\n输出:"
return context关键要点
- 分子上下文将指令与示例结合以提高 LLM 性能
- 少样本学习让模型识别并延续模式
- 模板结构很重要;不同格式适用于不同任务
- 示例选择是一门科学;多样性、边缘案例和排序都很重要
- 收益递减存在;每个额外的示例消耗 token,但收益递减
- 动态构建可以为每个特定输入优化上下文
实践练习
- 进行一个简单的分类任务,测量 0、1、3 和 5 个示例的性能
- 在同一任务上比较不同的模板结构
- 基于与新输入的相似性实现动态示例选择
- 为您关心的任务找到"最小可行分子"
下一步
在下一节中,我们将探讨细胞——在多次交互中维护记忆和状态的上下文结构。
深入探讨:提示工程 vs. 上下文工程
提示工程专注于制作完美的指令。上下文工程包含这一点以及更多:
┌─────────────────────────────────────────────────────────────────────┐
│ 上下文工程层次 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ │
│ │ 状态和记忆 │ 对话历史、持久变量 │
│ └─────────────────┘ │
│ ▲ │
│ │ │
│ ┌─────────────────┐ │
│ │ 检索的数据 │ RAG、工具输出、外部知识 │
│ └─────────────────┘ │
│ ▲ │
│ │ │
│ ┌─────────────────┐ │
│ │ 示例 │ 少样本学习、演示 │
│ └─────────────────┘ │
│ ▲ │
│ │ │
│ ┌─────────────────┐ │
│ │ 指令 │ 提示、系统消息、约束 │
│ └─────────────────┘ │
│ ▲ │
│ │ │
│ ┌─────────────────┐ │
│ │ 模型行为 │ 训练数据、对齐、能力 │
│ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘上下文工程让您可以控制更多这些层次,从而创建更强大的应用程序。