跳转至

RAG 链路设计

RAG 链路与分块策略

RAG 的完整链路,以及它和“直接 Prompt 塞文档”的根本区别

完整的 RAG 链路,本质上是一条“离线索引 → 在线检索 → 生成答案”的工业流水线。

很多初学者会认为,把文档扔进 Prompt 就算 RAG 了。但这两者之间的差距,就像“把整本书丢给一个人让他找答案”和“建好索引的图书馆”。

image.png

离线索引阶段:把文档变成可检索的“卡片”。

  • 清洗:去掉页眉页脚、特殊字符,保证文本干净。

  • 分块:将长文档切成语义相对完整的小段落(chunk),这是 RAG 效果的核心基础。

  • 向量化:用 Embedding 模型把每个 chunk 转成固定维度的向量。

  • 存储:写入向量数据库,同时保留元数据(来源、页码、时间等)。

在线检索生成阶段:

  • 用户问题同样向量化。

  • 在向量库中做相似度搜索,取出最相似的 K 个 chunk。

  • 可选地,用 Reranker(交叉编码器)对这些 chunk 重新精排,提升精度。

  • 将最相关的 chunk 与用户问题一起,按照精心设计的 Prompt 模板喂给大模型。

  • 模型生成答案,并(最好)附上引用来源。

它与“直接 Prompt 塞文档”的本质区别在哪里?

查看内嵌表格

一个简单的代码对比,比任何文字都直观:

# ❌ 直接塞文档:粗暴、低效、不可控
prompt = f"请根据以下整本手册回答问题:\n{whole_book}\n\n问题:{question}"
answer = llm(prompt)

# ✅ RAG 方式:精准、高效、可追溯
docs = vectorstore.similarity_search(question, k=5)
context = "\n".join([d.page_content for d in docs])
prompt = f"参考资料:\n{context}\n\n问题:{question}"
answer = llm(prompt)

RAG 的完整链路,本质上是在用工程手段,把“大海捞针”变成“按图索骥”。


文档分块策略有哪几种?各自的优缺点是什么?

分块是 RAG 里“细节见真章”的地方。切得好,检索准;切不好,后面整个链路都在为碎片信息买单。我习惯把主流策略分成四类,从左到右,智能程度递增,成本也递增。

固定长度切分 → 递归切分 → 句子/语义切分 → 结构感知切分
  简单粗暴      最常用       语义完整       结构化文档专属

查看内嵌表格

在生产中,绝大多数情况用“递归字符切分”就能达到不错的效果。 LangChain 和 LlamaIndex 都提供了现成的实现。

from langchain.text_splitter import RecursiveCharacterTextSplitter

splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,         # 每个 chunk 最大字符数
    chunk_overlap=50,       # 相邻 chunk 重叠长度,防止关键信息被切断
    separators=["\n\n", "\n", "。", ".", " ", ""],
    length_function=len,
)
chunks = splitter.create_documents([full_text])

Chunk Size 和 Overlap 的经验值:

  • chunk_size:通常设在 256~1024 token 之间。越小检索越精准,但可能丢失上下文;越大上下文越全,但检索粒度粗。对于中文,512 字符是个安全的起点。

  • chunk_overlap:取 chunk_size 的 10%~20%。目的是在边界处形成“语义重叠带”,防止答案的关键部分正好被截断在两个 chunk 的连接处。

分块策略选型,最重要的是看文档的“亲妈”是谁——是结构化的 Markdown 还是混乱的 PDF 扫描件?是严谨的法律条款还是随意的聊天记录?没有银弹,只有最适配的刀。


RAG 系统召回的文档相关性很差,如何系统性排查和优化?

召回差是 RAG 系统最常见的线上故障,而根因往往不止一个。我习惯用一条“顺藤摸瓜”式的排查路线,从问题本身倒推,找到病根再下药。

排查路线图:

image.png

具体排查步骤与优化手段:

第一步:先看 Query 有没有“带病上岗”

很多召回问题,根源在用户。用户问“那个红色的按钮是干嘛的?”,而知识库里写的是“紧急制动开关功能说明”。这种表达鸿沟靠单纯的向量相似度很难跨越。

  • 解决方案:引入 Query 改写。在检索前,用 LLM 把口语化、模糊化的问题,改写成更精准、更像文档语言的检索词。例如用 Multi-Query 生成多个角度的查询,或用 HyDE 先生成一篇假设文档再检索。
# 简单的 Query 改写示例:把口语变专业
rewrite_prompt = f"将以下用户问题改写成更适合检索的关键词:{question}"
search_query = llm(rewrite_prompt)
docs = vectorstore.search(search_query)

第二步:看检索结果,判断是“没召回”还是“召回了没用”

把检索返回的 Top-5 文档片段打印出来,和问题放在一起对比。

  • 如果完全不沾边:问题出在“语义鸿沟”或“专有名词”。 → 上混合检索(Hybrid Search):向量检索擅长语义,但对专有名词、缩写不敏感。加上 BM25 关键词检索,两路结果用 RRF(倒数排名融合)合并,能极大改善专有名词的召回。

  • 如果部分相关,但最准的那条排在后面:问题出在“初排不够精”。 → 引入 Reranker(精排模型):用 Cross-Encoder 对召回的候选(比如 top-50)重新打分排序,把真正相关的提到前几位。这是成本低、效果提升最显著的一步。

  • 如果检索到的片段像是“半句话”:问题出在分块切断了关键信息。 → 增大 chunk_overlap,或用“父子文档索引”:用小 chunk 做精准检索,但返回给 LLM 时,自动展开成包含该 chunk 的整个大段落(父文档)。

第三步:如果检索结果明明很准,但 LLM 还是答非所问

根因在“生成侧”。常见问题:

  • 上下文过长,模型注意力分散:精简上下文,只保留 Reranker 给出的 Top-3。

  • Prompt 指令不清晰:强化“只能根据提供的资料回答,不知道就明确说不知道”的指令。

  • 模型能力不足:从 GPT-3.5 换成 GPT-4o,或从 7B 小模型换成更强的模型。

第四步:做一个端到端的排查脚本

我习惯在生产日志里挂一个诊断钩子,当用户点“踩”时,自动抓取当时的 question、检索 docs 和最终 answer,然后跑一遍分析。

def diagnose_relevance(question, docs, answer):
    print(f"用户问题: {question}")
    for i, doc in enumerate(docs):
        print(f"检索结果 {i+1}: {doc.page_content[:200]}...")
    # 用 LLM 辅助判断
    judge_prompt = f"问题: {question}\n检索片段: {docs}\n答案: {answer}\n分析召回差的原因。"
    diagnosis = llm(judge_prompt)
    print(f"诊断意见: {diagnosis}")

优化不是一锤子买卖。每次改进分块、改写、混合检索后,都应该用同一批测试集(比如 100 条真实用户查询)来对比前后的召回率和最终答案的准确率。用数据说话,而不是凭感觉调参。


4.2 向量数据库与检索优化

向量数据库设计

🧱 1. 什么是向量数据库的 Namespace 隔离?

在向量数据库中,Namespace(命名空间)就像数据库里的 Schema 或文件夹——它在同一个 Collection(类似表)内部,将数据在逻辑上划分成多个相互独立的“隔间”。每个隔间里的向量只能被搜索该隔间的查询看到,不同 Namespace 之间互不干扰。

image.png

查询时的行为:你必须在查询请求中显式指定 namespace,如果不指定,有些向量库会返回错误,有些会查全部。指定 namespace="tenant_A" 后,检索就只会在这个隔离区里进行,绝不会跑到隔壁去。

它的核心价值:

  • 逻辑隔离:同一个 Collection,一套索引结构,但数据检索互不穿透。

  • 成本低:不需要为每个租户单独建 Collection(每个 Collection 都有独立的索引、内存和管理开销)。

  • 管理简单:批量操作(如备份、删除)可以按 Namespace 执行。

示例代码(以 Chroma 为例,也适用于 Milvus 的 partition key 等类似概念):

import chromadb

client = chromadb.PersistentClient(path="./db")
collection = client.get_or_create_collection("knowledge_base")

# 为 tenant_A 插入文档
collection.add(
    documents=["文档内容..."],
    metadatas=[{"source": "HR手册"}],
    ids=["doc_1"],
    namespace="tenant_A"   # 关键:指定命名空间
)

# 查询时也指定 namespace
results = collection.query(
    query_texts=["年假政策"],
    n_results=5,
    namespace="tenant_A"   # 只在这个租户的数据里搜
)

Namespace 隔离的实质:它是数据层面的“软隔断”,不是物理上的强隔离。如果代码里不小心写错了 namespace,或者被恶意注入,就可能发生跨租户访问。所以它通常需要配合应用层的权限校验一起使用。


⚖️ 2. SaaS 场景下,多租户 RAG 的隔离方案与权衡

在 SaaS 里,隔离绝不是“越强越好”,而是要在安全性、成本、性能、运维复杂度四者之间找到平衡。从上到下,隔离等级递增,成本也递增。

image.png

查看内嵌表格

