ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

从聊天到干活:LangChain与ChromaDB构建RAG知识库实战

从聊天到干活:LangChain与ChromaDB构建RAG知识库实战 1. 从能聊天到能干活对话式AI的第四道分水岭如果你已经跟着这个系列一路做下来前三篇里我们聊的都是怎么让模型把话说顺——提示词怎么写、上下文怎么管、多轮对话怎么不串味。但到了第四篇事情开始变得不一样了我们要解决的是模型怎么知道它原本不知道的事。这个问题的专业叫法是 RAGRetrieval-Augmented Generation检索增强生成。说白了就是大模型的知识截止在训练那一天你问它公司内部文档、最新产品手册、私有数据库里的内容它要么答不上来要么一本正经地胡说八道。RAG 的思路很朴素——先去你的资料库里把相关内容捞出来再连同问题一起塞给模型让它看着材料答题。我见过太多人卡在这一步LangChain 的文档翻了三遍ChromaDB 装好了向量也存进去了结果一问还是答非所问。问题往往不在模型而在检索这一环。这篇就把对话式 AI 从闲聊玩具升级成业务助手的完整链路拆开讲包括 LangChain 的组件怎么选、ChromaDB 怎么用才不踩坑、RAG 的瓶颈到底卡在哪、以及知识库该怎么分类。适合已经跑通过基础对话、想往实用方向推进的开发者也适合被检索不准折磨过的朋友。2. RAG 到底在解决什么问题先想清楚再动手2.1 大模型的三条知识边界在动手写代码之前得先明白模型的能力边界在哪。我把它归纳成三条线训练数据边界模型只知道训练语料里出现过的东西。你公司上周刚定的报销制度它不可能知道。时效边界即使训练数据里有也是截止到某个时间点。今天发生的新闻它答不了。私有数据边界内部文档、客户资料、代码仓库这些从来就不在公开训练集里。RAG 针对的正是后两条。第一条其实也能缓解——只要你的私有资料被检索进来模型就能临时学会。这就是为什么 RAG 成了企业落地大模型最主流的方案不用重新训练模型成本低、更新快、可溯源。2.2 为什么不是微调而是检索经常有人问我直接拿自己的数据微调一个模型不行吗行但要看场景。微调适合改变模型的风格和行为模式比如让它说话更像某个客服而 RAG 适合注入事实性知识。原因有三第一微调的知识更新成本极高。你改一份文档就得重新训一遍而 RAG 只要更新向量库里的那一条记录。第二微调容易灾难性遗忘新知识学进去旧能力可能掉下来。第三RAG 能给出引用来源用户能看到答案是从哪段材料来的这在客服、法务、医疗场景里几乎是刚需。所以我的建议很明确知识类需求优先上 RAG风格类需求才考虑微调两者可以叠加。2.3 一个最小可用的 RAG 心智模型别被各种框架名词吓到RAG 的核心就四步我用一个图书馆的类比讲清楚切块Chunking把厚书拆成一页页卡片。文档太长模型一次读不完得切。向量化Embedding给每张卡片编一个语义坐标内容相近的卡片坐标也相近。检索Retrieval用户提问时把问题也转成坐标找出坐标最近的几张卡片。生成Generation把卡片和问题一起交给模型让它基于卡片作答。LangChain 干的事就是把这四步用统一的接口串起来ChromaDB 干的事是当那个存卡片坐标的仓库。理解了这四步后面所有配置你都能对上号。3. LangChain 组件选型别被全家桶绑架3.1 为什么很多人用 LangChain 反而更乱LangChain 最大的问题是抽象层太多。一个简单的检索问答它能给你整出 DocumentLoader、TextSplitter、Embeddings、VectorStore、Retriever、Chain、Agent 七八个概念。新手一上来就想把所有组件都用上结果调试时根本不知道哪一层出了问题。我的经验是先用最少的组件跑通再按需加。一个能用的 RAG其实只需要四样东西——加载器、切分器、向量库、模型。其余的 Retriever 封装、Chain 编排等你发现原生写法不够用了再引入。3.2 核心组件的实际取舍下面这张表是我在实际项目里反复验证过的选型参考组件常见选项我的推荐场景文档加载PyPDF、Unstructured、TextLoader纯文本用 TextLoaderPDF 优先 Unstructured文本切分RecursiveCharacterTextSplitter九成场景够用按语义层级递归切向量化OpenAI Embeddings、本地 BGE有预算用云端数据敏感用本地模型向量库ChromaDB、FAISS、Milvus中小规模 ChromaDB超大规模上 Milvus这里重点说切分。很多人栽在 chunk_size 上随手设个 1000 就不管了。实际上切分大小直接决定检索质量切太大一块里混了好几个主题检索出来噪声多切太小一句话被拦腰截断语义不完整。我一般从 500 字符起步配合 50 到 100 的重叠overlap保证跨块的句子不被切断。3.3 一个不绕弯的 LangChain 骨架抛开那些花哨封装核心代码其实很直白from langchain_community.document_loaders import TextLoader from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain_community.vectorstores import Chroma from langchain_openai import OpenAIEmbeddings, ChatOpenAI # 1. 加载 loader TextLoader(knowledge.txt, encodingutf-8) docs loader.load() # 2. 切分 splitter RecursiveCharacterTextSplitter( chunk_size500, chunk_overlap80, separators[\n\n, \n, 。, , , , ] ) chunks splitter.split_documents(docs) # 3. 入库 vectorstore Chroma.from_documents( documentschunks, embeddingOpenAIEmbeddings(), persist_directory./chroma_db ) # 4. 检索 生成 retriever vectorstore.as_retriever(search_kwargs{k: 4}) question 报销流程是怎样的 context_docs retriever.invoke(question) context \n\n.join([d.page_content for d in context_docs]) llm ChatOpenAI(modelgpt-4o-mini, temperature0) prompt f仅根据以下材料回答问题材料中没有的信息就说不知道。\n\n材料\n{context}\n\n问题{question} print(llm.invoke(prompt).content)注意separators里我加了中文标点。默认的切分器只认英文标点和空格处理中文文档时经常在句子中间断开加上中文句号、问号、感叹号后切出来的块语义完整度高很多。这是中文场景下特别容易被忽略的一个细节。4. ChromaDB 实战向量库用对了才叫 RAG4.1 为什么选 ChromaDB 而不是别的向量库这个赛道选择很多FAISS 快但不管持久化Milvus 强但部署重。ChromaDB 的定位很讨巧轻量、能持久化、API 简单、支持元数据过滤。对于个人项目、中小团队、原型验证它几乎是零门槛——pip install chromadb就能跑数据默认落盘到本地目录。我特别看重它的元数据过滤能力。比如你有一堆文档想只检索2024年之后且属于技术部的内容ChromaDB 的where条件能直接搞定不用自己写过滤逻辑。这在多租户或者多分类知识库场景里非常实用。4.2 持久化与增量更新别每次都重建新手最常见的浪费是每次启动程序都重新from_documents一遍把整个知识库重新向量化。这既费钱又费时。正确做法是判断库是否存在存在就加载不存在才创建import os from langchain_community.vectorstores import Chroma from langchain_openai import OpenAIEmbeddings PERSIST_DIR ./chroma_db embeddings OpenAIEmbeddings() if os.path.exists(PERSIST_DIR) and os.listdir(PERSIST_DIR): vectorstore Chroma( persist_directoryPERSIST_DIR, embedding_functionembeddings ) print(已加载现有向量库) else: vectorstore Chroma.from_documents( documentschunks, embeddingembeddings, persist_directoryPERSIST_DIR ) print(新建向量库完成)增量更新时用add_documents追加新内容即可。但要注意ChromaDB 不会自动去重。同一份文档如果被加了两次检索时会返回重复结果白白占用上下文窗口。我的做法是给每个 chunk 生成一个基于内容哈希的 ID入库前先查一下这个 ID 存不存在。4.3 检索参数调优k 值和相似度阈值k值返回几条结果不是越大越好。k 太小可能漏掉关键信息k 太大噪声挤占上下文模型反而抓不住重点。我的经验区间是3 到 6具体看你的 chunk 大小——块小就多取几条块大就少取几条。更进阶的是加相似度阈值。ChromaDB 默认返回距离最近的 k 条哪怕这些结果其实和问题八竿子打不着。加一个阈值过滤能有效避免检索不到就硬编的情况retriever vectorstore.as_retriever( search_typesimilarity_score_threshold, search_kwargs{k: 5, score_threshold: 0.3} )阈值设多少要试。设太高正常问题也检索不到设太低等于没过滤。我一般从 0.3 开始根据实际问答效果微调。这一步做完你会发现答非所问的比例明显下降。5. RAG 的瓶颈到底卡在哪三个真实故障现场5.1 检索到了但模型没用上这是最隐蔽的瓶颈。你明明检索出了正确文档模型却还是按自己的记忆回答。原因通常是提示词没约束好。如果你只说参考以下材料回答模型很可能把材料当背景音继续用自己的知识。解决办法是把约束写死仅根据以下材料回答材料中没有的信息直接回答根据现有资料无法确定。这句话看着简单但能挡掉大量幻觉。另外把材料放在问题前面模型对靠前内容的注意力通常更高。5.2 检索不到因为问法和文档用词不一致用户问怎么请假文档里写的是休假申请流程。字面没有重叠纯关键词检索就废了。这正是向量检索的价值——它比的是语义相似度不是字面匹配。但向量检索也不是万能如果 embedding 模型对中文支持不好语义相近的词也可能距离很远。我的应对策略是混合检索向量检索 关键词检索BM25各取一批结果再合并去重。LangChain 里有EnsembleRetriever可以直接做这件事。实测下来混合检索在专业术语多的场景里召回率比纯向量高出一截。5.3 上下文塞太满模型中间失忆有个被反复验证的现象当上下文很长时模型对开头和结尾的信息记得牢中间部分容易忽略这叫迷失在中间Lost in the Middle。如果你一次塞进去十几条检索结果关键那条恰好排在中间模型很可能视而不见。对策有两个一是控制 k 值别贪多二是重排序Rerank——先粗检索一批再用一个专门的重排模型精排把最相关的放到最前面。重排模型比向量检索慢但只对少量候选做成本可控效果提升明显。6. 知识库不是一锅炖三类知识库的区分与选型6.1 RAG 知识库、KG 知识库、结构化知识库很多人把知识库当成一个东西其实至少要分三类它们的存储方式、检索方式和适用场景完全不同类型存储形式检索方式典型场景RAG 知识库向量 原文块语义相似度文档问答、客服KG 知识库实体-关系三元组图遍历、路径查询关系推理、风控结构化知识库表 / SQL精确查询、聚合报表、统计、订单RAG 知识库擅长从大段文字里找答案但它不擅长推理。你问张三的上级的部门经理是谁这种多跳关系问题向量检索基本抓瞎而知识图谱KG能顺着关系链一路查下去。结构化知识库则适合上个月销售额是多少这类精确计算。6.2 什么时候该上知识图谱不是所有项目都需要 KG。我的判断标准是如果你的问题涉及多跳关系、需要精确的实体连接才考虑 KG。比如企业组织架构、供应链关系、医疗诊断路径。如果只是文档里说了什么RAG 就够了硬上 KG 是过度设计。现在也有 Ontology RAG 这种融合思路——用本体Ontology来约束和增强检索让向量检索带上结构化的语义。这属于进阶玩法建议先把基础 RAG 跑稳再研究。6.3 混合架构让三类知识库各司其职真实的企业助手往往是混合的。用户问帮我查一下上季度华东区的销售情况并解释一下为什么下滑这个问题同时需要结构化查询销售数据和 RAG分析报告。我的做法是让一个 Agent 做路由先判断问题类型再决定调用哪个知识库最后把结果汇总给模型生成回答。这就是 Agent 框架的价值所在。LangChain、Dify、CrewAI 各有侧重LangChain 灵活但偏底层Dify 可视化适合快速搭建CrewAI 擅长多智能体协作。选哪个取决于你的团队——要快速出原型选 Dify要深度定制选 LangChain要做多角色协作看 CrewAI。7. 从零跑通一个可用的 RAG 助手完整实操链路7.1 环境准备与依赖安装先把地基打好。Python 建议 3.10 以上太老的版本有些库装不上python -m venv rag_env source rag_env/bin/activate # Windows 用 rag_env\Scripts\activate pip install langchain langchain-community langchain-openai chromadb如果你要用本地 embedding 模型还得装sentence-transformers。这里提醒一句别在全局环境里装这些库依赖冲突能让你排查到怀疑人生虚拟环境是底线。7.2 文档预处理清洗比切分更重要很多人跳过清洗直接切分结果检索质量一塌糊涂。PDF 提取出来的文本经常带页眉页脚、乱码、断行。我的预处理清单去掉重复的页眉页脚和页码合并被硬换行切断的句子统一全角半角标点去掉多余的空格和空行这一步花的时间会在检索效果上加倍还回来。清洗完再切分chunk 的语义完整度会好很多。7.3 构建与验证怎么判断 RAG 好不好用跑通不等于好用。我一般准备一组测试问题覆盖三类能直接检索到的、需要跨文档综合的、知识库里根本没有的。第三类最关键——好的 RAG 应该老实说不知道而不是编一个答案。验证时重点看两个指标召回率该找到的有没有找到和准确率找到的对不对。如果召回率低调切分和检索参数如果准确率高但回答还是错问题多半在提示词或模型。7.4 上线前必须做的几件事最后分享几个上线前容易漏掉的点。第一加日志把每次的检索结果和最终回答都记下来出问题能回溯。第二做缓存相同问题不必重复检索和调用模型省钱又提速。第三设兜底检索为空或模型超时的时候给用户一个体面的回复而不是报错。第四定期更新向量库知识是有保质期的。我在实际项目里踩过最深的一个坑是忘了处理编码问题。中文文档用默认编码读进来全是乱码向量化出来的结果自然一塌糊涂排查了半天才发现是encoding参数没设。这种低级错误往往比算法问题更耗时间。所以我的建议是每引入一个新数据源先打印前几段文本肉眼确认再往下走。这个习惯帮我省下了无数次返工。
返回列表