尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

RAG完整链路拆解:Embedding、向量检索与混合检索实战指南

RAG完整链路拆解:Embedding、向量检索与混合检索实战指南 这次我们直接把 RAG 的完整链路拆开看一遍。很多人接触 RAG 时看到的是一堆名词Embedding、向量检索、BM25、重排、TopK、知识库实际上跑通一个最小可用系统并不难难的是搞清楚每个环节在解决什么问题。这篇文章就从完整的 RAG 业务流程讲起把 Embedding 嵌入模型的运行机制、向量检索的工作原理、本地部署和 API 接入一次性理清楚。无论你是刚入门大模型应用开发还是准备把 RAG 接到自己的业务系统里都可以按这篇文章的流程过一遍。RAG 全称是 Retrieval-Augmented Generation也就是“检索增强生成”。核心思路是不直接让大模型凭记忆回答而是先从外部知识库中检索出与当前问题相关的片段再把片段和问题一起交给大模型生成答案。这样做的好处很直接——缓解大模型幻觉、支持私有知识问答、让模型能回答训练数据之外的内容同时对模型更新不敏感知识库更新后不需要重新训练模型。整条链路里最关键的三个技术点就是文本切块、Embedding 嵌入和向量检索。三者配合决定最终问答质量。尤其是 Embedding 模型的选择和向量检索的召回效果是 RAG 项目能否真正落地的分水岭。1. RAG 项目核心能力速览很多同学看到 RAG 的第一反应是“这又是一个大而全的框架”。其实 RAG 不是单一框架而是一套可拆成独立模块的架构。下面这张表把最常见的 RAG 项目能力整理出来方便先建立一个整体判断。能力项说明项目类型大模型应用开发 / 知识库问答系统核心功能文档加载、文本切块、语义向量化、向量索引、检索召回、LLM 答案生成核心技术组件Embedding 模型、向量数据库、向量检索算法、大模型推理服务、重排模型可选业务链路数据加载 → 文本切块 → Embedding → 向量入库 → Query Embedding → 检索召回 → 重排 → Prompt 组装 → LLM 生成适用场景企业知识库、私有文档问答、客服助手、技术文档检索、研报分析、智能助手运行环境Linux/Windows/macOS 均可具体资源需按所选 Embedding 模型和生成模型确定CPU 可运行但速度受模型规模影响启动方式命令行启动 / Docker 启动 / 自封装一键脚本API 能力常见模式是提供文档索引接口和问答接口便于外部系统集成批量任务可批量处理文档入库、批量问题评测配合队列和日志机制更容易落地部署复杂度中等。至少需要管理 Embedding 服务、向量库、LLM 服务三层组件这里不写死“16G 内存”“8G 显存”这类数字因为不同 Embedding 模型、不同向量库、不同生成模型的占用差异非常大。判断门槛的关键是Embedding 模型通常很轻量CPU 也能跑而生成大模型的资源要求更高可以选本地模型也可以走在线 API。实际部署前先确认好自己的算力和部署场景。2. RAG 完整业务流程拆解从文档到答案RAG 项目看起来组件多但业务流程可以分成三个主要阶段索引阶段、检索阶段、生成阶段。下面按顺序拆开讲。2.1 索引阶段把知识库变成可检索的向量库这一阶段做的事情是“离线建库”也就是把原始文档处理成向量检索系统能理解的格式。第一步是文档加载。不同来源的数据需要不同的加载器PDF、Markdown、Word、HTML、数据库记录、API 返回的 JSON 都可能成为知识来源。这一层要解决的是“把各类格式转成纯文本”同时尽量保留标题、段落边界、表格结构等语义信息。第二步是文本切块。直接把整篇文档丢给 Embedding 模型很容易超出输入长度限制而且一个段落包含多个主题时向量会变得模糊。常见做法是按固定字符数切块并设置重叠窗口或者在 Markdown/HTML 的标题层级处切块让每个块尽量表达一个完整主题。切块长度直接影响检索效果切得太短上下文信息缺失切得太长向量被无关内容稀释。后面专门有一节讲切块策略。第三步是 Embedding 向量化。每个文本块通过嵌入模型转换成固定维度的向量。这个向量好比是文本在“语义空间”中的坐标语义相近的文本坐标距离也近。向量化之后连同文本内容、文档元数据、唯一 ID 一起写入向量数据库。第四步是构建索引。向量数据库会对向量建立近似最近邻索引例如 HNSW、IVF 等这样在千万级向量里搜索时也能快速返回 TopK 结果。普通业务场景用 Chroma、Milvus、Qdrant 等都能满足需求。2.2 检索阶段把问题变成 Query再召回相关片段用户提问时系统先对问题做同样的 Embedding 处理得到 Query 向量。然后向量数据库在索引中查找与该向量最相似的若干个文档块返回 TopK 结果。这里有个容易被忽略的问题单纯向量检索擅长理解“语义相似”但对“精确关键词匹配”并不敏感。比如一份合同里写“甲方应于 30 日内付款”用户问“付款期限是多少天”语义检索可能召回但“30日”这种精确约束不一定稳定命中。于是很多 RAG 系统会加入混合检索一路走向量语义检索一路走 BM25 关键词检索最后把两路结果合并再用重排模型精排。2.3 生成阶段用召回片段增强 Prompt召回到相关片段后把它们按照一定格式拼进 Prompt同时告诉大模型请基于以下资料回答问题不要使用资料外的内容。然后把完整的 Prompt 发给生成模型得到最终答案。这里有两个关键点Prompt 里必须给出足够的上下文但也不是越多越好需要控制 token 长度。要给生成模型明确的指令约束否则模型可能凭记忆补充出与资料相矛盾的内容。生成结果还可以附上来源文档 ID方便用户追溯。这就是一个完整 RAG 业务流程离线建库、在线检索、生成回答。3. Embedding 嵌入模型运行机制与适用场景Embedding 是整个 RAG 系统的地基。地基不稳后面检索召回和答案生成都会受影响。3.1 Embedding 是什么Embedding 模型的核心能力是把一段文本映射成一个高维向量。这个向量不是随机生成的而是通过大量文本训练出来的它让语义相近的文本在向量空间中更接近。比如“怎么退款”和“退货流程是什么”字面差异大但语义相近它们的向量距离就会比较小。在实际工程中Embedding 模型通常输出固定维度的向量。文本长度不同模型会通过池化操作把整句编码成一个固定大小的表示。RAG 项目里这条链路是文本 → Tokenizer → Transformer 编码 → 池化 → 固定维度向量3.2 常见 Embedding 模型的选择思路目前社区常用的开源模型包括 BGE、BGE-M3、M3E、text-embedding 系列等。选择时有几个维度需要关注中文效果如果知识库以中文为主优先选择中文语料上表现更好的模型。最大输入长度有些模型只支持 512 token超过部分会被截断需要处理长文档时要考虑模型的上限或配合切块策略。向量维度维度越高不代表效果越好但维度越高通常存储和检索成本越大。是否支持稀疏向量部分模型同时输出稠密向量和稀疏向量可用来辅助混合检索。BGE-M3 这类模型在 RAG 社区被提及较多它能同时生成稠密向量、稀疏向量和多向量在检索召回上比较灵活。具体选哪个建议拿自己业务里的一批真实 Query 和文档做小规模评测不要只看榜单分数。3.3 Embedding 的适用场景Embedding 不只是 RAG 的专属组件它本身可以独立使用语义搜索不依赖关键词精确匹配能搜索出同义改写的内容。文本聚类把客户反馈、工单内容按语义分组方便归纳。数据去重判断两段文本是否表达同一意思用于清理重复文档。案例分析问答系统、推荐系统中的用户意图匹配。但 Embedding 也有边界。它不擅长精确数字匹配也不擅长长文本整体压缩后的细粒度检索。如果业务场景要求“必须包含某个ID”“数字范围过滤”建议额外叠加关键词检索和元数据过滤。4. 向量检索工作原理向量检索负责从向量库中快速找到与 Query 最相关的 TopK 向量。理解这部分才能在实际调优时知道问题出在“召回”还是“排序”。4.1 向量相似度计算向量检索的本质是计算相似度。常见度量方式有三种余弦相似度计算两个向量夹角的余弦值值越接近 1 表示越相似。对文本向量最常用。欧氏距离距离越小越相似。相比余弦它对向量模长更敏感。内积适合某些已归一化的模型计算效率高。大多数 Embedding 模型都建议使用余弦相似度但具体还是以模型文档为准。如果使用向量数据库通常在集合配置里指定 metric 类型。4.2 从暴力搜索到 ANN 近似检索最直观的检索方式是暴力计算 Query 与所有向量的相似度然后排序。这在几百万条数据以内也能接受但数据量变大后延迟会明显上升。向量数据库普遍采用 ANNApproximate Nearest Neighbor算法来加速。常见算法有HNSW基于图的近似检索召回率高检索速度快但内存占用相对高。IVF倒排索引思想先聚类再限定候选范围内存占用更可控。还有基于乘积量化的压缩方案适合超大规模场景。对 RAG 项目来说如果数据在百万级以内HNSW 通常是默认选择。4.3 混合检索与重排纯向量检索的召回并不是万能的。为了提升召回质量RAG 项目中经常使用混合检索一路使用向量语义检索捕捉同义改写、语义相关的片段。另一路使用 BM25 关键词检索捕捉精确关键词、专有名词、编号等。两路结果合并去重后再用重排模型精排。重排模型Reranker通常是 Cross-Encoder 结构能把查询和候选片段拼接后一起编码得到一个相关性分数。它比 Embedding 双塔模型的精度高但计算成本也高所以只对召回后的少量候选做重排。一个典型的多路召回流程是Query ├─ Embedding 向量检索 → Top 50 ├─ BM25 关键词检索 → Top 50 └─ 合并去重 → 取前 50 ↓ Reranker 重排 → 取前 5 ↓ Prompt 组装 → LLM 生成加入多路召回后即使某一路没召回其他路也能补齐整体鲁棒性更好。5. 环境准备与本地部署前置条件RAG 项目的环境准备不像单一模型推理那么简单需要同时准备 Python 环境、模型文件、向量数据库和可选的 GPU 环境。这里给出一套通用清单。5.1 运行环境检查清单检查项说明操作系统Linux 服务器最常用Windows/macOS 可做开发调试Python 版本优先使用项目要求版本通常建议 3.10 及以上依赖管理建议使用 conda 或 venv 隔离环境GPU 驱动与 CUDA如果使用本地 GPU 推理大模型需要按模型框架要求配置内存与磁盘需要给模型文件、向量库和临时数据预留足够空间具体按规模评估网络首次下载模型和依赖需要访问模型仓库确认网络策略端口占用Embedding 服务、向量库、LLM 服务、API 服务可能各占一个端口提前规划5.2 创建虚拟环境以下命令是通用模板实际项目需要按目录和版本调整# 创建虚拟环境 python -m venv rag-env # 激活虚拟环境Linux/macOS source rag-env/bin/activate # Windows PowerShell # rag-env\Scripts\Activate.ps15.3 安装核心依赖一个最精简 RAG 链路至少需要以下类型的依赖Embedding 模型推理sentence-transformers、transformers、torch向量数据库客户端chromadb 或 pymilvus、qdrant-clientAPI 服务fastapi、uvicorn数据处理pypdf、markdown、beautifulsoup4 等按文档格式选择示例安装命令pip install torch transformers sentence-transformers chromadb fastapi uvicorn pypdf实际项目通常有 requirements.txt请优先使用项目锁定的依赖版本避免版本冲突。5.4 模型文件准备Embedding 模型可以通过 Hugging Face 等渠道下载到本地。如果是内网环境可以提前下载好模型目录再通过代码指定本地路径加载。from sentence_transformers import SentenceTransformer # 方式一在线加载 model SentenceTransformer(BAAI/bge-m3) # 方式二本地路径加载适合离线环境 model SentenceTransformer(/models/bge-m3)6. 部署启动一个最小 RAG 服务这里提供一个不依赖具体框架的最小实现思路方便你理解组件怎么串起来。实际生产项目可以使用 LangChain、LlamaIndex 或自研管线但原理一致。6.1 初始化 Embedding 模型和向量库from sentence_transformers import SentenceTransformer import chromadb # 加载 Embedding 模型 embedder SentenceTransformer(BAAI/bge-m3) # 创建持久化向量数据库 client chromadb.PersistentClient(path./kb_store) collection client.get_or_create_collection(knowledge)6.2 文档切块与入库先写一个简单的切块函数再批量写入向量库。这里给出的是模板切块大小和重叠要根据文档类型和模型输入上限调整。def split_text(text, chunk_size512, chunk_overlap128): chunks [] start 0 while start len(text): end start chunk_size chunks.append(text[start:end]) start end - chunk_overlap return chunks def index_document(doc_id, text): chunks split_text(text) for i, chunk in enumerate(chunks): vector embedder.encode(chunk).tolist() collection.add( ids[f{doc_id}_{i}], documents[chunk], embeddings[vector], metadatas[{doc_id: doc_id}] )6.3 检索函数def search(query, top_k5): query_vector embedder.encode(query).tolist() results collection.query( query_embeddings[query_vector], n_resultstop_k, include[documents, metadatas, distances] ) return results[documents][0], results[metadatas][0]6.4 启动 LLM 问答服务LLM 部分可以接本地部署的模型也可以接在线 API。为了让示例完整先用一个抽象函数表示生成逻辑实际替换成自己的模型调用即可。from fastapi import FastAPI from pydantic import BaseModel app FastAPI() class ChatRequest(BaseModel): query: str class ChatResponse(BaseModel): answer: str sources: list def build_prompt(query, contexts): context_block \n\n.join(contexts) prompt f请根据以下资料回答问题不要使用资料之外的信息。 资料 {context_block} 问题{query} 回答 return prompt def llm_generate(prompt): # 这里替换成实际模型调用 return 生成的答案 app.post(/chat, response_modelChatResponse) def chat(req: ChatRequest): contexts, metadatas search(req.query) prompt build_prompt(req.query, contexts) answer llm_generate(prompt) return ChatResponse(answeranswer, sourcescontexts)启动服务uvicorn main:app --host 127.0.0.1 --port 8000这样你就得到了一个最简 RAG 服务先运行文档入库脚本再启动 API 服务就可以用 /chat 接口提问了。7. 功能测试与效果验证RAG 项目跑起来容易跑得好难。这里给出几组可复现的测试步骤每次改造切块、检索或 Prompt 之后都应该按这套流程验证。7.1 测试文档入库在入库脚本里加入日志输出每个文档切块数量、向量维度、入库耗时。判断入库成功的标准是向量库中能按元数据查询到对应文档块。def test_index(): doc_text RAG 是检索增强生成它通过外部知识库增强大模型回答能力。 index_document(doc001, doc_text) print(入库完成)7.2 测试检索召回质量准备一个语义相近但文本表述不同的 Query看 TopK 是否包含期望的文档块。例如知识库里有一段关于“退款规则”的内容Query 写成“怎么申请退货”看能否召回“退款规则”相关段落。判断标准相关片段是否出现在 TopK 中。不相关但字面相似的片段占比高不高。TopK 设置为 5 时前 1 名的相关性是否足够。如果召回效果差优先检查切块大小、Embedding 模型选择、是否缺少混合检索。7.3 测试完整问答效果向 /chat 接口发送问题观察答案是否基于资料内容而不是模型“自由发挥”。答案是否能追溯到具体文档块。答案是否覆盖资料中的关键信息。当问题超出知识库范围时模型是否明确说明“无法从资料中得知”。这组测试可以用脚本自动化把问题、期望答案来源、实际回答保存到日志中再人工复核。7.4 批量评测脚本为了对比不同切块策略或不同 Embedding 模型准备一个小批量测试集。test_cases [ {query: 如何申请退款, expected_doc: refund_policy.md}, {query: RAG 是什么, expected_doc: rag_intro.md}, {query: 系统支持哪些文件格式, expected_doc: file_format.md}, ] def evaluate(questions): hit_count 0 for case in questions: docs, metas search(case[query], top_k5) if case[expected_doc] in [m.get(doc_id) for m in metas]: hit_count 1 print(fTop5 命中率: {hit_count / len(questions):.2%})这个脚本可以放在知识库更新之后定期执行用来检查检索质量是否回退。8. 接口 API 调用示例与批量任务生产环境里RAG 服务通常以 API 形式暴露给上层应用。这里给出一个通用调用示例实际字段名以你自己项目的接口文档为准。8.1 调用问答接口假设服务地址是http://127.0.0.1:8000使用 curl 调用curl -X POST http://127.0.0.1:8000/chat \ -H Content-Type: application/json \ -d {query: 什么是向量检索}使用 Python requests 调用import requests url http://127.0.0.1:8000/chat payload {query: 什么是向量检索} resp requests.post(url, jsonpayload, timeout60) if resp.status_code 200: data resp.json() print(答案:, data[answer]) print(来源:, data[sources]) else: print(请求失败:, resp.status_code, resp.text)8.2 设计文档索引接口在实际系统中文档入库不能总靠脚本更多是通过接口触发。你可以在 FastAPI 里加一个 /ingest 接口from pydantic import BaseModel class IngestRequest(BaseModel): doc_id: str text: str app.post(/ingest) def ingest(req: IngestRequest): index_document(req.doc_id, req.text) return {status: ok, doc_id: req.doc_id}调用示例curl -X POST http://127.0.0.1:8000/ingest \ -H Content-Type: application/json \ -d {doc_id: doc001, text: RAG 是检索增强生成。}8.3 批量任务处理批量任务的核心是控制速度和可观测性。不建议直接 for 循环一次性灌入所有请求容易把服务打满。可以做一个简单的队列思路读取批量文档列表。逐个入库并记录成功/失败状态。失败任务记录原因稍后重试。所有任务完成后输出统计报告。示例伪代码import time import logging def batch_index(documents): success 0 failed 0 for doc in documents: try: index_document(doc[doc_id], doc[text]) success 1 except Exception as e: logging.error(f入库失败: {doc[doc_id]} - {e}) failed 1 time.sleep(0.1) return {success: success, failed: failed}批量问答测试同理可以对每个问题调用 /chat把答案记录到 JSON 文件再分析成功率与响应时间。9. 资源占用与性能观察RAG 服务和单模型推理不同资源占用分为多个部分。部署后需要分别观察。9.1 观察哪些指标Embedding 模型推理CPU 和内存占用如果使用 GPU则看显存。向量数据库内存占用、磁盘占用、连接数。LLM 推理服务显存或 CPU/内存占用这通常是最大资源消耗方。API 服务进程 CPU、内存、请求延迟。常用命令# 查看 GPU 显存占用 nvidia-smi # 查看 CPU 内存 top # 查看内存概况 free -h # 查看端口占用 netstat -tlnp | grep 80009.2 影响资源占用的因素文本切块大小切块越大单个向量化耗时会增加但入库总条数变少。Embedding 模型 batch size批量推理能提升吞吐但会占用更多内存。TopK 数量检索时返回的候选越多后续重排和 Prompt 组装开销越大。LLM 上下文长度Prompt 越长生成延迟和算力消耗越高。并发请求并发越多资源占用增长越快。9.3 如何降低资源占用如果知识库规模不大选择轻量 Embedding 模型。用向量数据库的批量写入接口代替逐条写入。LLM 推理使用量化版本模型或使用在线 API 减少本地资源压力。对 API 服务做限流避免突发流量打满 CPU 或显存。定时清理向量库中废弃索引和临时数据。资源占用的判断必须基于实际环境不要照搬别人的数字。建议先在小数据集上跑通再逐步扩大一边扩大一边观察各项指标变化。10. 常见问题与排查方法RAG 项目常见的坑不少下面整理了一张排查表遇到问题时按表定位。问题现象可能原因排查方式解决方案依赖安装失败Python 版本不匹配、依赖冲突查看 pip 报错信息确认 Python 版本使用虚拟环境按 requirements.txt 逐项安装模型下载慢或失败网络受限、模型仓库不可达测试网络检查模型路径使用镜像源预下载模型到本地改本地路径加载入库时报向量维度不一致不同来源文本使用了不同 Embedding 模型检查模型加载代码确认向量维度统一使用同一个 Embedding 模型和版本检索结果为空向量库中没数据、查询向量维度不匹配查询集合数量打印 Query 向量维度重新执行入库脚本确认写入成功中文检索效果差Embedding 模型对中文支持不足用典型中文 Query 对比不同模型换用中文效果更好的 Embedding 模型TopK 召回不相关切块过大、Query 表述与文档差异太大查看召回片段内容分析失败原因调整切块参数增加混合检索和重排答案与资料矛盾Prompt 约束不足、模型忽略上下文检查 Prompt 是否明确要求“仅基于资料回答”在 Prompt 中增加严格约束并附上原文引用显存不足生成模型过大、并发太高nvidia-smi 查看显存占用降低并发换模型量化版本或使用 API端口冲突其他进程占用了端口netstat 检查端口占用修改启动脚本中的端口号API 请求超时推理耗时过长、队列堆积查看服务日志和响应耗时降低 TopK、缩短上下文、增加超时时间11. 最佳实践与合规建议11.1 切块策略优先做实验切块没有绝对最优只有适合你的数据。可以准备三到五套切块参数用同一批测试问题跑检索对比 TopK 命中率。常见思路包括固定大小切块实现简单适合段落结构不明显的文本。重叠切块减少关键信息被截断的概率但要控制重叠比例避免冗余。结构切块按 Markdown 标题、段落、表格分隔符切分保留语义单元。父子块父块保留上下文子块做检索召回子块后展示父块。11.2 混合检索优先于单一向量检索如果你的知识库包含大量专有名词、编号、产品型号建议尽早加入 BM25 关键词检索和重排模型。多路召回能明显降低“语义匹配但字面不匹配”的遗憾。11.3 做好数据合规与授权RAG 的核心是把外部文档纳入知识库。使用这些文档前必须确认数据来源合法、有权使用。涉及企业内部资料、个人隐私、受版权保护的文本时要设置访问权限和审计日志。涉及人脸、声音、商业版权素材的场景必须获得明确授权。问答结果对外发布或商用前需要人工或规则复核避免脱敏不完整或内容错误。11.4 API 服务安全如果 RAG 服务暴露在网络上至少要做三件事加接口鉴权比如 API Key 或 Token。限制请求频率防止暴力调用。内网部署时不要随便映射公网端口。12. 总结与下一步RAG 的完整业务流程并不复杂文档切块 → Embedding → 向量检索 → Prompt 增强 → 大模型生成。真正影响效果的是细节切块策略合不合理、Embedding 模型适不适合你的语言和领域、向量检索是否加入了多路召回、Prompt 是否有足够强的约束。建议你先跑通一个最小链路选一个 Embedding 模型建一个小的向量库导入几篇文档再启动一个问答 API。这个流程验证通过后再逐步深入调整检索质量、接入重排、支持多轮对话甚至往 Agentic RAG 方向扩展。最容易踩的坑是跳过了检索质量验证直接调 Prompt。实际上大部分 RAG 效果差的问题都出在“该召回的内容没召回”。把注意力先放在切块和检索上效果提升往往会更明显。建议收藏备用RAG 这条链路越早亲手跑一遍后面看架构、调优都会更顺。
返回列表