外观
提示词和RAG
约 2819 字大约 9 分钟
次阅读
2026-04-15
先聊聊提示词
接触大模型应用开发之后,我发现提示词(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 这个领域还在快速演进,新的模型、新的策略层出不穷。但不管技术怎么变,核心思路不会变:让模型在正确的时间、拿到正确的知识。