RAG知识点_2_进阶技术

RAG 知识点(二):进阶技术

1. 文档切片策略

定义

文档切片(Chunking)是将长文档切分为适合检索的小块的过程。切片质量直接决定 RAG 系统的效果------切太大检索时混入无关信息,切太小语义不完整。

切片方法对比

方法 难度 适用场景 推荐参数 语义完整度
固定字符切片 快速原型 chunk=500, overlap=50 一般
滑动窗口切片 保持上下文 window=500, step=250 良好
AI 辅助切片 语义完整性 规则粗切+AI精切 优秀
摘要切片 长文档检索 摘要检索+原文返回 优秀

1.1 固定字符切片

python 复制代码
def fixed_char_chunking(text, chunk_size=100, overlap=20):
    """按固定字符数切片,相邻切片保留重叠"""
    chunks = []
    step = chunk_size - overlap
    for i in range(0, len(text), step):
        chunk = text[i:i + chunk_size]
        chunks.append(chunk)
    return chunks

text = "人工智能是模拟人类智能的计算机科学领域..."
chunks = fixed_char_chunking(text, chunk_size=500, overlap=50)
print(f"切片数量:{len(chunks)}")

1.2 滑动窗口切片

python 复制代码
def sliding_window_chunking(text, window_size=100, step_size=50):
    """固定窗口滑动,相邻切片有重叠,保持语义连续性"""
    chunks = []
    start = 0
    while start < len(text):
        chunks.append(text[start:start + window_size])
        start += step_size
        if start >= len(text):
            break
    return chunks

text = "人工智能是模拟人类智能的计算机科学领域..."
chunks = sliding_window_chunking(text, window_size=500, step_size=250)
print(f"切片数量:{len(chunks)}")

1.3 AI 辅助切片

python 复制代码
def ai_chunking_with_llm(text, max_chunks=5, api_key=None):
    """使用 LLM 识别语义边界,按主题自然分段"""
    from openai import OpenAI
    client = OpenAI(api_key=api_key, base_url="https://dashscope.aliyuncs.com/compatible-mode/v1")

    prompt = f"""请分析以下文本,找出{max_chunks}个语义边界。
    文本:{text}
    返回 JSON 格式:{{"split_points": [位置1, 位置2], "reasons": ["原因1", "原因2"]}}"""

    completion = client.chat.completions.create(model="qwen-plus", messages=[
        {"role": "user", "content": prompt}
    ])
    # 解析返回的切分点,按点分割文本
    return parse_and_split(text, completion.choices[0].message.content)

1.4 摘要切片

python 复制代码
def summary_chunking(chunks, api_key=None):
    """为每个切片生成摘要,用摘要检索,原文回答"""
    from openai import OpenAI
    client = OpenAI(api_key=api_key, base_url="https://dashscope.aliyuncs.com/compatible-mode/v1")

    summaries = []
    for chunk in chunks:
        resp = client.chat.completions.create(model="qwen-plus", messages=[
            {"role": "user", "content": f"请用30字概括核心内容:{chunk}"}
        ])
        summaries.append(resp.choices[0].message.content.strip())
    return summaries

# 检索时匹配摘要,返回原文
# 短文档(<500字):直接用原文检索
# 长文档(>2000字):用摘要检索

切片参数推荐

文档类型 chunk_size overlap 方法
短文本(<1000字) 200 50 固定/滑动
中文本(1000-5000字) 500 100 滑动窗口
长文本(>5000字) 800 150 滑动窗口
法律/合同 300 100 AI 切片

2. 检索方法

定义

检索方法决定了从向量数据库中找到"最相关"文档的策略。不同方法适用于不同场景。

2.1 标量查询(Scalar Query)

python 复制代码
# 按条件筛选,类似 SQL WHERE
results = client.query(
    collection_name="my_docs",
    filter="category == 'AI' and views > 500",
    output_fields=["title", "content"],
    limit=10
)
# 适用:有明确过滤条件(按类别、日期等)

2.2 向量检索(Vector Search)

python 复制代码
# 语义搜索,理解"意思"而非"字眼"
query_vector = embed("机器学习是什么")
results = client.search(
    collection_name="my_docs",
    data=[query_vector],
    limit=5,
    output_fields=["content", "category"]
)
# 适用:用户搜"机器学习是什么"(理解语义,无需精确匹配字眼)