权衡核心:

  • 免费用户 / 小微企业:文档少,付费意愿低,用 Namespace 或 Metadata Filter 足以。给他们独立 Collection 就像给路边的共享单车配私人飞机——浪费且没必要。

  • 付费企业客户:文档多,查询 QPS 高,对响应时间和数据安全性有合同要求。必须上 独立 Collection 甚至 独立实例,既能保证性能不相互干扰,也能满足审计和合规需求。

  • 中间层:可以用 Namespace 作为平滑过渡。Namespace 在 Milvus、Chroma、Pinecone(通过 index 区分)中都有支持,它比 filter 更安全,比独立 Collection 更省资源。

一个常见的错误:初期为了省事,全部用 metadata filter 一把梭。当租户量上来后,查询时 WHERE tenant_id=xxx 会在海量向量中做过滤,导致响应时间飙升,而且一旦 filter 写错就是安全事故。架构上,隔离策略必须与客户等级绑定,动态可配。


🏗️ 3. 5000 租户,分层付费场景下的隔离架构设计

场景重述:

  • 4950 个免费用户,每人 200 条文档,总共约 100 万条向量。

  • 50 个付费企业,每人 5 万条文档,总共 250 万条向量。

  • 免费用户要求低成本、基本可用;付费用户要求高性能、强隔离。

设计目标:在满足隔离需求的前提下,最小化资源消耗和运维负担,并为未来扩展留好口子。

推荐架构:分级隔离 + 动态路由

image.png

免费用户:使用独立 Collection + Namespace 或 Metadata Filter? 这里有一个资源权衡。4950 个免费用户如果每人一个 Namespace,管理上很清晰,底层仍是共享索引,成本可控。但 4950 个 Namespace 的管理本身也有开销(元数据、连接数)。更极致的省钱方案:所有免费用户扔进一个 free_tenants Collection,使用 tenant_id 做元数据过滤,查询时强制加 filter。虽然安全性稍弱,但对于免费用户的数据敏感度,这个风险是可接受的。并且可以通过代码层严格封装 filter,避免遗漏。

付费企业:独立 Collection 为每个企业客户创建一个独立的 Collection,名字如 ent_tenant_123。这样做的好处:

  • 性能隔离:大客户的频繁查询不会挤占免费用户的资源,也不会被其他大客户干扰。

  • 独立调优:可以为不同企业配置不同的索引参数(如 HNSW 的 MefConstruction)。

  • 精细运维:备份、恢复、迁移都可以针对单个 Collection 进行,不影响其他客户。

  • 安全合规:合同里承诺的“数据物理隔离”可以轻松满足。

路由层实现(伪代码):

from enum import Enum

class TenantTier(Enum):
    FREE = "free"
    ENTERPRISE = "enterprise"

# 根据租户 ID 解析其等级和对应的 Collection/Namespace
def get_tenant_context(tenant_id: str) -> dict:
    info = tenant_service.get_info(tenant_id)  # 从缓存或数据库查
    if info.tier == TenantTier.FREE:
        return {
            "collection_name": "free_tenants",
            "filter": {"tenant_id": tenant_id}  # 元数据过滤
        }
    else:
        return {
            "collection_name": f"ent_tenant_{tenant_id}",
            "filter": None  # 独立 Collection,无需额外过滤
        }

# 检索接口
def search_for_tenant(tenant_id: str, query: str):
    ctx = get_tenant_context(tenant_id)
    collection = get_collection(ctx["collection_name"])

    if ctx["filter"]:
        # 免费用户:走 metadata filter
        results = collection.query(
            query_texts=[query],
            n_results=5,
            where=ctx["filter"]
        )
    else:
        # 付费用户:直接查专属 Collection
        results = collection.query(
            query_texts=[query],
            n_results=5
        )
    return results

使用 Milvus 的 Partition Key 优化免费用户隔离 如果向量库是 Milvus,可以用 Partition Key 替代 metadata filter。Partition Key 在写入时根据 tenant_id 哈希分区,查询时指定分区键,能实现与 Namespace 类似的逻辑隔离,且查询效率远高于通用的 scalar filter。

from pymilvus import Collection, connections

# 免费用户共享 Collection,用 tenant_id 作为 partition key
connections.connect()
schema = ... # 定义向量字段 + tenant_id (VARCHAR, partition_key=True)
collection = Collection("free_tenants", schema)
collection.create_index("vector", ...)

# 查询时指定分区键
results = collection.search(
    data=[query_vec],
    anns_field="vector",
    expr=f'tenant_id == "{tenant_id}"',  # Milvus 会根据 partition key 优化
    limit=5
)

未来扩展性预留:

  • 如果某个免费用户成长为付费企业,只需将其数据从 free_tenants Collection 中迁移到独立的 ent_tenant_xxx Collection。迁移可以通过向量库的 export/import 或自研脚本完成,该过程对用户几乎无感知。

  • 定期对 free_tenants 做数据清理,删除僵尸用户,回收存储空间。

  • 在应用层加一个容量监控,当免费用户的数据量接近上限时,提示升级。

成本估算:

  • 免费用户:共享 Collection,索引内存占用 ≈ 向量维度 × 向量总数。100 万条 1536 维向量,约需 6GB 内存(加上索引开销,10GB 以内)。

  • 付费企业:50 个独立 Collection,每个 5 万条向量,各自索引。总内存占用会比共享模式略高(每个 Collection 的索引有固定开销),但换来的是绝对的性能隔离和灵活的 SLA 管理。

  • 运维:免费用户只需维护一个 Collection,备份简单;付费企业可用自动化脚本批量管理。

一句话总结:5000 租户的架构,不是在每一个细枝末节上追求“最高安全”,而是用分层隔离的策略,把好钢用在刀刃上——免费用户用逻辑隔离降本,付费客户用物理隔离提质,路由层屏蔽所有复杂度,让租户永远看不到底层的隔离逻辑。 这是 SaaS 架构最经典的“分级服务”思维在 RAG 时代的延伸。


数据更新权衡

1、基础题:为什么 RAG 系统的知识库更新不能简单地删掉旧数据再全量重新导入?

难度级别:⭐(增量更新必要性)

全量重建会对所有文档重新做 Embedding,成本随文档数量线性增长,对于大型知识库(几万到几十万文档)成本和时间都难以接受。全量重建期间知识库可能处于不可用状态,影响服务连续性。增量更新只处理有变化的文档,大幅降低成本,同时可以做到对用户无感知的滚动更新。


2、进阶题:当知识库的源文档发生变更时,如何设计 RAG 系统的增量更新机制?如何避免全量重建带来的性能问题?

难度级别:⭐⭐(文档变更检测、增量 Embedding、Upsert 幂等设计、删除同步)

1️⃣ Common Answer

RAG 系统的知识库需要随着源文档更新而更新。全量重建太慢成本也高。增量更新的思路是只处理有变化的文档,可以用 MD5 或者 SHA256 给每个文档算一个哈希值存起来,下次处理时先算新文档的哈希,和存储的哈希比对,只有哈希不一样的才需要重新处理。向量数据库支持 Upsert 操作,可以根据文档 ID 更新已有的向量,不需要先删再插。如果源文档被删除了,也需要同步把对应的向量从数据库里删掉。

2️⃣ Impressive Answer

我会把增量更新拆成四个核心步骤来设计,每个步骤都有容易踩的坑。

  1. 变更检测用内容哈希,不用文件时间戳。时间戳可能因为 touch 操作、系统迁移等原因不可靠,内容哈希(SHA256)才能准确反映文件是否真的变了。同时要维护一个文档注册表,记录每个文档的哈希值和对应的 chunk_id 列表,检测时做三路对比:新增(在源目录但不在注册表)、删除(在注册表但不在源目录)、更新(哈希值变化)。

  2. 更新文档要先删旧 chunk 再插新 chunk,不能直接 Upsert。因为文档内容变化可能导致分块数量和边界都发生变化,如果直接 Upsert 旧的 chunk_id,会出现旧 chunk 残留的问题——比如原来 10 个 chunk,更新后变成 7 个,直接 Upsert 只会更新前 7 个,后 3 个旧 chunk 会永久残留在向量库里污染检索结果。

  3. chunk_id 用确定性设计保证 Upsert 幂等性。推荐用文件路径 + chunk 序号做 MD5 生成 chunk_id,同一文件同一位置的 chunk ID 永远一样,这样即使因为网络中断重试处理,也不会产生重复数据。

  4. 删除同步是最容易被忽视的环节。源文档删除时必须同步清理向量库里的所有相关 chunk,否则检索时会召回已删除文档的内容,造成信息污染。工程上还要注意:批量处理时合并 Embedding API 请求降低成本;大批量更新要支持断点续传,单个文档失败不影响整体;把更新任务安排在低峰期执行,防止 Embedding API 请求过于集中。

3️⃣ Key Differences

查看内嵌表格


3、场景题:知识库有 10 万份文档,每天有约 1000 份文档更新,更新期间服务不能中断,如何设计更新流程?

难度级别:⭐⭐⭐(滚动更新、服务连续性、流量切换)

1️⃣ Common Answer

可以用异步更新,把更新任务放到队列里,后台慢慢处理,不影响前台服务。每天 1000 份文档,如果处理速度够快就不会有问题。可以在低峰期(比如凌晨)做批量更新。

2️⃣ Impressive Answer

