
1. 企业知识库问答 Agent 的整体设计思路1.1 为什么企业需要一个“会查资料”的 Agent企业知识库问答 Agent说白了就是给公司内部那堆散落在 Confluence、飞书文档、钉钉知识库、共享盘 PDF、甚至聊天记录里的资料配一个能听懂人话、能翻箱倒柜找答案、还能把答案组织成一段通顺回复的“数字老员工”。它和普通搜索框最大的区别在于搜索框只给你一堆链接Agent 会先理解你问的是什么再去检索再把检索到的碎片拼成一段有逻辑的回答必要时还会追问你一句“你说的是报销流程还是采购流程”。我做过好几个类似项目最深的体会是企业知识库问答的难点从来不是“模型够不够聪明”而是“资料找得准不准”。模型再强喂给它一堆无关段落它也只能一本正经地胡说。所以这类 Agent 的核心骨架一定是RAG检索增强生成再叠加Agent 的编排能力让它能多轮检索、能调用工具、能判断自己该不该继续查。从热词里也能看出来大家关心的点集中在几个方向RAG 检索增强、向量检索、MCP 协议、Agent 框架、私有化部署、并发扛不扛得住。这些词其实指向同一件事——怎么把“企业资料”和“大模型”安全、准确、稳定地接起来。1.2 方案选型为什么是 RAG Agent MCP 这套组合先拆开说。RAG解决的是“模型不知道你公司内部的事”。企业资料不能全塞进模型训练成本高、更新慢、还容易泄露。RAG 的做法是资料留在你自己的库里用户提问时临时检索相关片段拼进提示词让模型基于这些片段回答。这样资料更新只需重建索引模型本身不用动。Agent解决的是“一次检索不够用”。真实提问往往很绕比如“去年 Q3 华东区退货率最高的三个产品对应的售后政策是什么”。这一句话里包含时间、区域、指标、产品、政策多个维度单次向量检索很难一次命中。Agent 可以拆解问题、多次检索、交叉验证最后汇总。MCPModel Context Protocol解决的是“Agent 怎么标准化地调用外部工具和数据源”。没有 MCP 之前每接一个数据源就要写一套适配代码接十个库就是十套。MCP 把“工具调用”抽象成统一协议检索服务、数据库、工单系统都可以包装成 MCP ServerAgent 按协议调用即可。热词里反复出现 MCP 协议、MCP Server、各种工具桥接说明这已经是当前 Agent 生态的事实标准之一。至于模型选型热词里有人问“llama 适合国内企业拿来搞知识库问答和私有化 agent 部署吗”。我的实际经验是如果对数据出境极度敏感、必须完全私有化开源模型是可行路线但要做好“检索质量补模型能力”的心理准备。模型弱一点就把检索做精、把提示词写死、把答案约束严整体效果依然能打。如果允许调用外部 API那模型能力上限会高很多但资料脱敏和权限控制要额外下功夫。1.3 整体架构分层我把这套系统分成四层从下往上说。第一层是数据层原始文档、数据库、工单系统、Wiki 页面。这一层的关键是“清洗”和“切分”后面会细讲。第二层是检索层向量库 关键词索引 元数据过滤。向量检索负责语义相似关键词检索负责精确匹配比如产品编号、人名两者混合才能兼顾“意思对”和“字面对”。第三层是编排层也就是 Agent 本体。它负责理解问题、决定检索策略、调用 MCP 工具、判断是否需要二次检索、最后生成回答。第四层是接入层Web 页面、企业 IM 机器人、API 接口。用户从哪问Agent 就从哪答。这四层里最容易被低估的是数据层。我见过太多项目模型和框架选得很花哨结果文档切分一塌糊涂检索出来的片段前言不搭后语后面全白搭。所以下一节重点讲数据准备和检索优化。2. 核心细节解析与实操要点2.1 文档切分别让“一段话”被拦腰截断文档切分是 RAG 的地基。切得太碎一个完整意思被拆成好几块检索出来缺胳膊少腿切得太大一块里混了好几个主题向量表示被稀释检索精度下降。我的常用策略是按语义结构切而不是按固定字数切。具体做法Markdown、HTML 这类有标题层级的文档优先按标题切。一个二级标题下的内容作为一个 chunk如果太长再按段落细分。PDF 和 Word 没有清晰结构先按段落切再合并相邻短段落保证每个 chunk 在 300 到 800 字之间。表格单独处理不要把表格拆散整张表作为一个 chunk并在前面加上表头说明。代码块整体保留不要按行切。这里有个细节chunk 之间要留重叠。比如每个 chunk 末尾多带 50 到 100 字到下一个 chunk 开头防止关键信息正好卡在边界上。重叠比例一般设 10% 到 15%太高会冗余太低会丢信息。注意切分参数没有万能值。文档类型不同、语言不同、问题类型不同最优切分都不一样。我的建议是先跑一批真实问题看检索命中率再回头调切分而不是一开始就追求“完美切分”。2.2 向量检索与关键词检索的混合打法纯向量检索有个毛病它对“字面精确匹配”不敏感。比如用户问“X200 型号的保修期”向量检索可能返回一堆讲保修政策的段落但偏偏漏掉那个只提了一句“X200 保修 24 个月”的表格。这时候关键词检索就能补上。我的做法是双路召回 重排向量检索召回 Top 20关键词检索BM25 或类似算法召回 Top 20。两路结果合并去重得到候选集。用一个重排模型Rerank对候选集重新打分取 Top 5 到 Top 8 送给大模型。重排这一步很关键。向量检索的相似度分数和关键词检索的分数不在一个量纲上直接合并会偏袒某一路。重排模型专门做“query 和文档的相关性打分”能把真正相关的排到前面。实测下来加了重排之后答案准确率能提升 20% 到 30%这个投入非常值。关于向量库选型热词里有人提“langchain4j easy rag”“ollama 简易本地 rag 知识库”。如果是小规模验证本地向量库加轻量框架完全够用。但企业级场景要考虑并发、增量更新、权限过滤建议选支持这些特性的向量数据库别为了省事用文件存向量后期迁移很痛苦。2.3 MCP 工具接入让 Agent 能“动手”而不只是“动嘴”MCP 的价值在于标准化。假设你的 Agent 需要查三类东西知识库文档、工单系统、数据库。没有 MCP你要写三套调用逻辑有了 MCP你把这三个都包装成 MCP ServerAgent 通过统一协议调用。一个典型的 MCP Server 暴露的是“工具列表”和“工具调用接口”。比如知识库检索 Server 暴露一个search_knowledge_base工具参数是查询语句和返回条数工单系统 Server 暴露get_ticket_status工具参数是工单号。Agent 在编排时根据问题判断该调哪个工具、传什么参数。这里有个实操心得工具描述要写得像给新人看的说明书。Agent 决定调不调某个工具很大程度上依赖工具的名称和描述。描述写得太抽象Agent 就不知道该用写得太啰嗦又会占用上下文。我的经验是每个工具描述控制在两三句话说清楚“这个工具能干什么、什么时候用、参数是什么”。注意MCP Server 的权限控制要在服务端做不能指望 Agent 自己判断“这个用户能不能看这个数据”。Agent 可能被诱导调用不该调的工具服务端必须有一层鉴权。2.4 提示词约束把“胡说”的空间压到最小RAG 的提示词核心就一句话只根据检索到的内容回答检索不到就说不知道。但光写这一句不够模型还是会忍不住发挥。我的提示词模板一般包含这几块角色设定你是企业知识库助手只回答与公司资料相关的问题。检索内容把 Top 5 片段按相关度排序贴进去每段标注来源。回答要求先给结论再给依据依据必须来自检索内容如果检索内容不足以回答明确说“当前资料未覆盖该问题”。引用格式要求模型在答案里标注来源片段编号方便用户核对。这套约束下来模型胡说的概率会大幅下降。但要注意约束太死会导致“明明检索到了却不敢答”。所以我会在提示词里加一句“如果检索内容部分相关可以基于相关部分回答并说明哪些部分没有覆盖”。3. 实操过程与核心环节实现3.1 环境准备与依赖安装先列一下我常用的一套技术栈以 Python 为例# 创建虚拟环境 python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate # 核心依赖 pip install fastapi uvicorn pip install langchain langchain-community pip install sentence-transformers pip install pymupdf python-docx markdown pip install rank-bm25 pip install chromadb # 本地验证用生产环境换企业级向量库如果走本地模型路线还要装推理框架和模型权重。如果走 API 路线配好对应的 SDK 和密钥即可。MCP 部分需要装 MCP 的 Python SDK用来写 Server 和 Client。提示依赖版本要锁死。RAG 生态更新快今天能跑的代码下周可能因为某个包升级就报错。建议用pip freeze requirements.txt固定版本。3.2 文档入库从原始文件到可检索 chunk这一步我拆成四个小环节。第一格式统一。不管原始是 PDF、Word 还是 HTML先转成纯文本或 Markdown。PDF 用 PyMuPDF 提取Word 用 python-docxHTML 用 BeautifulSoup 去标签。转换时保留标题层级后面切分要用。第二清洗。去掉页眉页脚、页码、重复的空行、乱码字符。这一步看着琐碎但直接影响检索质量。我见过 PDF 提取出来每页都带“内部资料 请勿外传”结果每个 chunk 都带这句话向量表示被污染。第三切分。按前面说的语义切分策略切成 chunk每个 chunk 带上元数据来源文件、标题路径、页码、更新时间。元数据后面做权限过滤和引用展示都要用。第四向量化入库。用 embedding 模型把每个 chunk 转成向量存进向量库。同时把 chunk 原文和元数据存进普通数据库检索时先拿向量 ID再回表取原文。# 切分与入库的简化示例 from langchain.text_splitter import MarkdownHeaderTextSplitter from langchain.embeddings import HuggingFaceEmbeddings from langchain.vectorstores import Chroma headers_to_split_on [ (#, h1), (##, h2), (###, h3), ] splitter MarkdownHeaderTextSplitter(headers_to_split_onheaders_to_split_on) chunks splitter.split_text(markdown_text) embeddings HuggingFaceEmbeddings(model_nameBAAI/bge-large-zh) vectorstore Chroma.from_documents(chunks, embeddings, persist_directory./db)这段代码是验证用的简化版生产环境要加错误处理、批量入库、增量更新逻辑。3.3 检索服务双路召回 重排的完整实现检索服务我一般单独做成一个 HTTP 服务方便 Agent 通过 MCP 调用。核心逻辑def hybrid_search(query, top_k5): # 向量召回 vector_results vectorstore.similarity_search_with_score(query, k20) # 关键词召回 bm25_results bm25_index.search(query, k20) # 合并去重 candidates merge_and_dedup(vector_results, bm25_results) # 重排 reranked rerank_model.rerank(query, candidates) return reranked[:top_k]重排模型可以用开源的 cross-encoder也可以用 API。实测下来重排的延迟大概在几十到几百毫秒对整体响应时间影响可接受但准确率提升明显。这里有个参数要调向量召回和关键词召回的条数。召回太少重排没得选召回太多重排延迟高。我的经验是各召回 20 条合并后 30 到 40 条重排取前 5 到 8 条。这个比例在多数场景下比较平衡。3.4 Agent 编排多轮检索与工具调用的控制流Agent 的核心是一个循环理解问题 → 决定动作 → 执行动作 → 观察结果 → 判断是否继续。用伪代码表示def agent_loop(question, max_turns5): context [] for turn in range(max_turns): action llm_decide_action(question, context) if action.type retrieve: result mcp_client.call(search_knowledge_base, action.params) context.append(result) elif action.type answer: return action.content elif action.type ask_user: return action.content # 反问用户 return 抱歉我暂时无法回答这个问题。关键在llm_decide_action这个函数。它要判断当前信息够不够回答不够的话该查什么是继续检索还是调用别的工具这个判断的质量直接决定 Agent 好不好用。我的经验是给 Agent 设一个最大轮次比如 5 轮。超过就强制输出当前最优答案避免无限循环烧 token。同时要记录每一轮的检索内容最后生成答案时全部带上让模型有足够依据。3.5 并发与性能Agent 怎么扛住多人同时问热词里有人问“ai agent 怎么扛并发”这是企业落地必须面对的问题。Agent 的并发瓶颈通常不在模型推理而在检索和工具调用。我的优化思路检索服务无状态化可以水平扩展。多个检索实例前面挂负载均衡。向量库选支持并发的别用单文件存储。缓存高频问题。企业里很多问题是重复的比如“年假怎么算”“报销标准是什么”。把高频问题的答案缓存起来命中缓存直接返回不走完整 Agent 流程。异步化。检索、重排、模型调用都做成异步避免一个请求阻塞整个线程。限流和降级。并发太高时优先保证检索可用生成部分可以降级为“返回检索片段 简单拼接”。实测下来一套中等配置的服务支撑几十路并发问题不大。再往上就要做分布式和缓存优化了。4. 常见问题与排查技巧实录4.1 检索不准明明有资料却搜不到这是最高频的问题。排查顺序看切分。把检索到的 chunk 打出来看是不是被切碎了。如果是调整切分参数。看 embedding 模型。中文场景要用中文或中英双语模型用纯英文模型效果会差很多。看查询改写。用户问“咋报销”文档里写“费用报销流程”字面不匹配但语义匹配。如果向量检索还是没召回可能是 embedding 模型对口语化表达不敏感可以在检索前加一步“查询改写”把口语转成书面语。看元数据过滤。有时候是权限过滤或时间过滤把相关文档排除了检查过滤条件是不是太严。4.2 答案胡编检索到了却答错这种情况通常是提示词没约束好或者检索内容里混了干扰项。解决办法强化提示词里的“只根据检索内容回答”。在检索内容里标注来源和可信度让模型优先用高可信度内容。降低 temperature让输出更保守。如果还是不行加一个“答案校验”步骤用另一个模型判断答案是否忠于检索内容。4.3 响应太慢用户等不及拆解耗时检索多久、重排多久、模型生成多久。通常模型生成占大头。优化手段用流式输出让用户先看到部分答案。缓存高频问题。减少检索条数Top 5 够用就别给 Top 10。模型选型上在效果可接受的前提下选更快的。4.4 常见问题速查表问题现象可能原因排查方向解决手段检索不到相关内容切分过碎、embedding 不匹配、过滤过严打印 chunk、检查模型、检查过滤条件调切分、换模型、放宽过滤答案与资料不符提示词约束弱、干扰项多检查提示词、检查检索内容强化约束、加重排、降 temperature响应慢模型生成慢、检索条数多分段计时流式输出、缓存、减条数多轮对话跑偏上下文管理差检查历史拼接方式限制历史长度、每轮重新检索并发上不去检索服务阻塞、向量库瓶颈压测定位无状态化、缓存、异步4.5 几个踩过的坑坑一忽略文档更新时间。企业资料经常更新旧版本和新版本同时存在。如果检索到旧版本答案就错了。解决办法是在元数据里记更新时间检索时优先返回新版本或者在提示词里让模型注意版本。坑二权限控制做在 Agent 层。我一开始把权限判断写在 Agent 的提示词里结果发现模型有时候会“忘记”权限规则。后来改成在检索服务层做过滤Agent 拿到的就是已经过滤好的内容安全多了。坑三过度依赖单一检索源。只查向量库不查关键词遇到精确匹配就抓瞎。双路召回是必须的。坑四不做评测就上线。没有评测集根本不知道效果好不好。我的做法是上线前人工整理 100 到 200 个真实问题标注标准答案每次改动都跑一遍看命中率和准确率变化。5. 效果评测与持续迭代5.1 怎么判断这个 Agent 到底行不行评测分两个层面检索层面和生成层面。检索层面看命中率标准答案所在的 chunk 有没有被召回。这个指标反映检索质量和模型无关。命中率低先修检索别怪模型。生成层面看准确率和忠实度答案对不对、有没有胡编。这个需要人工标注或者用模型辅助评测。我一般会建一个评测集包含问题、标准答案、标准来源。每次改动后跑一遍记录命中率、准确率、平均响应时间。这三个指标能覆盖大部分问题。5.2 持续迭代的方向上线不是终点。根据用户反馈和评测结果迭代方向主要有补充资料。用户问的问题检索不到说明资料缺失补进去。优化切分。某些类型文档检索效果差针对性调整切分策略。扩充工具。用户需要查工单、查库存就接更多 MCP 工具。优化提示词。根据 bad case 不断调整提示词这是最便宜的优化手段。换模型或换 embedding。当前方案效果到瓶颈了考虑升级模型。我个人在实际操作中的体会是企业知识库问答 Agent 的效果七分靠数据准备和检索优化三分靠模型和编排。很多人一上来就纠结用哪个大模型其实先把文档切好、把检索做准用中等模型也能做出让人满意的效果。反过来检索一塌糊涂用再强的模型也是白搭。最后分享一个小技巧上线初期在回答末尾加一句“如果这个回答没帮到你可以点这里反馈”收集真实 bad case。这些反馈比任何评测集都宝贵是迭代的第一手材料。