2.3 BM25 关键字检索

python 复制代码
# BM25 基于 TF-IDF 改进,精确匹配关键词
results = client.search(
    collection_name="my_docs",
    data=["Milvus 2.4 版本"],   # 直接传文本
    anns_field="sparse_vector",  # 稀疏向量字段
    limit=5,
    search_params={"metric_type": "BM25"},
    output_fields=["title", "text"]
)
# 适用:专有名词需要精确匹配(如产品型号、版本号)

2.4 混合检索(Hybrid Search)--- 生产环境首选

python 复制代码
from pymilvus import AnnSearchRequest, Function, FunctionType

# 稠密向量检索请求(语义)
req_dense = AnnSearchRequest(
    data=[query_vector], anns_field="dense_vector",
    param={"nprobe": 10}, limit=5
)
# 稀疏向量检索请求(关键词)
req_sparse = AnnSearchRequest(
    data=[query_text], anns_field="sparse_vector",   # 传原始文本
    param={"metric_type": "BM25"}, limit=5
)
# RRF 融合排序
ranker = Function(name="rrf", function_type=FunctionType.RERANK,
                  params={"reranker": "rrf", "k": 100}, input_field_names=[])

results = client.hybrid_search(
    collection_name="my_docs",
    reqs=[req_dense, req_sparse], ranker=ranker,
    limit=5, output_fields=["title", "text"]
)
# 适用:语义 + 关键词互补,生产环境首选

检索方法选择指南

复制代码
精确匹配(ID/类别/日期) → 标量查询
语义相似               → 向量检索
关键词匹配             → BM25 关键词检索
综合最优               → 混合检索(RRF 融合)
精益求精               → 混合检索 + Rerank

3. 结果融合策略

定义

混合检索需要将多路检索的结果合并为最终排名。两种主流融合策略:RRF(倒数排名融合)和加权排序。

3.1 RRF(Reciprocal Rank Fusion)

公式:score(d) = Σ 1/(k + rank_i(d))

python 复制代码
# RRF 原理示例
vector_ranking = [0, 1, 2, 4, 3, 5]   # 向量检索排名
keyword_ranking = [0, 2, 1, 5, 3, 4]  # 关键词检索排名

k = 60
rrf_scores = {}
for doc_idx in range(6):
    v_rank = vector_ranking.index(doc_idx) + 1
    k_rank = keyword_ranking.index(doc_idx) + 1
    rrf_scores[doc_idx] = 1/(k + v_rank) + 1/(k + k_rank)

# 按分数降序得到最终排名
final_ranking = sorted(rrf_scores.items(), key=lambda x: x[1], reverse=True)

Milvus 中使用 RRF

python 复制代码
ranker = Function(name="rrf", function_type=FunctionType.RERANK,
                  params={"reranker": "rrf", "k": 100}, input_field_names=[])

RRF 优势:不受原始分数影响,只看排名;不同检索方法的分数可能不可比,但排名可比;鲁棒性好。

3.2 加权排序(Weighted)

公式 :score(d) = w1score1(d) + w2score2(d)

python 复制代码
# 加权融合示例
alpha = 0.6  # 向量权重 60%
for i in range(len(documents)):
    fused = alpha * vector_scores[i] + (1 - alpha) * bm25_scores[i]

Milvus 中使用加权排序

python 复制代码
ranker = Function(name="weighted", function_type=FunctionType.RERANK,
                  params={"reranker": "weighted", "weights": [0.6, 0.4], "norm_score": True})

RRF vs 加权排序对比

方法 优点 缺点 适用场景
RRF 无需调权重、鲁棒性好 无法控制比例 通用场景(推荐)
加权排序 可精确控制比例 需调参、需归一化 明确知道权重时

4. Rerank 重排序

定义

Rerank 是对检索结果进行精排序的二次处理。典型架构:检索(快,Top-50)→ Rerank(准,Top-5)。

核心原理

  • Bi-Encoder(检索阶段):问题和文档分别编码,计算向量相似度,速度快但精度有限

  • Cross-Encoder(Rerank 阶段):问题和文档拼接后一起编码,精度高但速度慢

    向量检索(Bi-Encoder,快)→ Top-50 候选 → Rerank(CrossEncoder,准)→ Top-5 最终