这道题的关键约束是"服务不能中断",意味着更新过程中检索必须始终可用,不能有 dirty read(读到更新了一半的数据)。

核心设计是写时隔离,切换生效。新文档的 chunk 写入时先打上 status=pending 标记,检索查询强制过滤 status=active 的 chunk,这样更新中的文档不会污染线上检索结果。一批文档的所有 chunk 全部写入完成后,原子地把状态从 pending 改为 active,同时把旧版本 chunk 标记为 deprecated,异步清理。

流量控制也很重要:1000 份文档同时 Embedding 会把 API 配额打满,要做速率限制(比如每分钟处理 50 份),均匀分散在低峰时段(凌晨 1-5 点)处理。同时维护一个更新队列,记录每份文档的处理状态(pending/processing/done/failed),支持失败重试和断点续传。

监控上要关注两个指标:更新队列积压量(防止更新速度跟不上)和 pending chunk 的年龄(超过 1 小时未切换 active 要告警,说明更新流程卡住了)。

3️⃣ Key Differences

查看内嵌表格


4、容易一起考的题

查看内嵌表格

Milvus / Qdrant 与检索策略

1、基础题:向量相似度检索常用哪几种距离度量?各自适合什么场景?

难度级别:⭐⭐(余弦相似度、欧氏距离、内积、归一化影响)


2、进阶题:HNSW 索引的原理是什么?为什么它比暴力搜索快?

难度级别:⭐⭐⭐(分层图结构、贪心搜索、构建参数 M/efConstruction、精度与速度权衡)

1️⃣ Common Answer

HNSW 是一种图结构的索引,把向量都连成图,查询的时候从图上跳着找,不用和所有向量都比较,所以比暴力搜索快。它有多层结构,上层稀疏下层密,先在上层快速定位,再在下层精细查找。M 参数控制每个节点的连接数,越大越准但越慢。

2️⃣ Impressive Answer

HNSW(Hierarchical Navigable Small World)的核心思想是分层跳跃 + 局部贪心搜索,我从结构和查询两个维度来说:

  1. 分层图结构:构建时将节点随机分配到多层,底层包含所有节点(全量),上层节点数指数级减少。每个节点与邻居建立双向连接,连接数由参数 M 控制(通常 16-64)

  2. 查询过程:从最高层入口节点开始,贪心地朝查询向量方向移动,逐层降落到底层;底层用 ef(动态候选列表大小)控制搜索范围,最终返回 Top-K 结果

  3. 核心参数权衡

  4. M:每节点最大连接数,越大精度越高,内存和构建时间成比例增加
  5. efConstruction:构建时的候选列表大小,越大索引质量越好,但构建越慢
  6. ef(查询时):搜索时动态候选数,可运行时调整,是精度和速度的实时权衡旋钮

  7. 复杂度优势:暴力搜索 O(N·d),HNSW 平均 O(log N ·d),百万级向量场景下速度差距可达百倍以上

3️⃣ Key Differences

查看内嵌表格


3、场景题:RAG 系统要同时支持语义检索和关键词过滤(如按部门、时间范围),如何设计?

难度级别:⭐⭐⭐(标量过滤 + 向量检索、预过滤 vs 后过滤、Payload 索引、性能影响)

1️⃣ Common Answer

可以先用关键词过滤一下文档,再对过滤后的结果做向量检索。或者向量检索完再根据部门、时间做过滤。用 Milvus 的话,好像可以在查询里加 filter 条件,直接一步到位。

2️⃣ Impressive Answer

向量检索和标量过滤的组合,核心在于过滤时机的权衡,有两种策略:

  1. 预过滤(Pre-filtering):先用标量条件(部门=XX,时间范围)筛出候选集,再在候选集上做向量检索
  2. 优点:最终结果一定满足过滤条件
  3. 风险:若候选集过小,向量检索空间不足,召回率下降,极端情况返回空

  4. 后过滤(Post-filtering):先向量检索 Top-K,再过滤不满足条件的结果

  5. 风险:过滤后结果数量不可控,可能所有 Top-K 都被过滤掉

  6. 生产推荐方案:在向量数据库(Qdrant/Milvus)中对标量字段建 Payload 索引,让数据库在向量检索时同步应用过滤条件,利用索引加速标量过滤,避免全量扫描;同时适当放大初始召回数(如 ef=200)保证过滤后仍有足够结果

  7. 分区隔离优化:若按部门过滤是高频场景,可以按部门做 Collection 分区(Milvus Partition),直接缩小检索范围,性能提升最显著

3️⃣ Key Differences

查看内嵌表格


4、容易一起考的题

查看内嵌表格


4.3 Embedding 模型原理与选型

Embedding 模型核心机制

基础题:Embedding 模型的作用是什么?好的 Embedding 有什么特征?

难度级别:⭐(语义向量化、语义相似度、聚类能力、泛化性)


进阶题:BGE、M3E、OpenAI Ada 这几款 Embedding 模型有什么区别?如何选型

难度级别:⭐⭐⭐(中英文能力差异、上下文长度、开源 vs 闭源、领域适配)

1️⃣ Common Answer

BGE 是智源开源的,有中文和英文版本,中文效果不错。M3E 也是开源的,主打中文场景。OpenAI Ada 是 API 调用的,效果好但要花钱,而且中文支持一般。选型的话,中文项目用 BGE 或 M3E,英文或者有预算就用 OpenAI。

2️⃣ Impressive Answer

我从模型能力、使用成本和适用场景三个维度对比:

  1. BGE(BAAI General Embedding)系列
  2. 智源开源,有 BGE-base/large 和 BGE-M3(多语言)多个版本
  3. 中文 MTEB 榜单第一梯队,最大上下文 512-8192 token(M3 支持长文本)
  4. 优势:开源免费、中文最强、可本地部署、支持微调
  5. 适合:中文 RAG、预算有限、数据敏感不能出域的场景

  6. M3E 系列

  7. 中文专用 Embedding,MTEB 中文榜单常客
  8. 轻量级(M3E-base 约 110M 参数),推理速度快
  9. 优势:中文场景精度高、资源消耗低、可蒸馏部署
  10. 适合:纯中文场景、边缘设备部署、低延迟要求

  11. OpenAI Ada-002 / text-embedding-3

  12. 闭源 API,text-embedding-3 支持 256-3072 维度可调
  13. 英文 MTEB 顶尖,中文中等偏上但不如 BGE
  14. 优势:开箱即用、无需维护、多语言均衡
  15. 劣势:按 token 计费、数据出域、延迟受网络影响
  16. 适合:英文 RAG、快速原型、团队无 ML 运维能力

选型决策树:中文优先 BGE-M3 → 纯中文轻量选 M3E → 英文/多语言选 OpenAI → 长文本选 BGE-M3(8K 上下文)

3️⃣ Key Differences

查看内嵌表格

进阶题:准确率到了瓶颈怎么办?有了解模型微调嘛?

1️⃣ Common Answer

针对垂直领域数据进行收集,并且对基模做 LoRa,实现准确率提升。

2️⃣ Impressive Answer

"我们在 RAG 召回系统中,对 Qwen2.5-7B 做了 LoRA 微调,主要解决通用模型在 xx 领域的两个问题:一是不熟悉 xx 术语和行业黑话,二是输出格式不符合 xx 报告的规范。具体流程是:先从业务日志里提取了 300 条真实的投研问答,再用 Claude 扩充到 800 条,人工筛选后保留 600 条高质量数据。用 LLaMA-Factory 对 Qwen2.5-7B-Instruct 做 LoRA 微调(rank=8,alpha=16),在单卡 A100 上训练了 3 个 epoch,约 2 小时。效果对比:领域问答准确率从 72% 提升到 89%,格式合规率从 60% 提升到 95%,幻觉率从 18% 降到 7%。微调后的模型集成进 RAG 系统,替换了原来的通用 Qwen3.5,整体答疑质量有明显提升。"

整体流程图

业务需求分析
数据准备(最关键,占 60% 工作量)
选择微调方法(LoRA / QLoRA / 全量)
配置训练参数
启动训练 + 监控 loss 曲线
效果评估(对比微调前后)
部署推理(vLLM / Ollama)

Step 1:数据准备(最重要)

数据格式:Alpaca 格式(最通用)

[
  {
    "instruction": "分析宁德时代2024年Q3的营收情况",
    "input": "营收数据:Q3营收1000亿,同比增长15%,毛利率28%",
    "output": "宁德时代2024年Q3营收表现稳健,营收1000亿元同比增长15%,毛利率维持在28%的健康水平,主要受益于储能业务放量和海外市场拓展..."
  }
]

数据来源

  • 方案 A(最快):用 GPT-4/Claude 生成种子数据,人工筛选

  • 方案 B(最真实):从业务日志里提取真实的用户问题 + 专家回答

  • 方案 C(混合):A 生成初稿,B 的真实数据做质量校验

数据量经验值

  • 500-1000 条:能让模型学会领域风格和格式

  • 3000-5000 条:能显著提升领域准确率

  • 10000+ 条:接近全量微调效果

Step 2:选择微调方法

查看内嵌表格

LoRA 核心原理

