记忆检索策略
记忆检索是连接长期记忆和短期记忆的桥梁。高效的检索策略能够从海量历史信息中快速找到相关内容,为 Agent 提供精准的知识支持。
一、什么是记忆检索?
1.1 定义
记忆检索(Memory Retrieval) 是从长期记忆存储中查找与当前查询相关的信息,并将其注入到短期记忆(Context Window)的过程。
1.2 检索的核心挑战
┌─────────────────────────────────────────────────────────────┐
│ 记忆检索的核心挑战 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 挑战 1:查全率 vs 查准率 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ • 查全率:相关记忆被检索出来的比例 │ │
│ │ • 查准率:检索结果中相关记忆的比例 │ │
│ │ • 权衡:提高查全率可能降低查准率 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 挑战 2:语义鸿沟 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ • 用户查询:口语化、模糊 │ │
│ │ • 存储记忆:正式、结构化 │ │
│ │ • 例如:"怎么用那个?" vs "XX工具使用教程" │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 挑战 3:规模与效率 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ • 记忆数量可能达到百万级 │ │
│ │ • 检索延迟需控制在毫秒级 │ │
│ │ • 实时性要求 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 挑战 4:上下文窗口限制 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ • 检索结果必须放入 Context Window │ │
│ │ • 结果数量和长度受限 │ │
│ │ • 需要精准筛选 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘1.3 检索流程概览
┌─────────────────────────────────────────────────────────────┐
│ 记忆检索流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ │
│ │ 用户查询 │ │
│ └──────┬──────┘ │
│ │ │
│ ↓ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Step 1: 查询理解与改写 │ │
│ │ • 意图识别 │ │
│ │ • 查询扩展 │ │
│ │ • Query 改写 │ │
│ └──────────────────────┬───────────────────────────────┘ │
│ │ │
│ ↓ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Step 2: 多路召回 │ │
│ │ • 向量检索(语义相似) │ │
│ │ • 关键词检索(精确匹配) │ │
│ │ • 元数据过滤 │ │
│ └──────────────────────┬───────────────────────────────┘ │
│ │ │
│ ↓ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Step 3: 结果融合与排序 │ │
│ │ • 分数归一化 │ │
│ │ • 加权融合 │ │
│ │ • Rerank 重排序 │ │
│ └──────────────────────┬───────────────────────────────┘ │
│ │ │
│ ↓ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Step 4: 结果筛选与截断 │ │
│ │ • Top-K 截断 │ │
│ │ • 相似度阈值过滤 │ │
│ │ • Token 预算控制 │ │
│ └──────────────────────┬───────────────────────────────┘ │
│ │ │
│ ↓ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Step 5: 上下文注入 │ │
│ │ • 格式化检索结果 │ │
│ │ • 注入到 Prompt │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘二、检索方式分类
2.1 语义检索
┌─────────────────────────────────────────────────────────────┐
│ 语义检索原理 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 原理: │
│ • 将文本转换为向量表示(Embedding) │
│ • 通过向量相似度计算语义相关性 │
│ • 能理解同义词、概念关联 │
│ │
│ 示例: │
│ 查询:"如何优化数据库性能?" │
│ 检索到: │
│ • "SQL 索引优化技巧" ← 语义相关 │
│ • "数据库查询调优方法" ← 语义相关 │
│ • "MySQL 性能优化实践" ← 语义相关 │
│ │
│ 优势: │
│ • 理解语义关联 │
│ • 无需精确关键词匹配 │
│ • 适合自然语言查询 │
│ │
│ 劣势: │
│ • 专有名词可能不准确 │
│ • 需要 Embedding 模型 │
│ • 计算开销较大 │
│ │
└─────────────────────────────────────────────────────────────┘Chroma 语义检索实现
import chromadb
from chromadb.utils import embedding_functions
# 初始化
client = chromadb.Client()
embedding_fn = embedding_functions.DefaultEmbeddingFunction()
collection = client.create_collection(
name="agent_memory",
embedding_function=embedding_fn
)
# 存储记忆
collection.add(
documents=[
"用户偏好使用中文进行技术讨论",
"用户喜欢简洁的回答风格",
"用户关注 Python 和机器学习领域",
"用户是一名后端开发工程师"
],
ids=["mem_1", "mem_2", "mem_3", "mem_4"]
)
# 语义检索
results = collection.query(
query_texts=["用户的职业是什么?"], # 自然语言查询
n_results=2
)
print(results["documents"])
# 输出: [["用户是一名后端开发工程师", "用户关注 Python 和机器学习领域"]]2.2 关键词检索
┌─────────────────────────────────────────────────────────────┐
│ 关键词检索原理 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 原理: │
│ • 基于 BM25、TF-IDF 等算法 │
│ • 精确匹配关键词 │
│ • 基于词频和文档频率计算相关性 │
│ │
│ 示例: │
│ 查询:"LangChain Agent" │
│ 检索到: │
│ • "LangChain Agent 开发教程" ← 精确匹配 │
│ • "使用 LangChain 构建 Agent" ← 精确匹配 │
│ • "Agent 框架对比" ← 部分匹配 │
│ │
│ 优势: │
│ • 精确匹配专有名词 │
│ • 检索速度快 │
│ • 不需要 Embedding 模型 │
│ │
│ 劣势: │
│ • 无法理解语义关联 │
│ • 对同义词无能为力 │
│ • 依赖查询词精确性 │
│ │
└─────────────────────────────────────────────────────────────┘BM25 关键词检索实现
from rank_bm25 import BM25Okapi
from typing import List, Tuple
import jieba
class KeywordRetriever:
"""关键词检索器"""
def __init__(self, documents: List[str]):
self.documents = documents
# 中文分词
self.tokenized_docs = [list(jieba.cut(doc)) for doc in documents]
self.bm25 = BM25Okapi(self.tokenized_docs)
def search(self, query: str, top_k: int = 5) -> List[Tuple[str, float]]:
"""搜索"""
tokenized_query = list(jieba.cut(query))
scores = self.bm25.get_scores(tokenized_query)
# 排序并返回 Top-K
ranked = sorted(enumerate(scores), key=lambda x: x[1], reverse=True)
results = [
(self.documents[idx], score)
for idx, score in ranked[:top_k]
if score > 0
]
return results
# 使用示例
documents = [
"LangChain 是一个 LLM 应用开发框架",
"Agent 是具有自主决策能力的 AI 系统",
"LangChain Agent 开发实战教程",
"向量数据库用于语义检索",
"RAG 是检索增强生成技术"
]
retriever = KeywordRetriever(documents)
results = retriever.search("LangChain Agent", top_k=3)
for doc, score in results:
print(f"[{score:.2f}] {doc}")2.3 混合检索
┌─────────────────────────────────────────────────────────────┐
│ 混合检索原理 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 原理: │
│ • 同时使用语义检索和关键词检索 │
│ • 融合两种检索结果 │
│ • 兼顾语义理解和精确匹配 │
│ │
│ 流程: │
│ │
│ ┌─────────────┐ │
│ │ 用户查询 │ │
│ └──────┬──────┘ │
│ │ │
│ ┌───────────┴───────────┐ │
│ ↓ ↓ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ 语义检索 │ │ 关键词检索 │ │
│ │ (Vector) │ │ (BM25) │ │
│ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │
│ ↓ ↓ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ 结果集 A │ │ 结果集 B │ │
│ │ Top-K │ │ Top-K │ │
│ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │
│ └───────────┬───────────┘ │
│ ↓ │
│ ┌─────────────┐ │
│ │ 结果融合 │ │
│ │ Reciprocal │ │
│ │ Rank Fusion│ │
│ └──────┬──────┘ │
│ │ │
│ ↓ │
│ ┌─────────────┐ │
│ │ 最终结果 │ │
│ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘混合检索实现
from typing import List, Dict, Tuple
import chromadb
from rank_bm25 import BM25Okapi
import jieba
class HybridRetriever:
"""混合检索器"""
def __init__(
self,
documents: List[str],
vector_weight: float = 0.5,
keyword_weight: float = 0.5
):
self.documents = documents
self.vector_weight = vector_weight
self.keyword_weight = keyword_weight
# 初始化向量检索
self.vector_client = chromadb.Client()
self.collection = self.vector_client.create_collection("hybrid_search")
self.collection.add(
documents=documents,
ids=[f"doc_{i}" for i in range(len(documents))]
)
# 初始化关键词检索
tokenized = [list(jieba.cut(doc)) for doc in documents]
self.bm25 = BM25Okapi(tokenized)
def search(self, query: str, top_k: int = 5) -> List[Dict]:
"""混合检索"""
# 1. 向量检索
vector_results = self.collection.query(
query_texts=[query],
n_results=top_k * 2 # 多召回一些
)
vector_docs = vector_results["documents"][0]
vector_distances = vector_results["distances"][0]
# 2. 关键词检索
tokenized_query = list(jieba.cut(query))
bm25_scores = self.bm25.get_scores(tokenized_query)
# 3. 分数归一化
vector_scores = self._normalize_scores(vector_distances, inverse=True)
bm25_norm_scores = self._normalize_scores(bm25_scores)
# 4. 构建文档分数映射
doc_scores = {}
# 向量检索结果
for doc, score in zip(vector_docs, vector_scores):
doc_scores[doc] = {
"vector_score": score,
"keyword_score": 0,
"doc": doc
}
# 关键词检索结果
for i, score in enumerate(bm25_norm_scores):
doc = self.documents[i]
if doc in doc_scores:
doc_scores[doc]["keyword_score"] = score
else:
doc_scores[doc] = {
"vector_score": 0,
"keyword_score": score,
"doc": doc
}
# 5. 加权融合
for doc_data in doc_scores.values():
doc_data["final_score"] = (
self.vector_weight * doc_data["vector_score"] +
self.keyword_weight * doc_data["keyword_score"]
)
# 6. 排序返回
sorted_results = sorted(
doc_scores.values(),
key=lambda x: x["final_score"],
reverse=True
)
return sorted_results[:top_k]
def _normalize_scores(self, scores: List[float], inverse: bool = False) -> List[float]:
"""分数归一化到 0-1"""
if not scores:
return []
min_s, max_s = min(scores), max(scores)
if max_s == min_s:
return [0.5] * len(scores)
if inverse:
# 对于距离,越小越好
return [1 - (s - min_s) / (max_s - min_s) for s in scores]
else:
return [(s - min_s) / (max_s - min_s) for s in scores]
# 使用示例
documents = [
"LangChain 是一个 LLM 应用开发框架",
"Agent 是具有自主决策能力的 AI 系统",
"LangChain Agent 开发实战教程",
"向量数据库用于语义检索",
"RAG 是检索增强生成技术",
"如何使用 LangChain 构建 Agent"
]
retriever = HybridRetriever(documents)
results = retriever.search("LangChain Agent 开发", top_k=3)
for r in results:
print(f"[{r['final_score']:.3f}] {r['doc']}")三、RAG 架构详解
3.1 RAG 定义
RAG(Retrieval-Augmented Generation,检索增强生成) 是将检索与生成相结合的技术架构,通过检索外部知识来增强 LLM 的生成能力。
3.2 RAG 架构图
┌─────────────────────────────────────────────────────────────────────┐
│ RAG 架构完整流程 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 【索引阶段】 │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 文档 │ → │ 切分 │ → │ Embedding │ → │ 向量存储 │ │
│ │ Documents│ │ Chunking │ │ │ │ Vector DB│ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ │
│ 【检索阶段】 │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 查询 │ → │ Embedding │ → │ 向量检索 │ → │ Top-K │ │
│ │ Query │ │ │ │ │ │ Results │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ │ │
│ ↓ │
│ 【生成阶段】 │ │
│ │ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Prompt │ ← │ 上下文 │ ← │ 格式化 │ ← │ 检索结果 │ │
│ │ 组装 │ │ Context │ │ │ │ │ │
│ └────┬─────┘ └──────────┘ └──────────┘ └──────────┘ │
│ │ │
│ ↓ │
│ ┌──────────┐ ┌──────────┐ │
│ │ LLM │ → │ 回答 │ │
│ │ │ │ Answer │ │
│ └──────────┘ └──────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘3.3 LangChain RAG 实现
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_chroma import Chroma
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate
class RAGSystem:
"""RAG 系统实现"""
def __init__(self, documents, persist_directory: str = "./chroma_db"):
self.documents = documents
self.persist_directory = persist_directory
# 初始化组件
self.embeddings = OpenAIEmbeddings()
self.llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
self.text_splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50
)
# 构建向量存储
self._build_vector_store()
# 构建 RAG Chain
self._build_rag_chain()
def _build_vector_store(self):
"""构建向量存储"""
# 切分文档
splits = self.text_splitter.split_documents(self.documents)
# 存入向量数据库
self.vectorstore = Chroma.from_documents(
documents=splits,
embedding=self.embeddings,
persist_directory=self.persist_directory
)
self.retriever = self.vectorstore.as_retriever(
search_type="similarity",
search_kwargs={"k": 4}
)
def _build_rag_chain(self):
"""构建 RAG Chain"""
# 自定义 Prompt
prompt_template = """
你是一个专业的问答助手。请根据以下检索到的上下文回答问题。
如果上下文中没有相关信息,请明确说明"根据现有信息无法回答"。
上下文:
{context}
问题:{question}
回答:
"""
prompt = PromptTemplate(
template=prompt_template,
input_variables=["context", "question"]
)
self.qa_chain = RetrievalQA.from_chain_type(
llm=self.llm,
chain_type="stuff",
retriever=self.retriever,
return_source_documents=True,
chain_type_kwargs={"prompt": prompt}
)
def query(self, question: str) -> Dict:
"""查询"""
result = self.qa_chain.invoke({"query": question})
return {
"answer": result["result"],
"source_documents": result["source_documents"]
}
# 使用示例
from langchain.schema import Document
documents = [
Document(page_content="LangChain 是一个用于开发 LLM 应用的框架,提供了链式调用、记忆、工具等核心组件。"),
Document(page_content="Agent 是具有自主决策能力的 AI 系统,能够根据目标选择合适的工具和行动。"),
Document(page_content="RAG(检索增强生成)是将检索与生成相结合的技术,通过检索外部知识增强 LLM 能力。"),
Document(page_content="向量数据库用于存储文本的向量表示,支持语义相似度检索。"),
]
rag = RAGSystem(documents)
result = rag.query("什么是 RAG?")
print(result["answer"])四、检索优化技术
4.1 Query 改写
┌─────────────────────────────────────────────────────────────┐
│ Query 改写技术 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 目的:解决用户查询与存储记忆之间的语义鸿沟 │
│ │
│ 方法 1:查询扩展 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 原查询:"Python 文件操作" │ │
│ │ 扩展后:["Python 文件操作", "Python read write file", │ │
│ │ "Python 文件读写", "Python IO 操作"] │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 方法 2:查询改写 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 原查询:"那个怎么用?" │ │
│ │ 改写后:"LangChain Agent 的使用方法" │ │
│ │ (结合上下文理解"那个"指代什么) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 方法 3:假设性文档生成(HyDE) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 原查询:"如何优化数据库查询?" │ │
│ │ 生成假设文档: │ │
│ │ "数据库查询优化可以从以下几个方面入手: │ │
│ │ 1. 建立合适的索引 │ │
│ │ 2. 优化 SQL 语句 │ │
│ │ 3. 使用查询缓存..." │ │
│ │ 然后用假设文档去检索相似的真实文档 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘Query 改写实现
from langchain_openai import ChatOpenAI
from langchain.prompts import PromptTemplate
class QueryRewriter:
"""查询改写器"""
def __init__(self, llm=None):
self.llm = llm or ChatOpenAI(temperature=0)
self.rewrite_prompt = PromptTemplate(
template="""
请将以下用户查询改写为更适合检索的形式。
要求:
1. 保持原意
2. 使用更正式的表述
3. 添加可能的关键词
原查询:{query}
改写后的查询:
""",
input_variables=["query"]
)
self.expand_prompt = PromptTemplate(
template="""
请为以下查询生成3个语义相同但表述不同的变体,用于扩展检索范围。
原查询:{query}
变体(每行一个):
""",
input_variables=["query"]
)
def rewrite(self, query: str) -> str:
"""改写查询"""
chain = self.rewrite_prompt | self.llm
result = chain.invoke({"query": query})
return result.content.strip()
def expand(self, query: str) -> list:
"""扩展查询"""
chain = self.expand_prompt | self.llm
result = chain.invoke({"query": query})
variants = result.content.strip().split("\n")
return [v.strip() for v in variants if v.strip()]
# 使用示例
rewriter = QueryRewriter()
# 改写
print(rewriter.rewrite("那个怎么用?"))
# 输出:LangChain Agent 框架使用方法教程
# 扩展
print(rewriter.expand("Python 数据处理"))
# 输出:['Python 数据处理', 'Python pandas 数据分析', 'Python 数据清洗方法']4.2 Rerank 重排序
┌─────────────────────────────────────────────────────────────┐
│ Rerank 重排序技术 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 原理: │
│ 1. 使用粗检索快速召回大量候选(如 Top-100) │
│ 2. 使用精细模型对候选重排序 │
│ 3. 返回最相关的 Top-K │
│ │
│ 优势: │
│ • 平衡检索效率和质量 │
│ • 精细模型考虑更多上下文 │
│ • 显著提升检索准确性 │
│ │
│ 常用 Rerank 模型: │
│ • Cohere Rerank │
│ • BGE Reranker │
│ • Cross-Encoder 模型 │
│ │
└─────────────────────────────────────────────────────────────┘Rerank 实现
from typing import List, Tuple
from langchain_openai import ChatOpenAI
from langchain.prompts import PromptTemplate
class LLMReranker:
"""基于 LLM 的 Reranker"""
def __init__(self, llm=None):
self.llm = llm or ChatOpenAI(temperature=0)
self.rerank_prompt = PromptTemplate(
template="""
请根据查询对以下文档进行相关性打分(0-10分)。
查询:{query}
文档:{document}
只输出分数(0-10的整数):
""",
input_variables=["query", "document"]
)
def rerank(
self,
query: str,
documents: List[str],
top_k: int = 5
) -> List[Tuple[str, float]]:
"""重排序"""
scored_docs = []
for doc in documents:
chain = self.rerank_prompt | self.llm
result = chain.invoke({"query": query, "document": doc})
try:
score = float(result.content.strip())
except:
score = 0
scored_docs.append((doc, score))
# 排序
scored_docs.sort(key=lambda x: x[1], reverse=True)
return scored_docs[:top_k]
# 使用示例
reranker = LLMReranker()
query = "LangChain Agent 开发"
candidates = [
"LangChain 是一个 LLM 应用框架",
"LangChain Agent 开发实战教程",
"向量数据库的使用方法",
"如何构建一个智能 Agent",
"LangChain 记忆系统详解"
]
results = reranker.rerank(query, candidates, top_k=3)
for doc, score in results:
print(f"[{score}] {doc}")4.3 元数据过滤
from typing import Dict, List, Optional
class MetadataFilteredRetriever:
"""支持元数据过滤的检索器"""
def __init__(self, vectorstore):
self.vectorstore = vectorstore
def search(
self,
query: str,
filters: Optional[Dict] = None,
top_k: int = 5
) -> List[Dict]:
"""
带元数据过滤的检索
filters 示例:
{
"user_id": "user_123",
"memory_type": "preference",
"created_at": {"$gt": "2024-01-01"}
}
"""
# 构建过滤条件
where_clause = self._build_where_clause(filters)
# 执行检索
results = self.vectorstore.similarity_search(
query,
k=top_k,
filter=where_clause
)
return results
def _build_where_clause(self, filters: Optional[Dict]) -> Optional[Dict]:
"""构建过滤条件"""
if not filters:
return None
conditions = []
for key, value in filters.items():
if isinstance(value, dict):
# 复杂条件
for op, val in value.items():
if op == "$gt":
conditions.append({key: {"$gt": val}})
elif op == "$lt":
conditions.append({key: {"$lt": val}})
elif op == "$in":
conditions.append({key: {"$in": val}})
else:
# 简单等值条件
conditions.append({key: value})
if len(conditions) == 1:
return conditions[0]
elif len(conditions) > 1:
return {"$and": conditions}
return None
# 使用示例
# retriever.search(
# query="用户的偏好",
# filters={
# "user_id": "user_123",
# "memory_type": "preference"
# },
# top_k=5
# )五、面试高频问题
Q1: 向量数据库检索不准怎么办?
答案要点:
解决方案(三个层次):
1. 数据层
• 数据清洗:去除噪声、重复数据
• Chunking 优化:合理切分,避免语义断裂
• 元数据丰富:添加时间、类型、来源等标签
2. 检索层
• 混合检索:语义 + 关键词
• Rerank 重排序:精细化排序
• Query 改写:优化用户查询
3. 应用层
• Prompt 指示:告诉 LLM "检索内容无关时回答不知道"
• 多轮检索:根据初步结果扩展查询
• 用户反馈:收集反馈优化检索Q2: 语义检索和关键词检索如何选择?
答案要点:
| 场景 | 推荐方式 | 理由 |
|---|---|---|
| 专有名词查询 | 关键词检索 | 精确匹配 |
| 概念性问题 | 语义检索 | 理解语义关联 |
| 混合场景 | 混合检索 | 兼顾两者 |
| 生产环境 | 混合检索 + Rerank | 最佳效果 |
最佳实践:生产环境推荐混合检索,权重可调。
Q3: RAG 和微调(Fine-tuning)有什么区别?
答案要点:
| 维度 | RAG | Fine-tuning |
|---|---|---|
| 知识更新 | 实时更新检索库 | 需重新训练 |
| 成本 | 检索成本 | 训练成本高 |
| 可控性 | 高(可追溯来源) | 低(黑盒) |
| 适用场景 | 知识频繁变化 | 特定任务优化 |
| 响应速度 | 检索+生成 | 直接生成 |
最佳实践:两者可结合使用,RAG 提供知识,Fine-tuning 优化任务表现。
Q4: 如何评估检索质量?
答案要点:
评估指标:
1. 离线指标
• Recall@K:Top-K 结果中相关文档的比例
• MRR:平均倒数排名
• NDCG:归一化折损累积增益
2. 在线指标
• 点击率:用户点击检索结果的比例
• 满意度:用户反馈评分
• 任务完成率:基于检索完成任务的比率
评估方法:
• 人工标注测试集
• A/B 测试
• 用户反馈收集Q5: 如何处理检索结果过多或过少的情况?
答案要点:
检索结果过多:
• 提高相似度阈值
• 增加 Rerank 步骤
• 使用更严格的元数据过滤
• 限制返回数量
检索结果过少:
• 降低相似度阈值
• 扩展查询(Query Expansion)
• 放宽元数据过滤条件
• 使用同义词扩展
• 提示 LLM 说明信息不足六、总结
核心概念回顾
| 概念 | 定义 | 关键要点 |
|---|---|---|
| 语义检索 | 基于向量相似度的检索 | 理解语义关联 |
| 关键词检索 | 基于 BM25 等算法的检索 | 精确匹配 |
| 混合检索 | 语义 + 关键词融合 | 生产环境首选 |
| RAG | 检索增强生成 | 检索 + LLM 生成 |
| Rerank | 结果重排序 | 提升检索精度 |
一句话总结
记忆检索是连接长期记忆和短期记忆的桥梁,通过语义检索、关键词检索、混合检索等多种策略,配合 Query 改写、Rerank 等优化技术,实现高效精准的知识召回。
设计口诀
记忆检索策略口诀:
语义理解向量查,关键词做精确抓
混合检索效果佳,Rerank 后排序它
Query 改写补语义,元数据过滤筛精华
RAG 架构增知识,检索生成两不差最后更新:2026年3月18日