做了将近半年的基于大模型的智能问答系统上线之后我最大的感受是**决定这套系统好不好用的往往不是大模型本身而是知识库和检索链路。**模型答得再聪明检索不到对的资料它也只能一本正经地编。我们做的不是那种聊天玩具而是一个企业内部的知识问答助手员工上传制度文档、操作手册、项目总结然后像跟人聊天一样提问系统给出答案并标注出处。这套系统从需求梳理到上线迭代我踩了很多坑也总结出一些真正能落地的经验。这篇文章想把完整的实现过程、选型逻辑和实战心得写下来。如果你正准备做一个RAG形态的问答系统或者已经在做了但效果不太理想应该能从这里找到一些可以复用的思路。1. 为什么从零做一套企业级问答系统而不是直接用现成产品1.1 最初的需求清单和隐含约束需求方最开始提得很简单用大模型做一个问答系统让我们查资料不用翻文件夹。但把这句话翻译成工程需求之后就没那么简单了。我们实际拆出来的需求是这样的文档问答主要覆盖制度文件、技术手册、项目交接文档、会议纪要等非结构化资料多轮对话用户能接着上一轮问题追问比如先问报销周期多久再问那发票呢系统要能理解那指的是上一轮报销流程里的发票要求引用溯源每个答复必须标出依据来自哪篇文档方便用户点开原文核对这也是内部信任感的关键权限收敛不同角色看到的知识范围不同项目组资料不能泄露给无关部门这个需求直接决定了检索阶段就要做权限过滤而不是生成之后再做数据安全约束内部资料不允许直接传到外部服务系统必须在可控环境内部署或至少确保传输和存储过程可控。这里最容易被忽略的是权限约束。权限听起来像个管理功能放在最后做也无所谓但实际它影响了整个架构如果你打算把问题和文档直接拼成提示词发给外部大模型接口那就必须确认请求链路里是否有可能夹带越权内容。我们最终把权限过滤做在检索阶段靠知识库元数据把用户和文档隔离这样模型拿到的材料本身就是该用户有权限看的那一坨。1.2 现成产品差在哪我们也认真评估过直接买这个选项。通用型问答产品的问题在于它们不了解企业内部的文档体系和人员权限而且很多产品只支持把文档往里一丢然后全网搜缺乏精细的引用管理。商用RAG平台虽然省事但当我们提出文档要按部门隔离引用格式要按我们内部模板输出检索日志要留痕这些要求时平台的定制能力往往跟不上或者费用直接翻了几倍。自己搭一套系统核心成本不在写代码而在于把文档处理、检索、模型编排、权限、评测这几条链路串起来并持续调优。但换来的是完全可控想改提示词模板就改想加一个重排阶段就加出了badcase能顺着日志一路查到是切分问题还是检索问题。对一个要长期维护的内部系统来说这个可控性值得投入。2. 架构定调数据管道比模型更值得先设计2.1 四条核心链路谁都绕不开很多关于大模型应用的分享会把重点放在选哪个模型、怎么调提示词上但真正从零做一套可用系统的时候我建议先把下面四条链路在纸上画清楚离线知识库构建链路文档上传 → 格式解析 → 内容清洗 → 结构切分 → 向量化 → 写入索引。这条链路是离线的文档更新时触发可以慢慢跑但每一步的错误都会放大到线上。在线问答链路用户问题 → 权限获取 → 意图识别是否需要改写/是否需要检索 → 检索召回 → 融合排序 → 可选重排 → 组装上下文 → 大模型生成 → 引用标注 → 流式返回。这条链路是用户实际感受到的路径每一跳的延迟和失败都会直接影响体验。管理运维链路知识库的版本管理、文档上下线、权限配置、模型服务健康检查、日志追踪。这块经常被忽略但实际运维中80%的问题排查都靠它。评测迭代链路评测集维护 → 批量跑分 → badcase聚类 → 定位根因 → 修改策略 → 回归验证。没有这条链路系统上线就是感觉还行改不改全靠运气。我把这四条链路列出来之后团队里所有人都达成了共识模型API只是在线链路里的一个组件前面还有一大段检索逻辑决定了模型能看到什么材料。2.2 分层设计小模型管理解大模型管生成我们的架构明确做了三层分离应用层负责权限、会话、知识库管理、日志是纯业务代码检索层负责QA对问题的理解、召回、排序、融合这里用了开源向量索引和关键词索引再加一个轻量级重排模型生成层负责最终答案的生成这一层交给大模型API并且做了主备切换和降级。一个小巧但重要的设计原则是**能用规则或小模型解决的问题绝不让大模型上。**比如是否需要对当前问题进行改写我们先用规则判断当前问题是不是追问比如判断检索材料里有没有包含答案这本来可以靠大模型判断但我们用召回分数和关键词覆盖做一个快速前置判断只有拿不准的时候才让大模型多看一遍。这样做的原因是成本和延迟企业内部问答的用户耐心很有限如果每一次对话都走完整的重模型链首字返回时间会慢到让人失去兴趣。3. 文档切分与向量化问答效果的第一道分水岭3.1 切分策略从固定窗口到结构切分我们第一批文档入库时采用的是最省事的固定大小切块每块500字重叠100字。当时想着反正有向量检索兜底怎么切都行。结果上线第一天就被用户问了一个制度里规定的报销流程有几步系统答得驴唇不对马嘴。我排查发现文档里第一步、第二步这样的流程内容刚好被切块拦腰截断第一块只给了第一步、第二步的标题第二块才是内容而向量检索把这两块都召回了但模型拼上下文的时候顺序错乱自然答不对。这次事故之后我们把切分策略改成了结构感知切分并且在线上对不同类型的文档配置不同参数策略做法适用场景我们踩过的坑固定大小切分按字数切带重叠窗口连续文本、对话记录结构信息被切断流程类内容错乱结构切分按Markdown/Word标题层级划分表格单独成块制度文档、技术手册同一章节过长时仍需要二次切分父块-子块检索命中子块生成时携带父块上下文合同条款、规范条文上下文超长需要控制携带范围结构切分的核心是让切出来的块尽量语义完整。比如一篇技术手册按章节→小节→段落的层级切每个块自带标题路径元数据。这样检索到某一小节时模型能看到它属于哪个章节上下文信息充足得多。至于那些没有明显结构的长文再退回到固定窗口切但窗口大小按文档类型调整操作流程类用较小的窗口400字左右背景介绍类用较大的窗口800字左右。3.2 Embedding模型选型关注的几个维度Embedding模型直接把文本映射成向量它的质量决定了向量召回的天花板。我们不追最新榜单只按实际场景评估。几个关键参考维度中文语义效果看它在行业术语上的表现而不是通用句子相似度最大输入长度很多模型默认只支持512个token如果你切出来的块超过这个长度就会被截断信息直接丢尾巴向量维度维度越高理论上表达力越强但索引内存和检索耗时也上升内部系统数据量在几十万级时中低维度完全够用部署成本Embedding模型通常不大可以本地部署不依赖外部API这对内部数据安全很重要。我们当时把几个候选模型在内部文档测试集上各跑了一遍做法很简单挑50个真实问答对分别看每个模型召回的块里有没有正确答案所在的原文块。结论是公开榜单上排名最高的模型在我们领域语料上未必最好反而是对长文本支持好、对中文专有名词理解稳的模型更实用。3.3 容易被忽略的元数据设计文档入库时我们会在索引里附上一批元数据字段文档标题、章节路径、更新时间、文档归属部门、权限标签、文档类型。这些字段在检索阶段是过滤利器。举个例子公司里同一份《差旅管理制度》每年更新一次库里同时存在2023版和2024版。如果用户问的是现在出差住宿标准是多少检索阶段除了看语义相似度还要按更新时间排序优先让新版本的块排在前面否则模型很可能引用了过期标准。再比如用户属于研发部门那检索时就先用权限标签过滤掉财务部门的文档块这样模型永远看不到不该看的内容。这里还有一个细节**检索过滤必须在向量召回之前或和向量召回同时生效。**如果先全局召回再在代码里做权限过滤很可能出现已召回但被过滤掉导致可用块不够的情况更严重的是如果日志记录了完整的候选块那日志本身就泄漏了越权内容。我们把权限条件拼进了检索查询让索引层面直接只返回有权限的块干净利落。4. 检索链路从纯向量到混合检索加重排4.1 纯向量检索翻车的真实场景第一版检索我们只用了向量相似度也就是把用户问题和所有文档块都转成向量算余弦相似度取TopK。内部文档测试时发现几个典型的翻车场景精确数值条件入职满一年可以休几天年假文档里写的是累计工作满12个月语义相近但向量召回也许能找到可一旦问题变成预算上限是5000还是3000向量模型对数值的敏感度就很低容易召回一堆提到预算但数值不对的块缩写与同义词研发文档里到处是CI业务人员问的是持续集成向量模型不一定能把缩写和全称关联起来表格语义丢失表格被切成块以后向量检索对表格里的行列关系理解很差经常答非所问。这些问题的共同点在于向量检索擅长语义相似但不擅长精确匹配和关系理解。企业内部问答里恰恰有大量精确匹配和关系型问题。4.2 关键词检索与向量检索的融合我们增加的第一个补丁是引入关键词检索。用经典的BM25算法基于倒排索引做词频统计。它最大的优势是精确匹配能力强5000就是5000CI能先按字面命中文档里的CI持续集成再通过文本里的括号注释兜住缩写问题。混合检索的合并逻辑我们没有用复杂的机器学习排序而是用了最朴素的RRFReciprocal Rank Fusion每个检索通道各自给出TopK排名然后按公式计算每个块的综合得分score Σ (1 / (k rank_i))其中k是平滑常数通常取60。这个公式的思想很简单一个块如果在多个检索通道里都排在靠前位置那它的综合分就高。我们试过加权分数融合和深度学习排序最终还是RRF最稳、最好解释、也最容易调参。融合后的排序仍然不能完全信任因为向量检索和关键词检索各自都有看起来相关但实际不相关的噪音。所以我们把召回数量调大向量召回50个关键词召回50个融合后保留30个候选交给下一步重排。4.3 重排模型值得多花的几十毫秒重排阶段我们用一个轻量级模型对每个候选块重新计算与用户问题的相关度。它和Embedding模型最大区别是输入能同时看到问题和文档块做的不是压缩成向量再比相似度而是类似文本对分类的任务相关性判断更准确。虽然重排给在线链路增加了大约50到80毫秒的延迟但这段时间花得非常值。重排后候选块的相关性明显更集中大模型拿到的上下文噪音少了幻觉率和答非所问的问题都下降了一个量级。如果检索召回Top1本身就是对的重排只是锦上添花但一旦Top1是错的重排能把对的块排上来这个收益立竿见影。5. 大模型接入的工程化细节提示词、流式输出与降级策略5.1 把提示词当成可维护的模板工程很多文章把提示词写得像玄学但实际工程里提示词就是一个需要版本管理的模板。我们最终的提示词结构是五段式你是企业知识助手严格基于提供的材料回答不要编造事实。 【检索材料】 [相关材料1编号[1]] [相关材料2编号[2]] [对话历史] [最近几轮问答] 【用户问题】 [当前问题] 【输出要求】 1. 先用一句话给出结论再给出依据 2. 引用材料时使用[1][2]等编号标注 3. 如果材料无法回答问题明确回答“根据现有资料无法确认”不要猜测 4. 答案控制在300字以内。这里最关键的是第三点和第二点。企业内部问答最怕模型为了显得有用而编造所以我们明确要求模型在材料不足时承认不知道。为了保证引用编号对得上我们在代码里给每块材料编好号再拼进提示词模型只需要在输出里引用编号前端再做高亮就完成了引用溯源。另外提示词版本要和代码版本一样管理。改了一句提示词可能让全站效果变化如果不可回滚线上出问题会很被动。5.2 流式输出不是可选项问答系统最影响体验的指标是首字返回时间。如果用户提问后盯着转圈三四秒才看到第一个字再准的答案也会被打低分。接入流式输出把模型生成的token逐片返回用户在1到2秒内就能看到文字开始滚动体感会好非常多。流式输出用SSE协议做起来并不复杂但有几个细节要处理超时控制大模型API偶发卡顿我们设置了连接超时和首字超时两个阈值超过就自动切换备用模型或降级断线处理用户网络闪断或服务端重启前端要能自动重连并继续接收剩余内容否则用户体验是答到一半没了;后处理流式输出过程中无法后置处理整个答案所以引用标注的渲染要特殊处理我们在流式文本里约定用特殊标记包裹引用编号前端解析后显示为高亮角标。还有一个细节**不要流式输出思维链。**内部问答只需要最终答案和引用让用户看到大模型推理过程反而会影响信任感而且思维链文本还会额外消耗token。5.3 超时、限流和降级的完整预案大模型接口再稳定也扛不住故障。我们设计了一套多级降级方案主模型超时等待回答超过设定阈值→ 自动切换备用模型重试一次备用模型也失败→ 返回引导话术当前服务繁忙请稍后重试同时把问题记录到失败队列方便事后补答限流保护按用户身份分配并发额度普通用户每秒不超过特定请求数防止个别用户刷屏拖垮服务高频问题缓存对于同一个权限组内的高频问题把答案缓存起来按文档更新事件自动失效。这个措施直接把大模型API费用砍掉了将近三分之一。缓存策略有个前提答案要与用户权限绑定。两个不同权限的用户看到的知识范围不同缓存key必须带上权限标识否则就会出现越权缓存泄露。6. 多轮对话的上下文管理别让对话历史拖垮效果6.1 上下文放多少放哪几轮第一版多轮对话我们把最近十轮历史全部拼进提示词结果模型反而开始记性好、答得差上下文太长把检索材料挤得只剩一小段模型注意力分散还容易把历史对话里的错误信息当成事实。后来我们改成了系统提示词 最近3轮对话 当前问题 检索材料的结构结构顺序也有讲究检索材料放最前面靠近系统指令对话历史放中间当前问题放最后。对话历史也不是每轮都要留。如果用户问了一个与历史完全无关的新问题旧历史反而会成为误导。我们做了一个轻量的话题切换检测计算当前问题与上一轮问题的语义相似度如果低于阈值就把历史清空当作全新会话处理。这个逻辑用一个小模型就够但收益非常明显——很多答非所问其实不是模型问题而是历史残留干扰。6.2 追问的指代消解与问题改写多轮对话最典型的场景是追问那它的预算是多少如果直接把这句话拿去检索检索系统根本不知道它指什么。我们加了一层问题改写当判断当前问题是追问时先结合最近几轮历史把问题补全成完整问句再用改写后的问句去检索。改写不一定要用大模型我们用了一个轻量提示词让一个小模型来做模板很简单根据对话历史把用户的追问改写成独立的完整问句不要添加历史里不存在的信息。注意不要添加历史里不存在的信息是关键约束否则改写很容易自己脑补出用户没提过的条件把检索方向带偏。改写之后还有一个细节**改写结果要同时用于检索和生成。**生成时我们不会拿长句原文提示模型而是让模型看到改写后的完整问题原始追问既保证检索精准又保证回答符合用户原本的口语化表达。7. 上线前的评测与迭代没有测试集别上线7.1 评测集怎么搭才靠谱我们刚开始犯的错是自己拍脑袋写测试题测出来效果很好上线后被真实用户问得破绽百出。后来改成从业务侧收集问题样本翻历史工单、看运营群里被问得最多的问题、从常见文档FAQ里提取再让业务方补充它们最关心的场景。评测集规模不大第一批只有80条左右但覆盖了五种类型类型示例问题模式通过标准事实查询型公司的年假政策是什么答案与原文一致并正确引用操作步骤型怎么申请会议室步骤完整、顺序正确对比分析型A方案和B方案有什么区别能分别说明两者要点数值条件型超过5000元的采购需要什么审批数值条件完全精确开放建议型如何提升项目复盘质量有依据、不编造每条评测样本都标注了参考答案和期望引用的源文档。人工标注80条很费时间但这是评测的根基后面自动化打分再快也弥补不了测试集本身是歪的。7.2 自动化评测与badcase的归因路径评测脚本每天跑一遍用一个大模型当裁判模型给系统回答打分维度包括相关度、正确性、引用一致性、是否拒绝回答。裁判模型本身也会偶然判错所以我们只把自动化打分作为初筛分数异常的样本再人工复核。真正让系统快速变好的是badcase的归因流程。拿到一个错误回答我们按下面的路径排查查召回把用户问题重复在离线检索流程里跑一遍看正确文本所在的文档块是否在Top30候选中查排序如果正确的块在候选中但排得靠后说明是排序问题优先调重排或融合逻辑查生成如果正确的块排得靠前但答案还是错了说明是模型生成问题改提示词或补充示例查切分如果正确的块根本不存在或内容残缺说明是离线切分问题回炉改切分参数。这个归因路径的精髓是把错误责任隔离到具体环节而不是笼统地说系统效果不好。我们每周都会把新badcase归类再决定下周改哪一块向量模型要不要换切分参数要不要调提示词要不要补充说明按照这个闭环跑了两个月评测集准确率从70%出头提升到了接近92%。8. 部署运行与成本控制的一笔账8.1 部署形态数据安全与效果之间的平衡考虑到内部数据的敏感性我们把部署形态定为私有化部署检索链路和小模型大模型API只接收脱敏后的必要上下文。具体来说文档向量化和权限过滤全部在内部环境完成只有最终拼好的提示词模板里的检索材料、对话历史、当前问题这些文本会传给大模型API。对话内容可能会被API服务用于改进模型——这是一个需要明示给用户的点我们在系统使用说明里写清楚了这个边界。如果你所在团队的数据安全要求更高也可以选择内部部署开源大模型但推理资源成本会明显上升。我们的取舍是内部部署小模型负责检索和改写生成用商业化API保证效果和稳定性中间再加一层服务端脱敏。这个组合在成本、效果、安全三方面取得了一个平衡。8.2 成本估算别等月底账单吓一跳大模型API按token计费所以估算成本不能只看每问一次多少钱。我们的成本模型是这样的输入token 系统提示词 检索材料拼接 3轮对话历史 当前问题平均一次对话大约消耗多少token根据统计取值输出token 大模型平均回答长度月成本 月问答次数 × (输入token数 × 输入单价 输出token数 × 输出单价) × 冗余系数。举个具体的简化案例。假设每次问答平均输入2000个token输出500个token输入单价0.001元/千token输出单价0.002元/千token那单次成本只有大约3厘钱。听起来很便宜但如果是每天1万次问答月成本就是10000 × 30 × 0.003 900元。再加上缓存命中率的折扣总算下来不至于离谱。可一旦检索材料写得贪多每次塞进一万个token成本就会翻几倍所以提示词里检索材料数量上限也是个成本控制参数。我把各量级成本做个表格方便你建立量感月问答量约输入token/次约输出token/次月成本量级按上述单价推算1千次2000500约9元1万次2000500约90元10万次2000500约900元10万次材料扩到1万token10000500约3000元降低成本的手段除了高频答案缓存之外还有三个控制检索材料长度我们限制最多拼接5个块每块不超过800字对话历史只留最近3轮对超长文档先做一次粗筛再精读而不是把所有候选块都堆给大模型。8.3 日常运维要盯的指标上线之后每天要关心的核心指标包括检索召回率、重排后Top1命中率、首字返回时间、回答失败率、引用被点击率、用户不满意反馈率。前几个是技术指标后两个是体验指标。我们会在日志里给每一次问答生成一个全链路ID从用户提问到最终展示每一步的时间、候选块、改写结果、模型输出全都能串起来debug的时候效率高很多。还有一条血泪教训**检索日志一定要脱敏。**因为日志会记录完整的用户问题和候选文档块如果不做权限过滤和访问控制日志系统本身就会变成一个越权信息泄露点。我们最后把日志按权限分级只有管理员能查看完整链路普通运营只能看统计数字。这套系统在内部跑了大半年说实话它没有一开始想象中那么智能甚至用户夸它的时候我心里清楚大部分功劳要记在把文档切得好、把检索做得准这两件事上。大模型只是最后那个把材料读出来、组织成通顺回答的朗读员。如果让我给后来者一个最实在的建议那就是先别急着换更大更强的模型沉下心把知识库的切分、元数据、检索召回、评测集这四块打到80分然后再回头看大模型你会发现它忽然变聪明了。我踩过的那些坑你大概率也会踩一遍希望这篇分享能让你少绕几段路。