不直接更新原始权重矩阵 W,而是训练两个小矩阵 A(d×r)和 B(r×d),其中 r 远小于 d(如 r=8,d=4096)。推理时 W' = W + BA,参数量从 d² 降到 2dr,当 r=8 时参数量减少 99.6%。

Step 3:用 LLaMA-Factory 训练(推荐工具)

# 安装
pip install llamafactory

# 启动 WebUI(最简单,不用写代码)
llamafactory-cli webui

# 或者用命令行
llamafactory-cli train \
  --model_name_or_path Qwen/Qwen2.5-7B-Instruct \
  --stage sft \
  --do_train \
  --finetuning_type lora \
  --lora_rank 8 \
  --lora_alpha 16 \
  --dataset my_finance_data \
  --template qwen \
  --num_train_epochs 3 \
  --per_device_train_batch_size 2 \
  --gradient_accumulation_steps 4 \
  --learning_rate 1e-4 \
  --output_dir ./output/qwen2.5-7b-finance-lora

关键参数说明(面试会问):

  • lora_rank=8:低秩矩阵的秩,越大效果越好但显存越多,通常 8-16

  • lora_alpha=16:缩放系数,通常设为 rank 的 2 倍

  • learning_rate=1e-4:LoRA 的学习率比全量微调大 10 倍(全量通常 1e-5)

  • gradient_accumulation_steps=4:梯度累积,等效于 batch_size×4,节省显存

Step 4:监控训练过程

看 loss 曲线判断是否正常

正常情况:
train_loss: 2.5 → 1.8 → 1.2 → 0.8 → 0.6(平滑下降后趋于平稳)
eval_loss:  2.6 → 1.9 → 1.3 → 0.9 → 0.85(略高于 train_loss,正常)

过拟合信号:
train_loss: 2.5 → 0.3(下降太快,说明数据太少或 epoch 太多)
eval_loss:  2.6 → 1.5 → 2.0 → 2.5(先降后升,已过拟合)

欠拟合信号:
train_loss 在 1.5 以上不再下降(数据太少或 learning_rate 太小)

Step 5:效果评估

# 对比微调前后在测试集上的表现
test_queries = [
    "分析新能源行业的投资机会",
    "宁德时代的核心竞争力是什么",
    ...  # 50条测试问题
]

# 评估指标
# 1. 领域准确率(人工标注):答案是否符合金融专业标准
# 2. 格式合规率:是否按要求输出结构化格式
# 3. 幻觉率:是否编造不存在的数据
# 4. BLEU/ROUGE:与标准答案的文本相似度

场景题:RAG 系统在专业领域(如医疗、法律)检索效果差,如何优化 Embedding?

难度级别:⭐⭐⭐⭐(领域微调、Contrastive Learning、数据构造、Adapter 方案)

1️⃣ Common Answer

领域效果差的话,可能是通用 Embedding 不懂专业术语。可以试试微调 Embedding 模型,用领域数据训练一下。或者用大一点的模型,比如 BGE-large。也可以考虑用领域专用的 Embedding,如果有的话。

2️⃣ Impressive Answer

领域适配的本质是分布对齐,我有三层递进方案:

  1. 第一层:Prompt 增强(零成本快速验证)
  2. 在查询和文档前加领域前缀,如"以下是医疗问题:"、"以下是医疗文档:"
  3. 利用模型预训练中的领域知识,无需训练,可快速验证是否有提升

  4. 第二层:Contrastive Learning 微调(核心方案)

  5. 构造三元组数据 (anchor, positive, negative):医疗问题 - 正确答案 - 错误答案
  6. 用 InfoNCE 或 Triplet Loss 微调,拉近正样本、推远负样本
  7. 数据构造可借力 LLM:用 GPT 生成相似问题和干扰答案
  8. 通常 1000-5000 条高质量三元组即可显著提升领域 MRR

  9. 第三层:Adapter 轻量化方案(资源受限时)

  10. 冻结 Embedding 主干,只训练少量 Adapter 层(LoRA 或 MLP Head)
  11. 参数量减少 90% 以上,训练速度快,适合多领域快速切换
  12. 配合知识蒸馏,可将微调后的模型压缩到 M3E-base 量级

关键点:先评估领域词分布偏移程度(KL 散度),若偏移小则 Prompt 增强足够;若偏移大则必须微调

3️⃣ Key Differences

查看内嵌表格


容易一起考的题

查看内嵌表格


4.4 混合检索与 Rerank 机制

BM25 + 向量融合策略

1、基础题:什么是混合检索?为什么需要结合 BM25 和向量检索?

难度级别:⭐⭐(关键词匹配、语义匹配、互补性、Reciprocal Rank Fusion)


2、进阶题:混合检索中 BM25 和向量检索的结果如何融合?RRF 的原理是什么?

难度级别:⭐⭐⭐(Reciprocal Rank Fusion、加权融合、归一化、参数调优)

1️⃣ Common Answer

混合检索就是把 BM25 和向量的结果合在一起。融合的话,可以把两个结果列表拼起来去重,或者给每个结果算个加权分。RRF 是一种融合方法,大概是按排名给个倒数分数,然后加起来。具体公式记不太清了,好像是 1/(k+rank) 这种形式。

2️⃣ Impressive Answer

融合策略的核心是排名融合而非分数融合,因为两种检索的分数空间不一致。主流方案有两种:

  1. RRF(Reciprocal Rank Fusion,倒数排名融合)
  2. 公式:RRF_score(d) = Σ 1 / (k + rank_i(d)),其中 i 遍历各检索源,k 是平滑常数(通常 60)
  3. 核心思想:排名越靠前贡献越大,k 控制头部排名的权重集中度
  4. 优势:无需归一化、对异常值鲁棒、参数少易调优
  5. 变体:加权 RRF 可为不同检索源分配不同权重,如向量权重 1.5、BM25 权重 1.0

  6. 归一化加权融合

  7. 先将 BM25 分数和向量相似度分别归一化到 [0,1](Min-Max 或 Z-Score)
  8. 然后线性加权:final_score = α * bm25_norm + (1-α) * vector_norm
  9. 需要调优 α 参数(通常 0.3-0.7),并用验证集网格搜索最优值

生产实践:RRF 是默认首选,无需调参且效果稳定;若追求极致效果且有标注数据,可用加权融合 + 网格搜索

3️⃣ Key Differences

查看内嵌表格


3、场景题:RAG 召回 Top-20 文档后,如何进一步提升最终回答质量?

难度级别:⭐⭐⭐⭐(Rerank 模型、Cross-Encoder、两阶段检索、精度与延迟权衡)

1️⃣ Common Answer

可以多召回一些文档,比如 Top-50,然后选一个更好的模型重新排序。Rerank 模型好像比向量模型更准,但速度更慢。也可以让 LLM 自己判断哪些文档更有用。总之就是加一道排序步骤,把最相关的放前面。

2️⃣ Impressive Answer

这是典型的两阶段检索架构,核心是用 Rerank 模型做精排:

  1. 架构设计
  2. 第一阶段(粗排):用轻量 Embedding(如 BGE-base)+ 向量检索快速召回 Top-50~100
  3. 第二阶段(精排):用 Cross-Encoder Rerank 模型(如 BGE-Reranker)对 Top-K 逐一计算 query-doc 相关性,重排后取 Top-5 给 LLM

  4. Rerank 模型原理

  5. Cross-Encoder 架构:将 query 和 doc 拼接后输入 BERT,CLS 向量经 MLP 输出相关性分数
  6. 相比向量检索的双塔架构(Bi-Encoder),Cross-Encoder 能做细粒度交互,精度通常高 5-15 个百分点
  7. 代价:需对每个候选逐一推理,复杂度 O(K·d),K 大时延迟显著增加

  8. 生产权衡

  9. 典型配置:粗排 Top-100 → Rerank → Top-5,Rerank 延迟约 100-300ms(可接受)
  10. 模型选型:BGE-Reranker-base(快)vs large(准),根据 SLA 选择
  11. 优化手段:Rerank 模型蒸馏到更小模型,或用 LoRA 微调领域数据

3️⃣ Key Differences

查看内嵌表格


4、容易一起考的题

查看内嵌表格


4.5 向量索引进阶优化

IVF、PQ 与大规模检索

1、基础题:除了 HNSW,向量索引还有哪些类型?各自适合什么场景?

难度级别:⭐⭐(IVF、PQ、LSH、暴力搜索、内存与速度权衡)


2、进阶题:IVF 索引的原理是什么?nlist 和 nprobe 参数如何影响检索效果?

难度级别:⭐⭐⭐⭐(倒排文件、聚类中心、探测列表、精度 - 内存 - 速度三维权衡)

1️⃣ Common Answer

IVF 是倒排文件索引,先把向量聚类成若干簇,每个簇有个中心,查询时先找最近的几个簇,然后在簇内搜索。nlist 是簇的数量,nprobe 是搜索时探测的簇数。nlist 越大越准但越慢,nprobe 也是越大越准。

2️⃣ Impressive Answer

