【大模型教程——第四部分:大模型应用开发】第1章:提示工程与上下文学习 (Prompt Engineering & ICL)

第1章:提示工程与上下文学习 (Prompt Engineering & ICL)

"In-Context Learning is meta-learning without gradient descent." ------ 上下文学习本质上是一种无需梯度更新的元学习。本章将深入探讨如何在不更新模型参数的情况下,通过提示工程(Prompt Engineering)和上下文学习(In-Context Learning)激发大模型的潜能,构建复杂的应用系统。


目录

  • 第一节:提示工程最佳实践
    • [1.1 结构化提示词 (Structured Prompt)](#1.1 结构化提示词 (Structured Prompt))
    • [1.2 角色与约束 (Role & Constraints)](#1.2 角色与约束 (Role & Constraints))
    • [1.3 输出控制 (Output Format)](#1.3 输出控制 (Output Format))
  • 第二节:上下文学习 (In-Context Learning)
    • [2.1 Few-Shot Learning 原理](#2.1 Few-Shot Learning 原理)
    • [2.2 动态示例选择 (Dynamic Few-Shot)](#2.2 动态示例选择 (Dynamic Few-Shot))
    • [2.3 实战:构建 Few-Shot 文本分类器](#2.3 实战:构建 Few-Shot 文本分类器)
  • 第三节:思维链推理 (Chain-of-Thought)
    • [3.1 Zero-Shot CoT](#3.1 Zero-Shot CoT)
    • [3.2 Manual CoT](#3.2 Manual CoT)
    • [3.3 Least-to-Most Prompting](#3.3 Least-to-Most Prompting)
  • [第四节:RAG 系统设计模式预览](#第四节:RAG 系统设计模式预览)
    • [4.1 为什么需要 RAG?](#4.1 为什么需要 RAG?)
    • [4.2 基础 RAG 流程](#4.2 基础 RAG 流程)
    • [4.3 模块化 RAG 架构](#4.3 模块化 RAG 架构)
  • 第五节:实战:从零构建智能对话系统
    • [5.1 系统架构设计](#5.1 系统架构设计)
    • [5.2 核心 Prompt 编排](#5.2 核心 Prompt 编排)
    • [5.3 完整代码实现](#5.3 完整代码实现)
  • [第六节:进阶应用:SetFit 与 语义聚类](#第六节:进阶应用:SetFit 与 语义聚类)
    • [6.1 SetFit:少样本分类微调](#6.1 SetFit:少样本分类微调)
    • [6.2 BERTopic:语义主题建模](#6.2 BERTopic:语义主题建模)
  • 本章小结
  • 思考练习
  • 参考资料

第一节:提示工程最佳实践

提示工程(Prompt Engineering)并非玄学,而是与模型沟通的编程语言。SOTA 的提示词设计通常遵循清晰的结构化原则。

1.1 结构化提示词 (Structured Prompt)

一个优秀的 Prompt 应该像代码一样具备模块化结构,通常包含以下要素:

  1. Role (角色):定义 AI 的身份和能力边界。
  2. Context (背景):提供任务背景信息。
  3. Instruction (指令):清晰、动词导向的任务描述。
  4. Data (数据):输入的数据内容。
  5. Output Indicator (输出指引):期望的输出格式。

代码示例

python 复制代码
PROMPT_TEMPLATE = """
### Role
你是一位资深的数据分析师,擅长从非结构化文本中提取关键商业洞察。

### Context
我们收到了一批用户关于"智能咖啡机"的产品反馈,需要整理用户的核心痛点。

### Instruction
请分析以下用户评论,提取出:
1. 情感倾向 (Positive/Negative/Neutral)
2. 核心关键词 (最多3个)
3. 问题摘要 (一句话)

### Data
用户评论:"{user_review}"

### Output Format
请仅输出 JSON 格式,不要包含Markdown标记:
{{
    "sentiment": "...",
    "keywords": ["...", "..."],
    "summary": "..."
}}
"""

1.2 角色与约束 (Role & Constraints)

角色设定不仅是"扮演游戏",它实际上是在潜在空间(Latent Space)中锁定模型的生成模式。

  • 弱角色:"帮我写个代码。"
  • 强角色:"你是一位 Google L5 级别的 Python 工程师,遵循 PEP8 规范,代码需包含类型提示(Type Hints)和 Google 风格的 Docstring。"

约束技巧

  • 负向约束 (Negative Constraints):明确告诉模型不要做什么(例如:"不要使用礼貌用语"、"不要解释代码")。
  • 长度约束:指定字数或段落数。

1.3 输出控制 (Output Format)

在工程化应用中,稳定的输出格式至关重要。

  • JSON Mode :现代 LLM(如 GPT-4o, DeepSeek-V3)通常支持 response_format={"type": "json_object"}
  • Structure Prompting:在 Prompt 末尾给出明确的 Schema 定义。
python 复制代码
# 使用 Pydantic 定义输出结构 (配合 Instructor 库)
from pydantic import BaseModel
from typing import List

class AnalysisResult(BaseModel):
    sentiment: str
    keywords: List[str]
    summary: str

# 这种方式能确保 100% 的格式稳定性

第二节:上下文学习 (In-Context Learning)

核心问题 :如何不重新训练模型,就能让它理解复杂任务?
答案:In-Context Learning (ICL)。利用模型强大的短期记忆(Context Window),直接在 Prompt 中提供示例。

2.1 Few-Shot Learning 原理

Few-Shot Learning(少样本学习)是指在 Prompt 中提供 Input-Output 对 作为示例。

为什么有效?

模型通过注意力机制(Self-Attention)"读取"这些示例,捕捉输入与输出之间的映射关系,并在推理时模仿这种模式。这本质上是一种无需梯度更新的元学习

示例对比

  • Zero-Shot:

    text 复制代码
    这句评论是正面的还是负面的?
    "快递慢得像乌龟。"
  • Few-Shot:

    text 复制代码
    判断评论情感:
    
    输入:"屏幕清晰度很高。"
    输出:正面
    
    输入:"电池用了半天就没电了。"
    输出:负面
    
    输入:"快递慢得像乌龟。"
    输出:

2.2 动态示例选择 (Dynamic Few-Shot)

当任务复杂且示例池很大时,固定示例效果不佳。最佳实践是根据 Query 动态检索最相似的示例

架构设计

  1. 建立一个 示例库 (Example Store) (Input-Output Pairs)。
  2. 为示例库中的 Input 计算 Embeddings,存入向量库。
  3. 用户输入 Query 时,先检索 Top-K 最相似的 Input 示例。
  4. 将这 K 个示例组装进 Prompt。

代码逻辑

python 复制代码
from sentence_transformers import SentenceTransformer, util

embedder = SentenceTransformer('BAAI/bge-large-zh-v1.5')

# 示例库: 输入-输出对
example_corpus = [
    "屏幕清晰度很高。",
    "电池用了半天就没电了。",
    "物流速度超快!"
]
example_labels = ["正面", "负面", "正面"]

example_embeddings = embedder.encode(example_corpus)

def get_dynamic_prompt(query):
    query_emb = embedder.encode(query)
    # 检索最相似的 3 个示例
    hits = util.semantic_search(query_emb, example_embeddings, top_k=3)

    prompt = "参考以下相似案例进行回答:\n\n"
    for hit in hits[0]:
        idx = hit['corpus_id']
        prompt += f"示例输入:{example_corpus[idx]}\n示例输出:{example_labels[idx]}\n\n"

    prompt += f"当前输入:{query}\n输出:"
    return prompt

2.3 实战:构建 Few-Shot 文本分类器

利用 ICL,我们可以快速构建一个高精度的意图分类器,无需任何训练。

(此部分整合了原章节的分类实战内容,但侧重于 Prompt 实现)

python 复制代码
# 核心 Prompt 模板
CLASSIFICATION_PROMPT = """
你是一个智能客服意图识别助手。请参考以下示例,确定用户问题的类别。
类别列表:[账号问题, 支付失败, 物流查询, 售后退换]

示例 1:
用户: "怎么还没发货?都三天了"
类别: 物流查询

示例 2:
用户: "充值成功了但是余额没变"
类别: 支付失败

示例 3:
用户: "我想修改绑定的手机号"
类别: 账号问题

用户: "{query}"
类别:
"""

第三节:思维链推理 (Chain-of-Thought)

3.1 为什么需要 CoT?(数学视角)

在直觉上,CoT (Chain-of-Thought) 是让模型"慢下来思考"。但在数学上,它的本质是引入了隐变量 (Latent Variable) 来解构复杂概率分布。

1. 贝叶斯视角

对于复杂问题(如数学题),直接建模 P ( a n s w e r ∣ q u e s t i o n ) P(answer|question) P(answer∣question) 是非常困难的,因为输入空间到输出空间的映射极其非线性。

CoT 引入了中间推理步骤 z z z (rationale):
P ( a ∣ q ) = ∑ z P ( a ∣ z , q ) ⋅ P ( z ∣ q ) P(a|q) = \sum_{z} P(a|z, q) \cdot P(z|q) P(a∣q)=z∑P(a∣z,q)⋅P(z∣q)

其中:

  • P ( z ∣ q ) P(z|q) P(z∣q):给定问题,生成推理步骤的概率(这一步往往更符合自然语言逻辑,容易建模)。
  • P ( a ∣ z , q ) P(a|z, q) P(a∣z,q):给定推理步骤,生成答案的概率(这一步通常是确定性的)。

2. 计算复杂度视角

Transformer 模型的计算深度(层数)是固定的。对于需要 N N N 步逻辑推理的问题,如果只输出一个 token 的答案,模型必须在有限的层数内完成所有计算。

CoT 允许模型生成 T T T 个 token 的推理过程,这相当于将计算时间线性扩展,用更多的 FLOPs (浮点运算) 换取更高的准确率。

3.2 经典 CoT 模式

(1) Zero-Shot CoT

最著名的"魔法咒语":

"Let's think step by step." (让我们一步步思考)

这句话会显著改变模型的生成概率分布,使其倾向于输出逻辑连接词(如 "First", "Therefore"),从而触发内部的推理电路。

(2) Manual CoT (Few-Shot)

在 Few-Shot 示例中,显式写出推理过程。

示例

text 复制代码
问题:Roger 有 5 个网球,他又买了两罐网球,每罐有 3 个。他现在一共有多少个网球?

思考过程:
1. Roger 起始有 5 个球。
2. 2 罐网球,每罐 3 个,共 2 * 3 = 6 个球。
3. 总数为 5 + 6 = 11 个。
答案:11

3.3 CoT 的缺陷与改进

1. 缺陷 A:错误级联 (Error Cascading)

由于 P ( a ∣ z ) P(a|z) P(a∣z) 强依赖于 z z z,如果推理链中的某一步 z t z_t zt 出现幻觉,后续的所有推理都会基于这个错误前提。

2. 缺陷 B:事后合理化 (Post-hoc Rationalization)

这是一种更隐蔽的缺陷,揭示了理论理想与实践现实的差距

  • 理论模型 (贝叶斯视角):CoT 应该遵循 P ( a ∣ z , q ) ⋅ P ( z ∣ q ) P(a|z,q) \cdot P(z|q) P(a∣z,q)⋅P(z∣q) 的链式推理,即先生成推理步骤 z z z,再基于 z z z 推导答案 a a a。
  • 实践现实 (Post-hoc):模型可能因为训练数据中存在某种"捷径",直接通过 P ( a ∣ q ) P(a|q) P(a∣q) 先"蒙"出了正确答案,然后为了满足 CoT 格式的要求,事后编造一段看似合理的推理过程。

现象 :推理过程 z z z 充满错误逻辑,但最终答案 a a a 竟然是对的。此时 CoT 的数学分解失效,变成了"马后炮"。这说明贝叶斯分解是 CoT 的理想工作机制,但在实际应用中,模型并不总是严格遵循这一机制。

3. 改进方案:Self-Consistency (自洽性)

利用温度采样(Temperature > 0)生成 K K K 条不同的推理路径,然后对最终答案进行投票 (Majority Vote)
a ^ = arg ⁡ max ⁡ a ∑ k = 1 K I ( a k = a ) \hat{a} = \arg\max_{a} \sum_{k=1}^K \mathbb{I}(a_k = a) a^=argamaxk=1∑KI(ak=a)

这利用了大数定律消除了单条推理路径的随机噪声。

3. 进阶结构:Tree of Thoughts (ToT)

将线性的 CoT 扩展为树状结构,允许模型在推理过程中:

  • 分支:探索多种可能性。
  • 回溯:如果当前路径不可行,退回上一步。
  • 评估:对每一步的状态进行自我打分。

3.4 Least-to-Most Prompting

对于极度复杂的问题,采用**"拆解-解决"**策略:

  1. Decomposition: 先让模型把大问题拆解为子问题列表。
  2. Sequential Solving: 逐个解决子问题,把上一步的答案作为下一步的输入。

从推理到检索:CoT 的局限性

CoT 强化了模型的推理能力,但它依然无法解决一个根本问题:知识的边界。无论推理链多么完善,如果模型的参数中没有存储相关知识(例如最新的市场数据、企业内部文档),它只能基于"幻觉"进行推理。

这就引出了下一个核心问题:如何让模型访问外部知识? 这正是 RAG (Retrieval-Augmented Generation) 的使命------将模型的生成能力与外部知识库的检索能力相结合。

第四节:RAG 系统设计模式预览

虽然 RAG (Retrieval-Augmented Generation) 也是提示工程的一种延伸(将检索结果作为 Context),但它已发展为独立领域。本节简要预览,详细内容见下一章。

4.1 为什么需要 RAG?

  • 幻觉 (Hallucination):模型会一本正经地胡说八道。
  • 时效性 (Cutoff Date):模型知识有截止日期(如 2023 年)。
  • 私有数据 (Private Data):模型不知道企业内部文档。

4.2 基础 RAG 流程与代码

Query -> Search(Vector DB) -> Context -> Augmented Prompt -> LLM -> Answer

极简代码示例

python 复制代码
# 1. 检索 (Retrieve)
docs = vector_db.similarity_search("公司Q3营收", k=3)
context = "\n".join([d.page_content for d in docs])

# 2. 增强 (Augment)
prompt = f"基于以下上下文回答问题:\n{context}\n\n问题:公司Q3营收是多少?"

# 3. 生成 (Generate)
response = client.chat.completions.create(
    model="gpt-4",
    messages=[{"role": "user", "content": prompt}]
)

4.3 模块化 RAG 架构

  • RRR 模式:Rewrite (改写问题) -> Retrieve (检索) -> Read (阅读回答)。
  • HyDE:先假设一个答案,用假设答案去检索,再生成真实答案。

第五节:实战:从零构建智能对话系统

本节我们将综合运用 Role, Few-Shot, CoT 技术,构建一个基于文档的智能问答助手

5.1 系统架构设计

系统分为三层:

  1. 输入处理层:Prompt 优化、意图识别。
  2. 上下文层:管理对话历史 (Memory)、检索知识库。
  3. 生成层:调用 LLM API,结构化输出。

5.2 核心 Prompt 编排

python 复制代码
SYSTEM_PROMPT = """
Role:
你是一个专业的金融文档助手。你的任务是依据提供的上下文(Context)回答用户关于财报的问题。

Constraints:
1. 只能基于 Context 回答,不要使用你的外部知识。
2. 如果 Context 中没有答案,请直接回答"根据当前文档无法回答",不要编造。
3. 保持客观、专业,引用 Context 中的数据时要保留 2 位小数。

Thinking Process (CoT):
请先分析用户的意图,然后在 Context 中寻找相关段落,最后整合成答案。
"""

5.3 完整代码实现

python 复制代码
from openai import OpenAI

client = OpenAI()

def chat_bot(user_query, context_chunks):
    # 1. 构建 Context 字符串
    context_str = "\n---\n".join(context_chunks)

    # 2. 组装 Prompt
    messages = [
        {"role": "system", "content": SYSTEM_PROMPT},
        {"role": "user", "content": f"Context:\n{context_str}\n\nQuestion: {user_query}"}
    ]

    # 3. 调用 LLM
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=messages,
        temperature=0.3 # 降低随机性,提高事实准确度
    )

    return response.choices[0].message.content

# 模拟运行
docs = [
    "Q3财报显示,公司营收达到 10.52 亿元,同比增长 15.3%。",
    "净利润为 2.1 亿元,主要得益于云服务业务的扩展。"
]
print(chat_bot("公司Q3营收表现如何?", docs))

第六节:进阶应用:SetFit 与 语义聚类

为了弥补传统 Prompt 在小样本高精度场景下的不足,我们可以引入更重的工程方案。这是连接 Prompt Engineering 与 Fine-tuning 的桥梁。

(本节保留了原"文本分类"章节的精华内容,作为高级实战案例)

6.1 SetFit:少样本分类微调

SetFit (Sentence Transformer Fine-tuning) 是一种无需大规模标注数据的高效分类框架。

  • 原理:先对 Embedding 模型进行对比学习微调(Contrastive Learning),再训练一个分类头(Classification Head)。
  • 优势:在只有 8 个样本/类的情况下,性能可媲美全量微调的 BERT。
python 复制代码
from setfit import SetFitModel, SetFitTrainer
from datasets import load_dataset

# 1. 加载少样本数据 (每类仅需8条)
dataset = load_dataset("SetFit/emotion", split="train[:32]") # 示例

# 2. 初始化模型
model = SetFitModel.from_pretrained("sentence-transformers/paraphrase-mpnet-base-v2")

# 3. 训练 (极快, CPU上几分钟)
trainer = SetFitTrainer(
    model=model,
    train_dataset=dataset,
    loss_class="CosFaceLoss",
    metric="accuracy",
    batch_size=16,
    num_iterations=20, # 对比学习迭代次数
)
trainer.train()

# 4. 推理
preds = model(["This movie is so boring..."])
print(preds)

6.2 BERTopic:语义主题建模

当没有标签时,如何理解大规模文本数据?Prompt Engineering 很难处理全量数据聚类,这时需要 BERTopic

代码实战

python 复制代码
from bertopic import BERTopic
from sklearn.datasets import fetch_20newsgroups

# 1. 加载数据
docs = fetch_20newsgroups(subset='all')['data'][:1000]

# 2. 训练模型 (Embed -> UMAP -> HDBSCAN -> c-TF-IDF)
topic_model = BERTopic(language="english", calculate_probabilities=True)
topics, probs = topic_model.fit_transform(docs)

# 3. 查看主题
topic_model.get_topic_info().head(3)
# 输出: Topic 0: [game, team, ball...], Topic 1: [key, chip, encryption...]

对比:

  • Prompt: 适合处理单条数据的精细理解。
  • BERTopic: 适合对百万级数据进行宏观鸟瞰。

本章小结

本章是应用开发的起点,我们完成了从指令设计系统构建的跨越:

  1. Prompt Engineering :不只是写句子,而是结构化编程(Role, Context, Constraints)。
  2. In-Context Learning :利用Few-Shot动态示例检索,无需训练即可通过图灵测试。
  3. CoT:通过显式思维链,解锁了模型的复杂推理能力。
  4. 实战落地 :通过SetFit等工具,我们将 Prompt 的思想延伸到了轻量级训练领域。

核心心法

"不要试图让模型'猜'你的意图,要像写代码一样,给它清晰、明确、结构化的指令。"


思考练习

  1. Prompt 逆向工程:找一个 ChatGPT 的优秀回答,尝试反推它的 System Prompt 是怎么写的?
  2. CoT 陷阱:在什么情况下,使用 CoT 反而会降低模型的效果?(提示:简单任务或知识检索任务)
  3. Few-Shot 鲁棒性:如果示例中的标签是错误的(Label Noise),LLM 还能正确分类吗?这说明了什么?

参考资料

  1. OpenAI Prompt Engineering Guide : platform.openai.com/docs/guides/prompt-engineering
  2. Chain-of-Thought Paper: "Chain-of-Thought Prompting Elicits Reasoning in Large Language Models" (Wei et al., 2022)
  3. SetFit : https://github.com/huggingface/setfit
  4. Lilian Weng Blog: "Prompt Engineering" (lilianweng.github.io)
相关推荐
LYS_06182 小时前
寒假学习10(HAL库1+模数电10)
学习
runningshark2 小时前
【项目】示波器学习与制作
学习
€8112 小时前
Java入门级教程24——Vert.x的学习
java·开发语言·学习·thymeleaf·数据库操作·vert.x的路由处理机制·datadex实战
自可乐2 小时前
n8n全面学习教程:从入门到精通的自动化工作流引擎实践指南
运维·人工智能·学习·自动化
深蓝海拓3 小时前
PySide6从0开始学习的笔记(二十六) 重写Qt窗口对象的事件(QEvent)处理方法
笔记·python·qt·学习·pyqt
星火开发设计4 小时前
C++ 预处理指令:#include、#define 与条件编译
java·开发语言·c++·学习·算法·知识
BackCatK Chen5 小时前
第 1 篇:软件视角扫盲|TMC2240 软件核心特性 + 学习路径(附工具清单)
c语言·stm32·单片机·学习·电机驱动·保姆级教程·tmc2240
深蓝海拓5 小时前
PySide6从0开始学习的笔记(二十五) Qt窗口对象的生命周期和及时销毁
笔记·python·qt·学习·pyqt
理人综艺好会5 小时前
Web学习之用户认证
前端·学习