---
url: /ai/iaf81vyb/index.md
---
## 先聊聊提示词

接触大模型应用开发之后，我发现提示词（Prompt）的组织方式其实挺有讲究的。最开始我也是想到什么就往里面塞，后来慢慢摸索出一些规律。

Prompt 的内容大致可以分成这几类：身份设定、背景设定、参考资料、样例、指令、限制条件。身份设定就是告诉模型"你现在是什么角色"，比如"你是一个资深的后端开发工程师"。背景设定是补充上下文，让模型知道当前的场景。参考资料和样例就是给模型看的例子，指令是你希望它做什么，限制条件则是划定边界。

按照样例数量来分，又可以分成 Zero-Shot、One-Shot、Few-Shot。Zero-Shot 就是不给例子，直接让模型干活；One-Shot 给一个例子；Few-Shot 给多个例子。实际用下来，Few-Shot 的效果通常最好，但代价是占用更多的 token。

把参考资料和样例放在 Prompt 里，这种方式叫做 In-Context Learning。说白了就是让模型在当前上下文里"学习"你要它做的事情。但问题来了：上下文窗口是有限的，你不可能把所有资料都塞进去。塞太多东西，模型的表现反而会下降，这就是所谓的"大海捞针"问题。

所以就需要一个知识库，需要的时候去里面找有用的东西出来——这就引出了 RAG。

## RAG 是什么

RAG，全称 Retrieval-Augmented Generation，翻译过来就是检索增强生成。核心思路很简单：在模型回答问题之前，先做一轮内部知识搜索，把相关的资料找出来，连同问题一起交给模型。

举个实际的例子。假设你在做一个客服机器人，用户问"你们的退货政策是什么"。模型本身不知道你们公司的退货政策，但如果你有一个知识库存着这些信息，RAG 就会先去知识库里检索相关内容，然后把这些内容和用户的问题一起发给模型，让模型基于这些资料来回答。

这个流程拆开来看就是三步：索引（Indexing）、检索（Retrieval）、生成（Generation）。

## 索引：把知识整理好

索引阶段做的事情，就是把你的知识库处理成方便检索的格式。

最常见的方式是把文档切分成小块（Chunk），然后用 Embedding 模型把每个小块转换成向量，存到向量数据库里。听起来简单，但这里面坑不少。

文档切分就有好几种策略。按固定长度切分最简单，但容易把完整的句子或者段落切断，导致语义不完整。按段落切分更合理一些，但段落长度差异可能很大。还有按语义切分的做法，用 NLP 模型识别语义边界，效果更好但成本也更高。

我之前做一个项目的时候，试过把 chunk 大小设成 512 token，重叠 50 token。结果发现有些问题需要跨 chunk 的信息才能回答好，后来改成了 1024 token，重叠 100 token，效果好了不少。但这不是万能的，具体设多少还得看你的数据特点。

Embedding 模型的选择也很关键。目前用得比较多的有 OpenAI 的 text-embedding-3、BGE 系列（比如 bge-m3）、Jina Embeddings 等。选 Embedding 模型主要看几个维度：维度数、支持的语言、最大输入长度、推理速度。如果你的场景涉及中文，BGE 系列是不错的选择，bge-m3 还支持多语言和长文本。

## 检索：找到相关的知识

有了索引，检索阶段就是根据用户的 Query 去向量数据库里找最相关的文档块。

最基础的做法就是算 Query 向量和文档块向量之间的余弦相似度，取 Top K 个最相似的。但实际应用中，单纯靠向量相似度检索经常不够用。

一个问题就是词汇不匹配。用户问"怎么退货"，但文档里写的是"退换货流程"，字面上不一样，向量相似度可能也不高。这时候混合检索就派上用场了——把向量检索和关键词检索（比如 BM25）结合起来，取两者的并集或者用 RRF（Reciprocal Rank Fusion）融合排序。

另一个问题是多跳推理。有些问题不是直接能从一个文档里找到答案的，需要综合多个文档的信息。比如"我们公司最便宜的手机支持快充吗"，你得先找到最便宜的手机是哪款，再去查这款手机是否支持快充。这种场景下，简单的单次检索就不够了，后面会讲到的 Agentic RAG 可以解决这个问题。

## 重排模型：精排的结果更加准确

检索出来的候选文档，质量参差不齐。Embedding 模型做的粗排只能保证大致相关，但要挑出最合适的那几个，还需要一步精排。

重排模型（Reranker）就是干这个的。它和 Embedding 模型的工作方式不一样：Embedding 模型是把 Query 和文档分别编码成向量再算相似度（Bi-Encoder），Reranker 则是把 Query 和文档一起输入模型，让模型直接判断相关性（Cross-Encoder）。