IVF(Inverted File Index)的核心思想是聚类剪枝,我从构建、查询、参数三个维度说:

  1. 构建过程
  2. 用 K-Means 将所有向量聚类成 nlist 个簇,每个簇有一个聚类中心(Centroid)
  3. 建立倒排索引:每个簇维护一个向量 ID 列表,指向属于该簇的所有向量
  4. 可选压缩:对簇内向量用 PQ(Product Quantization)压缩,大幅降低内存

  5. 查询过程

  6. 第一步:计算查询向量与所有 nlist 个聚类中心的距离,找出最近的 nprobe 个簇
  7. 第二步:只在这 nprobe 个簇内的向量中做精确搜索(暴力或 HNSW)
  8. 剪枝效果:若 nlist=1024、nprobe=8,则只需搜索约 0.8% 的向量

  9. 参数权衡三角

  10. nlist:簇数量,经验值 ≈ √N(N 为向量总数)。越大聚类越细,内存增加,构建时间线性增长
  11. nprobe:探测簇数,是精度 - 延迟的核心权衡旋钮。nprobe 翻倍,延迟近似翻倍,Recall@10 提升 5-10%
  12. 生产配置:千万级向量通常 nlist=4096、nprobe=16-32,Recall@10 可达 0.9 以上

对比 HNSW:IVF 内存占用更低(尤其加 PQ 压缩),适合亿级超大规模;HNSW 查询更快,适合十亿以下、低延迟场景

3️⃣ Key Differences

查看内嵌表格


3、场景题:亿级向量库(10 亿 +)如何设计索引方案?内存和延迟如何权衡?

难度级别:⭐⭐⭐⭐⭐(大规模索引、PQ 压缩、IVF-PQ、多索引融合、SLA 保障)

1️⃣ Common Answer

10 亿向量很大了,可能需要用 IVF 加 PQ 压缩来减少内存。也可以用分布式向量数据库,把数据分到多台机器。或者用 HNSW 但减少连接数。延迟的话,可以加缓存或者用近似检索。具体要看硬件条件和 SLA 要求。

2️⃣ Impressive Answer

10 亿向量是大规模 RAG的典型场景,我从存储、索引、查询三层设计:

  1. 存储层:PQ 压缩是核心
  2. 原始向量:10 亿 × 768 维 × 4 字节(float32)≈ 3TB,无法全量驻留内存
  3. PQ(Product Quantization):将 768 维拆分为 M=32 个子向量,每子向量用 8-bit 量化(256 码本)
  4. 压缩后:10 亿 × 32 字节 ≈ 32GB,压缩率 100 倍,精度损失约 3-5%
  5. 变体:OPQ(Optimized PQ)加旋转矩阵提升精度,RQ(Residual Quantization)用残差量化进一步提升

  6. 索引层:IVF-PQ 组合

  7. IVF 倒排索引 + PQ 压缩存储,是 FAISS 推荐的大规模方案
  8. nlist ≈ √10 亿 ≈ 32768,实际取 65536(2^16)便于计算
  9. nprobe 根据 SLA 调整:nprobe=64 时 Recall@100≈0.95,延迟约 50-100ms

  10. 查询层:多索引融合 + GPU 加速

  11. 若单一 IVF-PQ 精度不足,可用 IVF-PQ + HNSW 双索引,RRF 融合结果
  12. GPU 加速:FAISS 支持 GPU 暴力搜索,单卡可处理 1 亿向量,多卡并行可扩展到 10 亿
  13. 延迟优化:批量查询、向量预取、热点缓存(高频查询结果缓存)

生产参考:10 亿向量、IVF-65536 + PQ32、单卡 A100,Recall@100=0.93 时延迟约 80ms

3️⃣ Key Differences

查看内嵌表格


4、容易一起考的题

查看内嵌表格


4.6 RAG 幻觉检测与抑制

可信 RAG 与事实一致性

1、基础题:什么是 RAG 幻觉?产生的原因有哪些?

难度级别:⭐⭐(事实编造、检索失败、注意力偏差、训练数据污染)


2、进阶题:如何检测 RAG 系统是否产生了幻觉?有哪些自动化检测方法?

难度级别:⭐⭐⭐⭐(NLI 模型、事实一致性评分、Self-Check、引用溯源)

1️⃣ Common Answer

RAG 幻觉就是模型瞎说不真实的内容。检测的话,可以让人工审核一下,或者用另一个模型来判断回答是否正确。也可以让模型自己检查一遍。还可以看模型的回答有没有引用检索到的文档,没引用的可能是幻觉。

2️⃣ Impressive Answer

幻觉检测的核心是事实验证,主流方案有四类:

  1. NLI(Natural Language Inference)模型检测
  2. 用 DeBERTa、RoBERTa-MNLI 等模型,判断"回答"是否被"检索文档"蕴含(Entailment)
  3. 三分类:Entailment(一致)、Contradiction(矛盾)、Neutral(无关)
  4. 若 Contradiction 分数高,则判定为幻觉;精度高但需额外模型推理

  5. Self-Check 自检

  6. 让 LLM 对回答中的每个原子事实逐一提问:"文档中是否提到 X?"
  7. 例如回答"马斯克 2024 年收购了 Twitter",自检问题:"检索文档是否提到马斯克收购 Twitter 的时间是 2024 年?"
  8. 无需额外模型,但增加 LLM 调用次数,延迟增加 2-5 倍

  9. 引用溯源(Citation Grounding)

  10. 要求模型为每个句子标注引用来源(如 [doc1, para3])
  11. 验证引用是否真实存在且内容匹配,无引用或引用错配则标记为幻觉
  12. 生产友好:LangChain、LlamaIndex 均支持引用标注

  13. 一致性投票(Self-Consistency)

  14. 对同一问题采样多次生成(temperature=0.7),若关键事实在多数回答中一致则可信
  15. 适用于事实型问题,对开放性问答效果有限

生产推荐:引用溯源 + NLI 模型双保险,低延迟场景用前者,高可信场景叠加后者

3️⃣ Key Differences

查看内嵌表格


3、场景题:如何抑制 RAG 幻觉?从检索和生成两端分别有哪些优化手段?

难度级别:⭐⭐⭐⭐⭐(检索质量、Prompt 约束、事实校验、拒绝回答机制)

1️⃣ Common Answer

抑制幻觉的话,检索端可以多召回一些文档,提高相关性。生成端可以加 Prompt 约束,告诉模型不要瞎说。也可以让模型在不确定时说不知道。

2️⃣ Impressive Answer

幻觉抑制需要检索 - 生成两端协同,我有五层防御体系:

  1. 第一层:检索质量保障(源头控制)
  2. 提高检索相关性:用混合检索 + Rerank,确保 Top 文档真正相关
  3. 添加相关性阈值:若最高相似度<0.7,判定为"无可靠信息",不进入生成阶段
  4. 文档质量过滤:移除低可信来源(如论坛、未验证内容),只保留权威文档

  5. 第二层:Prompt 约束(生成时引导)

  6. 显式指令:"请严格基于以下文档回答,若文档中没有相关信息,请明确说明'根据提供的信息无法回答'"
  7. 引用强制:"每个论断必须标注引用来源,格式为 [doc_id]"
  8. 负面约束:"不要编造数据、日期、人名,不要添加文档中没有的信息"

  9. 第三层:Fact-Checking 模块(生成后验证)

  10. 用 NLI 模型对"文档 - 回答"对做蕴含判断,矛盾分数>阈值则拦截
  11. 对关键实体(人名、日期、数字)做抽取验证,与文档逐一比对

  12. 第四层:拒绝回答机制(边界控制)

  13. 当检索结果为空或相关性过低时,触发拒绝回答模板
  14. 明确告知用户:"当前知识库中没有相关信息",而非强行生成

  15. 第五层:反馈闭环(持续优化)

  16. 收集用户反馈的幻觉案例,加入负样本训练 Rerank 模型
  17. 定期用对抗测试(红队测试)主动发现幻觉边界
用户提问
检索层(召回Top10文档)
[检索质量评估] ← 在这里判断召回文档质量
    ├── Context Relevance < 阈值?
    │       ↓ Yes
    │   触发查询改写 → 重新检索
    │       ↓ No
    └── 继续生成
生成层(LLM 基于召回文档生成答案)
[生成质量评估] ← 在这里判断答案是否有幻觉
    ├── Faithfulness < 阈值?
    │       ↓ Yes
    │   在答案中标注"⚠️ 以下内容可能超出文档范围"
    │       ↓ No
    └── 正常返回答案 + 引用来源

指标解释

查看内嵌表格

3️⃣ Key Differences

查看内嵌表格


4、容易一起考的题

查看内嵌表格


4.7 RAG 评估体系与可观测性

RAG 质量评估与监控

1、基础题:RAG 系统有哪些核心评估指标?如何量化检索和生成的质量?

难度级别:⭐⭐(Recall@K、MRR、NDCG、Faithfulness、Answer Relevancy)

RAG 评估需要检索质量生成质量两个维度分别度量:

检索质量指标

  1. Recall@K:Top-K 召回结果中包含正确文档的比例,衡量"能不能找到"

  2. MRR(Mean Reciprocal Rank):正确文档首次出现的排名倒数的均值,衡量"排得准不准"

  3. NDCG(Normalized Discounted Cumulative Gain):考虑排名位置的加权相关性评分,排名越靠前权重越高

