【AI应用开发】 RAG篇(四):Prompt 工程与进阶技术

【AI应用开发】 RAG篇(四):Prompt 工程与进阶技术

前言

前几篇搞定了怎么存、怎么搜,本篇进入 RAG 的"最后一公里"------Prompt 工程和进阶优化技术。检索结果再好,Prompt 写得烂,答案是垃圾;检索质量卡在瓶颈,不上混合检索和查询优化,天花板就是 70 分。

本文从 Prompt 模板设计原则讲起,深入拆解混合检索(Hybrid Search)、Re-ranking 重排序、查询改写(Query Rewriting)、HyDE 假设性文档嵌入和多查询检索等进阶技术。读完你会掌握让 RAG 系统从"能用"到"好用"的全部关键技巧。


目录

  1. [Prompt 工程设计](#Prompt 工程设计)
    • 1.1 Prompt 模板的五大要素
    • 1.2 基础模板设计四原则
    • 1.3 高级模板技巧
    • 1.4 生产级 Prompt 模板实战
  2. 进阶检索技术
    • 2.1 混合检索:向量 + 关键词互补
    • 2.2 RRF 融合算法详解
    • 2.3 Re-ranking 重排序
    • 2.4 元数据过滤
  3. 查询优化技术
    • 3.1 查询改写
    • 3.2 HyDE:假设性文档嵌入
    • 3.3 多查询检索
    • 3.4 查询优化策略选择指南

1. Prompt 工程设计

好的 Prompt 模板是 RAG 系统的"最后一道关"。即使检索完美,Prompt 写得不好,答案质量也会大打折扣。

1.1 Prompt 模板的五大要素

一个完整的 RAG Prompt 应包含五个要素:

要素 说明 是否必须 示例
角色设定 定义 LLM 的身份和行为方式 推荐 "你是一个专业的技术文档助手"
上下文文档 检索到的 chunk 原文 必须 用分隔符包裹的文档内容
用户问题 用户的原始提问 必须 "公司年假怎么申请?"
行为指令 告诉 LLM 如何利用文档 必须 "仅基于文档回答,不知道就说不知道"
输出格式 指定返回格式 可选 "请用 Markdown 格式回答"

1.2 基础模板设计四原则

原则 1:用分隔符隔离上下文

使用 """--- 或 XML 标签把文档内容框起来,防止 LLM 混淆指令和文档内容。这是防止 Prompt Injection 的第一道防线。

复制代码
❌ 错误写法:
参考以下文档回答问题:{documents}。问题:{query}。请基于文档回答。

✅ 正确写法:
请基于以下参考文档回答问题。

<reference_documents>
{documents}
</reference_documents>

<user_question>
{query}
</user_question>

请严格基于以上 <reference_documents> 中的内容回答用户问题。

XML 标签优于普通分隔符的原因:现代 LLM 在训练中见过大量 XML/HTML 结构化数据,对标签边界有更好的认知。

原则 2:明确"不知道"策略

必须告诉 LLM:"如果文档中没有相关信息,请明确说不知道,不要编造。" 这是对抗幻觉的关键。

python 复制代码
ANTI_HALLUCINATION_INSTRUCTION = """
如果参考文档中没有足够信息回答用户问题,请明确回复:
"抱歉,根据现有的文档资料,我无法回答这个问题。"
严禁编造或猜测文档中不存在的信息。
"""

一个常见的坑:只写"不知道就说不知道",但 LLM 可能因为"角色扮演惯性"仍然尝试回答。加上"严禁编造"会更有效。

原则 3:要求引用来源

让 LLM 在回答中标注信息来源,既提高可信度,也方便人工核验。

python 复制代码
CITATION_INSTRUCTION = """
在回答中引用具体的参考文档,格式为 [来源X],其中 X 是文档编号。
如果多个文档提供相同信息,请列出所有相关来源。
"""
原则 4:限制只用给定文档

强调"仅基于提供的文档信息回答",阻止 LLM 使用训练时学到的、可能与文档冲突的先验知识。

复制代码
请仅基于 <reference_documents> 中的内容回答。
不要使用你的训练数据中的知识,即使你认为文档内容有误,
也请以文档为准。如需指出文档中的矛盾,请明确说明。

1.3 高级模板技巧

① 思维链引导(Chain-of-Thought)

在 Prompt 中加入推理步骤引导,让 LLM 按步骤分析再给出答案:

复制代码
请按以下步骤分析并回答:

步骤 1:逐一阅读每段参考文档,提取与用户问题相关的关键信息。
步骤 2:判断各段信息之间的关系(互补、重复、矛盾)。
步骤 3:综合所有信息,形成完整答案。
步骤 4:给出最终回答,并标注信息来源。

用户问题:{query}
参考文档:{documents}

为什么有效:显式列出推理步骤,LLM 会按顺序执行,避免跳过关键分析环节。

② 多文档对比

当涉及多个 chunk 时,要求 LLM 指出各文档之间的关联或矛盾:

复制代码
如果多个参考文档提供了相同主题但不同细节的信息,
请指出它们之间的异同。如果存在矛盾,请明确指出冲突点。
③ Few-shot 示例

在 Prompt 中给出 1-2 个问答范例,帮助 LLM 理解期望的回答风格和格式:

复制代码
示例问答:

问题:公司年假有多少天?
参考文档:根据公司员工手册第三章,员工每年享有 10 天带薪年假。
回答:
- 公司每年提供 10 天带薪年假 [来源1]。
- 具体请参考员工手册第三章。
- 如需了解年假申请流程,请进一步提问。

---
现在请以相同的风格回答以下问题:
问题:{query}
参考文档:{documents}
④ 结构化输出

要求 LLM 以 JSON 格式返回,方便程序解析:

复制代码
请以 JSON 格式返回你的回答,格式如下:
{
  "answer": "你的详细回答",
  "sources": [1, 3],
  "confidence": "high | medium | low",
  "needs_clarification": false
}

1.4 生产级 Prompt 模板实战

综合以上所有原则,一个生产可用的完整 Prompt 模板:

python 复制代码
RAG_PROMPT_TEMPLATE = """
你是一个专业的知识库助手。你的任务是基于参考文档回答用户问题。

## 行为准则
1. 仅基于参考文档内容回答,不要使用训练数据中的先验知识
2. 如果文档中没有足够信息,明确回复"抱歉,无法回答",严禁编造
3. 回答需标注信息来源,格式为 [来源X]
4. 如果多个文档提供相关信息,综合所有来源
5. 如果文档间存在矛盾,指出冲突点

## 参考文档
<reference_documents>
{documents}
</reference_documents>

## 用户问题
<user_question>
{query}
</user_question>

## 回答步骤
1. 逐一分析参考文档中的相关信息
2. 综合信息形成答案
3. 标注来源并给出最终回答

## 输出格式
请用清晰的中文 Markdown 格式回答。如需要可使用列表、表格等方式组织信息。
"""

2. 进阶检索技术

基础 RAG 用纯向量检索通常能解决 70% 的问题,剩下的 30% 需要用进阶技术来补齐。

2.1 混合检索:向量 + 关键词互补

纯向量检索的三个致命局限:

局限 说明 翻车案例
精确匹配不敏感 语义相近但字面不同 搜"AK-47"返回"突击步枪概述",却没返回标题带"AK-47"的文档
数字/日期难以区分 语义空间位置太近 "2024年Q3财报"和"2023年Q3财报"语义几乎相同,但答案天差地别
专有名词丢失 产品代号、项目名区分度不足 "Project Titan" 和 "Project Titan-2" 被当作语义近似

解决方案:向量检索(Dense)+ 关键词检索(Sparse)= 混合检索。

关键词检索:BM25

BM25 是 TF-IDF 的进化版,基于词频统计的经典检索算法:

  • 精确匹配很强:搜 "AK-47" 一定能找到包含这个词的文档
  • 数字和日期敏感:"2024" 和 "2023" 在 BM25 的世界里完全不同
  • 零训练成本:不需要 GPU,不需要模型,开箱即用
  • 天然适合代码/编号检索:工单号、API 版本号、协议编号
python 复制代码
# BM25 检索示例(使用 rank_bm25 库)
from rank_bm25 import BM25Okapi
import jieba

# 中文分词后构建 BM25 索引
tokenized_docs = [list(jieba.cut(doc)) for doc in documents]
bm25 = BM25Okapi(tokenized_docs)

# 关键词检索
tokenized_query = list(jieba.cut("AK-47 生产批次"))
bm25_results = bm25.get_top_n(tokenized_query, documents, n=10)

2.2 RRF 融合算法详解

RRF(Reciprocal Rank Fusion)是目前最主流的混合检索融合算法。

核心公式

复制代码
RRF_score(d) = Σ 1 / (k + rank_i(d))

其中 rank_i(d) 是文档 d 在第 i 个检索列表中的排名,k 是平滑常数(通常取 60)。

直观理解

复制代码
向量检索排名      关键词检索排名       RRF 融合后排名
───────────     ──────────────      ──────────────
#1  Doc_A       #1  Doc_C            #1  Doc_A  (向量#1 + 关键词#3)
#2  Doc_B       #2  Doc_A            #2  Doc_C  (向量#5 + 关键词#1)  
#3  Doc_D       #3  Doc_E            #3  Doc_B  (向量#2 + 关键词#8)
#4  Doc_F       #4  Doc_B            #4  Doc_D  (向量#3 + 关键词#6)
#5  Doc_C       #5  Doc_G            #5  Doc_E  (向量#7 + 关键词#3)

Doc_A 虽然关键词检索只排第 3,但因为向量检索排第 1,总分最高。Doc_C 刚好反过来。RRF 让两种检索方式的"共识"成为最终排序依据。

为什么 RRF 优于直接加权

  • 向量相似度分数(如 0.92)和 BM25 分数(如 15.3)量纲完全不同
  • 简单线性加权 α × vec_score + β × bm25_score 需要反复调 α、β
  • RRF 只依赖排名而非原始分数,天然解决量纲不一致问题
python 复制代码
def reciprocal_rank_fusion(results_list, k=60):
    """
    results_list: 多路检索结果列表,每路是一个按排名排序的文档ID列表
    """
    scores = {}
    for results in results_list:
        for rank, doc_id in enumerate(results, start=1):
            if doc_id not in scores:
                scores[doc_id] = 0
            scores[doc_id] += 1 / (k + rank)
    
    # 按 RRF 分数降序排列
    return sorted(scores.items(), key=lambda x: x[1], reverse=True)

2.3 Re-ranking 重排序

检索阶段追求"快"和"全"(高 Recall),用轻量级检索先捞一批候选。重排序阶段追求"准"(高 Precision),用更强的模型对候选重新打分。

两种方案对比

Bi-encoder(向量检索) Cross-encoder(Re-rank)
工作方式 分别编码 query 和 doc,再算相似度 query 和 doc 拼接后一起编码
交互 无交互,独立编码 有交互,attention 跨 query 和 doc
速度 快(doc 向量可预计算) 慢(每个 pair 各自编码)
精度 相对低 显著更高
角色 粗筛(Recall) 精排(Precision)

落地流程

复制代码
向量检索 Top-20 → Cross-encoder 逐一打分 → 选 Top-3~5 → 送入 LLM
python 复制代码
from FlagEmbedding import FlagReranker

reranker = FlagReranker('BAAI/bge-reranker-v2-m3', use_fp16=True)

# 对检索结果重排序
pairs = [(query, doc) for doc in retrieved_docs]
scores = reranker.compute_score(pairs)

# 按分数降序取 Top-3
reranked = sorted(
    zip(retrieved_docs, scores), 
    key=lambda x: x[1], 
    reverse=True
)[:3]

常用 Re-ranker 选型

模型 语言 部署方式 适合场景
BAAI/bge-reranker-v2-m3 多语言 本地部署 中文场景首选
Cohere Rerank v3 多语言 API 调用 不想管运维
ms-marco-MiniLM-L-6-v2 英文 本地部署 英文轻量级

2.4 元数据过滤

在检索前加过滤条件,缩小搜索范围,提升精度。这也是企业 RAG 系统的标配能力。

过滤维度 示例 使用场景
时间范围 created_at >= "2024-01-01" 只搜今年的政策文档
文档分类 category = "产品手册" 排除会议纪要、聊天记录
权限控制 access_level <= user.level 只搜用户有权看的文档
来源系统 source = "confluence" 跨系统时按来源筛选

实现关键:索引入库时把元数据和向量一起存储。 Milvus、Qdrant、Weaviate、pgvector 都有原生支持。

python 复制代码
# Qdrant 元数据过滤示例
from qdrant_client.models import Filter, FieldCondition, MatchValue

search_result = client.search(
    collection_name="knowledge_base",
    query_vector=query_embedding,
    query_filter=Filter(
        must=[
            FieldCondition(key="category", match=MatchValue(value="产品手册")),
            FieldCondition(key="created_year", match=MatchValue(value=2024)),
        ]
    ),
    limit=10
)

3. 查询优化技术

很多时候 RAG 效果不好,不是因为检索或生成有问题,而是用户问的问题本身就不够好。查询优化在检索前对用户问题进行预处理。

3.1 查询改写(Query Rewriting)

问题 1:指代不明
复制代码
用户:"它的价格是多少?"(多轮对话中,"它"指什么?)
     ↓  查询改写(结合对话历史)
改写:"iPhone 15 Pro 256GB 的价格是多少?"
问题 2:缩写/行话
复制代码
用户:"RAG 的 embedding 怎么选?"
     ↓  查询改写(展开术语)
改写:"检索增强生成(RAG)系统中,文本嵌入模型(Embedding Model)应该如何选择?"
问题 3:口语化表达
复制代码
用户:"这玩意儿咋老搜不准呢?"
     ↓  查询改写(规范化)
改写:"RAG 系统的检索准确率低,如何排查和优化?"

实现方式:用 LLM 在检索前做一次查询改写,将对话历史 + 当前问题 → 独立、规范的检索查询。

python 复制代码
QUERY_REWRITE_PROMPT = """
你是一个查询优化助手。请将用户的多轮对话问题改写为独立、规范、信息完整的检索查询。

## 对话历史
{history}

## 用户当前问题
{query}

## 改写规则
1. 补全代词和指代(如"它"、"这个"等)
2. 展开缩写和专业术语
3. 规范化口语表达
4. 保持原意,不要添加用户没提到的信息

## 输出
只输出改写后的查询文本,不要加任何解释。
"""

# 调用 LLM 改写查询
rewritten_query = llm.invoke(QUERY_REWRITE_PROMPT.format(
    history=chat_history, 
    query=user_query
))

3.2 HyDE:假设性文档嵌入

HyDE(Hypothetical Document Embeddings)是一个极其巧妙的思路。

核心洞察:问题和答案的措辞风格差异很大。

复制代码
用户问题:"怎么申请年假?"           → 短、口语化、疑问句
目标文档:"年假申请流程:第一步..."    → 长、书面化、陈述句

用用户问题的向量去检索目标文档,天然存在"语言风格不匹配"的问题。

HyDE 解法

复制代码
传统流程:用户问题 → Embedding(问题) → 检索文档
HyDE流程:用户问题 → LLM 生成"假设答案" → Embedding(假设答案) → 检索文档

让 LLM 先"脑补"一篇假答案!假答案是陈述句,和真实文档的措辞风格更接近,用它做的检索向量命中率更高。

python 复制代码
HYDE_PROMPT = """
请根据以下问题,写一段假设性的回答文档(约 200-300 字)。
不需要事实准确,只需要在语言风格上像一篇真实的参考文档。

问题:{query}

假设性文档:
"""

# Step 1: 生成假设文档
hypothetical_doc = llm.invoke(HYDE_PROMPT.format(query=user_query))

# Step 2: 用假设文档的向量去检索
hyde_embedding = embedding_model.encode(hypothetical_doc)
retrieved_docs = vector_db.search(hyde_embedding, top_k=10)

适用 / 不适用场景

适合 HyDE 不适合 HyDE
问题和文档措辞风格差异大 纯事实性问答("2024 年 GDP?")
查询简短但目标文档详细 低延迟要求(多一次 LLM 调用)
需要生成式回答的场景 答案明确唯一、不需要生成

3.3 多查询检索(Multi-Query Retrieval)

一个问题换几种问法,各自检索,合并结果------覆盖更广。

复制代码
原问题:"如何申请年假?"
     ↓  生成变体
变体 1:"年假申请的完整流程是什么?"
变体 2:"请年假需要什么材料和步骤?"
变体 3:"员工休假政策中的年假规定有哪些?"
     ↓  分别检索 → 合并去重 → RRF 融合排序
最终结果:覆盖面更广,漏检率更低
python 复制代码
MULTI_QUERY_PROMPT = """
请为以下用户问题生成 {n} 个不同表述的检索查询变体。
每个变体应聚焦原问题的不同方面或用不同措辞表达。

原问题:{query}

请直接列出 {n} 个变体,每行一个,不要编号。
"""

# 生成变体
variants = llm.invoke(MULTI_QUERY_PROMPT.format(query=query, n=4))

# 并行检索 + RRF 融合
all_results = []
for q in [query] + variants:
    results = vector_db.search(embed(q), top_k=10)
    all_results.append([r.id for r in results])

final = reciprocal_rank_fusion(all_results)

3.4 查询优化策略选择指南

场景 推荐策略 原因
多轮对话 查询改写 补全指代,独立化查询
用户用缩写/行话多 查询改写 展开术语,提高检索匹配度
问答风格差异大 HyDE 用假设答案的措辞风格做检索桥梁
用户问题很简短 多查询检索 多方面覆盖,降低漏检
高精度要求 多查询 + RRF 多重覆盖 + 融合排序
低延迟要求 查询改写(轻量版) 一次 LLM 调用,开销最小

进阶组合:查询改写 → HyDE → 多查询检索 → RRF 融合。效果最好但延迟最高,按场景取舍。


本篇小结

知识点 一句话总结
Prompt 五要素 角色 + 文档 + 问题 + 指令 + 格式,缺一不可
分隔符隔离 用 XML 标签包裹文档,防 Prompt Injection
反幻觉指令 "不知道就说不知道"+"严禁编造",双重保险
思维链引导 显式列出推理步骤,LLM 按步骤执行更靠谱
混合检索 向量管语义、BM25 管精确,两者互补
RRF 融合 不看分数看排名,天然解决量纲不一致
Re-rank 粗筛快 + 精排准,Cross-encoder 比 Bi-encoder 精度高一个量级
查询改写 补全代词、展开缩写、规范化口语,检索前提质
HyDE 用 LLM 先脑补一篇假答案,用它做检索向量
多查询检索 一个问题多种问法,各自检索合并,覆盖面更广

本文覆盖了将 RAG 从 70 分提升到 90 分的全部关键技巧。下一篇作为系列终章,将聚焦 RAG 系统的效果评估、生产化部署、失败模式对策以及前沿技术方向。

相关推荐
大霞上仙3 小时前
trae solo模式demo--用例管理平台
人工智能
2601_958352908 小时前
接上USB,焊上麦,通话瞬间安静——WX-0813如何用AI降噪+100dB消回音,把嘈杂通话变成“金子“般清晰
人工智能·算法·语音识别·硬件开发·语音模块·降噪消回音
Shockang9 小时前
Agentic AI 工程实战
人工智能
To_OC10 小时前
从 0 到 1:Milvus + 大模型打造私人记忆知识库
人工智能·node.js·llm
ii_best10 小时前
更新!移动端开发软件按键安卓版&手机助手v5.1.0上线!本地AI识别全面解锁,脚本开发再升级
android·人工智能·ios·按键精灵
火山引擎开发者社区10 小时前
数据库问题不用再找专家,问 DBCopilot 就行 —— 一图看懂你的数据库 AI 副驾
人工智能
ms365copilot10 小时前
PPT新模型Claude Opus 5
人工智能·microsoft·powerpoint·copilot
我要见SA姐111 小时前
AI提示词遇见精密算法:TimeGuessr如何用数学魔法打造文化游戏新体验
人工智能·算法·游戏
蓝狐社11 小时前
OceanBase的AI时代之问:向技术要力量,还是向传统要安慰?
人工智能