
1. 为什么我要用大模型搭一个答疑机器人先说结论我搭这个答疑机器人核心诉求就三个字——省人力。团队里每天有大量重复问题产品怎么用、接口怎么调、报错怎么解翻来覆去就那些。之前靠人工在群里轮值回答一个人一天至少搭进去两小时还经常答得不一致。用大模型加一层知识库和 Agent 逻辑来做能把这部分重复劳动接过去人只需要处理真正复杂的边角问题。这个答疑机器人本质上是一个LLM 驱动的 Agent 应用底层接大模型中间挂知识库也就是常说的 llm wiki 知识库上层用 Agent 框架编排检索—推理—回答的流程输出侧用流式输出SSE把回答实时渲染出来让用户感觉像在跟真人打字聊天。它解决的问题很明确把散落在文档、历史问答、工单里的知识变成一个随时能问、答得靠谱的对话入口。适合谁参考如果你是会写点后端代码、想给自己项目或团队加一个智能问答入口的开发者这篇可以直接抄作业。如果你只是想了解 Agent 和 LLM 应用大概怎么落地也能从里面看到完整的思路和踩坑记录。我不打算写成教科书就按我自己搭这套东西的真实顺序来讲包括选型时纠结了什么、哪些地方返工了、哪些参数是拍脑袋定的后来又改的。需要提前说明的是答疑机器人和通用聊天机器人最大的区别在于**答得对比答得花哨重要得多**。通用聊天可以天马行空答疑不行答错一个接口参数用户照着做就出事故。所以整套设计里我把大量精力放在了知识召回准确性和回答可控性上而不是让模型自由发挥。下面从整体设计开始拆。2. 整体架构设计与技术选型思路2.1 答疑机器人的四层结构拆解我把整个系统拆成四层这样每层职责清晰出问题也好定位。第一层是接入层负责接收用户提问、维持会话、把流式结果推回前端。这一层用 SSEServer-Sent Events做流式输出因为答疑场景大多是纯文本SSE 比 WebSocket 更轻浏览器原生支持 EventSource前端几乎零成本接入。第二层是编排层也就是 Agent 的核心。它决定这次提问要不要查知识库、查几轮、要不要调用工具比如查订单状态、查接口文档版本最后把上下文拼好交给模型。这一层是答疑机器人跟直接调 API最大的区别所在。第三层是知识与模型层包含向量库、知识切片、大模型本身。知识库负责记得住模型负责说得清。第四层是数据与运维层包括对话日志、召回命中率统计、badcase 回收。这层最容易被忽略但它是机器人能不能越用越准的关键。提示四层不是必须严格物理隔离小项目完全可以塞进一个服务里但逻辑上一定要分清楚否则后期加功能会互相打架。2.2 为什么选 Agent 而不是简单 RAG很多人第一反应是答疑嘛RAG 就够了检索加生成。我一开始也这么想但实际跑下来发现纯 RAG 有几个硬伤。纯 RAG 是一问一检索一回答的单轮模式。可真实提问经常是模糊的比如那个接口报 401 怎么办它既没说是哪个接口也没说是什么场景。纯 RAG 会把401直接拿去检索召回一堆不相关的东西。而 Agent 可以先做一步意图澄清或查询改写先判断这是认证类问题再把查询改写成接口认证失败 401 排查召回质量立刻不一样。再比如多跳问题A 功能的配置项在哪改完要不要重启。这需要先查 A 功能配置文档再查重启策略两次检索的结果拼起来才能答。Agent 能编排这种多步流程纯 RAG 做不到。所以我的选型结论是用 Agent 框架做编排把 RAG 当作 Agent 的一个工具。Agent 负责决策要不要查、查什么、查几次RAG 负责具体召回。这样既保留了 RAG 的准确性又有了 Agent 的灵活性。2.3 大模型选型能力、成本、延迟的三角权衡选模型这块我踩过坑值得单独说。答疑场景对模型的要求排序是指令遵循 知识准确性 表达流畅度 创造力。创造力在这里几乎没用甚至有害。我对比过几类模型。通用大模型能力强、指令遵循好但成本和延迟偏高适合做复杂推理和最终生成。轻量模型便宜、快适合做意图分类、查询改写这类小判断。所以最后我用的是大小模型配合小模型做路由和改写大模型做最终回答生成。具体怎么定我列了个简单的评估维度表你可以照着给自己的场景打分。维度权重说明我的取值参考指令遵循高能否严格按格式、按边界回答必须支持 system 角色强约束知识准确性高结合知识库后的事实正确率靠知识库兜底模型别乱编首字延迟中影响流式体验首 token 控制在 1.5s 内单次成本中决定能不能长期跑按日调用量算总账上下文长度中决定能塞多少知识至少 8K最好 32K 以上注意不要一上来就上最强的模型。先用中等模型跑通全流程把知识库和 Agent 逻辑调好最后再决定要不要换更强的模型。我见过太多人卡在选型上结果流程根本没跑起来。2.4 流式输出为什么是体验的分水岭答疑机器人如果等模型全部生成完再一次性返回用户要盯着空白屏幕等三五秒体感极差。流式输出把回答一个字一个字推出来首字延迟可能只有几百毫秒用户立刻知道它在答了。技术上SSE 是最省事的选择。服务端保持一个长连接模型每吐出一个 token 就通过data:事件推给前端前端用 EventSource 监听收到就追加到消息气泡里。整个过程不需要前端做轮询也不需要 WebSocket 那样的双向握手。但流式输出有个副作用一旦开始推就没法回头改。如果模型前面答错了后面想纠正也来不及。所以我在流式之前加了一道预检——先用小模型判断这个问题该不该答、要不要走知识库确认没问题再开流。这个细节后面会展开。3. 核心细节解析与实操要点3.1 知识库切片答疑准确率的命门知识库这块切片策略直接决定召回质量。我一开始图省事按固定字数切500 字一段结果召回经常断章取义——一个完整的操作步骤被切成两半模型只拿到后半段答出来的步骤缺了第一步。后来改成按语义结构切优先按标题层级切一个三级标题下的内容作为一段如果某段太长再按段落切代码块和表格尽量不拆开。这样每段都是语义完整的召回后模型能直接读懂。切片长度我最后定在300 到 800 字之间。太短信息不全太长会稀释相关性、还占上下文。这个区间是实测出来的300 字以下经常缺上下文800 字以上召回精度明显下降。还有一个容易忽略的点给每段加元数据。我在每段前面附上来源文档名、章节路径、更新时间。这样召回后模型能知道这段来自哪个文档的哪一节回答时可以引用来源用户也更信任。元数据还能用于过滤比如只召回某个产品版本的文档。3.2 检索策略向量、关键词还是混合纯向量检索对语义相似的问题很友好但对精确匹配的词比如错误码、参数名反而不敏感。用户问错误码 E1024向量检索可能召回一堆错误处理的泛泛内容就是找不到 E1024。所以我的方案是混合检索向量检索负责语义召回关键词检索BM25 之类负责精确匹配两路结果用加权融合排序。权重上我让关键词检索在包含明确错误码、参数名、版本号的查询里占更高权重其余情况以向量为主。融合之后还要做一步重排rerank。初筛召回 20 条用重排模型按相关性重新排序取前 3 到 5 条喂给大模型。这一步能显著提升精度因为初筛追求不漏重排追求精准。检索方式擅长短板我的用法向量检索语义相近、口语化提问精确词不敏感主力召回关键词检索错误码、参数名、专有名词语义泛化差补充召回混合重排兼顾两者实现稍复杂最终方案3.3 Agent 编排让机器人学会先想再答Agent 编排是这套系统的灵魂。我给它设计了一个简单的决策流程用提示词工程约束模型的行为。第一步是意图识别判断用户是在提问、在闲聊、还是在反馈问题。闲聊直接走通用回答不查知识库省成本。第二步是查询改写把口语化、模糊的提问改写成适合检索的查询。比如登录不上改写成登录失败 排查 认证。这一步用轻量模型做快且便宜。第三步是检索决策判断需要查几次知识库。简单问题查一次多跳问题查多次每次基于上一次结果决定下一步查什么。第四步是生成与引用把召回的知识和用户问题一起给大模型要求它只基于给定资料回答资料里没有的就说不知道并给出资料来源。这套流程用提示词就能实现不一定非要上复杂的 Agent 框架。我一开始用现成的 Agent 框架后来发现流程其实很固定直接用提示词加几个函数调用反而更可控、更好调试。框架是工具不是目的这点想清楚能省很多事。3.4 提示词工程把边界写死答疑机器人的提示词核心不是让模型答得好而是让模型别乱答。我在 system 提示词里写死了几条硬约束只依据提供的资料回答资料没有的内容明确说暂未收录。涉及操作步骤时必须按资料原文顺序输出不得自行增删步骤。涉及参数、错误码时必须原样引用不得改写。回答末尾附上资料来源的文档名和章节。这几条看起来简单但效果立竿见影。加了资料没有就说不知道之后模型胡编的情况大幅下降。这里的关键是给模型一个明确的退路让它知道说不知道是被允许的而不是硬编一个答案。实操心得提示词里的约束要具体、可验证。回答要准确这种话没用参数必须原样引用才有用。模型对具体规则的遵循度远高于抽象要求。4. 实操过程与核心环节实现4.1 从零搭建环境与依赖准备我用的技术栈是 Python 为主向量库用轻量级的本地方案起步模型通过 API 调用。这样起步快不用一上来就搞复杂的部署。核心依赖大概这几类大模型 SDK、向量库客户端、Web 框架提供 SSE 接口、文本处理库。版本上我建议锁死因为大模型 SDK 更新频繁接口偶尔会变锁版本能避免昨天还好好的今天跑不起来。环境准备好之后先跑一个最小闭环用户提问 → 调模型 → 返回回答。这一步不接知识库、不做流式就是确认模型能通。很多人跳过这步直接上复杂架构结果出问题时分不清是模型的问题还是自己代码的问题。4.2 知识入库把文档变成可检索的切片知识入库分三步解析、切片、向量化。解析阶段要把各种格式的文档Markdown、PDF、网页统一转成纯文本同时保留结构信息标题层级。这一步的坑在于 PDF格式一乱解析出来的文本顺序就错切片也跟着错。我的做法是优先用结构化的源文档比如 MarkdownPDF 只作为补充。切片阶段按前面说的语义结构切每段附上元数据。这里有个细节切片之间要有重叠。我在相邻切片间保留 50 到 100 字的重叠避免关键信息正好卡在切分点上被割裂。向量化阶段把每段文本转成向量存进向量库。这里要注意向量模型和检索时的查询向量必须用同一个模型否则向量空间不一致检索结果会莫名其妙地差。我一开始换过向量模型但忘了重建索引召回质量直接崩了排查半天才发现。4.3 对话主流程一次完整问答的代码骨架下面是一次完整问答的流程骨架用伪代码表示重点是流程而不是具体语法。def answer(user_query, session): # 1. 意图识别轻量模型 intent classify_intent(user_query) if intent chitchat: return stream_generate(user_query) # 直接生成不检索 # 2. 查询改写 rewritten rewrite_query(user_query) # 3. 混合检索 重排 candidates hybrid_search(rewritten, top_k20) docs rerank(user_query, candidates, top_n4) # 4. 拼上下文 context build_context(docs) # 5. 流式生成 return stream_generate(user_query, context, session)stream_generate里就是 SSE 的核心模型每吐一个 token就通过yield推给前端。前端收到data:事件就追加渲染。整个链路是边生成边推送用户看到的是逐字出现的回答。4.4 SSE 流式输出的实现与中断处理SSE 服务端的关键是设置正确的响应头然后持续推送事件。响应头里Content-Type要是text/event-stream并且要关闭缓冲否则内容会被攒着一起发流式就失效了。前端用 EventSource 监听收到消息就更新界面。这里有个体验细节流式过程中要允许用户中断。用户看到答偏了想停下来重新问得有个停止按钮。实现上就是前端关闭 EventSource 连接服务端检测到连接断开后停止生成避免白白消耗 token。中断处理还有个坑如果服务端还在生成但连接断了要确保资源被正确释放否则并发一高就会堆积僵尸任务。我的做法是在生成循环里定期检查连接状态断了就 break。注意流式输出和预检要配合好。预检阶段意图识别、检索是同步的会有一点延迟但通常几百毫秒内能完成。如果预检太慢用户会感觉点了没反应所以预检用的模型一定要轻。4.5 参数计算上下文预算怎么分配上下文窗口是有限资源得精打细算。假设模型上下文是 8K token我的分配是这样的用途预算token说明system 提示词500固定约束和角色设定历史对话1500保留最近几轮超出就截断召回知识30004 段每段约 750用户当前问题300一般够用回答预留2000给生成留空间缓冲700应对估算误差这个分配不是死的得根据实际调整。如果发现回答经常被截断就压缩历史对话如果发现召回知识不够用就减少历史、增加知识预算。核心原则是优先保证知识召回和回答空间历史对话可以适当牺牲。token 估算上中文大致按1 个字约 1.5 个 token粗算英文按1 个词约 1.3 个 token。不用太精确留足缓冲就行。5. 常见问题与排查技巧实录5.1 回答不准从召回和提示词两头查回答不准是最常见的问题排查要分两头。先看召回对不对把这次问答召回的知识片段打出来看如果召回的内容本身就答非所问那是检索的问题得调切片或检索策略。如果召回对了但回答还是错那是生成的问题得调提示词。我遇到过一次典型情况召回的知识完全正确但模型答的时候自作主张补充了资料里没有的步骤。这就是提示词约束不够加了不得自行增删步骤之后就好了。所以排查顺序永远是先看召回再看生成别一上来就怀疑模型不行。5.2 流式中断与超时连接层的坑流式输出最常见的故障是推到一半断了。原因通常有几类模型 API 超时、网络抖动、服务端缓冲没关、前端连接被浏览器回收。排查时先看服务端日志确认是模型侧断了还是推送侧断了。如果是模型侧加超时重试如果是推送侧检查响应头有没有正确关闭缓冲。浏览器对空闲连接有回收机制如果两次推送间隔太久连接可能被断所以生成要尽量连续别在中间做耗时操作。5.3 幻觉与越界回答给模型划死红线答疑机器人最怕的就是一本正经地胡说。防幻觉的核心手段有三个一是提示词里明确资料没有就说不知道二是召回时提高精度宁可少召回也别召回错的三是回答里强制附来源让用户能自己核对。我还会定期做badcase 回收把答错的案例收集起来看是知识库缺内容、还是检索没召回、还是模型乱答分别处理。知识库缺内容就补文档检索问题就调策略模型问题就调提示词。这个闭环跑起来机器人会越用越准。5.4 常见问题速查表现象可能原因排查方向解决手段回答答非所问召回不准打印召回片段调切片、改检索策略回答缺步骤切片割裂检查切片边界按语义切、加重叠模型胡编提示词约束弱看是否越界加不知道退路、强制引用流式中断连接或超时看服务端日志关缓冲、加重试首字很慢预检太重计时各阶段换轻量模型做预检成本偏高大模型调用过多统计调用量小模型做路由、缓存结果5.5 几个我踩过的坑第一个坑是向量模型换了没重建索引召回质量断崖式下跌排查了大半天。教训是向量模型和索引必须绑定换模型必须重建。第二个坑是切片太长一段塞了两千字召回后模型抓不住重点回答很泛。改成 300 到 800 字后明显改善。第三个坑是没做预检直接开流结果模型答到一半发现方向错了但已经推给用户了只能尴尬地让用户重新问。加了预检之后方向不对的问题在开流前就被拦下了。第四个坑是历史对话无限累积聊得越久上下文越满最后把知识召回的空间挤没了回答质量反而下降。后来改成只保留最近几轮并做摘要压缩。6. 让答疑机器人越用越准的迭代思路搭起来只是第一步真正决定它好不好用的是后续迭代。我现在的做法是每周看一次对话日志挑出答得不好的案例归类到知识缺失检索失败生成越界三类分别处理。知识缺失就补文档检索失败就调策略生成越界就改提示词。还有一个我觉得很值的方向是把高频问题沉淀成标准问答对。有些问题被问了几十次与其每次让模型现场生成不如直接维护一个标准答案命中就直接返回又快又准。模型只处理那些没见过的、需要推理的问题。这样既省成本又保证了高频问题的稳定性。另外知识库的更新要跟上产品迭代。文档改了但知识库没更新机器人就会答旧版本的内容这比不答还危险。我的做法是把知识库更新接进文档发布的流程里文档一改就触发重新切片和向量化保证知识库和文档同步。这套东西我从零搭到能用前后大概花了两三周其中一半时间花在调切片和检索上。模型和框架反而是最省心的部分因为现成方案足够成熟。如果你也想搭一个我的建议是先把知识库和检索调好再考虑模型和 Agent 的花活因为答疑机器人的上限取决于它能不能找到对的知识而不是模型有多聪明。