生成质量指标

  1. Faithfulness(忠实度):回答中的事实是否都能在检索文档中找到依据,衡量"有没有瞎说"

  2. Answer Relevancy(回答相关性):回答是否真正回答了用户的问题,衡量"答没答到点上"

  3. Context Precision(上下文精度):检索到的文档中,真正对回答有用的比例

  4. Context Recall(上下文召回):回答所需的信息在检索文档中的覆盖程度

核心原则:检索指标和生成指标要联合看——检索好但生成差说明 LLM 没用好上下文,检索差但生成好可能是模型在"编"


2、进阶题:如何搭建 RAG 自动化评估流水线?RAGAS 框架的原理和核心指标是什么?

难度级别:⭐⭐⭐(RAGAS、TruLens、LangSmith、Context Precision/Recall、Faithfulness 自动评估)

1️⃣ Common Answer

RAGAS 是一个评估 RAG 的框架,可以自动打分。它有几个指标,比如忠实度、相关性什么的。用的话就把问题、回答、检索文档传进去,它会给个分数。也可以用 LangSmith 来做评估,它有个 dashboard 可以看结果。具体怎么搭流水线,大概就是定期跑一下评估脚本吧。

2️⃣ Impressive Answer

自动化评估流水线的核心是用 LLM 评估 LLM,我从框架原理和流水线设计两个角度来说:

  1. RAGAS 框架核心原理
  2. 无需人工标注,用 LLM 作为评判者(LLM-as-Judge)自动评估
  3. 四大核心指标:

    • Faithfulness:将回答拆解为原子事实,逐一判断是否被检索文档蕴含
    • Answer Relevancy:用 LLM 从回答反向生成问题,计算生成问题与原始问题的相似度
    • Context Precision:检索文档中排名靠前的是否真正相关(加权精度)
    • Context Recall:标准答案中的关键信息是否被检索文档覆盖
  4. 评估流水线架构设计

  5. 数据层:维护 Golden Set(标注的问答对 + 标准检索文档),覆盖高频、长尾、边界场景
  6. 执行层:CI/CD 集成,每次模型/分块/Prompt 变更自动触发评估
  7. 指标层:RAGAS 四指标 + 自定义业务指标(如拒绝回答率、引用准确率)
  8. 告警层:设置基线阈值(如 Faithfulness < 0.85 触发告警),防止质量回退

  9. 工具选型对比

  10. RAGAS:开源免费,指标体系完善,适合离线评估
  11. TruLens:支持实时追踪每次调用的评估分数,适合线上监控
  12. LangSmith:LangChain 生态,集成 Trace + 评估 + 数据集管理,适合全链路可观测

3️⃣ Key Differences

查看内嵌表格


3、场景题:RAG 系统上线后如何做持续监控和质量回归?发现质量下降如何排查?

难度级别:⭐⭐⭐⭐(A/B 测试、Golden Set 回归、漂移检测、告警策略、反馈闭环)

1️⃣ Common Answer

上线后可以看看用户反馈,如果有人说回答不对就去查一下。也可以定期抽样检查一些回答的质量。如果质量下降了,可能是数据变了或者模型有问题,逐个排查一下。也可以做 A/B 测试,比较新旧版本的效果。

2️⃣ Impressive Answer

线上质量保障需要主动监控 + 被动反馈双通道,我有四层监控体系:

  1. 第一层:实时指标监控
  2. 用 TruLens 或自建模块对每次调用计算 Faithfulness 和 Relevancy 分数
  3. 监控检索相似度分布:若平均相似度持续下降,说明新增文档与查询分布偏移
  4. 监控拒绝回答率:突然升高可能是索引故障,突然降低可能是阈值被误改

  5. 第二层:定期回归测试

  6. 维护 Golden Set(200-500 条标注问答对),覆盖核心业务场景
  7. 每次变更(Prompt 修改、模型升级、文档更新)自动跑回归,对比基线分数
  8. 设置红线:任一核心指标下降超过 3% 则阻断上线

  9. 第三层:漂移检测

  10. 查询漂移:监控用户查询的 Embedding 分布,用 KL 散度检测是否偏离训练分布
  11. 文档漂移:新增文档的主题分布是否与索引整体一致,异常文档可能污染检索
  12. 模型漂移:API 模型(如 GPT-4)的隐式更新可能导致输出风格变化,需定期校验

  13. 第四层:排查 SOP

  14. 质量下降时按链路分层排查:检索层(相似度分布)→ 文档层(新增/删除文档)→ 模型层(Prompt/模型变更)→ 数据层(用户查询模式变化)
  15. 用 Trace 工具(LangSmith/LangFuse)回溯具体 Bad Case 的完整链路
  16. 建立 Bad Case 库,定期复盘并转化为回归测试用例

3️⃣ Key Differences

查看内嵌表格


4、容易一起考的题

查看内嵌表格


4.8 查询理解与改写

查询优化与意图理解

1、基础题:什么是查询改写?为什么用户原始查询直接检索效果往往不好?

难度级别:⭐⭐(口语化查询、语义鸿沟、模糊表述、多意图混合)

查询改写是指在用户原始查询进入检索系统之前,对其进行优化转换,使其更适合检索的过程。

用户原始查询效果差的四个原因

  1. 口语化表述:用户说"那个东西怎么弄",但文档写的是"配置方法"——口语和书面语之间存在语义鸿沟

  2. 模糊和省略:用户省略了关键上下文,如"怎么部署"没说部署什么、用什么环境

  3. 多意图混合:一个查询包含多个子问题,如"Redis 和 MySQL 的区别以及各自的使用场景",单次检索难以同时覆盖

  4. 词汇不匹配:用户用的词和文档用的词不同(如"挂了"vs"服务不可用"),导致关键词检索和语义检索都可能失效

查询改写的本质:弥合用户表达和文档表达之间的语义鸿沟,让检索系统"听懂"用户真正想问什么


2、进阶题:HyDE、Multi-Query、Step-Back Prompting 等查询改写技术的原理和区别是什么?

难度级别:⭐⭐⭐(假设文档生成、多查询扩展、抽象化提问、Query Decomposition)

1️⃣ Common Answer

HyDE 是让模型先生成一个假的答案,然后用这个答案去检索,因为答案和文档更像。Multi-Query 就是把一个问题改写成多个不同的问法,然后分别检索再合并结果。Step-Back 是让问题变得更抽象一些。这几种方法都是为了提高检索效果,具体用哪个看情况。

2️⃣ Impressive Answer

查询改写的核心思路是缩短查询与文档的语义距离,主流技术有四种,各自解决不同的问题:

  1. HyDE(Hypothetical Document Embeddings)
  2. 原理:让 LLM 先生成一个"假设性回答",用这个回答的 Embedding 去检索
  3. 为什么有效:假设回答的语言风格更接近文档(都是陈述句),比问句检索效果好
  4. 局限:若 LLM 生成的假设回答方向错误,会导致检索偏移;适合事实型问题,不适合开放性问题

  5. Multi-Query(多查询扩展)

  6. 原理:用 LLM 将原始查询改写为 3-5 个不同角度的查询,分别检索后合并去重
  7. 为什么有效:不同表述覆盖不同的语义空间,提高召回覆盖率
  8. 示例:"Redis 缓存穿透怎么解决" → ["缓存穿透的防护方案"、"布隆过滤器防止缓存穿透"、"空值缓存策略"]

  9. Step-Back Prompting(后退提问)

  10. 原理:将具体问题抽象为更高层的问题,先检索通用知识,再结合具体问题回答
  11. 示例:"BGE-M3 的 efConstruction 参数设多少合适" → 先检索"HNSW 索引参数调优原则"
  12. 适合:具体参数/配置类问题,文档中可能没有精确匹配但有通用指导

  13. Query Decomposition(查询分解)

  14. 原理:将复杂多跳问题拆解为多个子问题,逐一检索后合成
  15. 示例:"对比 Milvus 和 Qdrant 在百万级场景下的性能" → ["Milvus 百万级性能基准"、"Qdrant 百万级性能基准"、"向量数据库性能对比维度"]

选型建议:事实型问题首选 HyDE,覆盖率不足用 Multi-Query,具体问题用 Step-Back,复杂问题用 Decomposition

3️⃣ Key Differences

查看内嵌表格


3、场景题:用户输入模糊的对话式查询(如"最近那个政策怎么改的"),RAG 系统如何理解并检索?

难度级别:⭐⭐⭐⭐(意图识别、指代消解、时间感知、对话上下文融合、多轮查询改写)

1️⃣ Common Answer

这种模糊查询确实不好处理。可以让用户说清楚一点,或者用 LLM 帮忙改写一下查询。"最近"可以理解为最近一段时间,"那个政策"需要从上下文推断是哪个政策。也可以多召回一些文档,让 LLM 自己判断哪个是用户想要的。

2️⃣ Impressive Answer

