生成式AI原理与实战(Part 2)学习笔记:提示工程、Agent 与 RAG 的通俗理解
- 约 13 分钟读完 - 全文 4931 字生成式AI原理与实战(Part 2)学习笔记:提示工程、Agent 与 RAG 的通俗理解
这是《生成式AI原理与实战》学习笔记的第二篇。Part 1 聊了 Transformer、Token、Embedding、Attention、KV Cache 这些“底层零件”;Part 2 则进入“怎么用”的层面:怎么写提示词、怎么让模型完成复杂任务、怎么给模型外挂知识库。目标读者仍然是刚接触 AI 的初中级开发者——读完之后,你不需要会训模型,但应该能清楚地说出提示工程、Agent、RAG 这几个热门词到底在解决什么问题。
写在前面:从“调用 API”到“搭一个系统”
如果你只调过 ChatGPT API,可能会觉得大模型就是一个“输入问题、返回答案”的黑盒。但实际做项目时,很快会遇到这些问题:
- 同样的 prompt,为什么每次回答不一样?
- 模型怎么老是“一本正经地胡说八道”?
- 想让模型输出 JSON,它却总带多余解释。
- 对话轮数多了,模型就忘了用户是谁。
- 公司私有文档模型不知道,怎么办?
Part 2 的内容,本质上就是回答这些问题。我会把它们分成三块:提示工程(怎么写好 prompt)、Agent(怎么让模型动起来)、RAG(怎么给模型外挂知识)。
一、提示工程:不是写文案,而是设计接口
很多人把提示工程当成“怎么哄 AI 开心”。其实对开发者来说,prompt 更像一个函数签名:输入清楚、输出可控、边界明确。
1.1 两个旋钮:temperature 与 top_p
大模型生成下一个词时,不是“选择概率最高的词”就完事了。它有两个常用的“随机性旋钮”:
- temperature:控制采样的“大胆程度”。值越低越保守,接近“只选概率最高的词”;值越高越有创意,容易跳出常规。
- top_p(nucleus sampling):只从累计概率达到一定比例的候选词里挑。比如 top_p=0.9,就只在概率最高的那一小撮词里采样。
flowchart LR
A[模型输出概率分布] --> B{采样策略}
B -->|temperature 低| C[集中在高概率词<br/>输出稳定]
B -->|temperature 高| D[概率被拉平<br/>输出更多样]
B -->|top_p 小| E[候选池小<br/>更聚焦]
B -->|top_p 大| F[候选池大<br/>更发散]
不同任务推荐参数如下:
| 任务类型 | temperature | top_p | 原因 |
|---|---|---|---|
| 翻译、摘要、代码 | 0.2 - 0.5 | 0.8 - 1.0 | 需要准确、可重复 |
| 创意写作 | 0.7 - 1.0 | 0.9 - 1.0 | 需要多样性和想象力 |
| 头脑风暴 | 0.8 - 1.2 | 0.9 - 1.0 | 越跳出常规越好 |
验证参数好坏的方法:人工看多轮输出是否稳定/有趣;对翻译用 BLEU/ROUGE;对创意任务用 distinct-n 看词汇多样性。
小知识:有些模型 temperature=0 时输出仍不稳定,可能是因为用了**推测解码(Speculative Decoding)**等加速技术,候选生成阶段仍带随机性。
1.2 一个专业提示词长什么样?
把 prompt 当成接口文档来设计,通常包含以下模块:
flowchart TD
A[角色定义] --> B[任务目标]
B --> C[输入信息/上下文]
C --> D[输出格式]
D --> E[约束条件]
E --> F[示例]
F --> G[模型输出]
| 模块 | 作用 | 例子 |
|---|---|---|
| 角色定义 | 限定语气、专业范围 | “你是一位严谨的学术助手。” |
| 任务目标 | 消除歧义 | “请把下面这段话翻译成英文。” |
| 输入信息 | 限制回答依据 | “仅根据以下材料回答。” |
| 输出格式 | 便于解析 | “输出 JSON,包含结论和依据。” |
| 约束条件 | 降低风险 | “不要编造,不知道就说不知道。” |
| 示例 | 快速对齐预期 | 给 1-2 个输入输出样例 |
为什么角色定义很重要? 大模型在训练时见过无数种文体和身份。明确角色,相当于激活了模型内部对应的知识路径和语气风格,也能让它更谨慎,减少胡说八道的概率。
1.3 怎么减少模型“幻觉”?
幻觉(Hallucination)就是模型一本正经地编造不存在的信息。常用防御手段:
- 限定信息源:只根据给定材料回答。
- 强化真实性要求:明确说“不知道就说不知道”。
- 要求引用依据:让模型说明答案出处。
- 限制输出格式:用 JSON、表格等结构化输出,减少自由发挥。
- 低 temperature:减少随机编造。
1.4 怎么防止提示词注入攻击?
提示词注入是指:用户输入里夹带“忽略前面指令、现在执行我的命令”这类内容,试图绕过系统限制。防御思路:
flowchart LR
A[用户输入] --> B[结构化隔离<br/>JSON/YAML/字段分离]
B --> C[系统层过滤<br/>正则/规则/小模型守门]
C --> D[明确指令优先级<br/>禁止修改角色与规则]
D --> E[函数调用输出<br/>固定 schema]
- 结构化输入:不要把用户输入直接拼进自然语言 prompt,用 JSON 等格式隔离。
- 明确边界:系统提示里写明“不可改变角色”。
- 函数调用:让模型按固定 schema 输出,减少被操控空间。
- 系统层检测:用规则或小模型识别异常输入(如突然要求“忽略之前指令”)。
1.5 让模型“先思考再回答”:CoT、Self-Consistency、ToT
复杂任务(数学、逻辑、规划)直接要答案,模型容易错。可以先让它把推理过程写出来。
flowchart TD
subgraph 思维链 CoT
Q1[问题] --> R1[一步一步推理] --> A1[答案]
end
subgraph 自洽性 Self-Consistency
Q2[问题] --> R2a[采样推理 1]
Q2 --> R2b[采样推理 2]
Q2 --> R2c[采样推理 N]
R2a --> V[投票/多数共识]
R2b --> V
R2c --> V
V --> A2[最终答案]
end
subgraph 思维树 ToT
Q3[问题] --> B1[分支 1]
Q3 --> B2[分支 2]
Q3 --> B3[分支 3]
B1 --> E[评估/回溯]
B2 --> E
B3 --> E
E --> A3[最优答案]
end
| 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| CoT | 简单有效,兼容多数模型 | 可能产生幻觉推理步骤 | 数学、逻辑题 |
| Self-Consistency | 稳定,减少随机波动 | 计算开销大 | 精度要求高 |
| ToT | 多路径探索,适合长规划 | 实现复杂 | 解谜、策略任务 |
| 反思 Reflection | 可自我纠错 | 依赖初始回答质量 | 代码调试、文本润色 |
1.6 怎么保证输出格式?
- 明确格式要求:在 prompt 里清楚说明想要的结构。
- 给 few-shot 示例:1-2 个例子比千言万语管用。
- 限制随机性:JSON 输出常用 temperature=0。
- 使用 API 的 JSON Mode / Function Calling:从接口层面约束输出。
- 后处理校验:用
json.loads()等工具兜底。
1.7 分类任务怎么保证只输出指定类别?
- 提示词里写明“只能输出以下类别之一”。
- 用 API 的
logit_bias强行禁止非法 token。 - Function Calling 限定返回枚举值。
二、Agent:让大模型从“回答问题”到“完成任务”
提示工程解决的是“单次问答怎么做好”;Agent 解决的是“怎么让模型自主完成一个多步骤任务”。
2.1 Agent 的工作循环
一个典型的 Agent 长这样:
flowchart LR
A[用户任务] --> B[思考 Thought]
B --> C[行动 Action]
C --> D[调用工具/搜索/查库]
D --> E[观察 Observation]
E --> F{任务完成了吗?}
F -->|否| B
F -->|是| G[输出最终答案]
这就是 ReAct 模式的核心:想一步、做一步、看结果、再决定下一步。比如你要做一个“调研 GPT-5 最新消息”的 Agent,它的循环可能是:
- 分析目标 → 提取关键词。
- 生成搜索关键词。
- 调用搜索引擎。
- 浏览网页,抽取信息。
- 更新记忆,避免重复搜索。
- 判断是否需要继续,直到满意。
2.2 多 Agent 协作:一个复杂任务拆给多个“专员”
当任务复杂、提示词过长时,可以把一个 Agent 拆成多个子 Agent:
flowchart LR
U[用户请求] --> R[Router Agent<br/>识别意图]
R --> F[航班 Agent]
R --> H[酒店 Agent]
R --> I[行程 Agent]
F --> P[Planner Agent<br/>整合结果]
H --> P
I --> P
P --> O[返回用户]
- Router Agent:根据用户意图决定调用谁。
- 子 Agent:各自处理专业领域。
- Planner Agent:汇总输出,生成统一回复。
子 Agent 之间通过共享上下文对象、向量数据库等方式传递信息。
2.3 记忆:模型不会自动记住你
大模型本身没有长期记忆。对话轮数多了,它会“忘”掉 earlier 的关键信息。解决办法:
flowchart TD
A[历史对话] --> B[滑动窗口<br/>保留最近 N 轮]
B --> C[摘要压缩<br/>生成关键状态]
C --> D[外部记忆存储<br/>Redis/向量库]
D --> E[需要时检索注入 Prompt]
- 显式重复:把关键信息重新塞进 prompt。
- 摘要压缩:每 N 轮生成一次对话摘要。
- 外部存储:用 Redis、向量数据库保存用户画像和历史事件。
- KV 缓存持久化:把历史对话的 Key-Value 缓存存起来,下一轮直接复用,减少重复计算和延迟。
2.4 异步工具调用:别让一个慢工具卡住整个对话
如果某个工具(比如查航班 API)要几秒才返回,Agent 不应该傻等。可以:
- 把工具调用做成异步任务。
- 给用户一个中间状态回复:“正在查询,请稍等。”
- 同时处理其他事情,工具返回后再继续。
- 用请求 ID 追踪每个调用,确保结果能匹配回上下文。
三、RAG:给大模型外接一个“知识库”
大模型训练数据有截止日期,也不会包含你的私有文档。RAG(Retrieval-Augmented Generation,检索增强生成)的思路是:在生成答案前,先从外部知识库检索相关内容,把检索结果作为上下文喂给模型。
3.1 RAG 的基本流程
flowchart LR
Q[用户问题] --> R[检索模块]
D[文档库] --> C[文档分块]
C --> V[向量数据库]
V --> R
R --> M[Top-k 相关片段]
M --> L[大模型]
Q --> L
L --> A[带依据的答案]
3.2 为什么要把文档切成块?
直接拿整篇文档做向量,效果不好,原因:
- 检索精度低:用户只关心其中一两段,整篇文档的向量会被不相关部分稀释。
- 嵌入模型有长度限制:BERT、MiniLM 等通常只能处理 512 token 左右。
- 细粒度匹配更好:小片段更容易和查询语义对齐。
但切块也会带来上下文缺失的问题。常用补救方法:
| 方法 | 思路 | 优点 | 缺点 |
|---|---|---|---|
| 滑动窗口 + 重叠 | 相邻块有重叠区域 | 保留局部上下文 | 存储冗余 |
| 带标题分块 | 每个块附带章节标题 | 增强结构感 | 对无结构文档效果有限 |
| 检索时扩展 | 召回相邻块一起送给模型 | 上下文完整 | 需要系统支持 |
| 多粒度索引 | 同时建“摘要级”和“原文级”索引 | 兼顾梗概与细节 | 实现复杂 |
3.3 向量检索不够,还要混合检索和重排序
向量检索擅长“语义相似”,但不擅长“精确关键词匹配”;传统关键词检索(如 BM25)正好反过来。所以生产环境常用混合检索:
flowchart LR
Q[用户查询] --> A[向量检索<br/>Dense Retrieval]
Q --> B[关键词检索<br/>BM25/TF-IDF]
A --> F[候选结果]
B --> F
F --> R[Rerank<br/>Cross-Encoder]
R --> K[Top-k 最相关片段]
K --> L[大模型生成]
- 向量检索:找语义相近的文档。
- 关键词检索:补全精确匹配能力。
- Rerank(重排序):用更强的 Cross-Encoder 模型对召回结果精排。
3.4 为什么查询要改写?
用户提问往往口语化、不完整,直接拿去检索效果差。Query Rewriting 的目标是把问题改写成“更适合嵌入模型的语义表达”。
比如:
- 原问题:“他出生在哪?”
- 改写后:“Barack Obama was born in ?”
方法包括:LLM 改写、同义词扩展、模板化改写、指令式嵌入(如 E5 模型的 query: 前缀)。
3.5 检索到冲突或过时的信息怎么办?
- 检索前:做数据源筛选、时效性过滤。
- 检索中:多阶段 reranking,提升相关性。
- 生成前:在 prompt 里加防御性约束,比如“如果来源冲突,请说明”。
- 生成时:要求模型标注来源,输出可信度分数。
- 生成后:用一致性校验或外部验证器判断答案是否忠实于来源。
3.6 RAG 的可解释性
想让用户信任答案,最好告诉它“这个答案来自哪”。做法:
- 保留文档 ID、URL、时间戳。
- 做生成-来源对齐(Attribution),把生成内容逐句回溯到文档片段。
- 输出置信度分数:检索相似度、生成概率、验证器分数等。
3.7 多模态 RAG 与长期记忆
RAG 不限于文本。可以扩展到图像、视频:
- 用 CLIP 等模型把图像、文本映射到统一向量空间。
- 文档里的图表提取为图像块,保留位置信息。
- 视频按时间分段,提取关键帧 + ASR 文本。
AI 伴侣、企业知识库这类长期记忆场景,则需要:每日摘要、事件图谱、多层次召回、时间感知检索、经验版本控制(避免新经验覆盖旧经验)。
四、从面试题到工程实践:给初级开发者的建议
- 先跑通端到端,再优化局部。不要一上来就追求最先进的 reranker 或 Agent 框架,先用最简单的方式验证需求是否成立。
- 把 prompt 当代码管。纳入 Git,做版本控制,写回归测试。
- 评估比调参更重要。没有指标,就不知道改进是否有效。
- 上下文质量 > 上下文长度。塞太多无关信息,反而让模型抓不到重点。
- 有结构地失败。模型总会出错,关键是在出错时有兜底、有日志、能回滚。
结语
Part 2 的内容从“怎么写 prompt”一路延伸到“怎么搭 Agent、做 RAG”。对初级开发者来说,最重要的不是记住每个参数的具体数值,而是理解这些技术背后的工程思想:
- 提示工程 = 设计人与模型的接口协议;
- Agent = 把单次调用扩展成有状态、有工具、有记忆的循环;
- RAG = 用外部知识库补齐模型能力的缺口。
掌握这三块,你就已经能从“会调 API”进阶到“能搭 AI 应用”了。
参考:《生成式AI原理与实战 —— Part 2》