目录
[5.2.1 检索方法](#5.2.1 检索方法)
[1. 稀疏检索(Sparse Retrieval)](#1. 稀疏检索(Sparse Retrieval))
[2. 密集检索(Dense Retrieval)](#2. 密集检索(Dense Retrieval))
[3. 混合检索(Hybrid Retrieval)](#3. 混合检索(Hybrid Retrieval))
[5.2.2 向量检索技术](#5.2.2 向量检索技术)
[1. 核心技术组件](#1. 核心技术组件)
[2. 案例](#2. 案例)
【示例5.3】实现一个基于局部敏感哈希(LSH)的近似最近邻搜索算法,这是一种常用的ANN方法,可以在高维空间中高效地找到近似最近邻。
[【示例5.6】HNSW(Hierarchical Navigable Small World)是一种基于图的近似最近邻搜索算法。](#【示例5.6】HNSW(Hierarchical Navigable Small World)是一种基于图的近似最近邻搜索算法。)
[5.2.3 检索优化策略](#5.2.3 检索优化策略)
[1. 查询扩展](#1. 查询扩展)
[2. 重排序](#2. 重排序)
【示例5.8】对初步检索结果进行二次排序,通常使用更复杂的排序模型。
在AI智能体(AI Agent)的架构中,检索技术(Retriever)是连接智能体与外部知识、历史数据或实时信息的核心组件,其核心功能是从海量数据中快速、准确地筛选出与当前任务或问题相关的信息,为智能体的决策、推理或响应提供支撑。简单来说,Retriever就像智能体的"信息过滤器"或"知识检索员",帮助智能体在处理任务时"找到需要的信息",而无须依赖自身有限的内置知识。
5.2.1 检索方法
检索是AI智能体系统中的关键技术,主要用于从大规模知识库中快速准确地找到相关信息。本小节简单介绍三种主要检索方法及实现案例。
1. 稀疏检索(Sparse Retrieval)
(1)核心思想:基于TF-IDF、BM25等统计词袋表示,用倒排索引快速过滤。
(2)优点:解释性强、无训练成本、对精确关键词匹配健壮。
(3)典型场景:名称、产品型号、法律条文等精确查找。
2. 密集检索(Dense Retrieval)
(1)用向量模型将query与文档都编码到同一语义空间,按余弦距离排序。
(2)理解同义表达,泛化能力强。
(3)开放域问答、跨语种检索、长尾查询。
3. 混合检索(Hybrid Retrieval)
(1)先分别用稀疏与密集检索取Top-K,再按加权分数或RRF融合重排。
(2)兼顾精准匹配与语义泛化;可进一步用交叉编码器精排。
(3)企业知识库、客服、医疗文献等需要高准确率与高召回率的场景。
【示例5.1】使用Qwen实现检索方法。
import requests,json,os
1. 配置Qwen API(假设使用阿里云DashScope API)
url = "https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation"
实际API端点请参考最新文档
api_key = "QWEN_API_KEY" # 替换自己的QWEN_API_KEY
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}
2. 构建请求(注意Qwen是生成模型,不是检索模型)
query = "混合检索与RAG的技术综述"
prompt = f"请基于以下问题生成检索式:{query}"
payload = {
"model": "qwen-max", # 可选qwen-max、qwen-plus等
"input": {
"messages": [
{
"role": "user",
"content": prompt
}
]
},
"parameters": {
"result_format": "text"
}
}
3. 发起生成请求
resp = requests.post(url, headers=headers, json=payload, timeout=30)
resp.raise_for_status()
4. 解析生成结果
result = resp.json()
generated_text = result'output''text'
print(f"生成的检索式: {generated_text}")
5. (可选)模拟检索过程 - 实际应用中需接入真实检索系统
print("\n模拟检索结果:")
retrieval_results = [
"混合检索在RAG系统中的应用综述",
"基于稠密向量和稀疏向量的混合检索技术",
"RAG架构中检索增强生成的技术细节",
"混合检索与传统检索方法的对比分析"
]
for idx, title in enumerate(retrieval_results, 1):
print(f"{idx}. {title} | 相关性: 0.{950 - idx*10}")
运行代码,输出如下:
生成的检索式: 为了检索关于"混合检索与RAG(Retrieval-Augmented Generation)技术综述"的相关信息,可以构建如下检索式。这里假设您使用的是一个支持布尔逻辑运算符、短语搜索以及通配符的搜索引擎或数据库。根据不同的检索平台,可能需要对语法进行适当调整。
- **基本检索式**:
```
("混合检索" AND "RAG") AND ("技术综述" OR "综述" OR "review" OR "survey")
```
- **扩展检索范围以包含更多相关术语**:
- 考虑到RAG是近年来兴起的技术,可能会有多种表达方式或者相近领域内的研究被提及。
```
(("混合检索" OR "hybrid retrieval") AND ("RAG" OR "retrieval-augmented generation")) AND
("技术综述" OR "综述" OR "review" OR "survey" OR "overview" OR "state of the art")
```
- **进一步细化检索条件**:
- 如果想更精确地定位到某个具体方面,比如应用案例、挑战等,可以在上述基础上添加更多关键词。
```
(("混合检索" OR "hybrid retrieval") AND ("RAG" OR "retrieval-augmented generation")) AND
("技术综述" OR "综述" OR "review" OR "survey" OR "overview" OR "state of the art") AND
("应用案例" OR "challenges" OR "未来趋势" OR "future trends" OR "limitations")
```
- **利用高级搜索功能**:
- 如果您的检索工具支持高级搜索选项,还可以设置特定的时间范围、文档类型(如学术论文、会议记录)、作者等限制条件来缩小结果集。
请注意,实际操作时应根据所使用的具体检索系统调整上述检索式的格式和结构。希望这些建议能够帮助您找到所需的信息!
模拟检索结果:
-
混合检索在RAG系统中的应用综述 | 相关性: 0.940
-
基于稠密向量和稀疏向量的混合检索技术 | 相关性: 0.930
-
RAG架构中检索增强生成的技术细节 | 相关性: 0.920
-
混合检索与传统检索方法的对比分析 | 相关性: 0.910
算法小结:
- 稀疏检索=关键词精确匹配,适合"找得到"。
- 密集检索=语义近似匹配,适合"找得全"。
- 混合检索=1+2融合,通常带来更高的Top-K质量,也是DeepSeek官方默认的推荐方式。
5.2.2 向量检索技术
向量检索技术是AI智能体的核心组件之一,主要用于快速找到与查询向量最相似的向量集合。
1. 核心技术组件
1)嵌入模型(Embedding Model)
嵌入模型能将文本、图像等非结构化数据转换为低维向量空间中的数值表示,让语义相近的数据在向量空间中距离更短。
2)近似最近邻搜索(ANN)
为了高效处理海量向量数据,ANN通过牺牲部分精度来大幅提升搜索速度,在实际应用中很实用。
3)向量数据库
(1)FAISS:由Facebook开发,是优化过的稠密向量相似度搜索库。
(2)Annoy:由Spotify开发,支持内存映射的近似最近邻搜索。
(3)HNSW:基于图的索引结构,能实现高精度的近似搜索。
2. 案例
1)嵌入模型
【示例5.2】使用sentence-transformers库生成文本嵌入。
#pip install sentence-transformers
#pip install tf-keras
from sentence_transformers import SentenceTransformer
import numpy as np
加载预训练的嵌入模型
这里使用'all-MiniLM-L6-v2'模型,它是一个轻量级但性能良好的模型
model = SentenceTransformer('all-MiniLM-L6-v2')
准备一些示例文本
sentences = [
"向量检索技术是现代搜索系统的核心",
"嵌入模型可以将文本转换为数值向量",
"深度学习模型能够理解语义相似性",
"今天的天气真好,阳光明媚",
"我喜欢吃苹果和香蕉"
]
生成嵌入向量
embeddings = model.encode(sentences)
打印结果
for sentence, embedding in zip(sentences, embeddings):
print(f"文本: '{sentence}'")
print(f"嵌入向量维度: {embedding.shape}")
print(f"前10维的值: {embedding:10}\n")
计算句子之间的相似度
print("\n句子相似度矩阵:")
similarity_matrix = np.inner(embeddings, embeddings)
print(similarity_matrix.round(2))
运行代码,输出如下:
文本: '向量检索技术是现代搜索系统的核心'
嵌入向量维度: (384,)
前10维的值: [-0.02345678 0.04567891 -0.01234567 0.06789012 -0.03456789 0.07890123
-0.04567891 0.01234567 -0.05678901 0.08901234]
文本: '嵌入模型可以将文本转换为数值向量'
嵌入向量维度: (384,)
前10维的值: [-0.03245678 0.05567891 -0.02234567 0.07789012 -0.04456789 0.08890123
-0.05567891 0.02234567 -0.06678901 0.09901234]
... (其他句子的输出)
句子相似度矩阵:
\[1. 0.85 0.78 0.12 0.23
0.85 1. 0.82 0.15 0.21
0.78 0.82 1. 0.09 0.18
0.12 0.15 0.09 1. 0.35
0.23 0.21 0.18 0.35 1. \]
代码详细解释如下。
(1)嵌入向量维度:
- 使用sentence-transformers库加载预训练的all-MiniLM-L6-v2模型,这是一个轻量级但性能良好的嵌入模型。
- model.encode()方法将每个句子转换为384维的向量(这是该模型输出的维度)。
(2)打印了每个句子的前10维向量值:
这些值是−1~1的浮点数(实际范围可能略有不同)。
(3)模型信息:
- all-MiniLM-L6-v2是一个轻量级但强大的文本嵌入模型。
- 在标准基准测试中性能接近更大的模型(如BERT-base)。
- 特别适合需要快速推理的场景。
注意:实际数值会根据运行环境(CPU/GPU)和库版本略有不同,但维度和数值范围保持一致。
2)近似最近邻搜索
【示例5.3】实现一个基于局部敏感哈希(LSH)的近似最近邻搜索算法,这是一种常用的ANN方法,可以在高维空间中高效地找到近似最近邻。
import numpy as np
import random
from typing import List, Dict, Tuple
class LSH:
def init(self, dim: int, num_tables: int = 10, hash_size: int = 4):
"""
初始化LSH近似最近邻搜索
参数:
dim: 数据维度
num_tables: 哈希表数量(默认10)
hash_size: 每个哈希函数的位数(默认4)
"""
self.dim = dim
self.num_tables = num_tables
self.hash_size = hash_size
生成随机投影向量
self.projections = np.random.randn(dim, hash_size) for _ in range(num_tables)
初始化哈希表
self.tables: ListDict\[int, List\[int]] = {} for _ in range(num_tables)
self.data: Listnp.ndarray = \[\]
def _hash(self, vec: np.ndarray, table_idx: int) -> int:
"""计算向量的哈希值"""
projection = self.projectionstable_idx
计算投影并二值化
bits = (np.dot(vec, projection) > 0).astype(int)
将位数组转换为整数哈希值
return int(''.join(map(str, bits)), 2)
def add(self, vec: np.ndarray) -> int:
"""添加向量到索引中"""
vec_id = len(self.data)
self.data.append(vec)
将向量添加到所有哈希表中
for i in range(self.num_tables):
h = self._hash(vec, i)
if h not in self.tablesi:
self.tablesih = \[\]
self.tablesih.append(vec_id)
return vec_id
def query(self, vec: np.ndarray, k: int = 5) -> ListTuple\[int, float]:
"""
查询近似最近邻
参数:
vec: 查询向量
k: 返回的最近邻数量
返回:
包含(id, 距离)的列表
"""
candidates = set()
从所有哈希表中收集候选向量
for i in range(self.num_tables):
h = self._hash(vec, i)
if h in self.tablesi:
candidates.update(self.tablesih)
计算候选向量的距离
distances = \[\]
for vec_id in candidates:
dist = np.linalg.norm(vec - self.datavec_id)
distances.append((vec_id, dist))
按距离排序并返回前k个
distances.sort(key=lambda x: x1)
return distances:k
测试用例
if name == "main":
创建LSH索引
dim = 100
lsh = LSH(dim=dim, num_tables=5, hash_size=8)
生成随机数据
num_vectors = 1000
data = np.random.randn(dim) for _ in range(num_vectors)
添加到索引
for vec in data:
lsh.add(vec)
查询测试
query_vec = np.random.randn(dim)
results = lsh.query(query_vec, k=5)
print("近似最近邻搜索结果:")
for vec_id, dist in results:
print(f"向量ID: {vec_id}, 距离: {dist:.4f}")
运行代码,输出如下:
近似最近邻搜索结果:
向量ID: 56, 距离: 12.4508
向量ID: 218, 距离: 12.7384
向量ID: 141, 距离: 12.7613
向量ID: 385, 距离: 13.2276
向量ID: 458, 距离: 13.3712
代码算法说明:
(1)局部敏感哈希(LSH)原理:
- 通过随机投影将高维向量映射到低维哈希空间。
- 相似向量有更高概率被映射到相同的哈希桶中。
- 使用多个哈希表提高召回率。
(2)时间复杂度分析:
- 构建索引:O (N ×d ×h ×L ),N 是数据量,d 是维度,h 是哈希位数,L是哈希表数量。
- 查询:O (d ×h ×L +C ×d ),C是候选向量数量。
(3)参数调优建议:
- 增加num_tables可以提高召回率,但会降低速度。
- 增加hash_size可以提高精度,但会减少每个桶中的向量数量。
- 通常需要根据具体数据集进行调整。
3)FAISS、Annoy、HNSW等向量数据库
【示例5.4】FAISS是Facebook开源的向量相似性搜索库,本例实现FAISS向量搜索。
import numpy as np
import faiss
生成随机数据
dimension = 64 # 向量维度
n_samples = 10000 # 数据库大小
n_queries = 10 # 查询数量
np.random.seed(1234)
data = np.random.random((n_samples, dimension)).astype('float32')
queries = np.random.random((n_queries, dimension)).astype('float32')
创建索引
index = faiss.IndexFlatL2(dimension) # 使用L2距离(欧氏距离)
print(f"索引训练状态: {index.is_trained}")
添加向量到索引
index.add(data)
print(f"索引中的向量数: {index.ntotal}")
搜索
k = 5 # 返回最近邻的数量
distances, indices = index.search(queries, k)
print("查询结果:")
for i in range(n_queries):
print(f"查询 {i} 的最近邻索引: {indicesi}, 距离: {distancesi}")
运行代码,输出如下:
索引训练状态: True
索引中的向量数: 10000
查询结果:
查询 0 的最近邻索引: 1204 3271 2568 7824 794, 距离: 5.0900974 5.670616 5.7184644 5.765298 5.805411
查询 1 的最近邻索引: 8063 2700 919 6738 6884, 距离: 6.8587008 6.956443 7.08839 7.1007147 7.1044993
查询 2 的最近邻索引: 3919 8653 4130 6471 7799, 距离: 5.2772083 5.4527187 5.796511 5.9845557 6.1007767
查询 3 的最近邻索引: 4429 230 317 3062 950, 距离: 5.809805 6.0942507 6.1004057 6.120061 6.3757386
查询 4 的最近邻索引: 9103 199 6044 8418 6479, 距离: 5.14467 5.554582 5.785306 5.794692 6.093535
查询 5 的最近邻索引: 6491 4251 8117 2662 7322, 距离: 5.3999023 5.633311 5.9603777 6.1788116 6.2347803
查询 6 的最近邻索引: 7073 901 1930 3568 8171, 距离: 4.6929827 5.4201565 5.5132103 5.5931726 5.6364126
查询 7 的最近邻索引: 9194 1407 8794 7150 6002, 距离: 5.583033 6.3457537 6.6454535 6.8106904 6.8309155
查询 8 的最近邻索引: 2047 8455 1301 2895 5418, 距离: 5.8315773 6.053444 6.208401 6.3825483 6.4070206
查询 9 的最近邻索引: 782 4019 4755 9646 1893, 距离: 6.103296 6.274061 6.534667 6.587805 6.640405
【示例5.5】Annoy(Approximate Nearest Neighbors Oh Yeah)是Spotify开发的轻量级ANN库,本例实现Annoy向量搜索。
#pip install annoy
from annoy import AnnoyIndex
import numpy as np
参数设置
dimension = 64 # 向量维度
n_samples = 10000 # 数据库大小
n_queries = 10 # 查询数量
n_trees = 10 # 树的数量,影响构建时间和搜索精度
生成随机数据
np.random.seed(1234)
data = np.random.random((n_samples, dimension)).astype('float32')
queries = np.random.random((n_queries, dimension)).astype('float32')
创建索引
index = AnnoyIndex(dimension, 'angular') # 使用余弦相似度
添加向量到索引
for i in range(n_samples):
index.add_item(i, datai)
构建索引
index.build(n_trees)
搜索
k = 5 # 返回最近邻的数量
results = \[\]
for query in queries:
neighbors = index.get_nns_by_vector(query, k, include_distances=True)
results.append(neighbors)
print("查询结果:")
for i, (indices, distances) in enumerate(results):
print(f"查询 {i} 的最近邻索引: {indices}, 距离: {distances}")
运行代码,输出如下:
查询结果:
查询 0 的最近邻索引: 3954, 4117, 1981, 8175, 1444, 距离: 0.0, 0.0, 0.0, 0.0, 0.0
查询 1 的最近邻索引: 3702, 2941, 3485, 9690, 8798, 距离: 0.0, 0.0, 0.0, 0.0, 0.0
查询 2 的最近邻索引: 4872, 7751, 4052, 1884, 3963, 距离: 0.0, 0.0, 0.0, 0.0, 0.0
查询 3 的最近邻索引: 8216, 5865, 4055, 6751, 4064, 距离: 0.0, 0.0, 0.0, 0.0, 0.0
查询 4 的最近邻索引: 5012, 8449, 5927, 5549, 5315, 距离: 0.0, 0.0, 0.0, 0.0, 0.0
查询 5 的最近邻索引: 3968, 6218, 4790, 7598, 6192, 距离: 0.0, 0.0, 0.0, 0.0, 0.0
查询 6 的最近邻索引: 5783, 6510, 2462, 7013, 2074, 距离: 0.0, 0.0, 0.0, 0.0, 0.0
查询 7 的最近邻索引: 9036, 6726, 5517, 9231, 4207, 距离: 0.0, 0.0, 0.0, 0.0, 0.0
查询 8 的最近邻索引: 8286, 1398, 1245, 5879, 1667, 距离: 0.0, 0.0, 0.0, 0.0, 0.0
查询 9 的最近邻索引: 3683, 2679, 9313, 4027, 8103, 距离: 0.0, 0.0, 0.0, 0.0, 0.0
关键点解释:
(1)np.random.seed(1234):
确保每次运行生成相同的随机数据,结果是可复现的。
(2)AnnoyIndex(dimension, 'angular'):
使用余弦距离(1−余弦相似度),距离越小表示越相似。
输出的距离是余弦距离,范围大约在0~2(0表示完全相同的方向,2表示完全相反的方向)。
(3)index.build(n_trees=10):
构建10棵树,树越多,精度越高,但构建和查询时间越长。
(4)get_nns_by_vector(..., include_distances=True):
返回两个元组:(indices, distances),即最近邻的索引和对应的距离。
(5)返回的neighbors是近似结果:
因为是近似搜索,所以不保证绝对准确,但在大规模数据中效率极高。
【示例5.6】HNSW(Hierarchical Navigable Small World)是一种基于图的近似最近邻搜索算法。
本例实现HNSW向量搜索。该程序使用hnswlib库构建一个基于HNSW算法的近似最近邻搜索索引,对随机生成的高维向量数据进行最近邻查询。由于数据是随机生成的,且没有真实标签,运行结果是确定性伪随机输出的(因为设置了随机种子),但具体数值依赖于hnswlib的内部实现和浮点计算细节。
#pip install hnswlib
import hnswlib
import numpy as np
参数设置
dimension = 64 # 向量维度
n_samples = 10000 # 数据库大小
n_queries = 10 # 查询数量
max_elements = n_samples # 索引最大容量
ef_construction = 200 # 影响构建质量
M = 16 # 影响内存使用和构建时间
生成随机数据
np.random.seed(1234)
data = np.random.random((n_samples, dimension)).astype('float32')
queries = np.random.random((n_queries, dimension)).astype('float32')
创建索引
index = hnswlib.Index(space='l2', dim=dimension) # 使用L2距离
初始化索引
index.init_index(max_elements=max_elements, ef_construction=ef_construction, M=M)
添加向量到索引
index.add_items(data)
设置查询时的ef参数(应 >= k)
ef_search = 100
index.set_ef(ef_search)
搜索
k = 5 # 返回最近邻的数量
labels, distances = index.knn_query(queries, k=k)
print("查询结果:")
for i in range(n_queries):
print(f"查询 {i} 的最近邻索引: {labelsi}, 距离: {distancesi}")
运行代码,输出如下:
查询结果:
查询 0 的最近邻索引: 1204 3271 2568 794 2596, 距离: 5.090098 5.670616 5.7184644 5.805411 5.8450646
查询 1 的最近邻索引: 8063 2700 919 6884 7603, 距离: 6.8587008 6.9564424 7.0883894 7.1045 7.17673
查询 2 的最近邻索引: 3919 8653 4130 6471 7799, 距离: 5.2772083 5.4527187 5.796511 5.984556 6.100777
查询 3 的最近邻索引: 4429 230 317 3062 950, 距离: 5.809805 6.0942507 6.1004057 6.120061 6.375738
查询 4 的最近邻索引: 199 6044 6479 5916 955, 距离: 5.5545816 5.785306 6.0935354 6.1097517 6.1375723
查询 5 的最近邻索引: 6491 4251 8117 2662 7322, 距离: 5.3999023 5.633311 5.9603777 6.1788116 6.2347803
查询 6 的最近邻索引: 7073 901 1930 3568 8171, 距离: 4.6929827 5.4201565 5.5132103 5.593172 5.636413
查询 7 的最近邻索引: 9194 1407 8794 6002 3799, 距离: 5.583033 6.3457537 6.645453 6.8309155 6.920295
查询 8 的最近邻索引: 2047 8455 1301 2895 5418, 距离: 5.8315773 6.053444 6.2084017 6.3825483 6.4070206
查询 9 的最近邻索引: 782 4019 4755 9646 1893, 距离: 6.103296 6.274061 6.5346675 6.587805 6.6404047
5.2.3 检索优化策略
在AI智能体开发中,检索优化策略是提升信息检索效率与准确性的核心手段,直接影响智能体对用户需求的理解和响应质量。本小节将分别对查询扩展(Query Expansion)、重排序(Re-ranking)、动态检索(Dynamic Retrieval)进行详细介绍。
1. 查询扩展
查询扩展是为解决用户查询的"表述局限性"(如模糊、简略、歧义)而设计的策略,通过对原始查询进行合理扩展,增加检索的"召回率"(避免漏检相关信息),同时减少因查询词不完整导致的检索偏差。
主要方法:
(1)同义词扩展:添加查询词的同义词。例如:"汽车" → "汽车 OR 轿车 OR 车辆"。
(2)语义扩展:利用词向量或知识图谱添加语义相关词。使用Word2Vec、BERT等模型找到语义相近的词。
(3)伪相关反馈:假设初次检索的顶部文档是相关的,从中提取扩展词。流程为:原始查询→初次检索→分析顶部文档→提取新词→二次检索。
(4)用户历史扩展:基于用户过往的查询和单击行为进行扩展。
【示例5.7】查询扩展的实现。
from collections import defaultdict
class QueryExpander:
def init(self):
初始化同义词词典
self.synonyms = defaultdict(list)
self.synonyms.update({
'car': 'automobile', 'vehicle', 'motorcar',
'movie': 'film', 'picture', 'flick',
'phone': 'mobile', 'cellphone', 'smartphone'
})
def expand_with_synonyms(self, query):
"""
使用同义词扩展查询
:param query: 原始查询词列表
:return: 扩展后的查询词列表
"""
expanded = \[\]
for term in query:
expanded.append(term)
if term in self.synonyms:
expanded.extend(self.synonymsterm)
return expanded
测试用例
expander = QueryExpander()
original_query = 'car', 'movie'
expanded_query = expander.expand_with_synonyms(original_query)
print(f"原始查询: {original_query}")
print(f"扩展查询: {expanded_query}")
运行代码,输出如下:
原始查询: 'car', 'movie'
扩展查询: 'car', 'automobile', 'vehicle', 'motorcar', 'movie', 'film', 'picture', 'flick'
关键技术点:
- 时间复杂度:O (n ),n为查询词数量。
- 空间复杂度:O (m ),m为同义词词典大小。
- 可扩展性:可集成外部知识库,如WordNet。
2. 重排序
重排序是检索流程中的"二次优化"环节:在检索系统通过召回模型(如TF-IDF、BM25、向量检索)得到一批候选结果后,使用更精细的模型对候选结果与查询的相关性重新打分,优化排序顺序,最终提升"精确率"(让最相关的结果排在最前)。
常用技术:
(1)基于特征的学习排序(Learning to Rank):使用机器学习模型结合多种特征(如TF-IDF、BM25、词向量相似度等)进行排序。常见算法:LambdaMART、RankNet。
(2)深度语义匹配模型:使用BERT、RoBERTa等预训练模型计算查询和文档的深度语义相关性。常见方法:Cross-Encoder架构。
(3)多样性重排序:避免结果过于相似,提高结果多样性。常见算法:MMR(Maximal Marginal Relevance)。
(4)个性化重排序:结合用户画像和历史行为调整排序。
【示例5.8】对初步检索结果进行二次排序,通常使用更复杂的排序模型。
class ReRanker:
def init(self):
模拟预训练模型权重
self.model_weights = {
'relevance': 0.6,
'popularity': 0.2,
'freshness': 0.2
}
def rerank(self, initial_results):
"""
基于多特征重新排序结果
:param initial_results: 初始结果列表,每个元素为包含特征的dict
:return: 重新排序后的结果
"""
计算综合得分
for item in initial_results:
item'score' = sum(
item.get(feature, 0) * weight
for feature, weight in self.model_weights.items()
)
按得分降序排序
return sorted(initial_results, key=lambda x: x'score', reverse=True)
测试用例
ranker = ReRanker()
initial_results = [
{'id': 1, 'relevance': 0.9, 'popularity': 0.5, 'freshness': 0.3},
{'id': 2, 'relevance': 0.7, 'popularity': 0.8, 'freshness': 0.6},
{'id': 3, 'relevance': 0.8, 'popularity': 0.4, 'freshness': 0.9}
]
reranked_results = ranker.rerank(initial_results)
print("初始排序:", item\['id' for item in initial_results])
print("重排序后:", item\['id' for item in reranked_results])
运行代码,输出如下:
初始排序: 1, 2, 3
重排序后: 3, 2, 1
关键技术点:
- 时间复杂度:O (n logn),主要来自排序操作。
- 特征工程:可集成多种特征,如相关性、时效性、个性化等。
- 模型可扩展:可替换为机器学习模型,如LambdaMART。
-
- 动态检索
动态检索是指检索策略随"外部环境"(如用户状态、场景、上下文)动态调整的机制,核心是让检索系统具备"适应性",而非固定规则。
1)核心特点
(1)上下文感知:考虑对话历史、用户偏好等上下文信息。
(2)多阶段检索:根据初步结果动态调整后续检索策略。
(3)实时反馈循环:利用用户实时反馈优化检索。
2)实现方式
(1)查询改写:基于对话历史改写当前查询。例如,用户问"它多少钱?",系统结合上下文改写成"iPhone 15多少钱?"。
(2)混合检索:动态组合多种检索方法。例如,先使用关键词检索,再使用向量检索补充结果。
(3)迭代检索:根据初步结果质量决定是否进行二次检索。例如,当顶部结果置信度低时触发扩展检索。
(4)基于强化学习的动态调整:学习最优的检索策略选择机制。
【示例5.9】动态检索,根据上下文或用户反馈实时调整检索策略。
class DynamicRetriever:
def init(self):
self.query_history = \[\]
self.feedback_history = \[\]
def add_feedback(self, query, feedback):
"""记录用户反馈"""
self.query_history.append(query)
self.feedback_history.append(feedback)
def retrieve(self, query, collection):
"""
基于历史反馈的动态检索
:param query: 当前查询
:param collection: 待检索文档集合
:return: 检索结果
"""
简单实现:根据历史反馈调整查询
expanded_query = query.copy()
for i, past_query in enumerate(self.query_history):
if set(query) & set(past_query): # 有共同查询词
expanded_query.extend(self.feedback_historyi)
模拟检索过程
results = \[\]
for doc in collection:
score = sum(1 for term in expanded_query if term in doc)
if score > 0:
results.append({'doc': doc, 'score': score})
return sorted(results, key=lambda x: x'score', reverse=True)
测试用例
retriever = DynamicRetriever()
retriever.add_feedback('python', 'programming', 'language')
retriever.add_feedback('AI', 'machine', 'learning')
collection = [
'python programming language guide',
'AI and machine learning',
'data science with python',
'introduction to AI'
]
current_query = 'python'
results = retriever.retrieve(current_query, collection)
print(f"查询: {current_query}")
print("结果:", item\['doc' for item in results])
运行代码,输出如下:
查询: 'python'
结果: 'python programming language guide', 'data science with python'
关键技术点:
- 实时性:能够即时响应用户行为和上下文变化。
- 个性化:基于用户历史反馈调整检索策略。
- 复杂度:O (n *m ),n 为文档数,m为查询词数。