模糊对话式查询需要多层理解 + 渐进式改写,我分四步处理:

  1. 指代消解
  2. 结合对话历史,将"那个政策"替换为具体实体(如上文提到的"个人所得税专项扣除政策")
  3. 实现方式:将最近 3-5 轮对话作为上下文,让 LLM 输出消解后的完整查询
  4. 输出:"个人所得税专项扣除政策最近怎么改的"

  5. 时间感知处理

  6. "最近"是相对时间,需转换为绝对时间范围
  7. 策略:默认映射为最近 3 个月(可配置),在检索时加时间过滤条件
  8. 若文档有时间戳字段,用预过滤限定范围;否则在查询中注入时间关键词

  9. 意图识别与查询改写

  10. 识别用户意图:这是一个"变更查询"(问的是"怎么改的"而非"是什么")
  11. 改写为检索友好的查询:"个人所得税专项扣除政策 2024 年修订内容 变更要点"
  12. 可选:用 Multi-Query 生成多个变体覆盖不同表述

  13. 兜底策略

  14. 若消解后仍不确定指代对象,生成澄清问题反问用户:"您是指个人所得税专项扣除政策,还是企业所得税优惠政策?"
  15. 设置置信度阈值:消解置信度 > 0.8 直接检索,< 0.8 触发澄清

3️⃣ Key Differences

查看内嵌表格


4、容易一起考的题

查看内嵌表格


4.9 多模态 RAG

多模态文档处理与检索

1、基础题:多模态 RAG 和纯文本 RAG 有什么区别?支持哪些模态?

难度级别:⭐⭐(图片、表格、PDF、音视频、多模态 Embedding、模态对齐)

多模态 RAG 是将 RAG 的能力从纯文本扩展到图片、表格、PDF、音视频等多种数据模态的技术。

与纯文本 RAG 的核心区别

  1. 数据处理层:纯文本只需分块,多模态需要先做模态解析(OCR、表格提取、音频转写等),将非文本内容转化为可检索的形式

  2. Embedding 层:纯文本用文本 Embedding,多模态需要跨模态 Embedding(如 CLIP 将图文映射到同一向量空间)或分模态独立 Embedding

  3. 检索层:纯文本单一索引,多模态可能需要多索引(文本索引 + 图片索引)或统一索引

  4. 生成层:纯文本用文本 LLM,多模态需要视觉语言模型(如 GPT-4V、Gemini)理解图片内容

支持的模态及处理方式

  • 图片:CLIP/SigLIP Embedding 或 VLM 生成描述文本

  • 表格:结构化提取为 Markdown/JSON,或转为自然语言描述

  • PDF:Layout 分析 + OCR + 表格提取 + 图片提取的组合处理

  • 音视频:ASR 转写为文本,关键帧提取为图片


2、进阶题:如何对图文混合文档(如技术文档含架构图)做 RAG?有哪些技术方案?

难度级别:⭐⭐⭐⭐(多模态 Embedding、CLIP、ColPali、视觉语言模型、图文联合索引)

1️⃣ Common Answer

图文混合文档的话,可以把图片用 OCR 识别出文字,然后和正文一起做 RAG。或者用多模态模型直接理解图片内容。架构图的话,可以让模型描述一下图片内容,把描述文本存进去检索。具体用什么工具看项目需求。

2️⃣ Impressive Answer

图文混合文档的 RAG 有三种主流方案,复杂度和效果递增:

  1. 方案一:图片转文本(Text-Only Pipeline)
  2. 对图片用 VLM(如 GPT-4V)生成详细文本描述,与正文一起做纯文本 RAG
  3. 优点:复用现有文本 RAG 全套基础设施,改造成本最低
  4. 缺点:描述可能丢失图片细节(如架构图中的箭头方向、颜色含义)
  5. 适合:快速验证、图片信息密度低的场景

  6. 方案二:多模态 Embedding 统一索引

  7. 用 CLIP/SigLIP 将图片和文本映射到同一向量空间
  8. 查询时用文本 Embedding 检索,可同时命中文本块和图片
  9. 优点:端到端统一,检索时无需区分模态
  10. 缺点:CLIP 对复杂图表(如架构图、流程图)的理解能力有限
  11. 适合:图片以照片、示意图为主的场景

  12. 方案三:ColPali / 文档视觉检索(最前沿)

  13. ColPali 将整个文档页面作为图片输入视觉模型,生成 Token 级 Embedding
  14. 跳过 OCR/Layout 分析,直接从视觉层面理解文档
  15. 优点:保留完整的视觉布局信息,对表格、图表、公式效果最好
  16. 缺点:计算开销大,索引构建慢,模型选择有限
  17. 适合:PDF 报告、学术论文等视觉布局重要的场景

生产推荐:方案一快速上线 → 方案二提升图片召回 → 方案三处理复杂文档,渐进式升级

3️⃣ Key Differences

查看内嵌表格


3、场景题:企业内部有大量 PDF 报表(含表格和图表),如何设计 RAG 系统?

难度级别:⭐⭐⭐⭐(PDF 解析、表格结构化、OCR、图表描述生成、混合索引、Layout 分析)

1️⃣ Common Answer

PDF 的话,先把 PDF 转成文本,然后做 RAG。表格可以用工具提取出来,图表的话用 OCR 或者多模态模型识别。然后把所有内容都存到向量数据库里检索。可能需要用 PyPDF 或者 pdfplumber 这些库来解析 PDF。

2️⃣ Impressive Answer

企业 PDF 报表的 RAG 需要分层解析 + 多模态索引,我从解析、索引、检索三层设计:

  1. 解析层:结构化提取
  2. Layout 分析:用 LayoutLMv3 或 Unstructured.io 识别 PDF 的版面结构(标题、正文、表格、图片区域)
  3. 文本提取:对正文区域用 pdfplumber 提取,保留段落和标题层级关系
  4. 表格提取:用 Camelot/Tabula 提取表格结构,转为 Markdown 格式保留行列关系;复杂合并单元格用 Table Transformer 模型
  5. 图表处理:提取图片后用 VLM 生成结构化描述(如"柱状图显示 2024 年 Q1-Q4 营收分别为 X、Y、Z、W 亿元")
  6. 元数据保留:提取页码、章节标题、文档标题作为元数据,用于后续过滤

  7. 索引层:混合索引设计

  8. 文本块:正常分块 + 文本 Embedding 索引
  9. 表格块:整表作为一个块(保留结构),同时生成自然语言摘要辅助检索
  10. 图表块:VLM 描述文本 + 原始图片双存储
  11. 元数据索引:对文档名、日期、部门等字段建标量索引,支持过滤

  12. 检索层:多路召回 + 智能路由

  13. 判断查询类型:数据查询("Q3 营收多少")路由到表格索引,概念查询路由到文本索引
  14. 表格查询增强:将用户问题转为 SQL-like 查询,在结构化表格上精确匹配
  15. 图表查询:用多模态检索匹配相关图表,将图表描述 + 原图一起传给 VLM 生成回答

3️⃣ Key Differences

查看内嵌表格


4、容易一起考的题

查看内嵌表格


4.10 RAG 与 Agent 结合(Agentic RAG)

自适应检索与推理增强

1、基础题:什么是 Agentic RAG?和传统 RAG 有什么区别?

难度级别:⭐⭐(主动检索决策、多轮推理、自适应检索、路由选择)

Agentic RAG 是将 Agent 的自主决策能力引入 RAG 系统,让模型主动决定何时检索、检索什么、是否需要再次检索,而非传统 RAG 的"有问必检"。

与传统 RAG 的核心区别

查看内嵌表格

核心能力

  1. 检索路由:根据问题类型选择最合适的数据源

  2. 自我反思:评估检索结果是否足够回答问题,不够则补充检索

  3. 查询规划:将复杂问题分解为子问题,逐步检索和推理


2、进阶题:Self-RAG、CRAG(Corrective RAG)的原理是什么?如何让模型自主决定何时检索?

难度级别:⭐⭐⭐⭐(检索判断 Token、相关性评估、自我反思、检索-生成交替、Critique Token)

1️⃣ Common Answer

Self-RAG 是让模型自己决定要不要检索,如果觉得自己知道就直接回答,不知道再去检索。CRAG 是检索完之后检查一下结果对不对,不对就修正。具体怎么实现的不太清楚,好像是在训练时加了一些特殊的 token 来控制。

2️⃣ Impressive Answer

Self-RAG 和 CRAG 代表了 Agentic RAG 的两个核心方向:检索前决策检索后纠正

  1. Self-RAG(Self-Reflective RAG)
  2. 核心机制:在模型中训练特殊的 Critique Token,用于自主决策
  3. 四种 Critique Token:
    • [Retrieve]:判断是否需要检索(Yes/No/Continue)
    • [IsRel]:判断检索文档是否与查询相关
    • [IsSup]:判断生成内容是否被文档支持(Fully/Partially/No)
    • [IsUse]:判断最终回答是否有用
  4. 工作流程:生成过程中动态插入 Critique Token → 若 [Retrieve]=Yes 则触发检索 → 用 [IsRel] 过滤无关文档 → 用 [IsSup] 验证生成内容
  5. 训练方式:用 GPT-4 标注 Critique Token,然后微调开源模型(如 Llama)

  6. CRAG(Corrective RAG)

  7. 核心机制:在检索后增加质量评估和纠正环节
  8. 三级评估:对检索文档评分为 Correct / Ambiguous / Incorrect
  9. 纠正策略:
    • Correct:直接使用,提取关键信息去除噪声
    • Ambiguous:结合检索结果和 Web 搜索补充信息
    • Incorrect:丢弃检索结果,转为 Web 搜索兜底
  10. 优势:无需微调模型,用轻量评估器即可实现,工程落地更简单

  11. 生产选型

  12. Self-RAG 精度更高但需要微调,适合有训练资源的团队
  13. CRAG 无需微调,用 Prompt + 评估器即可实现,适合快速落地
  14. 两者可组合:Self-RAG 做检索决策 + CRAG 做结果纠正

