您的位置:首页 > 手游攻略 > 第十三章 检索增强提示工程

第十三章 检索增强提示工程

作者:互联网  时间: 2026-07-24 17:35:02  

大语言模型的知识受限于训练数据截止日期,且存在幻觉风险。检索增强生成(Retrieval-Augmented Generation, RAG)通过将外部知识库与语言模型相结合,让 AI 能够基于真实、鲜活的文档内容生成回答,从根本上提升了回答的准确性和可信度。

第13章 检索增强提示工程

本章将系统性地讲解 RAG 技术与提示词的协同工程,从基本原理到高级优化策略,帮助您构建可靠的检索增强应用。

13.1 RAG 技术的基本原理与工作流程

13.1.1 核心概念

RAG(检索增强生成) 是一种将信息检索系统与文本生成模型相结合的技术架构。核心思路是"先检索、再生成":先从外部知识库中检索与用户问题相关的文档片段,再将这些片段注入到提示词上下文中,最后让大模型基于检索到的上下文生成回答。

┌─────────────────────────────────────────────────────────────────┐│ RAG 系统工作流程│├─────────────────────────────────────────────────────────────────┤│ ││┌──────────────┐ │││原始文档│──▶ 索引阶段│││ (PDF/网页等) │▼││└──────────────┘┌────────────────┐││ │文档切块 │││ └───────┬────────┘││ ▼││ ┌────────────────┐││ │向量化嵌入 │││ └───────┬────────┘││ ▼││ ┌────────────────┐││ │存入向量数据库 │││ │(Pinecone等)│││ └────────────────┘││ ││┌──────────────┐ │││用户查询│──▶ 检索阶段││└──────────────┘▼││ ┌────────────────┐││ │查询向量化 │││ └───────┬────────┘││ ▼││ ┌────────────────┐││ │相似度搜索│││ │(Top-K)│││ └───────┬────────┘││ ▼││ ┌────────────────┐││ │返回相关文档 │││ └────────────────┘││ ││┌──────────────┐ │││相关文档│──▶ 生成阶段│││(上下文)│▼││└──────────────┘┌────────────────┐││ │构建增强提示 │││ │(RAG Prompt) │││ └───────┬────────┘││ ▼││ ┌────────────────┐││ │LLM 生成回答 │││ └────────────────┘││ │└─────────────────────────────────────────────────────────────────┘

13.1.2 RAG 的三大核心阶段

阶段一:索引(Indexing)

将原始文档转换为可检索的向量表示:

import voyageai# Anthropic 官方推荐的嵌入模型vo = voyageai.Client()documents = ["The Mediterranean diet emphasizes fish, olive oil, and vegetables...","Photosynthesis in plants converts light energy into glucose...","Machine learning is a subset of artificial intelligence...",]# 嵌入文档(使用 document input_type)doc_embeddings = vo.embed(documents,model="voyage-3",input_type="document").embeddings# 将向量存入向量数据库(以 Pinecone 为例)import pineconepc = pinecone.Client()index = pc.Index("knowledge-base")for i, embedding in enumerate(doc_embeddings):index.upsert([(f"doc-{i}", embedding, {"text": documents[i]})])

阶段二:检索(Retrieval)

将用户查询转换为向量,并在向量数据库中搜索最相关的内容:

# 嵌入用户查询query = "What is machine learning?"query_embedding = vo.embed([query],model="voyage-3",input_type="query").embeddings[0]# 在向量数据库中搜索# Voyage 嵌入已归一化,点积等于余弦相似度response = index.query(vector=query_embedding,top_k=5,include_metadata=True)# 获取检索结果retrieved_docs = [match["metadata"]["text"] for match in response["matches"]]print(retrieved_docs)

阶段三:生成(Generation)

将检索到的文档作为上下文,让 LLM 生成回答:

# 构建 RAG 提示词rag_prompt = f"""请根据以下参考文档回答用户问题。如果参考文档中没有包含回答所需的信息,请明确说明"根据已知信息无法回答"。不要基于训练知识编造答案。参考文档:---{chr(10).join([f'[文档{i+1}] {doc}' for i, doc in enumerate(retrieved_docs)])}---用户问题:{query}回答:"""# 调用 LLM 生成response = client.chat.completions.create(model="gpt-4",messages=[{"role": "system", "content": "你是一个基于文档回答问题的助手。"},{"role": "user", "content": rag_prompt}],temperature=0# RAG 场景通常使用确定性生成)

13.1.3 Anthropic 的 Citations 功能

Anthropic Claude 提供原生引用功能,可自动将回答中的声明关联到源文档的具体位置:

import anthropicclient = anthropic.Anthropic()response = client.messages.create(model="claude-opus-4-7",max_tokens=1024,messages=[{"role": "user","content": [{"type": "document","source": {"type": "text","media_type": "text/plain","data": "The grass is green. The sky is blue."},"title": "My Document","citations": {"enabled": True},},{"type": "text", "text": "What color is the grass and sky?"},],}],)# 响应中自动包含引用信息print(response.content)# [{type: "text", text: "According to the document, "},#{type: "text", text: "the grass is green", citations: [{cited_text: "The grass is green.", ...}]}]

13.1.4 RAG 的适用场景与优势

最佳适用场景:

  • ✅ 需要最新信息的任务:新闻查询、股价、天气
  • ✅ 私有知识库问答:企业文档、内部知识库
  • ✅ 长文档理解与问答:合同分析、研究报告
  • ✅ 减少幻觉:要求回答基于特定文档时

RAG vs 纯 LLM 的优势:

维度纯 LLMRAG
知识时效性受限于训练数据可实时更新
幻觉风险较高显著降低
可追溯性可引用来源
隐私安全无法控制可限制数据范围
成本固定检索+生成

13.2 检索结果的提示词融合方法

13.2.1 基本融合模板

将检索结果注入提示词的核心原则:

def build_rag_prompt(query: str, retrieved_docs: list[str],include_citations: bool = True) -> str:"""构建 RAG 提示词核心设计原则:1. 明确告知模型上下文边界2. 提供无法回答时的退出路径3. 为每个文档块添加来源标识4. 控制上下文总量避免注意力分散"""# 构建参考文档部分docs_section = "nn".join([f"【来源 {i+1}】n{doc}" for i, doc in enumerate(retrieved_docs)])prompt = f"""你是一个专业的问答助手。请根据以下参考文档回答用户问题。## 参考文档{docs_section}## 回答要求1. 仅基于上述参考文档中的信息进行回答2. 不要使用文档之外的训练知识3. 如果文档中没有包含回答问题所需的信息,请明确说明"根据提供的文档无法回答此问题"4. 在回答中使用【来源X】标注信息来源## 用户问题{query}## 回答"""return prompt

13.2.2 检索结果排序与截断策略

排序策略:

def rank_and_truncate(retrieved_docs: list[dict],# [{"text": ..., "score": ...}, ...]max_docs: int = 5,min_score: float = 0.7) -> list[dict]:"""检索结果排序与截断策略:1. 按相似度得分从高到低排序2. 设定最低分数阈值过滤低相关结果3. 限制返回文档数量"""# 按相似度降序排序sorted_docs = sorted(retrieved_docs, key=lambda x: x["score"], reverse=True)# 过滤低分结果filtered_docs = [doc for doc in sorted_docs if doc["score"] >= min_score]# 截断到最大数量return filtered_docs[:max_docs]

Token 预算截断:

import anthropicclient = anthropic.Anthropic()def truncate_by_token_budget(query: str,retrieved_docs: list[str],max_input_tokens: int = 80000,# 保留一些空间给系统提示和问题model: str = "claude-sonnet-4-6") -> list[str]:"""按 token 预算截断文档"""# 构建测试提示test_prompt = f"Query: {query}nnDocs:n" + "nn".join(retrieved_docs)# 计算当前 token 数count = client.messages.count_tokens(model=model,messages=[{"role": "user", "content": test_prompt}])# 如果超预算,按顺序移除文档直到满足要求while count.input_tokens > max_input_tokens and retrieved_docs:retrieved_docs.pop()test_prompt = f"Query: {query}nnDocs:n" + "nn".join(retrieved_docs)count = client.messages.count_tokens(model=model,messages=[{"role": "user", "content": test_prompt}])return retrieved_docs

13.2.3 上下文位置效应

"Lost in the Middle" 现象:当相关信息放置在输入中间时,模型准确率可能下降超过 30%。研究表明,Transformer 对开头和结尾的信息关注度最高,形成 U 型注意力曲线。

应对策略:

def position_sensitive_rerank(query: str,retrieved_docs: list[dict],top_k: int = 10,strategy: str = "interleave"# interleave | front_weight | back_weight) -> list[dict]:"""位置敏感的重排序策略策略说明:- interleave: 将文档交替放置,利用首尾效应- front_weight: 将高分文档放在前面- back_weight: 将高分文档放在后面"""sorted_docs = sorted(retrieved_docs, key=lambda x: x["score"], reverse=True)if strategy == "interleave":# 交替放置:高分组、低分组交替high, low = sorted_docs[:top_k//2], sorted_docs[top_k//2:top_k]result = []for i in range(len(high)):result.append(high[i])if i < len(low):result.append(low[i])return resultelif strategy == "front_weight":return sorted_docs[:top_k]elif strategy == "back_weight":# 翻转:将低分文档放在前面,高分放在后面docs = sorted_docs[:top_k]return docs[::-1]return sorted_docs[:top_k]

13.2.4 多样性重排序

避免检索结果过于相似(内容重复),增加多样性:

def diverse_rerank(retrieved_docs: list[dict],top_k: int = 5,diversity_threshold: float = 0.8) -> list[dict]:"""多样性重排序:避免返回过于相似的文档"""result = []remaining = retrieved_docs.copy()while len(result) < top_k and remaining:# 每次选择当前最高分且与已有结果不太相似的文档best_idx = 0best_score = -float('inf')for i, doc in enumerate(remaining):# 综合相关性得分和多样性diversity_penalty = 0for selected_doc in result:# 简单相似度估计(实际可用嵌入余弦相似度)similarity = len(set(doc["text"].split()) & set(selected_doc["text"].split())) / len(set(doc["text"].split()) | set(selected_doc["text"].split()))diversity_penalty += similarity * diversity_threshold 综合得分 = doc["score"] - diversity_penaltyif 综合得分 > best_score:best_score = 综合得分best_idx = iresult.append(remaining.pop(best_idx))return result

13.3 分块与引用提示:让模型准确引用来源

13.3.1 文本分块策略

策略一:固定大小分块(Fixed-Size Chunking)

最简单的分块方式,按固定 token 数或字符数切分:

def fixed_size_chunking(text: str,chunk_size: int = 500,# tokenschunk_overlap: int = 50# tokens) -> list[str]:"""固定大小分块"""tokens = text.split()# 简化的 token 化chunks = []start = 0while start < len(tokens):end = start + chunk_sizechunk = " ".join(tokens[start:end])chunks.append(chunk)# 移动窗口(考虑重叠)start = end - chunk_overlapreturn chunks

策略二:递归分块(Recursive Chunking)

LangChain 官方推荐的通用文本分割器,按分隔符优先级递归切分:

from langchain_text_splitters import RecursiveCharacterTextSplitter# 中文文本处理text_splitter = RecursiveCharacterTextSplitter(separators=["nn",# 优先按段落切分"n",# 其次按句子换行"。",# 中文句号"!","?",",",# 中文逗号" ","",# 最后按单词/字符],chunk_size=500,chunk_overlap=50,)chunks = text_splitter.split_text(long_chinese_document)

策略三:语义分块(Semantic Chunking)

基于嵌入向量的语义相似性对句子进行分组:

def semantic_chunking(text: str,sentences: list[str],similarity_threshold: float = 0.5) -> list[str]:"""语义分块:根据句子间语义相似度进行分组核心思想:当相邻句子的语义相似度低于阈值时,进行切分"""if not sentences:return [text]# 计算相邻句子的嵌入相似度embeddings = vo.embed(sentences, model="voyage-3", input_type="document")chunks = []current_chunk = [sentences[0]]for i in range(1, len(sentences)):# 计算相似度similarity = embeddings[i-1] @ embeddings[i]# 点积=余弦相似度if similarity >= similarity_threshold:# 相似度高,继续当前块current_chunk.append(sentences[i])else:# 相似度低,保存当前块并开始新块chunks.append("".join(current_chunk))current_chunk = [sentences[i]]# 保存最后一个块if current_chunk:chunks.append("".join(current_chunk))return chunks

13.3.2 让模型准确引用来源的提示技术

方法一:基于提示词的引用(通用方案)

citation_prompt = """请根据以下参考文档回答问题。## 参考文档{formatted_docs}## 回答要求1. 仅基于参考文档中的信息回答,不要编造信息2. 在回答的每个事实性声明后,使用 [来源X] 的格式标注信息来源3. 如果某个信息来自多个来源,标注所有相关来源,如 [来源1, 来源3]4. 如果文档中没有相关信息,请明确说明## 用户问题{query}## 回答(请使用引用标注)"""

方法二:使用 Anthropic Citations API(推荐)

Anthropic 的 Citations 功能提供了比纯提示词方案更可靠的引用机制:

def rag_with_citations(query: str, documents: list[str]) -> dict:"""使用 Anthropic Citations 实现结构化引用"""response = client.messages.create(model="claude-opus-4-7",max_tokens=1024,messages=[{"role": "user","content": [{"type": "document","source": {"type": "content","content": [{"type": "text", "text": doc} for doc in documents],},"title": "知识库文档","citations": {"enabled": True},},{"type": "text", "text": query},],}],)# 解析响应中的引用信息answer_text = ""citations = []for block in response.content:if block.type == "text":answer_text += block.textif hasattr(block, 'citations') and block.citations:citations.extend(block.citations)return {"answer": answer_text,"citations": citations}

13.3.3 引用格式设计

格式类型示例适用场景
编号引用"...根据研究[1],效果提升了30%..."学术、技术文档
括号来源"...(来源:产品手册第5页)..."面向终端用户
脚注引用"...效果显著提升[^1]"长文档、报告
Claude Citations自动生成带位置的引用API 集成应用

13.4 多轮检索与动态提示生成

13.4.1 查询重写(Query Rewriting)

查询重写是将用户原始查询转换为更适合检索的形式:

def rewrite_query_for_retrieval(query: str, history: list[str] = None) -> str:"""查询重写:优化查询以获得更好的检索结果"""rewrite_prompt = f"""你是一个查询优化专家。将以下用户查询重写为更适合信息检索的形式。重写原则:1. 识别查询中的核心概念和关键词2. 补充可能的同义词和相关术语3. 将模糊问题改写为更精确的检索 query4. 如果有对话历史,将其整合到当前查询中{ '对话历史:n' + 'n'.join([f'- {h}' for h in history]) + 'n' if history else '' }原始查询:{query}重写后的查询:"""response = client.chat.completions.create(model="gpt-4",messages=[{"role": "user", "content": rewrite_prompt}],temperature=0)return response.choices[0].message.content.strip()

13.4.2 HyDE(Hypothetical Document Embedding)

HyDE 不直接改写 query,而是让 LLM 先根据 query 生成一个"假设性答案文档",然后用这个假设文档的嵌入向量替代原始 query 的嵌入向量进行检索。

def hyde_retrieval(query: str, documents: list[str]) -> str:"""HyDE 检索:使用假设性文档增强检索效果核心原理:假设性答案通常比原始问题包含更多领域术语,在嵌入空间中与真实相关文档的距离更近。"""# 步骤1:让 LLM 生成假设性答案文档hyde_prompt = f"""请写一段详细的内容,回答以下问题。这段内容应该是对问题的全面解答,不需要完全准确,但要有信息量。问题:{query}假设性内容:"""hypothetical_doc = client.chat.completions.create(model="gpt-4",messages=[{"role": "user", "content": hyde_prompt}],temperature=0.7).choices[0].message.content# 步骤2:用假设文档的嵌入进行检索hypothetical_embedding = vo.embed([hypothetical_doc],model="voyage-3",input_type="document"# 注意:使用 document 而非 query).embeddings[0]# 步骤3:在向量数据库中搜索similarities = [hypothetical_embedding @ doc_emb for doc_emb in doc_embeddings]top_indices = sorted(range(len(similarities)), key=lambda i: similarities[i], reverse=True)[:5]# 步骤4:用真实文档生成最终回答retrieved_docs = [documents[i] for i in top_indices]final_prompt = f"""基于以下文档回答问题:文档:{chr(10).join([f'{i+1}. {doc}' for i, doc in enumerate(retrieved_docs)])}问题:{query}回答:"""final_response = client.chat.completions.create(model="gpt-4",messages=[{"role": "user", "content": final_prompt}])return final_response.choices[0].message.content

13.4.3 多轮检索策略

迭代检索:基于前一轮结果进行补充检索:

def multi_round_retrieval(query: str,max_rounds: int = 3,initial_k: int = 5,expand_k: int = 3) -> list[str]:"""多轮检索:迭代扩展检索结果"""all_docs = []seen_ids = set()for round_num in range(max_rounds):if round_num == 0:# 第一轮:直接检索current_query = queryelse:# 后续轮次:基于前一轮回答生成追问current_query = generate_followup_query(original_query=query,previous_docs=all_docs[-initial_k:],previous_answer=last_answer)# 检索results = retrieve_documents(current_query, top_k=initial_k if round_num == 0 else expand_k)# 去重合并for doc in results:if doc["id"] not in seen_ids:all_docs.append(doc)seen_ids.add(doc["id"])# 检查是否需要继续if round_num > 0 and should_stop(all_docs, query):breakreturn all_docs

13.5 RAG 与提示词工程的协同优化

13.5.1 RAGAS 评估指标体系

RAGAS 是最主流的 RAG 系统自动评估框架:

from ragas import EvaluationDatasetfrom ragas.dataset_schema import SingleTurnSamplefrom ragas.metrics import Faithfulness, AnswerRelevancy, ContextPrecision# 构建测试数据集data_samples = [SingleTurnSample(user_input="What is Ragas?",response="Ragas is an evaluation framework for LLM applications",reference="Ragas is an evaluation framework for LLM applications",retrieved_contexts=["Ragas provides objective metrics for evaluation."],),]dataset = EvaluationDataset(samples=data_samples)# 运行评估results = dataset.evaluate(metrics=[Faithfulness(), AnswerRelevancy(), ContextPrecision()])print(results)

三大核心指标:

指标含义衡量内容
Faithfulness忠实度生成回答与上下文中事实的一致性
Answer Relevancy答案相关性回答与用户问题的相关程度
Context Precision上下文精确度相关文档排在前面、不相关文档排在后面的能力

13.5.2 提示词对 RAG 效果的影响

关键因素:

  1. 上下文位置:将检索到的文档放在提示词的前部还是后部会影响模型注意力分配
  2. 指令明确性:明确要求"仅基于提供的文档回答"可以显著降低幻觉率
  3. 来源标注要求:要求模型标注来源可以同时提高忠实度和可追溯性
  4. 格式约束:指定回答格式(如结构化输出)可以提升回答的一致性
  5. 不确定性处理:提供"不知道"的退出路径可以减少编造

13.5.3 端到端 RAG 优化框架

class RAGOptimizer:"""RAG 系统优化器"""def __init__(self):self.retriever = Noneself.generator = Noneself.evaluator = Nonedef optimize(self, train_samples: list, n_iterations: int = 5):"""迭代优化 RAG 系统"""best_score = 0best_config = {}for iteration in range(n_iterations):# 1. 使用当前提示词生成回答responses = []for sample in train_samples:query = sample["query"]docs = self.retriever.retrieve(query)prompt = self.build_prompt(query, docs)response = self.generator.generate(prompt)responses.append({"query": query,"response": response,"retrieved_docs": docs})# 2. 评估当前性能scores = self.evaluator.evaluate(responses)avg_score = sum(scores) / len(scores)print(f"迭代 {iteration+1}: 得分 = {avg_score:.4f}")# 3. 如果有提升,保存配置if avg_score > best_score:best_score = avg_scorebest_config = self.get_current_config()# 4. 基于反馈调整提示词self.adjust_prompt_based_on_errors(responses, train_samples)return best_config, best_score

13.5.4 RAG 与其他技术的融合

RAG + 长上下文:2025 年的主流架构是 Hybrid Context(混合上下文)—— 用 RAG 做宽召回,用长上下文做深推理。

RAG + Agent:RAG 系统作为 Agent 的工具之一,实现自主知识探索:

def rag_agent_system(user_query: str):"""RAG + Agent 协作系统"""agent = Agent(instructions="你是知识探索专家,可以自主决定是否需要检索外部知识。",tools=[retrieve_documents_tool,# RAG 检索工具web_search_tool,# 网页搜索工具# 其他工具])return agent.run(user_query)

本章小结

核心概念回顾

概念说明
RAG检索增强生成,先检索后生成,提升准确性和可信度
索引将文档切块、向量化,存入向量数据库
检索查询向量化,在向量库中相似度搜索
生成将检索结果注入提示词,LLM 基于上下文生成
CitationsAnthropic 原生引用功能,不占用输出 token
HyDE假设性文档嵌入,用假设答案增强检索效果
RAGASRAG 评估框架,Faithfulness/AnswerRelevancy/ContextPrecision

关键要点

  1. RAG 三大阶段:索引(切块→向量化→入库)→ 检索(查询→搜索→返回)→ 生成(构建提示→调用 LLM)
  2. 检索质量决定上限:RAG 的效果主要由检索质量决定,Anthropic 强调"检索质量比模型大小更重要"
  3. 分块策略影响显著:递归分块兼顾语义完整性和嵌入效果
  4. 引用格式可设计:从提示词标注到 Anthropic 原生 Citations
  5. HyDE 增强检索:通过生成假设性答案来弥合 query-doc 语义鸿沟
  6. RAGAS 量化评估:Faithfulness(忠实度)、Answer Relevancy(相关性)、Context Precision(精确度)

参考来源:

来源链接
Anthropic Embeddings 指南docs.anthropic.com/en/docs/bui…
Anthropic Citationsdocs.anthropic.com/en/docs/bui…
Anthropic Cookbook (GitHub)github.com/anthropics/…
OpenAI Embeddings 指南platform.openai.com/docs/guides…
LangChain Recursive Text Splitterpython.langchain.com/docs/how_to…
RAGAS GitHubgithub.com/explodinggr…
RAGAS 官方文档docs.ragas.io/
HyDE Paperarxiv.org/abs/2212.09…
Voyage AI 嵌入文档docs.voyageai.com/docs/embedd…

最新游戏

更多

Copyright©2010-2019. All rights reserved | 波波三国游戏官网|[email protected]

备案编号:湘ICP备2022015115号-4