
1. 两周时间拆解从“需要知识库”到“决定私有化”的理由事情得从一次内部审计说起。公司团队这两年沉淀了大量技术文档、产品手册、售前方案和售后工单分散在飞书、语雀、本地磁盘和每个人的微信聊天记录里。新同事入职第一周几乎每个问题都要在群里问一遍老同事被反复打扰文档明明存在但没人知道去哪里找。最开始的想法很简单上一套现成的知识库系统。市面上SaaS类产品体验确实好开箱即用上传文档就能问答。但一谈到数据归属问题就来了——我们的文档里有客户资料、内部架构图、未发布的方案版本这些内容放到第三方平台先把法务这一关就过不了。再加上团队本来就计划后续把问答能力嵌到内部OA和企微机器人里API调用的频次和数据流向也必须自己可控。于是结论明确必须私有化部署自己搭一套RAG知识库。所谓RAGRetrieval-Augmented Generation检索增强生成核心思路就是先在大模型外部建一个知识库用户提问时先检索出相关知识片段再把这些片段和问题一起拼给大模型生成答案。模型不“背”你所有的文档它只负责基于检索到的内容做摘要、综合和推理。两周时间白天还要处理本职工作的前提下我和另一位后端同事把这件事从零到一跑通了。这篇文章就把完整的架构、选型逻辑、踩坑记录都摊开写出来。如果你也面临类似场景——想要私有化知识库、但不太确定从哪下手或者已经搭过一套但效果不太理想——这篇内容应该能帮你省下至少一半的调研时间。2. 技术选型不做纠结向量库、模型、框架怎么定下来选型是第一天最大的消耗战。市面上选项实在太多而且每过几个月就会出现新东西。我的经验是先把需求边界划清楚再一条条对照。2.1 需求边界确定我们到底要什么开工之前我们先把需求收敛成这几条文档类型以 Markdown、Word、PDF、PPT 为主另有少量结构化表格数据数据规模初始约2万份文档加上版本迭代预计一年内不超过20万份场景要求内部问答为主回答需要自动附带引用来源用户能回溯到原文安全要求完全内网部署不能有任何外部API调用并发要求初期团队规模50人左右QPS不需要太高但要有稳定性和可观测性把需求列出来之后选型就变得非常具体了。凡是需要调用云端API的方案全部排除凡是社区不活跃、文档残缺的组件也全部排除。2.2 框架层LangChain vs LlamaIndex vs 自研框架是所有“RAG项目”绕不开的第一道选择题。我对比了三个方向维度LangChainLlamaIndex自研上手成本中概念多低面向RAG场景高灵活性高但版本变动大中封装较深最高长期维护依赖社区更新更新节奏可控完全自主我们最终选择排除借鉴思路轻量自研我最后的选择是不用框架自己在中间层做一套轻量编排。原因是LangChain这种框架的抽象层更新太快网上教程写的是0.1版本的写法过两周接口变了又要重新调。企业内网环境里没有太多族谱依赖核心链路就那几步——检索、拼Prompt、调模型、流式返回自己写无非是多花一两天时间但后面调优和排障时会省下大量“这个框架为什么跟我预期不一样”的排查成本。不排斥框架但也不迷信框架。如果团队没人写过Python后端用成熟的框架项目后面会提到完全可以如果像我这样有后端基础建议走轻量自研后面每出一类问题你都能立刻定位到自己的代码而不是去翻框架源码。2.3 向量库选型Milvus、Qdrant、pgvector 还是 Elasticsearch向量库是RAG检索增强生成的存储底座。文档切片后要转成向量存进去用户提问时也要转成向量去检索。候选有四个Milvus业界名声最大功能全支持分布式但对内网部署来说偏重需要额外的etcd、MinIO依赖运维成本偏高QdrantRust写的单机性能好有丰富的过滤条件API设计干净文档质量高部署只需要一个Docker容器pgvector基于PostgreSQL的扩展优势是如果你本来就重度依赖PG可以少一套组件。但检索性能和高级过滤能力比专业向量库弱千万级数据之后会有明显瓶颈Elasticsearch如果检索侧还要做大量全文检索ES天然合适同时也可以存向量。但是ES的运维成本同样不低内存消耗大我们这个规模单机完全够用所以选了Qdrant。原因有三一是部署简单一个镜像起来就能用二是支持payload过滤后面做部门、文档类型过滤很顺手三是它支持内置的BM25全文检索可以和我们用到的向量检索做混合。这一点非常关键因为后面你会发现只用向量检索会漏掉很多精确命中的结果。2.4 模型选型Embedding、Rerank 和生成模型各是各的事这是最容易踩坑的地方。很多人以为RAG只有一个生成模型其实整套链路至少涉及三个模型Embedding模型负责把文本转成向量。中文场景我们选了bge-m3它同时支持中英文并且支持稀疏向量和稠密向量混合检索对长文档也有比较好的支持一个模型就够用Rerank模型检索出Top K个片段后用一个更精细的模型做二次排序把最相关的排最前。选了bge-reranker-v2-m3生成模型也就是最终回答用户的大模型。我们内部有多套可选优先用 Qwen2.5-14B-Instruct 的量化版本主要考虑是中文能力扎实、上下文长度够用、单卡可部署模型这块牵扯到显存和推理性能后面专门用一章说。这里只强调一点Embedding模型的选择直接决定了检索的上限。如果Embedding模型本身分不清语义后面Rerank和LLM再强也无力回天。拿中文场景来说千万别直接用开源的英文Embedding模型比如原版E5效果会非常拉胯也不要选那种只优化了“句子相似度”的小模型企业文档动辄几百字一个段落语义复杂模型容量太小完全吃不消。2.5 开源知识库项目要不要直接用调研过程中也看了不少开源知识库项目比如Dify、FastGPT、MaxKB这些。它们的共同点是界面做得漂亮通常指那种开箱即用的Web端、流程编排能力强、对非技术用户友好。但落到我们这种私有化、后续要二次开发的场景发现普遍有这几个问题项目体积大依赖多很多默认配置是为公网SaaS设计的内置的Prompt编排、知识库分段逻辑是“通用”的碰到企业文档的复杂性不一定够用如果要做深度定制要啃完整套前后端代码改造时间可能比从零写还长不是说这些项目不好如果你团队只有两三个人、对检索质量要求不高、想快速出个Demo它们绝对是好选择。但我要做的是可以持续演进、所有逻辑都可控的系统所以最终决定在它们的设计思路基础上自己搭一套精简版。 Dify的流水线设计值得参考但代码不会直接用。3. 架构分层从数据流入到答案输出每一层在干什么选型定了第二天开始画架构。整个系统按数据流的方向拆成五层接入层、处理层、存储层、检索层、生成层。每层职责尽量单一层与层之间通过明确的接口交互。3.1 接入层管好“文档从哪里来”接入层负责对接内部各套系统的文档。我们这个环境里主要有三个来源内部Wiki/语雀通过导出API定时拉取Markdown内容共享盘存放历史遗留的Word、PPT、PDF文件写了个脚本监控目录变化新增文件自动进入处理流程企微群文件团队讨论产生的临时文档由管理员主动上传接入层做了个统一入口不关心文档来源只把文档转成标准格式文本基础元数据后丢进处理队列。提示这一步千万别忽略元数据。文档的“部门”“密级”“更新时间”“作者”这些信息在检索过滤阶段和权限控制阶段非常重要。先想好要哪些字段后面补起来远比一开始定义好要麻烦。3.2 处理层文档解析、清洗、切片、向量化所有文档进来后进入处理管线。这一层是RAG搭好之后90%的调优空间所在具体操作放到第四章讲。这里只说明它在架构里的位置——处理层输出的产物有三样切片后的文本块每个文本块的向量每个文本块的元数据这三样同步写入存储层其中向量和元数据进Qdrant原始切片和文档可回溯信息还要保留一份在PostgreSQL里便于人工审计和原文对照。3.3 存储层向量库与结构化库的配合存储层我们用了两套数据库。Qdrant存向量和payload过滤字段PostgreSQL存文档元信息、切片原文、对话历史以及索引状态。有人问为什么不都用Qdrant原因是Qdrant定位是向量检索事务性不强对话历史、文件管理这类结构化数据用关系库更顺手查询也灵活。两个库通过文档ID关联删除某份文档时先删PostgreSQL记录再根据文档ID删Qdrant里的向量避免出现“文档没了但还能被检索到”的脏数据。3.4 检索层召回、过滤、排序一气呵成检索层是用户提问后的第一站。流程是对用户问题做向量化得到query向量同时提取用户问题的关键词走一遍BM25全文检索两者结果在Qdrant内做混合召回返回Top 20候选按部门、文档类型、密级做payload过滤把候选片段交给Rerank模型二次排序取Top 5检索层的接口设计的核心是接收用户原始问题 上下文过滤条件返回排序后的文本块列表。这个层不关心大模型怎么用这些片段逻辑上完全解耦后面做A/B测试也方便——我可以只换检索策略不动生成层代码。3.5 生成层Prompt编排与流式输出最后是生成层。从检索层拿到Top 5片段后组装成限定格式的Prompt带上用户问题调推理模型。这里有几个设计细节Prompt里明确要求模型只能基于给定片段回答片段不足时直接说明“知识库中没有找到相关信息”回答需要标注引用来源编号生成的答案要求流式输出前端逐字显示体感上比等待一整个响应好太多生成过程全程记录输入和输出日志方便事后分析“这次回答为什么不准”整条链路用消息队列解耦接入层产生“待处理文档”处理层消费检索层和生成层则是同步调用用一个简单的编排服务串起来。之所以检索和生成不放异步队列是这两个环节用户有实时等待体感串成同步链路在同一HTTP请求里完成才能给前端提供更好的交互体验。4. 数据切分与向量化80%的工程质量问题都出在这一步如果你搭建RAG只打算听一句建议那应该记住这句检索质量的上限永远取决于你的数据切分和索引策略而不是你选的大模型。切片切得好三流模型也能给出合格答案切片切得烂满血版旗舰模型也会一本正经胡说八道。4.1 为什么“切片”是个真问题而不是配置项大模型上下文窗口有限不可能把整本《产品操作手册》一次性塞进Prompt。RAG的做法是把长文档切开检索时只取最相关的几个片段。听起来简单但切片的粒度、边界、重叠策略直接决定了检索时“能不能命中正确答案”。想象一个场景文档里讲“A接口的鉴权参数”前两页是接口说明后三页是调用示例和错误码。如果你按固定500字符切块很可能把“鉴权参数”的解释切在上面一块把“调用示例”切在下面一块中间夹着另一个主题的内容。用户问“A接口怎么鉴权”检索出来的第1块有参数名但没说明第3块有说明但没上下文模型一拼就拼出个四不像。更麻烦的是表格和代码块。Word文档里的表格一列一行直接按行切成文本表格就碎成一个个孤立的短语语义全丢。PPT里每一页的文本框顺序也不一定是阅读顺序切出来经常是乱序的。4.2 切片策略从固定长度到语义切片的升级过程我们切片的演进分了三个阶段第一阶段固定长度切块。按512字符硬切重叠128字符。简单但问题非常多标题和正文被切开表格被拆碎代码块从中间断开导致代码语义缺失。这个版本大概花了一天时间测试检索命中率只在60%出头。第二阶段结构感知切块。根据文件类型做不同处理对Markdown和HTML按标题层级H1/H2/H3来切标题和它下面的内容作为一个语义块对Word先解析段落样式Heading、正文等结合段落PIL映射做切分对PDF优先尝试保留原有版面结构标题、段落、表格分区域处理对PPT以单页为切块单位页内按阅读顺序拼接同时加入“重叠”策略相邻两个块之间保留约10%的内容重叠防止硬边界把关键语句切断。这一版命中率提升到75%左右。第三阶段父子切块Parent-Child Chunking。这是我们最终沿用的方案。核心思想是小片段负责精确匹配大片段负责上下文补充。具体做法是先用较大单元比如一个标题下的整个章节作为“父块”再在这个父块内部切出更小的“子块”。检索时用子块向量去匹配用户问题找到最相关的子块后Prompt里带上它所属的父块一起给模型。这样既保证检索精度高小片段语义聚焦又保证模型有足够上下文来理解内容大片段提供背景。效果立竿见影命中率直接跳到接近90%。4.3 Embedding模型的这类细节很多人完全没注意Embedding模型看似只有一个接口调用“传文本、拿向量”实际上有大量影响效果的细节输入长度限制bge-m3支持单次最长8192 token但并不意味着切块越大越好。太长的文本会让Embedding被无关内容稀释检索精度反而下降。我们最终把子块控制在300~500字父块控制在800~1500字是否加提示词Instructionbge系列在查询侧加了“为检索生成表示”这类指令后效果往往更好。不同模型有不同的推荐用法需要去官方文档确认千万别拿过来就裸调不要在文档块里混语言中英文混杂的技术文档是常态但Embedding模型对跨语言的语义匹配能力有限能用M3这种多语言模型就尽量不用单语模型归一化向量归一化影响余弦相似度计算的正确性最好在代码里显式做L2归一化不要依赖底层框架的默认行为4.4 元数据设计从“能检索”到“好用的检索”的分水岭数据切完紧跟着就是元数据。我们设计了一套最小可用的元数据方案doc_id文档唯一标识chunk_id切片唯一标识title文档标题url源文档链接用于引用溯源dept所属部门doc_type文档类型体系、方案、工单等last_modified最后修改时间这套元数据有三个作用一是做权限和范围过滤某些部门的同事只能检索本部门的文档二是做来源展示答案下面附引用链接三是做更新管理按时间戳识别哪些文档需要重新处理和重新索引。4.5 处理管线的工程化细节处理层最容易被忽视的是工程化问题。我们的处理队列里跑着这样几个环节先用pypandoc把Word和部分PDF转成Markdown再用beautifulsoup清洗HTML标签残留扫描版PDF先走paddleocr做OCR识别识别结果保留版面顺序对代码块和表格做特殊标记切块时不打断它们全部文本做一轮清理去页眉页脚、去无意义空行、压缩多余空格、统一换行符和编码这个环节的难点不在“能做”而在于“能稳定地处理各种意外”。我们遇到的意外包括PDF里嵌字体导致转Markdown乱码、Word文档里嵌套表格导致结构解析错误、扫描件识别后将“l”和“1”混淆——这些都是实际存在的真实问题。为应对这些问题需要把文档解析包装成幂等操作任何一步失败都能把该文档标记为处理失败并重新排队并记录失败原因。提示企业文档里PDF的比例往往不低而PDF这块水最深。有一类PDF是Word直接另存生成的文本层比较完整简单的PDF解析器就能处理另一类是印刷品扫描件需要OCR。最好在接入层就先做PDF类型识别分两条管线走不要混在一起。4.6 索引更新的思路全量重建还是增量更新上线的第一版我们直接全量跑了批处理把两万份文档全部切片向量化耗时约10个小时。后续每天凌晨做增量更新只处理当天变更的文档。增量更新的核心在于“如何识别变化”——通过文件修改时间戳、Wiki API的更新时间字段来做。如果数据源不提供更新时间信息最笨但也最可靠的办法是定期全量扫描比对哈希值然后重新索引变更部分。还有一个细节文档在Qdrant里的索引状态要与PostgreSQL保持一致。我们给每份文档记录一个index_status字段取值有pending待处理、indexed已索引、failed处理失败、deleted待删除。任何一步失败巡检脚本都会自动扫描这个字段并重新触发处理不会静默丢文档。5. 混合检索与重排序让答案“找得对”而不是“找得多”最早一版的检索只有向量相似度这一个维度。实测发现向量检索在处理同义词、语义相近的表达上确实好但在精确匹配场景下会翻车。举两个真实例子用户问“HTTP状态码503怎么处理”向量检索把“503”当成普通语义词召回一堆关于“HTTP错误”的泛泛内容精确的“503 Service Unavailable 处理手册”反而排到后面用户问“密码重置后仍无法登录”片段里明明有一句话原样写着“密码重置后仍无法登录”但向量相似度没有把原文完全命中放在最高的优先级这时候就能明显感觉到BM25全文检索的价值。BM25是经典的关键词匹配算法它对精确词项和稀有词项的打分非常敏感能快速锁定包含“503”“密码重置”这些原词的内容。但BM25对同义词匹配几乎无能为力用户说“登录”而文档写“认证”它就傻眼了。所以正确的做法不是二选一而是把两者结果合并起来。合并策略我们试过几种简单取并集去重后全部交给Rerank模型费用较高但效果最稳对两条结果分别设阈值按分排序取前若干后合并加权融合RRFReciprocal Rank Fusion对每个结果在两条链路里的排名取倒数求和综合排序我们最终用了RRF阈值结合向量检索取Top 20BM25取Top 20RRF融合后截断Top 20再统一交给Rerank模型排序取Top 5。这个方案对“原始词精确匹配”和“语义近似匹配”都有兼顾。Rerank这个环节很多入门教程会忽略但它对最终效果的影响非常显著。直接上公式解释一下向量检索阶段用的是粗粒度匹配在几万个片段里快速筛掉不相关的而Rerank是精排它把召回来的几十个片段放到一个更强的模型里逐一计算与用户问题的相关性分数再从头排序。说得直白一点向量检索负责“别漏”Rerank负责“别错”。我们的线上实验数据是没有Rerank时Top 3片段完全命中用户问题核心意图的比例大约72%加上Rerank之后这个数字提升到了接近93%。提升幅度这么大是因为向量检索阶段许多“语义相近但关键信息不一致”的片段被召回了而Rerank能把它们精准地拉下去。Rerank的部署相对简单它是纯推理任务不需要生成能力显存占用比生成模型小很多。我们用的是一个单独的服务部署通过gRPC与主服务通信超时设了500毫秒。这里要注意用户请求的并发量直接决定Rerank服务的负载最好做异步批处理把一批请求聚合起来一次推理能显著提升吞吐量。注意混合检索有一个常见的坑——不同检索路返回的分数scale不一样。向量相似度可能是0~1的浮点BM25可能是几甚至几百的总分。如果直接比较或混合排序必然被BM25主导。所以要么先做归一化再融合要么直接用基于排名的RRF方法绕开分数尺度问题。我们直接选了后者少了一个“归一化调参”的烦恼。6. 推理部署与并发调优私有化环境下最容易被低估的一环选模型和搭架构都还算顺利真正的痛苦是从“Demo能跑”到“多人都能用”这一大步。Demo阶段一个人调用显存不被打满速度也在可接受范围。一旦有三五个同事同时提问问题立刻暴露。6.1 模型部署方式API框架、量化与显存估算生成模型我们开始直接用HuggingFace的transformers库加载很多人会这么干简单是简单但并发一上来就卡死。后来换了vLLM做推理服务框架这个框架的优势在于Continuous Batching机制可以同时处理多个请求而不阻塞并且自带OpenAI兼容的API接口调用方式不用改。模型量化也是一个必然动作。企业内网部署通常没有A100/H100这种显卡预算我们要在一张24GB显存的卡上单机跑14B模型就需要量化。实验对比下来量化方式模型显存占用推理速度对比效果损失FP16原始约29GB超显存无法单卡无AWQ 4bit约10GB快极小GPTQ 4bit约11GB快极小GGUF Q4约9GB中略明显最终选了AWQ 4bit效果上几乎感知不到损失速度也够用。尤其注意GGUF适合Ollama这类轻量工具但在企业级并发场景下明显不如vLLM稳定所以虽然网上Ollama搭RAG的教程很多我们的方案还是用vLLM当推理后端。Embedding和Rerank模型单独部署。Embedding模型用一个独立的FastAPI服务封装加了一层缓存相同文本的向量直接走缓存不重复计算。Rerank服务也独立部署与生成模型的显存互不干扰。总体显存规划生成模型AWQ量化14B约14GBRerank模型约2GBEmbedding模型约2GB其余系统开销约5GB单张24GB显卡刚好放下还留了余量。如果并发再上一个大台阶可以把Rerank模型挪到CPU上跑它计算量相对小给生成模型腾显存。6.2 并发优化与服务治理最开始并发一多vLLM打印一串OOM警告一查是显存碎片和KV cache占用过高。调优方案有几个限制最大并发数vLLM里设置--max-num-seqs默认会激进复用显存限制到合理的值防止OOM流式输出把一次性生成改为逐token返回不仅前端体验好后端显存压力也大幅降低上下文长度限制Prompt里明确截断超长片段不允许无限制拼接防止单请求把KV cache打满请求超时与重试每个环节都设resilience机制——检索连接超时3秒、推理总超时60秒、失败自动降级返回“当前服务繁忙”我们还在网关层做了最简单的并发控制令牌桶限流每个用户30秒内最多发10个请求。当前50人的团队规模下这个配额绰绰有余限流主要是防止有人写脚本批量灌流量把推理服务拖垮。6.3 效果评估别靠感觉建立一套可复现的评测集搭建过程中最容易被忽略的是“怎么判断自己改进了”。只看几个手工测试的案例很难说明问题我把内部高频问题整理成了大约200条小红书形式QA对——每条包含用户可能的提问、对应的知识块、期望答案的要点。每次调完参数就跑一遍这个评测集计算“命中率”检索到正确知识块的比例和“满意度”让内部同学对生成答案打分。这一套评测集是整个项目投入产出比最高的资产之一。没有它每次修改都是玄学有了它每次修改都能量化地看到涨跌。先建立评测集再上线优化建议所有准备做RAG的都这么干。7. 踩坑清单两周时间里最值得记录的十个问题这两周大概记录了三十多个问题篇幅有限挑十个最典型、最反复出现的放在这里每个都是“现象—原因—解决”三段式。7.1 多份相似文档互相干扰答案引用了错误版本公司里经常有《产品手册v1.0》《产品手册v2.0》同时存在检索时不区分版本经常把v1的内容拼进v2的答案里。根本原因是元数据过滤没生效检索层没有按版本条件过滤。解决在切片元数据里增加version字段检索时默认排除“已废弃”版本的文档并在Payload过滤条件里强制执行。同时给每份文档建立了“替代链”新版本上线后自动将旧版本标记为superseded。7.2 上万套提示词测试反馈在知识库里的“最佳实践”反而成了噪音企业Wiki里有很多“如何写请假申请”“如何提交报销”这类行政类内容和我们的技术知识库混在一起。用户问技术问题时这些行政管理内容经常被检索出来当上下文。这类干扰不仅浪费上下文窗口还拉低了答案的准确性。解决在元数据里给每条内容打上category标签检索时指定技术类目优先行政类默认不参与检索。7.3 表格数据结构化后被切碎无法回答问题这是老问题。切片时遇到表格把每行拆成独立的文本块检索到了却没有表头信息模型看不出这一行数字是什么意思。解决对表格做整体块处理先保留整表作为“父块”检索时如果子块是表格行直接返回包含表头的完整小块如果完整表太长就把“表头前五行”作为默认返回范围。7.4 专有名词和英文缩写被错误切分比如“RAG”在Embedding模型里被当成普通英文词和“RAG”相关的中文上下文匹配效果差又比如“K8s”在切块时被拆成“K”和“8s”概率虽然不高但偶尔发生。这些都会直接导致检索不准。解决在切块之前先做一个专有名词保护步骤把“K8s”“QPS”“API”“跨域”等常见术语通过替换占位符的方式保护起来切完再替换回来确保这些词不会被破碎。7.5 Rerank模型分数分布不均误杀大量正确结果给某个文档A评分普遍偏低给文档B评分普遍偏高。原因是不同来源的文档在语言风格、写法结构上差异很大Rerank模型对某种风格的内容会系统性给低分。解决对Rerank分数按数据来源分组再在各自组内做归一化而不是全数据集一个阈值。相当于把不同风格的内容放到各自的赛道上比较。7.6 页面加载慢用户体验差——根因是“顺序调用”最初版接口里检索、Rerank、生成是严格的串行调用一次用户问答总耗时接近8秒。后来发现检索和Rerank可以并行优化把其中一环节改成异步并发整体耗时降到3秒左右。再后来通过预处理提前对热点问题做缓存和流式输出用户在1秒内就能看到首个token。7.7 上下文超长后被模型截断有些文档切片内容太长Prompt拼起来超过了模型上下文窗口。vLLM会直接截断多余部分但截断点恰好落在关键信息中间答案质量大降。解决在拼Prompt前做硬性长度限制用一个tokenizer的encode方法实时计算长度超出则优先裁掉上下文窗口中最靠后的内容同时把“关键要求”放在Prompt前部避免被裁掉。7.8 中文分词语境下的混合检索BM25索引没有中文分词Qdrant内置的全文检索在英文上表现正常但中文默认按字切分导致“知识库”被拆成“知”“识”“库”三个字精确匹配效果大打折扣。解决在Qdrant的全文检索配置里启用自定义分词器支持jieba分词队列并在导入文档时预先分词把分词结果存入文本索引。这项改动让BM25召回质量直接翻倍。7.9 用户问题含“文档中没有”的信息模型强行编答案比如用户问“某个同事的微信号”知识库里根本没有这个信息模型还是凭常识编了一个号码。这是RAG天然的幻觉风险。解决在Prompt里加硬约束——“如果给定片段中没有明确答案请直接回答‘知识库中未找到相关信息’不要编造任何内容”。同时在评分阈值上做兜底如果检索到的片段与问题相关度低于阈值直接拒绝回答而非调用模型生成。这套组合拳把幻觉率从肉眼可见降到基本不再出现。7.10 部署环境差异导致模式重训练、迁移头大开发环境是Python 3.11 CUDA 12生产环境的GPU驱动版本较老Ubuntu系统只支持CUDA 11.8导致vLLM版本只能降级某些功能丢失。解决用Docker镜像把整个推理环境固定下来——CUDA运行时、Python依赖、vLLM版本全部打进镜像生产环境只依赖Docker引擎和GPU驱动。同时给每台推理机装了NVIDIA Container Toolkit让容器能直接使用GPU。8. 最后想说的几点实际心得两周时间从零搭起来一套能用的私有化RAG知识库确实做到了。但真正想提醒准备入坑的朋友的是“能用的知识库”和“好用的知识库”之间隔着一次次的细节调优。能用的标准是多文档检索、模型回复、引用来源——这些一两周就能实现好用的标准是“被问的次数越多越准”“切换文档版本不混乱”“规则权限不错放”——这些需要更多耐心和更完善的评测集来打磨。我的实操体会是如果重新做一遍我会把更多时间花在数据管线和评测集上而不是模型选型和模型效果上。模型是相对标准化的产品而企业文档的复杂度、脏乱程度才是真正拉开差距的地方。最后再分享一个小技巧日志里一定要记录“用户实际接受了哪个答案”。很多人搭完就完完全不看用户重新提出了哪些问题、点击了哪条引用。我们上线后每天拉对话日志整理“没有命中知识库的提问”清单这些就是知识库边界的真实反馈。持续把它们补充到文档里或者优化检索策略知识库的使用效果会以惊人的速度上升。