代码示例(BGE-Reranker)

python 复制代码
# 使用 BGE-Reranker 进行重排序
from FlagEmbedding import FlagReranker

reranker = FlagReranker('BAAI/bge-reranker-large', use_fp16=False)

query = "机器学习需要什么基础?"
candidates = [
    "深度学习是机器学习的子集。",
    "机器学习通过训练数据让计算机自动学习。",
    "机器学习需要数学基础,包括线性代数和概率统计。",
]

scores = reranker.compute_score([[query, doc] for doc in candidates])
ranked = sorted(enumerate(scores), key=lambda x: x[1], reverse=True)

for rank, (idx, score) in enumerate(ranked, 1):
    print(f"[{rank}] 分数:{score:.4f} | {candidates[idx]}")

Rerank 模型选择

场景 推荐模型
中文 BAAI/bge-reranker-large
英文 cross-encoder/ms-marco-MiniLM-L-12-v2
多语言 BAAI/bge-reranker-v2-m3
API 方案 Cohere Rerank API

参数建议

  • 召回数量:50-100 条(给 Rerank 足够选择)
  • 返回数量:5-10 条(LLM 上下文限制)
  • 分数阈值:过滤掉 <0.3 的低相关结果

5. BM25 Function(Milvus 2.4+)

定义

Milvus 2.4+ 支持在 Schema 中直接定义 BM25 Function,自动从文本字段生成稀疏向量,无需手动计算。

核心步骤

  1. 文本字段开启 enable_analyzer=True(启用分词)
  2. 文本字段开启 enable_match=True(启用 BM25 匹配)
  3. 定义 BM25 Function:text → sparse_vector
  4. 为稀疏向量字段创建索引:SPARSE_INVERTED_INDEX + metric_type=BM25

代码示例

python 复制代码
from pymilvus import MilvusClient, DataType, Function, FunctionType

client = MilvusClient(uri="http://localhost:19530")

# 1. 定义 Schema
schema = client.create_schema()
schema.add_field(field_name="id", datatype=DataType.INT64, is_primary=True, auto_id=True)
schema.add_field(field_name="text", datatype=DataType.VARCHAR, max_length=2000,
                 enable_analyzer=True, enable_match=True)       # 开关1+2
schema.add_field(field_name="dense_vector", datatype=DataType.FLOAT_VECTOR, dim=1024)
schema.add_field(field_name="sparse_vector", datatype=DataType.SPARSE_FLOAT_VECTOR)

# 2. 定义 BM25 Function
bm25_fn = Function(name="text_bm25", input_field_names=["text"],
                   output_field_names=["sparse_vector"], function_type=FunctionType.BM25)
schema.add_function(bm25_fn)

# 3. 创建索引
idx = client.prepare_index_params()
idx.add_index(field_name="dense_vector", index_type="AUTOINDEX", metric_type="COSINE")
idx.add_index(field_name="sparse_vector", index_type="SPARSE_INVERTED_INDEX", metric_type="BM25")

# 4. 创建 Collection
client.create_collection(collection_name="hybrid_docs", schema=schema, index_params=idx)

# 5. 插入数据(sparse_vector 自动生成!)
client.insert("hybrid_docs", data=[
    {"text": "人工智能是模拟人类智能的计算机科学", "dense_vector": [0.1]*1024}
])
# 注意:不需要手动提供 sparse_vector,BM25 Function 自动生成

6. 查询重写与扩展(Query Rewriting)

定义

用户提出的问题往往不是最适合检索的形式。查询重写通过 LLM 将模糊问题转化为更精确的检索语句,提高检索召回率。

常见策略

6.1 查询改写:将用户口语化问题转为正式检索语句

python 复制代码
def rewrite_query(question, llm_client):
    prompt = f"""将以下问题重写为3个不同角度的检索查询:
    原问题:{question}
    每行一个查询:"""
    response = llm_client.chat.completions.create(model="qwen-plus", messages=[
        {"role": "user", "content": prompt}
    ])
    return response.choices[0].message.content.strip().split("\n")