3️⃣ Key Differences

查看内嵌表格


3、场景题:设计一个能处理复杂多跳问题的 Agentic RAG 系统(如"对比 A 公司和 B 公司最近两年的营收趋势")

难度级别:⭐⭐⭐⭐⭐(查询分解、迭代检索、中间推理、结果合成、多源信息融合)

1️⃣ Common Answer

这种对比类问题,可以先分别检索 A 公司和 B 公司的营收数据,然后让 LLM 做对比分析。如果一次检索不够,可以多检索几次。也可以把问题拆成几个小问题分别回答,最后合并。用 Agent 框架的话,可以让 Agent 自己规划检索步骤。

2️⃣ Impressive Answer

复杂多跳问题需要规划-检索-推理-合成的迭代闭环,我设计四阶段架构:

  1. 阶段一:查询规划(Planner)
  2. 用 LLM 将复杂问题分解为子任务 DAG(有向无环图):
    • Task1:检索 A 公司 2023 年营收数据
    • Task2:检索 A 公司 2024 年营收数据
    • Task3:检索 B 公司 2023 年营收数据
    • Task4:检索 B 公司 2024 年营收数据
    • Task5(依赖 1-4):对比分析营收趋势
  3. 识别可并行的子任务(Task1-4 可并行),减少总延迟

  4. 阶段二:自适应检索(Retriever Agent)

  5. 每个子任务独立检索,检索后用 CRAG 评估结果质量
  6. 若检索结果不足(如只找到年度总营收,缺少季度明细),自动生成补充查询
  7. 数据源路由:结构化数据(营收数字)优先查数据库/表格,非结构化分析查文档

  8. 阶段三:中间推理(Reasoner)

  9. 对每个子任务的检索结果做中间推理,提取关键数据点
  10. 交叉验证:若 A 公司营收在不同文档中数据矛盾,标记冲突并选择更权威的来源
  11. 生成中间结论:"A 公司 2023→2024 营收增长 15%,B 公司增长 8%"

  12. 阶段四:结果合成(Synthesizer)

  13. 将所有中间结论合成最终回答,确保对比维度一致
  14. 添加数据来源引用,标注每个数据点的出处
  15. 若存在数据缺失,明确告知用户哪些信息未找到

关键设计决策

  • 最大迭代深度限制(如 3 轮),防止无限循环

  • 每轮检索设置超时,超时后用已有信息生成部分回答

  • 用 LangGraph 实现状态机,管理多阶段流转和错误恢复

3️⃣ Key Differences

查看内嵌表格


4、容易一起考的题

查看内嵌表格


4.11 RAG 生产工程实践

企业级 RAG 架构与运维

1、基础题:RAG 系统的数据更新策略有哪些?如何处理文档的增删改?

难度级别:⭐⭐(增量索引、全量重建、版本管理、一致性保障、软删除)

RAG 系统的数据更新需要保证索引与源数据的一致性,主要有三种策略:

  1. 增量更新
  2. 新增文档:分块 → Embedding → 写入向量数据库,不影响已有索引
  3. 修改文档:先删除旧文档的所有向量,再重新分块写入(先删后增)
  4. 删除文档:软删除(标记为不可检索)或硬删除(物理删除向量)
  5. 优点:速度快,适合频繁更新;缺点:长期可能产生索引碎片

  6. 全量重建

  7. 定期(如每天凌晨)对全部文档重新分块、Embedding、建索引
  8. 用蓝绿部署切换:新索引构建完成后原子切换,旧索引保留用于回滚
  9. 优点:索引质量最高,无碎片;缺点:耗时长,资源消耗大

  10. 版本化管理

  11. 每个文档维护版本号,索引中保留版本信息
  12. 检索时只返回最新版本的结果,历史版本可按需查询
  13. 适合:法规、合同等需要追溯历史版本的场景

一致性保障:用消息队列(如 Kafka)异步处理文档变更事件,保证源数据和索引的最终一致性


2、进阶题:RAG 系统如何做成本控制?Token 消耗、Embedding 计算、向量存储的成本如何优化?

难度级别:⭐⭐⭐(Semantic Cache、分层检索、按需加载、Token 压缩、批量 Embedding)

1️⃣ Common Answer

成本控制的话,可以少召回一些文档减少 Token 消耗。Embedding 可以用开源模型自己部署,不用付 API 费用。向量存储的话,用 PQ 压缩减少内存。也可以加缓存,相同的问题不用重复检索。总体就是能省则省。

2️⃣ Impressive Answer

RAG 成本主要来自三个环节:LLM Token、Embedding 计算、向量存储,我逐一优化:

  1. LLM Token 成本优化(占总成本 60-80%):
  2. Semantic Cache(语义缓存):对相似查询(余弦相似度 > 0.95)直接返回缓存结果,可减少 30-50% 的 LLM 调用
  3. 上下文压缩:用 LLMLingua 或 Recomp 压缩检索文档,去除冗余信息,Token 减少 50-70% 且精度损失 < 3%
  4. 分层模型策略:简单问题用小模型(GPT-3.5/Qwen-7B),复杂问题用大模型(GPT-4),通过路由器分流
  5. 控制召回数量:Rerank 后只取 Top-3~5 而非 Top-10,减少 Context 长度

  6. Embedding 计算成本优化

  7. 批量处理:文档入库时批量 Embedding(batch_size=256),比逐条调用 API 便宜 50%+
  8. 本地部署:用 BGE/M3E 本地部署,单卡 A10 可支撑日均百万次 Embedding,成本远低于 API
  9. 增量 Embedding:只对新增/修改的文档做 Embedding,避免全量重算

  10. 向量存储成本优化

  11. PQ 压缩:768 维 float32(3KB/向量)→ PQ32(32B/向量),压缩 100 倍
  12. 冷热分层:高频访问的向量放内存(HNSW),低频放磁盘(IVF-PQ),按访问频率自动迁移
  13. TTL 过期:对时效性文档设置过期时间,自动清理过期向量

成本量化参考:百万文档 RAG 系统,优化前月成本约 $3000,优化后可降至 $800-1200

3️⃣ Key Differences

查看内嵌表格


3、场景题:从 0 到 1 搭建一个企业级 RAG 系统,技术选型和架构设计怎么做?

难度级别:⭐⭐⭐⭐⭐(全链路架构、技术栈选型、容量规划、灰度上线、降级方案)

1️⃣ Common Answer

从 0 到 1 的话,先选个框架,比如 LangChain 或 LlamaIndex。然后选个向量数据库,Milvus 或者 Qdrant。Embedding 用 BGE 或 OpenAI。LLM 用 GPT-4 或者开源模型。然后把文档处理、检索、生成串起来就行了。部署的话用 Docker 容器化。

2️⃣ Impressive Answer

企业级 RAG 从 0 到 1 需要分阶段交付,我按四个阶段设计:

  1. 阶段一:MVP 验证(2-4 周)
  2. 目标:验证 RAG 在目标场景的可行性,用最小成本跑通链路
  3. 技术选型:LlamaIndex(快速原型)+ Qdrant(轻量部署)+ BGE-M3(中文最优)+ GPT-4(效果天花板)
  4. 关键动作:用 50-100 篇核心文档跑通端到端,人工评估 Top-20 问题的回答质量
  5. 交付物:可行性报告 + 基线指标(Faithfulness、Relevancy)

  6. 阶段二:工程化落地(4-8 周)

  7. 架构设计:
    • 数据层:文档处理 Pipeline(Unstructured.io)→ 分块 → Embedding → 向量数据库
    • 检索层:混合检索(BM25 + 向量)→ Rerank(BGE-Reranker)→ Top-5
    • 生成层:Prompt 模板管理 + LLM 调用 + 引用标注 + 流式输出
    • 评估层:RAGAS 自动评估 + Golden Set 回归
  8. 关键决策:

    • 向量数据库:< 1000 万向量选 Qdrant(运维简单),> 1000 万选 Milvus(分布式扩展)
    • LLM:数据敏感选私有化部署(Qwen/Llama),否则选 API(GPT-4/Claude)
  9. 阶段三:生产加固(4-6 周)

  10. 高可用:向量数据库主从复制,LLM 多 Provider 故障转移
  11. 降级方案:LLM 不可用时返回检索文档摘要;向量库不可用时降级为 BM25 关键词检索
  12. 安全防护:输入过滤(Prompt 注入检测)、输出过滤(敏感信息脱敏)、访问控制(RBAC)
  13. 可观测性:LangFuse 全链路追踪 + Prometheus 指标监控 + 告警

  14. 阶段四:持续优化(长期)

  15. 数据飞轮:收集用户反馈 → 标注 Bad Case → 优化分块/Prompt/Rerank
  16. 成本优化:Semantic Cache、模型分层、上下文压缩
  17. 能力扩展:多模态支持、Agentic RAG、多轮对话

3️⃣ Key Differences

查看内嵌表格


4、容易一起考的题

查看内嵌表格