Cross-Encoder 能看到 Query 和文档之间的完整交互，判断更准确。但计算量大，不能提前编码文档，所以不能直接用来检索海量数据。实际的做法是先用 Bi-Encoder 快速召回一批候选（比如 Top 100），再用 Cross-Encoder 精排选出最终的 Top K。

常见的 Reranker 有 BGE-reranker-v2-m3、Cohere Rerank、Jina Reranker 等。我在项目里用过 BGE-reranker-v2-m3，效果确实比单纯用 Embedding 检索好不少，尤其是文档内容比较长、信息比较分散的时候。

不过 Reranker 会增加检索延迟。如果你的应用对响应时间要求很高，需要在效果和速度之间做取舍。

## 查询改写：让机器替你问问题

用户提的问题往往不够精确，直接拿去检索效果不好。查询改写就是用模型把用户的问题转换成更适合检索的形式。

最简单的做法是让模型把用户的问题扩写成更完整的表述。比如用户问"怎么退货"，模型可以改写成"如何申请商品退货，退货流程是什么，需要准备哪些材料"。这样检索的时候能匹配到更多相关内容。

还有一种做法叫 HyDE（Hypothetical Document Embeddings），思路挺有意思：先让模型根据问题生成一个"假设性的答案"，然后用这个假设答案去检索，而不是用原始问题。因为假设答案和真实文档在形式上更接近，检索效果往往更好。

另外，对问题进行多角度改写也很有用。可以用不同的模型来改写，或者把问题翻译成其他语言再翻译回来（回译），这样能产生更多样化的查询变体。把这些变体都拿去检索，取结果的并集，覆盖面会更广。

## 检索评估：怎么知道检索得好不好

做 RAG 项目，检索质量的评估是个绕不开的问题。你得知道你的系统到底检索得怎么样，才能有针对性地优化。

常用的评估指标有召回率（Recall）和精确率（Precision）。召回率衡量的是"相关的文档有没有被找出来"，精确率衡量的是"找出来的文档是不是都相关"。在 RAG 场景下，我更关注召回率——因为漏掉了关键信息，模型就没法生成正确答案；但多召回一些不太相关的文档，模型通常能自己过滤掉。

评估的时候需要构建测试集：准备一批问题，每个问题标注出知识库里哪些文档是相关的。然后跑一遍检索，看检索结果和标注的匹配程度。这个过程比较费人力，但没有捷径。

实际操作中，我建议先手动标注几十个典型问题，跑出基线指标，然后每次改了检索策略或者换了模型，都对比一下指标变化。不用追求完美的测试集，够用就行。

## Agentic RAG：让模型自己决定怎么检索

前面讲的 RAG 流程是线性的：收到问题 -> 检索 -> 生成回答。但现实中很多问题不是一次检索就能搞定的。

Agentic RAG 的思路是引入一个 Agent（智能体）来控制整个检索和生成的过程。Agent 可以根据问题的复杂程度决定策略：简单问题直接回答，复杂问题分步检索，检索结果不满意就换个方式再搜。

比如前面提到的多跳问题"我们公司最便宜的手机支持快充吗"，Agent 可以先检索"最便宜的手机是哪款"，得到结果后再检索"这款手机是否支持快充"。这就是迭代检索。

Agent 还可以判断什么时候不需要检索。大语言模型本身就有一定的知识储备，像《红楼梦》《西游记》这种常识性内容，完全不需要专门建知识库，模型本来就知道。让模型自己判断哪些问题需要检索、哪些不需要，既能节省资源，也能提高响应速度。

还有一种做法叫 Self-RAG，模型在生成过程中会自我反思：当前检索到的资料是否真的支持这个观点？如果不支持，就标记出来或者重新检索。这让生成结果更可靠。

## 总结

RAG 不是什么银弹，但它确实解决了大模型应用中的一个核心问题：如何让模型基于特定知识生成准确的回答。

从提示词工程到 RAG，本质上是一条从"把所有资料塞进 Prompt"到"按需检索、动态组装"的演进路线。提示词工程解决的是"怎么让模型理解你的意图"，RAG 解决的是"怎么给模型提供它需要的知识"。

实际做项目的时候，我的经验是不要一上来就搞得很复杂。先用最简单的方案跑通——固定长度切分、基础 Embedding 检索、直接生成，然后根据实际效果逐步优化。需要精排就加 Reranker，需要处理复杂问题就引入 Agent。每一步优化都要有数据支撑，不要凭感觉。

RAG 这个领域还在快速演进，新的模型、新的策略层出不穷。但不管技术怎么变，核心思路不会变：让模型在正确的时间、拿到正确的知识。
