尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

【手搓 Agent 第2.1关】搭建 Agent 进阶能力:RAG(下)

【手搓 Agent 第2.1关】搭建 Agent 进阶能力:RAG(下) 上篇手写极简 RAG 让我们吃透了核心流程但极简关键词检索存在语义缺失、匹配精度低、无法处理海量文档、不可持久化等问题完全无法用于真实业务场景。想要落地企业级私有知识库问答必须依托标准化框架、向量模型与向量数据库搭建完整工业级 RAG 架构。承接中篇沙盒实操本篇进入正式工程落地阶段。我们将从零配置全套工业级依赖对比业界主流技术选型实现文档智能分块、本地私有化 Embedding、向量库持久化存储最终改造适配 Stage1 基础 Agent让智能体具备专业、精准、可溯源的私有知识库问答能力完成整套 RAG 模块闭环落地。一、RAG 工业级调包流程理解底层逻辑后在实际业务中使用框架和向量库来处理百万字级别的文档。我们先使用本地文档练手以确定 RAG 流程没有问题。当该流程可以运转后再把“读取本地文件”的动作替换为“接收网页抓取工具传来的文本”。这样我们可以更好判断大模型返回文字不当时是由于 RAG 流程问题还是网页环境抓取问题如反爬虫 403 报错、满是乱码的广告弹窗、以及排版极其不规则的 HTML等。在一开始需要先安装以下几个核心库# 安装 LangChain 核心与扩展包 骨架与大脑接口pipinstalllangchain langchain-openai langchain-community langchain-text-splitters pypdf langchain-huggingface langchain-chroma# 安装本地极其轻量级的向量数据库 Chroma 海马体 / 向量数据库pipinstallchromadb# 安装本地中文 Embedding 所需的库pipinstallsentence-transformers补充这 3 个库适合我们目前搭建一个完整的工业级 RAG 最小可行性产品MVP实际工业界落地时会有其他替代方案。RAG 核心模块本次演练选型业界主流替代方案及适用场景编排框架 (Framework)LangChain-LlamaIndex:目前做纯 RAG 体验最好的框架专门为数据摄取和检索优化。-Semantic Kernel:微软主推适合 C#/.NET 企业的技术栈。-纯原生手写 (Custom):很多大厂为了极致的性能和可控性后期都会剥离 LangChain自己造轮子。向量数据库 (Vector DB)ChromaDB-FAISS:Facebook 开源老牌且轻量、快速适合本地开发 。-Milvus / Qdrant:支持亿级数据的分布式数据库企业级大规模部署的首选 。-Pinecone / Weaviate:托管型云服务不想自己运维服务器的商业化应用最爱 。向量化模型 (Embeddingsentence-transformers-BGE (BAAI):智源开源的中文最强向量模型免费且支持本地部署。-Nomic / Ollama:适合隐私敏感场景的本地轻量级部署 。1. 本地知识库构建在这一步我们将告别之前手工写的split()使用工业界最常用的滑动窗口切分法。我们现在需要新建一个 Python 脚本比如step1_split.py作为离线数据准备阶段。文档加载、分块Chunking、向量化Embedding和存入 ChromaDB这是一个“后台预处理”动作。通常是由一个独立的 Python 脚本在系统启动前或者每天半夜定时跑一次把知识库建好以避免每次询问大模型时大模型都要跑一遍既费时又费钱。这样我们 Stage 1 写的主循环代码执行时就可以直接查阅step1_split.py这个独立脚本。文档加载与摄取第一步是将原始资料导入系统同脚本放在相同目录下。常见的文档来源包括PDF、Word、Markdown、TXT 等本地文件网页内容通过爬虫或 API 抓取数据库或企业知识库导出内容from langchain_community.document_loaders import PyMuPDFLoader print(正在加载本地文档...) loader PyPDFLoader(粮仓建设标准.pdf) docs loader.load() print(f成功加载文档当前文档总数{len(docs)}) # PDF总页数注目前使用这个库会报警因为 LangChain 现在正在从langchain_community拆分工具库以避免原有库极其臃肿。但是目前 LangChain 主推的独立 PDF 解析集成安装包太大了。对于我们这个项目用目前的PyPDFLoader就足够了。如果想用最新的包可以下载pip install langchain-unstructured unstructured[pdf]。文档分块策略大语言模型的输入长度有限如 4K、8K、32K tokens所以我们需要将长文档切分成更小的“语义单元”每个单元称为一个chunk。常见分块策略按段落/句子/章节切分每段/句/章为一个 chunk适合结构清晰的文档。固定长度滑窗每 N 个 token 为一个 chunk支持重叠overlap以保留上下文。Overlap 是滑动窗口重叠的字符数防止一句话被从中间硬生生劈开。按语义切分使用句法分析或语义边界进行智能切分如 NLTK、spaCy。示例代码固定长度切分from langchain.text_splitter import RecursiveCharacterTextSplitter print(\n正在启动字符切分器...) text_splitter RecursiveCharacterTextSplitter( chunk_size500, # 每个 Chunk 的最大字符数可适当调整 chunk_overlap100, # 滑动窗口重叠的字符数可适当调整 separators[\n\n, \n, 。, , , , , ] # 按照这个优先级去寻找切分点 ) chunks text_splitter.split_documents(docs)RecursiveCharacterTextSplitter叫递归字符分割器核心逻辑优先用靠左的分隔符切文本保证尽量完整语义断开不把一句话拦腰截断执行顺序先拿\n\n两段换行段落分隔切割文本切出来的片段 ≤ chunk_size500 字直接保留不用再切片段超 500 字递归用下一级分隔符\n单行换行再切换行切完还是超长 → 用中文句号。分句句号不行再用感叹号、问号、分号、逗号最后兜底用空格强制切割最差情况会拆碎句子。简单说越靠左分割粒度越大、语义越完整越靠右拆分越碎。结果可视化可在脚本末尾加上以下代码观察输出结果print(f文档已被切分为{len(chunks)}个 Chunk。具体内容如下\n)fori,chunkinenumerate(chunks):print(f--- Chunk{i1}---)print(chunk.page_content)print(-*20)如果只想打印前几个 chunk 看结果可以更换第二行代码如以下代码表示打印前 3 个 chunkfor i, chunk in enumerate(chunks[:3]):。我们观察打印结果是为了加深对 chunk 的理解观察后就可以把这几行Ctrl /一键注释掉。在实际工程中不需要一直打印结果因为把几十万字的碎片全部打印在终端不仅毫无意义还会导致内存溢出或日志爆炸。2. 本地向量化与存储Embedding向量化什么是向量化向量化Embedding是将文本转化为多维空间中的向量表示使得语义相近的文本在向量空间中距离更近。这样我们就可以用“找最近的向量”来实现语义检索。向量化实践使用云端 API 向量化如OpenAI核心特点需要联网文档文本会上传至 OpenAI 服务器按 Token 计费无需占用本地磁盘 / 算力开箱即用不适合涉密、隐私敏感文档。依赖安装pipinstalllangchain-openai openai示例代码from langchain_openai import OpenAIEmbeddings # 自动读取环境变量OPENAI_API_KEY embedding_model OpenAIEmbeddings() # 批量对所有文档块生成向量 vectors embedding_model.embed_documents([chunk.page_content for chunk in chunks])本地部署向量模型推荐无需外网、无调用费用文档隐私性强分为两种落地方式方式 AHuggingFace 直接加载 BGE 中文模型本文采用适用场景纯 Python 脚本运行不需要额外启动软件轻量化中文向量首选智源人工智能研究院BAAI开源的bge-small-zh-v1.5它是目前公认最轻量且强悍的中文开源模型。示例代码fromlangchain_huggingfaceimportHuggingFaceEmbeddings# 唤醒本地纯正中文 Embedding 模型print(正在加载本地 BGE 中文向量模型 (首次运行自动下载数百MB的模型权重耐心等待)...)model_nameBAAI/bge-small-zh-v1.5model_kwargs{device:cpu}# 有N卡改为 cuda 加速encode_kwargs{normalize_embeddings:True}# 向量归一化适配余弦相似度计算embeddingsHuggingFaceEmbeddings(model_namemodel_name,model_kwargsmodel_kwargs,encode_kwargsencode_kwargs)方案 BOllama 托管向量模型nomic-embed-text独立本地服务适用场景统一管理大模型 向量模型提供 HTTP 接口多程序共享向量服务模型由 Ollama 进程常驻。终端拉取向量模型ollama pull nomic-embed-textLangChain 调用代码pipinstalllangchain-ollamafromlangchain_ollamaimportOllamaEmbeddings# 调用Ollama本地运行的向量模型embeddingsOllamaEmbeddings(modelnomic-embed-text)vectorsembeddings.embed_documents([chunk.page_contentforchunkinchunks])三种方案对比方案是否联网数据是否外传额外软件成本适合场景OpenAI 云端 API必须联网文本上传第三方无按 token 收费无隐私要求、快速调试本地 BGE (HuggingFace)仅首次下载模型需网完全本地无一次性占用磁盘单机脚本、中文文档、涉密文件Ollama nomic-embed-text仅拉模型需网完全本地需安装 Ollama 客户端免费多程序复用向量服务、统一本地大模型栈向量间的相似度计算当用户提问时我们会将问题也向量化然后与知识库中的所有向量进行相似度计算找出最相关的几个 chunk。常见相似度指标指标公式特点余弦相似度cos⁡(θ)A⃗⋅B⃗∣A⃗∣∣B⃗∣\cos(\theta) \frac{\vec{A} \cdot \vec{B}}{|\vec{A}| |\vec{B}|}cos(θ)∣A∣∣B∣A⋅B​仅衡量向量方向相似度不受文本长短影响前文 BGE 模型开启normalize_embeddings归一化就是为了适配余弦计算文本检索通用首选欧氏距离L2L2(A,B)∑i1n(ai−bi)2L_2(A, B) \sqrt{\sum_{i1}^{n} (a_i - b_i)^2}L2​(A,B)i1∑n​(ai​−bi​)2​衡量向量空间直线距离容易受向量长度干扰文本场景很少单独使用实际使用中向量数据库通常默认使用余弦相似度。Indexing构建向量索引单纯存放向量无法实现快速相似检索需要将向量存入专用优化的向量数据库Vector DB并建立检索索引整套流程称为Indexing索引构建。向量数据库分为本地轻量库、分布式企业库、云端托管库三类本次代码使用轻量化本地库 Chroma。主流向量数据库功能对比向量数据库特点是否开源适合场景ChromaPython 原生集成、零额外服务、一键本地持久化上手成本最低✅单机本地开发、小体量 PDF 知识库原型本次代码使用FAISSMeta 开源检索速度快无原生持久化文件夹能力✅本地高性能检索需自行封装存储逻辑Milvus分布式架构支持亿级海量向量存储✅企业级大规模生产部署Weaviate自带 HTTP 接口支持图文多模态向量✅前后端分离、多语言调用项目Pinecone云端托管服务无需本地部署维护❌商业线上项目快速上线免运维如何选型向量数据库业务需求推荐向量库本地脚本调试、PDF 知识库、需要永久保存向量文件Chroma本次实操纯本地追求极致检索速度、可自行处理持久化FAISS对外提供接口、多语言程序调用向量库Weaviate / Qdrant百万 / 亿级海量文档、多机器分布式部署Milvus / Qdrant不想搭建本地服务、直接云端调用Pinecone、Zilliz Cloud向量库构建实践Chroma 无需单独安装后台服务仅依靠 Python 依赖即可运行支持把文本块、向量、文件页码等元数据全部持久化到本地文件夹下次运行程序可直接加载存量知识库不用重复解析 PDF、切分文本、向量化。示例代码# 构建并持久化 Chroma 向量数据库fromlangchain_chromaimportChroma# 定义本地存储文件夹向量库会生成在当前脚本同级目录persist_directory./chroma_db_wheatprint(f\n正在将{len(chunks)}个 Chunk 翻译成向量并存入本地档案柜 {persist_directory} ...)vectordbChroma.from_documents(documentschunks,embeddingembeddings,persist_directorypersist_directory)print(\n✅ 知识库构建完成向量数据已永久保存在本地文件夹中。)参数释义documentschunks经过文本分割器拆分好的文档块列表embeddingembeddings提前配置完成的向量模型本地BGE/OpenAI/Ollama均可兼容persist_directory本地文件夹路径向量、原文、元数据全部落盘永久保存执行结果代码执行后项目目录会自动生成chroma_db_wheat文件夹内部存储全部向量索引、原文内容、PDF 页码等元数据以后 Agent 只需要读取这个文件夹不需要重新读 PDF 和切分了。3. 在主循环调用向量库我们回到之前在的主代码文件开始改动。唤醒数据库在代码的开头或者在原来get_weather的位置我们需要写一个新的函数query_knowledge_base。这个函数的作用是接收大模型给的关键词然后去你刚建好的 ChromaDB 里把最相关的 3 个 Chunk 捞出来。用以下代码替换掉你原来的get_weather函数和在前文中创建的retrieve函数# 导入所需的库fromlangchain_huggingfaceimportHuggingFaceEmbeddingsfromlangchain_chromaimportChroma# 1. 重新唤醒“翻译官”必须和存入时使用的是同一个模型print(正在连接本地向量知识库...)embeddingsHuggingFaceEmbeddings(model_nameBAAI/bge-small-zh-v1.5,model_kwargs{device:cpu},encode_kwargs{normalize_embeddings:True})# 2. 连接刚才落盘的那个“档案柜”persist_directory./chroma_db_wheatvectordbChroma(persist_directorypersist_directory,embedding_functionembeddings)defquery_knowledge_base(query:str)-str: 供大模型调用的真实检索工具根据 query 从 ChromaDB 中查找最相关的资料 print(f\n[执行工具] 正在向量数据库中搜索与 {query} 相关的资料...)# 执行余弦相似度检索k3 表示取出最相关的 3 个 Chunkdocsvectordb.similarity_search(query,k3)ifnotdocs:return知识库中未找到相关内容。# 把找到的 3 个 Chunk 拼成一段大文本返回给大模型results[]fori,docinenumerate(docs):# 获取页码信息如果有的话page_numdoc.metadata.get(page,未知)results.append(f【参考资料{i1}】第{page_num}页:\n{doc.page_content})return\n.join(results)修改大模型的“工具说明书”大模型不知道我们写了新函数我们需要在tools列表里给它发一份新的说明书告诉它现在它拥有了查阅企业知识库的能力。找到你原来的tools [...]列表替换为以下内容tools[{type:function,function:{name:query_knowledge_base,description:查询本地知识库中的相关信息,parameters:{type:object,required:[query],properties:{query:{type:string,description:用户的查询问题}}}}},]更新动态分发字典当大模型说“我要调用query_knowledge_base”时你的代码得知道具体去执行哪个 Python 函数。找到你核心循环里的available_tools字典更新它available_tools{query_knowledge_base:query_knowledge_base}重塑大模型人设修改它的系统提示词确保它认为自己是资料研究助手system_prompt{role:system,content:你是一个严谨的资料研究助手。请严格根据用户提供的【参考资料】回答问题。如果资料中没有相关信息请直接回答‘根据资料我不知道’。}其他确保你client.chat.completions.create里的toolstools和tool_choiceauto都是开启状态没有被注释掉。记得删除我们在搓纯手工 RAG 核心流时加的手工知识库和 Chunking Grounded answer prompt 强制引用改为让大模型自己判断是否要引用。二、本篇总结 下期预告至此Stage 2 三大模块之首RAG 检索增强模块完整完结。我们从理论原理、手写最简源码、工业级工程落地三个维度完整实现了 Agent 知识库增强能力彻底解决大模型幻觉、知识滞后、无溯源的核心痛点。RAG 赋予了 Agent 查阅知识的能力接下来我们将攻坚 Stage 2 第二个核心模块自定义工具系统。让 Agent 不再局限于问答检索可自主调用各类自定义业务工具真正具备落地实操、处理复杂业务的能力。
返回列表