queries = rewrite_query("AI怎么学", client)
# 输出示例:
# 1. 人工智能学习路径和方法
# 2. AI入门需要哪些基础知识
# 3. 如何系统学习人工智能技术

6.2 HyDE(假设文档嵌入):让 LLM 先生成假设答案,再用假设答案去检索

python 复制代码
def hyde_search(question, llm_client, embed_fn, milvus_client):
    # Step 1: 生成假设答案
    response = llm_client.chat.completions.create(model="qwen-plus", messages=[
        {"role": "user", "content": f"请回答:{question}"}
    ])
    hypothetical_answer = response.choices[0].message.content

    # Step 2: 用假设答案的向量去检索真实文档
    query_vector = embed_fn(hypothetical_answer)
    results = milvus_client.search("knowledge", data=[query_vector], limit=5)
    return results

6.3 子问题分解:将复杂问题拆分为多个子问题分别检索

python 复制代码
def decompose_query(question, llm_client):
    prompt = f"""将以下复杂问题分解为2-3个简单的子问题:
    问题:{question}
    子问题(每行一个):"""
    response = llm_client.chat.completions.create(model="qwen-plus", messages=[
        {"role": "user", "content": prompt}
    ])
    return response.choices[0].message.content.strip().split("\n")

# 示例:"RAG和微调有什么区别,各自适合什么场景?"
# → 子问题1: "RAG是什么,有什么优缺点?"
# → 子问题2: "微调是什么,有什么优缺点?"
# → 子问题3: "RAG和微调的选择标准是什么?"

7. Contextual Retrieval(上下文检索)

定义

Anthropic 在 2024 年提出的技术。在切片时,为每个 chunk 添加其所属文档的上下文摘要,减少因切片导致的语义丢失。实验表明可减少 67% 的检索失败率。

代码示例

python 复制代码
def contextual_chunking(document, chunk_size=500, overlap=50):
    """为每个切片添加文档级上下文摘要"""
    # 1. 先生成整个文档的摘要
    doc_summary = llm_generate(f"用一句话概括:{document[:2000]}")

    # 2. 切片
    chunks = sliding_window_chunking(document, chunk_size, overlap)

    # 3. 为每个切片添加上下文
    contextual_chunks = []
    for i, chunk in enumerate(chunks):
        # 在 chunk 前添加:文档摘要 + 当前位置
        context_prefix = f"[文档摘要:{doc_summary}]\n[第{i+1}/{len(chunks)}段]\n"
        contextual_chunks.append(context_prefix + chunk)

    return contextual_chunks

# 检索时,context_prefix 帮助向量模型理解 chunk 的上下文
# 效果:减少"Lost in the Middle"和语义切断问题

相关推荐
小猴子下山1233 小时前
2026年无锡细胞存储市场格局观察:四家企业的传承脉络与业务分野
大数据·人工智能·精选
Database_Cool_3 小时前
数据库慢查询优化首选方案:阿里云 RDS 性能洞察+自动诊断
数据库·人工智能·阿里云
北邮刘老师3 小时前
国标配套开源实现再升级!AIP智能体互联开源项目v2.1.0正式发布
人工智能·开源·大模型·智能体·智能体互联网
zhoupenghui1683 小时前
【AI大模型应用开发】【项目实战】13.RAG智慧问答项目-(一)项目介绍&项目架构&项目环境配置
人工智能·docker·ai·milvus·rag·attu·rag智慧问答项目
神奇小汤圆3 小时前
AI Coding 不只靠 Prompt:Agent 工程闭环如何接入 DevOps
人工智能
瓶中怪3 小时前
ROS2 机器人软件系统
linux·c++·python·ubuntu·vmware·ros2·机器人软件开发
hongmai6668883 小时前
ESP32-S2-MINI-2U-N4R2:一款为灵活部署而生的Wi-Fi MCU模组
人工智能·单片机·嵌入式硬件·物联网·智能家居
神奇小汤圆3 小时前
AI Agent 替你写代码没问题,但这 3 类后端任务让它当场翻车
人工智能
Li-Yongjun3 小时前
claude 安装使用教程
ai编程
满怀冰雪3 小时前
22_Runnable接口源码拆解_LCEL管道语法背后_invoke_stream_batch究竟做了什么
python·batch