DeepSeek本地化部署+RAG知识库从零搭建:Ollama、LangChain与向量检索实战
简介这是一份面向开发者与AI应用爱好者的DeepSeek本地化部署实操指南围绕基于RAG搭建本地知识库展开。内容从DeepSeek-R1开源模型入手介绍LM Studio等本地部署方式与硬件配置要求随后对比微调和RAG两种让模型成为领域专家的方法并以RAG为例演示创建知识库、导入语料、关联大模型与助手的完整流程。PDF共1个文件大小约4.91MB阅读轻量但覆盖从部署到应用的闭环。目前已有344人学习下载。读者可从中掌握本地化部署的硬件选型思路、微调与RAG的取舍逻辑以及快速搭建私有知识问答系统的操作路径适合希望摆脱公网API依赖、在本地落地AI问答场景的入门与进阶用户。1. DeepSeek本地化部署与RAG知识库为什么我劝你先跑通一轮问答再谈优化一个很常见的场景企业手里有几百份制度文档、产品手册、会议纪要想用AI做内部问答但数据不能出内网公有云API直接出局。于是“DeepSeek本地化部署 基于RAG搭建本地知识库”成了最现实的解法——模型部署在自己机器上文档切块后向量化到本地向量库用户提问时先检索相关片段再把片段交给DeepSeek生成回答。这套方案听起来不复杂但我见过太多人卡在第一步要么模型没起来就急着调prompt要么知识库建好了但回答跟文档完全没关系。这篇文章按我实际跑过的路线来写先讲怎么把DeepSeek本地化部署做扎实再拆RAG最小闭环最后落到案例实操和踩坑记录。适合想从零搭建、并且准备拿真实文档投入使用的从业者照着复现一轮至少能跑出可用的问答效果。2. 本地化部署DeepSeek显存、量化等级与推理引擎怎么选2.1 先算显存账从1.5B到70B每一档模型需要多大显存本地化部署碰到的第一个问题是“我的机器能跑多大模型”。DeepSeek开源出来的模型分两类满血版的671B MoE架构基本不用考虑单机显存放不下真正适合本地化部署的是R1蒸馏系列从1.5B到70B都有用Qwen和Llama做底座。选型的核心指标是显存有一个粗略但好用的经验公式FP16精度下10亿参数大约占2GB显存加上KV Cache和推理开销7B模型至少要准备16GB14B要32GB32B要64GB。这个数字对大多数人的单卡工作站都不友好所以实际部署几乎都要上量化。量化就是把模型权重从FP16压缩到INT8或INT4模型体积直接缩水显存占用随之下降。以Ollama仓库里的GGUF格式为例deepseek-r1:7b默认拉下来的是Q4_K_M量化版本显存占用大概5-6GB12GB显存就能跑得动。再往上14B的INT4需要10GB左右32B的INT4需要20GB左右48GB显存的单卡或者双卡可以勉强托住。下面是常用的选型参考表模型规格全精度显存参考4-bit量化显存参考可运行设备deepseek-r1:1.5b~3GB~1.5GB纯CPU都能跑适合验证链路deepseek-r1:7b~14GB~5GB12GB显存可流畅运行deepseek-r1:8b~16GB~5.5GB12GB显存可流畅运行deepseek-r1:14b~28GB~9GB16-24GB显存推荐deepseek-r1:32b~64GB~20GB48GB单卡或双卡24GBdeepseek-r1:70b~140GB~42GB多卡或大内存CPU硬扛我的建议很直白个人调试选7B或8B企业内网服务至少14B起步。知识库问答的难点往往不在推理能力而在检索质量模型小一点影响没那么大但14B在理解复杂指令和长上下文上的表现会比7B明显稳一截。如果你的机器只有16GB内存没有独立显卡也别放弃1.5B和7B的量化版可以用CPU推理单条问答慢一些但链路完全走得通适合先验证方案。参数细节上要留意量化等级不是越高越好。Q8_0比Q4_K_M质量好但要大一倍显存Q4_K_M是社区里验证过的性价比甜点追求极限显存才用Q3或Q2但回答质量下滑肉眼可见遇到长文档时尤其明显。2.2 推理引擎怎么选Ollama上手最快vLLM留给高并发模型文件只是第一步你还需要一个推理引擎把它跑起来。当前本地化部署DeepSeek最常见的两条路Ollama和vLLM。Ollama的优势是零配置——安装完拉模型直接起服务自动管理显存自带OpenAI兼容API非常适合个人研发和小团队内网试用。vLLM则是一个更重的推理服务框架用PagedAttention等技术把吞吐量做上去适合同时服务几十个用户的企业场景但需要自己处理模型下载、启动参数、监控告警这些事。我一般这样引导第一次做的人先装Ollama把链路跑通确认效果和性能瓶颈再决定要不要迁移到vLLM。不要一开始就上vLLM因为你还没数据判断QPS需求却要先交一大笔配置成本。对比维度OllamavLLM安装门槛低一个安装包搞定中需要Python环境与CUDA版本匹配显存管理自动按需加载可设置模型驻留时间常驻显存启动即占满并发能力默认适合轻量并发可调参高并发吞吐强支持连续批处理适用阶段研发调试、小团队内部服务正式服务化、稳定流量接入还有个细节值得关注Ollama启动后默认只监听127.0.0.1如果团队其他人也要访问需要设置环境变量OLLAMA_HOST0.0.0.0再重启服务同时在内网环境下注意访问控制别把没有任何鉴权的推理端口裸奔到整个网段。2.3 最小部署命令和自检一条curl确认服务真的能推理下面是Ollama部署DeepSeek的最小命令集按顺序执行即可# 安装Ollama后拉取DeepSeek R1蒸馏7B模型Q4量化 ollama pull deepseek-r1:7b # 首次运行会加载模型进入交互对话 ollama run deepseek-r1:7b # 另开一个终端用API方式验证服务是否正常 curl http://localhost:11434/api/generate -d { model: deepseek-r1:7b, prompt: 用一句话解释RAG是什么, stream: false }这段流程里ollama pull负责从模型仓库下载GGUF文件7B量化版大概4-5GB下载耗时取决于网络环境国内可以用ModelScope作为备选下载渠道。ollama run是交互式聊天入口适合随手测试真正给RAG程序调用的是后面这个/api/generate接口stream: false表示等完整回答生成后一次性返回调试阶段比流式输出容易看结果。为什么要用curl而不是直接依赖聊天窗口因为后续LangChain接入时用的是HTTP API这里的验证能确认端口、模型名、请求格式三者都对得上。看到返回的JSON里包含response字段就说明推理链路通了。这里有个容易踩的坑ollama run和API请求走的不是同一套上下文管理前者会保留会话历史后者每次请求是独立的后面做RAG时不要指望服务端帮你记状态。部署完成后模型默认会在内存里驻留5分钟之后自动卸载释放显存。如果你的知识库问答是周期性调用这个机制会导致每次提问都重新加载模型等好几秒才能开始推理。需要在启动时加环境变量延长驻留时间或者调大请求频率保持模型活跃。2.4 记住这个OpenAI兼容地址后续所有代码都靠它Ollama从0.1.x版本开始提供OpenAI兼容的接口地址是http://localhost:11434/v1这个地址太关键了后面所有RAG代码都通过它接入DeepSeek。LangChain、Dify、VSCode插件里配置模型服务时填的都是这个Base URL而不是/api/generate那个原生接口。用法上它和OpenAI SDK完全兼容只是API Key随便填一个非空字符串即可本地服务不校验。from openai import OpenAI client OpenAI( base_urlhttp://localhost:11434/v1, api_keyollama, # 本地服务不校验随便填 ) resp client.chat.completions.create( modeldeepseek-r1:7b, messages[{role: user, content: 你好}], ) print(resp.choices[0].message.content)这段代码是后续所有生成逻辑的骨架。注意model参数必须和ollama pull时用的名字完全一致不一致会报model not found。另外本地模型没有OpenAI那些gpt-4o之类的模型名别照搬网上的例子不换模型名就调做成配置文件放外面后面换14B或32B时只改一行。3. 基于RAG搭建本地知识库用Ollama加LangChain加Chroma跑通最小闭环3.1 架构与数据流为什么是这三件套RAG全称Retrieval-Augmented Generation检索增强生成解决的问题是让模型在回答时“有据可依”。流程分四步文档切块、片段向量化、按问题检索相关片段、把片段拼进Prompt交给大模型。整个过程围绕“本地”两个字选型三个核心组件缺一不可Ollama跑DeepSeek负责生成回答同时也可以跑一个嵌入模型负责向量化。LangChain编排层负责文档加载、文本切块、调用检索和拼Prompt。Chroma本地向量数据库负责存储向量并做相似度检索。有人会问RAG和MCP哪个更好这两个根本不是同一层面的东西。RAG是知识检索增强范式解决“模型不知道”的问题MCP是工具调用协议解决“模型不能操作外部系统”的问题。做本地知识库主链路是RAGMCP可以留到以后需要接数据库、查工单系统时再引入别混在一起选型。数据流可以这样理解文档先变成文本块文本块再变成向量存进Chroma用户提问时问题本身也被向量化然后去Chroma里找最相似的文本块最后文本块和问题一起交给DeepSeek模型基于这些片段生成回答。整个过程不涉及任何外部网络请求数据不出内网这正是本地化部署的核心价值。3.2 安装依赖LangChain版本分裂是个老坑pip install langchain langchain-community langchain-chroma chromadb pip install pypdf ollama我用的这几个库都是当前稳定路线。注意一点LangChain拆包之后Chroma向量库的封装被挪到了独立的langchain-chroma包里只装langchain是找不到Chroma类或from_documents方法的。文档加载器在langchain-community里也别漏装。版本上建议langchain和langchain-community保持同大版本混装主版本不一致经常出现pydantic校验报错一旦看到ValidationError先怀疑版本不匹配再怀疑自己代码写错。3.3 文档切块与向量化入库chunk_size500只是一个起点from langchain_community.document_loaders import PyPDFLoader from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain_community.embeddings import OllamaEmbeddings from langchain_chroma import Chroma # 1. 加载PDF文档 loader PyPDFLoader(./员工手册.pdf) docs loader.load() # 2. 递归切块保持段落语义完整 splitter RecursiveCharacterTextSplitter( chunk_size500, chunk_overlap50, separators[\n\n, \n, 。, , , , ], ) chunks splitter.split_documents(docs) print(f切块数量: {len(chunks)}) # 3. 拉取本地嵌入模型并向量化入库 # 先执行: ollama pull bge-m3 embeddings OllamaEmbeddings( modelbge-m3, base_urlhttp://localhost:11434, ) vectorstore Chroma.from_documents( documentschunks, embeddingembeddings, persist_directory./chroma_db, )切块参数是RAG效果的第一道分水岭。chunk_size500表示每个文本块约500个字符chunk_overlap50表示相邻块有50个字符重叠防止跨块语义被切断。RecursiveCharacterTextSplitter会按separators里的优先级依次尝试分隔先按双换行分段落再按单换行、句号、问号逐级下降相比固定长度切块它能尽量保住完整语义单元。嵌入模型这里用bge-m3智源开源的中英双语向量模型在Ollama上直接可用对中文长文档效果明显好于默认的nomic-embed-text。首次调用时Ollama会自动加载模型等待时间稍长之后常驻。入库之后检查一下./chroma_db目录是否存在且非空。注意不要每次运行脚本都执行from_documents否则会把重复内容再存一遍向量库迅速膨胀。常见做法是加一个存在性判断import os if os.path.exists(./chroma_db): vectorstore Chroma( embedding_functionembeddings, persist_directory./chroma_db, ) else: vectorstore Chroma.from_documents(...)3.4 检索与生成把“找得准”和“答得对”接在一起向量化入库只是准备工作真正的问答要从检索开始。这里我先不用LangChain封装好的RetrievalQA而是手动写成三步方便看清每一步的输入输出出问题时能快速定位是检索出了问题还是生成出了问题。from langchain_chroma import Chroma from langchain_community.embeddings import OllamaEmbeddings from openai import OpenAI # 加载已有向量库 embeddings OllamaEmbeddings(modelbge-m3, base_urlhttp://localhost:11434) vectorstore Chroma(embedding_functionembeddings, persist_directory./chroma_db) # 第一步检索 question 员工请假超过三天需要谁审批 docs vectorstore.similarity_search_with_score(question, k4) for doc, score in docs: print(f[相似度: {score:.4f}] {doc.page_content[:60]}...) # 第二步拼Prompt context \n---\n.join([doc.page_content for doc, _ in docs]) prompt f你是企业制度问答助手。请只依据下面的资料回答问题。 如果资料中没有相关内容直接回答“资料中没有相关说明”不要编造。 资料 {context} 问题{question} # 第三步调用DeepSeek生成 client OpenAI(base_urlhttp://localhost:11434/v1, api_keyollama) resp client.chat.completions.create( modeldeepseek-r1:7b, messages[{role: user, content: prompt}], temperature0.3, ) print(resp.choices[0].message.content)这段代码有三个值得说的地方。第一similarity_search_with_score返回的是(文档, 相似度分数)对分数越小代表距离越近、匹配度越高我按从小到大排序取前4条。这个分数在调试时非常有用如果你发现答非所问先打印分数看检索结果里到底进了什么内容。第二k4不是固定真理问制度条款时3-5条足够问开放性问题可以放到6-8条但要控制总上下文长度DeepSeek的上下文窗口有限塞入了太多不相关内容反而干扰回答。第三Prompt里明确写了“只依据资料回答”和“没有就直说”这一步是防止模型幻觉的关键很多人效果不好就是没在Prompt里做这个约束。temperature0.3是知识库问答的常用取值。做制度问答、代码生成这类追求确定性结果的场景温度越低越好如果让模型做头脑风暴、写文案才需要拉高到0.7以上。3.5 第一轮效果的验证方法先看检索再谈回答很多新人第一次跑通代码后只看最终回答回答不对就怀疑模型不行。正确的排查顺序是先看检索片段再看生成质量。因为RAG的输出质量上限由检索决定检索到错误内容DeepSeek再聪明也无济于事。我的验证方法是把“检索到的片段”单独打印出来人工判断这些片段与问题的相关性。如果第三步的最终回答不对但检索出的片段明显相关说明问题在Prompt或模型理解上如果检索出的片段本身就风马牛不相及那请回到切块和嵌入模型这一步调参或换模型。另外可以设计一个“必答问题集”比如从文档里挑出10个有明确答案的问题每次修改参数后都跑一遍这10个问题记录命中率。这个习惯会在第6章展开但第3章就应该开始攒这个测试集。4. 案例实操从企业制度PDF到能回答追问的多轮知识库4.1 数据清洗PDF里的页眉页脚会毁掉你的检索质量上一章的最小闭环能跑通但真实企业文档远比“员工手册.pdf”要脏。最常见的问题是PDFLoader会把页眉、页脚、页码当成正文读进来切块时这些噪声会混进文本块。更麻烦的是扫描版PDF文字根本提取不出来检索出来的全是一堆乱码。from langchain_community.document_loaders import PyPDFLoader import re loader PyPDFLoader(./差旅管理制度.pdf) docs loader.load() def clean_text(text: str) - str: # 去掉页眉页码 text re.sub(r第\s*\d\s*页, , text) text re.sub(r\d{4}[-/]\d{1,2}[-/]\d{1,2}, , text) # 日期水印 text re.sub(r\n{3,}, \n\n, text) # 压缩多余空行 # 常见页眉如 XX公司 内部资料 按实际文档补充 text re.sub(rXX公司\s*内部资料, , text) return text.strip() for doc in docs: doc.page_content clean_text(doc.page_content)清洗规则没有通解需要先打印原始PDF的page_content前几页看看到底混入了什么再写对应的正则。我见过有人用一行正则把所有数字删掉结果报销金额全没了这就是没先看数据就动手的下场。如果遇到扫描件先用OCR把图片转成文本本地可以跑PaddleOCR或RapidOCR这一步会显著增加处理时间但好过让向量库里全是乱码。4.2 切块策略怎么选按文档类型调参而不是一个参数走天下第3章的chunk_size500只是通用默认值真实场景里不同类型文档的最优切块差异很大。制度条款类讲究完整性一条条款不能被拦腰切断操作手册类讲究步骤连续FAQ类的单条记录应该保持独立不要和别的问答混在一起。文档类型建议chunk_size建议chunk_overlap推荐理由制度规范/管理办法400-60050-80条款语义完整操作手册/流程文档300-50050步骤相对独立FAQ问答记录150-30020-30保持单条问答自包含技术方案/论文600-1000100-200段落内部逻辑连贯切块后建议加一步清洗过滤掉长度过短的块。比如低于50个字符的文本块往往是目录、页眉残留或表格碎片直接丢弃。chunks [c for c in chunks if len(c.page_content) 50]为什么rag切块这么重要因为检索是“按块”匹配的块切得好不好直接决定匹配粒度。块太大检索结果里混入大量无关内容DeepSeek容易被带偏块太小语义不完整匹配倒是准了但信息不够回答问题。这个平衡没有银弹只能靠你的测试集反复试。4.3 嵌入模型选型中文场景优先考虑bge系列向量化用的是另一个模型和DeepSeek是两码事。嵌入模型好坏直接影响“语义相似”的判断质量中文场景下我推荐从两个方向选bge-m3和text2vec-large-chinese。bge-m3支持8192 token的长文本多语言能力强在Ollama一条命令就能拉下来text2vec在纯中文短文本上表现扎实但对长文档支持较弱。ollama pull bge-m3from langchain_community.embeddings import OllamaEmbeddings embeddings OllamaEmbeddings( modelbge-m3, base_urlhttp://localhost:11434, )嵌入模型和生成模型一样一旦选定就不要频繁换因为换嵌入模型意味着所有文档要重新向量化这是一次全量重建。从text2vec换成bge-m3时千万别忘了清空chroma_db目录重新入库否则旧向量和新向量混在一起维度都对不上检索结果全乱。4.4 检索质量调优相似度阈值和重排让结果更可控基础检索用Top-K截断但Top-K只能控制数量控制不了质量。我的做法分两步先设相似度阈值过滤低质量匹配再做一次重排。from langchain_chroma import Chroma from langchain_community.embeddings import OllamaEmbeddings embeddings OllamaEmbeddings(modelbge-m3, base_urlhttp://localhost:11434) vectorstore Chroma(embedding_functionembeddings, persist_directory./chroma_db) retriever vectorstore.as_retriever( search_typesimilarity_score_threshold, search_kwargs{score_threshold: 0.35, k: 8}, ) docs retriever.invoke(员工请假超过三天需要谁审批)similarity_score_threshold这个检索类型会先取k条候选再过滤掉分数不达标的。阈值的取值不是玄学但需要实验把问题跑一遍打印所有检索结果的分数分布看相关和不相关的分界线在哪里。常见范围在0.3-0.5之间偏差太大说明嵌入模型和文档风格不匹配要先回去调切块或换模型。重排则对Top-K结果做二次精排。bge-reranker-base是目前常用的本地重排模型它对“问题和文本块的相关性”做深度匹配比纯向量余弦相似度准一截。from sentence_transformers import CrossEncoder reranker CrossEncoder(/path/to/bge-reranker-base) pairs [(question, doc.page_content) for doc in docs] scores reranker.predict(pairs) reranked sorted(zip(docs, scores), keylambda x: x[1], reverseTrue) for doc, score in reranked[:3]: print(f[重排分数: {score:.4f}] {doc.page_content[:50]})重排有代价慢且需要额外下载模型。用户量小、知识库几千个块的时候纯向量检索完全够用一旦检索结果频繁“看起来像但实际不对”再考虑加重排层。4.5 多轮对话怎么设计查询重写比把历史全塞进Prompt更省事RAG多轮对话是最容易翻车的场景。用户先问“差旅费报销标准是什么”系统答了接着问“那住宿超标了怎么办”这里的“那”指代的是差旅费。如果直接拿“那住宿超标了怎么办”去检索大概率检索不到正确条款因为文档里不会写“那”这个字。解决方案是查询重写每轮提问时把对话历史一起送给模型让模型把“最新问题”改写成一个不依赖上下文的完整问题再用改写后的问题去检索。def rewrite_query(history: list, question: str) - str: client OpenAI(base_urlhttp://localhost:11434/v1, api_keyollama) history_text \n.join( f用户{m[user]}\n助手{m[assistant]} for m in history[-3:] ) prompt f根据对话历史把用户的最新问题改写成可独立检索的完整问题。 只输出改写结果不要解释。 对话历史 {history_text} 最新问题{question} resp client.chat.completions.create( modeldeepseek-r1:7b, messages[{role: user, content: prompt}], temperature0, ) return resp.choices[0].message.content重写时最多保留最近3轮历史太长的历史既费token又容易把重写结果带偏。temperature0保证重写结果稳定。改写后的query再进入vectorstore检索而不是直接用原始提问。这个方法在工程上成本低、效果明显比把历史全部塞进检索query要干净得多。另一种备选方案是历史内容直接拼进检索query比如“差旅费报销标准是什么 住宿超标了怎么办”检索时用两个句子的向量叠加。但问题在于“差旅费报销标准是什么”这种已答过的高频词会稀释“住宿超标”这个真正的检索意图效果不如重写。这也是我推荐查询重写的原因。5. 本地知识库避坑指南检索翻车与推理报错的5个典型场景5.1 检索结果与问题无关但相似度分数看起来还挺高现象问“转正需要准备什么材料”检索出来的文档片段是考勤制度里的“请假超过三天需要领导审批”。原因这类问题大概率是文本里存在共同的高频词而嵌入模型把高维空间中的距离拉近了。另一个常见原因是文档片段本身过于零碎比如chunk_size设成200一条制度被切成好几段每一段都包含“审批”、“材料”这类词但完整语义已经被拦腰截断。解决先打印检索片段的完整内容确认问题出在语义匹配还是片段切分。如果片段结构完整但匹配错尝试调节score_threshold或把嵌入模型换成bge-m3加深语义理解如果片段本身就残缺增大chunk_size和chunk_overlap或者改用4.2节按文档类型定切块参数的策略。记住相似度分数高不等于内容相关这个分数只是排序依据不是正确性证明。5.2 模型回答不引用知识库反而自顾自地编现象知识库里有明确答案但DeepSeek回答的是通用知识看起来合理实际上和文档内容完全不符。原因Prompt里没有约束回答必须基于给定上下文。DeepSeek本身是生成模型给它一个问题它会倾向用预训练知识回答而不是用你给的检索片段。你给了资料但Prompt里没说“必须用”模型就当成背景信息忽略掉了。解决把Prompt里的指令写成硬约束并给出拒绝选项prompt f你只能依据下面资料回答用户问题。 如果资料中找不到对应答案请回答资料中未找到相关信息。 禁止使用资料以外的知识作答禁止编造。 资料 {context} 用户问题{question} 这个改动可能会让模型在一些确实没有答案的问题上变“笨”但知识库型应用的可靠性优先于灵活性。宁可让模型说不知道也不能让它编一个听起来合理的答案——企业内部制度问答翻车一次就没人敢用了。5.3 出现“tool calls need immediate results”报错链路直接中断现象通过OpenAI兼容接口调用DeepSeek时首次请求正常返回但代码一处理工具调用就报错提示messages tool calls need immediate results有些前端控制台还会出现cannot read properties of undefined之类的空值读取错误。原因DeepSeek在支持Function Calling时第一轮可能会返回带tool_calls字段的响应表示模型希望调用某个工具函数。此时调用方必须执行工具并把结果以tool角色消息回传给模型这是OpenAI兼容协议的规定。如果代码只处理了普通回答没处理tool_calls下一轮请求里就缺失了工具结果服务端就认为消息序列不完整直接报错。解决写一个循环检测到tool_calls就执行对应函数然后把结果以roletool的消息追加回messages再发起下一次请求messages [{role: user, content: 查询张三今年的累计请假天数}] while True: resp client.chat.completions.create( modeldeepseek-r1:7b, messagesmessages, toolstools, # 其中tools描述了可用的函数 ) msg resp.choices[0].message if not msg.tool_calls: print(msg.content) break for call in msg.tool_calls: result run_local_function(call.function.name, call.function.arguments) messages.append({ role: tool, tool_call_id: call.id, content: str(result), })关键点是tool_call_id必须原样回传content为工具执行结果。只把工具返回追加进去但丢了tool_call_id同样会报错。这个坑在本地搭建RAG时不一定遇到一旦你接入数据库查询、API调用等工具场景就会知道这个循环结构是必须的。5.4 第二轮提问后模型完全“失忆”答非所问现象第一轮用户问“员工有几天年假”得到答案第二轮追问“新入职的也有吗”模型直接胡答甚至把第一轮的背景全忘了。原因本地推理API默认是无状态的每次调用之间不保存任何上下文。第二轮请求里只带了“新入职的也有吗”这几个字模型没有上一轮的上下文可用。解决在应用层维护消息历史把每轮问答追加到messages列表里下一轮请求带上最近几轮历史。注意历史不要无限增长控制最近3-5轮即可超出部分做截断或摘要压缩。查询重写可以解决“检索时该搜什么”的问题但“模型生成时能参考什么”由消息历史决定两者都要管。5.5 推理越来越慢甚至直接OOM现象知识库问答服务跑了两天响应时间从2秒涨到30秒服务器内存和显存被吃满应用崩溃。原因大多数情况是向量库膨胀。调试阶段反复执行from_documents同一个文档入库了几百遍每次检索都要扫描大量重复向量速度自然下降。另一种可能性是Ollama常驻多个模型DeepSeek和bge-m3同时驻留在显存里互相抢占资源。解决先用ollama ps查看当前驻留模型把不用的ollama stop杀掉再检查向量库大小和记录数如果chroma_db目录异常膨胀清空重建并确认入库逻辑有幂等判断。向量库服务的常规巡检就三件事文档计数、检索耗时、显存占用每周跑一次比临时救火强得多。6. 进阶用Dify或RAGFlow把脚本升级成团队可用的知识库服务脚本方案解决了“能不能用”但没解决“好不好用”。没有可视化界面同事问两句就没耐心学每次更新知识库都要手动跑脚本重建索引权限、日志、审计一概没有。这些需求催生了平台化工具——Dify和RAGFlow是当前本地知识库领域社区热度最高的两个开源方案。Dify更像一个完整的LLM应用开发平台工作流编排、模型管理、知识库、日志监控都在里面RAGFlow的强项在于文档解析它用DeepDoc把PDF里的表格、图片、复杂版面还原成结构化内容处理制度条款这种格式杂乱的文档效果比纯文本切块好很多。Dify本地化部署用Docker Compose一条命令拉起git clone https://github.com/langgenius/dify.git cd dify/docker cp .env.example .env docker compose up -d部署完最重要的一步是把本地DeepSeek接入Dify。在右上角用户设置里的模型供应商页面选择新增“OpenAI-API-compatible”供应商填上本地Ollama的地址。这里有个关键坑Dify跑在Docker容器里容器内localhost指向容器自身不是宿主机。填Ollama地址时要用http://host.docker.internal:11434/v1Windows和macOS适用Linux下则要用http://172.17.0.1:11434/v1这类Docker桥接网关地址。API Key随意填“ollama”Dify只做透传不校验。知识库方面Dify里上传文档后设置分段模式选择“父子分段”可以兼顾检索精度和上下文完整性——父段是完整章节子段是小片段检索命中子段后把父段喂给模型回答质量比直接用小片段生成高一个档次。RAGFlow则更激进它的DeepDoc解析流程在处理扫描件和复杂表格时几乎能省掉你第4章写的一堆正则。最后一个进阶建议无论用脚本还是平台花一小时把回归测试集建起来。从知识库里挑30个高价值问题写进CSV每次调整切块、换嵌入模型或改Prompt后批量跑一遍检索并把Top-5结果导出人工对比是否有退化。这个习惯能让你在“调参一时爽”之后仍然保持系统可控也让我避开了好几次自以为优化、实际变差的改动。知识库系统的核心资产是数据质量和检索效果提示词调得再花哨也补不回来这是我踩过坑后最想提醒后来者的一点。希望帮到你。本文还有配套的精品资源点击获取