这个“每天了解几个 MCP Server”系列,我原本以为多是聊些玩具级的小工具,直到上周帮团队把内部文档库接进 AI 助手,我才意识到,MCP Server 最值钱的落地场景之一,就是企业知识库。这篇就聊聊我基于某云厂商官方文档库做的一次尝试:把 AWS 相关内部文档变成 AI 可以直接查询的知识源,RAG 接入企业知识库,以及整个过程踩过的坑。
这篇文章适合两类人:一类是给企业做知识库 AI 检索的工程师,另一类是已经跑通了 MCP Demo、但不知道下一步怎么落地的朋友。我会从设计取舍、协议拆解、实操代码写到生产化改造,尽量把关键决策背后的理由也讲清楚。
1. 企业文档为什么这么难查:知识库问题的本质不是“检索”
先别急着写代码。我见过不少团队一上来就准备做 PDF 问答,结果数据还没整理完就卡住了。企业知识库难查,难的根本不是算法,而是文档本身的组织方式。
1.1 文档分散在多个系统,权限和格式都不统一
真正在企业里待过的人都有体感。研发文档在某 wiki 上,运维手册在某共享盘里,产品需求在另一个协作平台里,中间还夹杂着几十份只有某个老工程师电脑里才有的 Markdown 文件。它们不在一处、格式不统一、更新频率也完全不一样。
我在做这次 AWS 内部文档接入时,第一步做的不是 Embedding,而是盘点数据源。最后选了三个相对好处理的来源:版本化的 Markdown 文档、HTML 导出的云服务指南、以及少量结构化 JSON 格式的 API 参考。格式不统一意味着清洗逻辑必须为每种来源单独写一层适配器,而不是一个 splitter 走天下。这一点大家心里要有数。
1.2 权限问题让“全局向量检索”天然不成立
知识库和公开搜索引擎最大的区别,就是权限。同一个文档,管理层能看到成本数据,一线工程师看不到;同一份事故复盘,核心成员能看完整版,外包同学只能看脱敏版。如果直接把所有文档都切块丢进一个向量库,本质上是把权限模型给抹掉了。
这是很多 RAG 方案在企业里推不动的主要原因。你检索得很准,但答案里带着不该出现的内容,法律和合规部门那一关就过不了。我自己的原则是:索引里可以放全部文档,但召回阶段和最终输出阶段,必须基于用户身份做过滤。后面我会专门讲怎么在 MCP Server 里把用户身份一路传到检索层。
1.3 传统 RAG 的“万能切片法”为什么效果不稳定
很多人提到知识库 AI 问答,第一反应就是“把文档喂给大模型”。这里藏着两个常见误区:
- 误区一:文档切得越碎,召回越准。实际上切得太碎,上下文被切断,模型拿到的是无头无尾的碎片,回答经常张冠李戴。
- 误区二:语义向量搜索能解决一切。实际上对于 API 参数、错误码、版本号这类精确信息,向量检索的准确率远不如传统关键词匹配。
所以真正可用的方案,不是纯向量检索,而是“混合检索 + 重排”。我在后文会给出具体做法。这个认知上的转变,比我写的任何代码都更重要。
1.4 MCP Server 在这件事里到底补了什么
聊清楚痛点之后,MCP 的价值就很明显了。它不是一个检索算法,而是一个标准化的“工具接入层”。AI 对话模型本身不知道怎么连企业内部的向量数据库,也不知道怎么解析私有文档格式。MCP Server 就是中间那个适配器,让模型通过标准协议去调用一个名叫query_knowledge_base的工具,拿到检索结果后再组织成回答。
对企业来说,最大收益是解耦。知识库系统只需要实现 MCP 协议,AI 前端无论是用哪种对话产品、哪个模型,都可以复用同一套检索能力。这比“每个前端各自写一套 Function Calling 调知识库 API”干净得多。
2. MCP Server 的设计骨架:Tools、Resources 和 Prompts 到底管什么
很多刚接触 MCP 的同学会被“协议”两个字吓到。说白了,它就是一套规定 AI 应用和工具之间怎么对话的规则。真正上手后你会发现,核心概念没多少。
2.1 架构:Client 和 Server 之间的会话规则
MCP 采用经典的客户端-服务器模型。AI 应用是 MCP Client,负责把用户的自然语言请求转换工具调用;MCP Server 是能力提供方,负责把检索、数据库查询、工单创建等能力暴露出来。
从我实际项目的角度看,框架本身已经帮你处理了绝大多数协议细节。你用 SDK 定义一个 Server,注册好工具列表,然后启动一个标准输入输出或 HTTP 服务,剩下的握手、请求响应对齐、错误返回,SDK 都包掉了。写业务代码的人,重点只需要关心三个概念:工具、资源、提示词。
2.2 三个原语在知识库场景里的映射
MCP 协议里最核心的三个原语是 Tools、Resources、Prompts。很多教程把它们讲得很玄,但放到知识库场景里就一目了然:
| 原语 | 协议层定位 | 知识库场景映射 |
|---|---|---|
| Tools | 模型主动调用、有输入输出 | 检索知识库、获取文档全文、查询权限列表 |
| Resources | 给模型提供上下文数据 | 某个文档的最新版本、某个 API 的变更日志 |
| Prompts | 预置的提示模板 | “按标准格式总结这篇故障复盘”这类固定指令 |
我这次实践里主要用 Tools。原因是知识库的核心交互是“模型不确定答案时主动去检索”,这正好是 Tools 的职责。Resources 适合那种“每次对话都必须带上的基础信息”,比如团队的项目代号对照表,我会把这种内容注册成 Resource,让模型天然可以看到。Prompts 则用来兜底一些高频场景,比如“用表格对比两个 SDK 版本差异”。
2.3 为什么不用 Function Calling 硬怼
你可能会问:现在的模型不都支持 Function Calling 吗?直接在系统提示里定义几个函数,让模型去调后台 API,不也能实现吗?
能用,但成本在后面。Function Calling 的写法是“每个前端各自定义函数结构”,如果同时有 Web 端、IM 机器人、自动化工作流,每个地方都要维护一份结构定义,字段一变到处改。MCP 做的是把函数定义和实现放在 Server 端,Client 通过协议动态发现能力,前端不用写死任何工具逻辑。
这个差别,单点 Demo 时感觉不出来,等你有三四个数据源、上百个工具函数的时候就非常明显。我这次做 AWS 相关文档库接入,同时还要接一个工单查询系统,用 MCP 的话,我只需要在同一个 Server 里加一个 Tool,前端完全不用动,这种“新增能力不碰客户端”的体验,确实是传统 REST API + Function Calling 给不了的。
3. 动手接企业内部 AWS 文档库:从数据流水线到 MCP Server
写到这里,我开始说实际的落地过程。场景是:某团队内部积累了大量云平台使用文档,格式以 Markdown 和 HTML 为主,之前没有统一的搜索入口。我是怎么把它们变成 AI 可以直接查询的知识库的,下面按步骤来讲。
3.1 数据采集与清洗:决定 RAG 天花板的环节
很多人以为 RAG 效果差是 Embedding 模型不行,其实大部分问题是数据没洗干净。我这次的处理流程分四个阶段:
- 采集:用脚本批量拉取内部文档源,保留原始格式和元信息(文档编号、更新时间、权限标识)。
- 清洗:去掉导航栏、重复页脚、广告位相关的 HTML 噪声,把 Markdown 里的多余注释清理掉。
- 结构化:对 API 类文档,尝试解析出接口名、参数表、返回码;对操作手册,保留步骤型段落。
- 备份:保留原始文件的快照,方便追踪“AI 用的版本”和“用户看的版本”是否一致。
清洗这一步我额外多说一句:不要迷信通用解析库。HTML 文档里大量的是模板噪声,通用库处理不了的,必须针对你那几个文档源写规则。我当时写了大约几百行清洗脚本,去掉的垃圾字符占比远超想象。
3.2 切片策略:按语义边界,不按字符数
切片是 RAG 绕不开的一步。我的经验是:先用标题和段落结构识别出语义边界,再看长度决定是否合并或拆分。比如一个文档里有三级标题,就把每个三级标题下的内容作为一个候选块,如果太长再按句子或段落二次切分。
参数上我自己常用的配置是:块大小 800-1200 token,相邻块重叠 100-150 token。这个数值不是从论文里抄的,而是根据实际问答效果调的。块太小,上下文不完整;块太大,向量检索的精度会下降,重排阶段的成本也会上去。
3.3 向量库与召回链路:混合检索 + 重排
存好了切片,下一步就是检索。我只用向量检索时发现,问一些精确问题时效果很差,比如“某个错误码 EC2 实例是什么意思”,向量召回的前几名里经常没有正确答案。后来改成了混合检索结构:
- 向量召回:用 Embedding 模型把查询和切片都转成向量,取 TopN。
- 关键词召回:走传统的 BM25 或倒排索引,取 TopN。
- 合并:两个结果集合并去重,再做 Rerank,把真正相关的片段排到前面。
Rerank 我用的是 Cross-Encoder 类型的模型,效果立竿见影。第一次跑完对比,混合检索 + Rerank 在精确问题上的 Top5 命中率比纯向量检索提高了将近三成。代价是多一次模型推理,但对一次查询来说,增加几百毫秒完全值得。
3.4 在 MCP Server 里注册知识库工具
数据链路就绪后,我把检索能力封装成了两个 Tools。第一个叫query_knowledge_base,功能是接收用户问题,返回最相关的若干文档片段;第二个叫get_document,功能是根据文档 ID 获取完整内容。为什么要两个工具而不是一个?因为很多时候,模型只需要通过阅读片段就能回答;只有当片段不够完整、需要看全文档时,才需要二次拉取全文。这样能减少不必要的大块数据传输。
代码层面,我用了 MCP 的 Python SDK。关键的结构如下:
from mcp.server import Server from mcp.server.stdio import run_server from mcp.types import Tool, TextContent app = Server("enterprise-knowledge-mcp") @app.list_tools() async def list_tools(): return [ Tool( name="query_knowledge_base", description="在企业知识库中检索与问题最相关的文档片段,返回片段内容、文档ID、来源标题和置信度。", inputSchema={ "type": "object", "properties": { "query": {"type": "string", "description": "用户的自然语言查询"} }, "required": ["query"] } ), Tool( name="get_document", description="根据文档ID获取完整文档内容,用于需要查看完整上下文的情况。", inputSchema={ "type": "object", "properties": { "doc_id": {"type": "string", "description": "文档唯一标识"} }, "required": ["doc_id"] } ) ] @app.call_tool() async def call_tool(name: str, arguments: dict): if name == "query_knowledge_base": results = await hybrid_search(arguments["query"]) return [TextContent(type="text", text=format_search_results(results))] if name == "get_document": doc = await load_full_document(arguments["doc_id"]) return [TextContent(type="text", text=doc)] raise ValueError(f"Unknown tool: {name}") if __name__ == "__main__": run_server(app)这段代码实际上是整个接入工作的 20%。剩下 80% 的精力,都在hybrid_search背后的数据清洗、索引更新、权限过滤和重排逻辑上。MCP 的意义就是把复杂的东西收敛成一个标准接口,让上层 AI 不用感知这些细节。
3.5 工具返回格式:让模型能“引用来源”
工具返回的内容不能只是一段裸文本。我会把每条结果包成结构化的文本,包含标题、文档路径、更新时间、命中片段,以及一个可追溯的文档 ID。这样模型在回答时就能指出“根据某文档中的描述,结论是什么”。
我之前踩过坑:返回结果里只有相似度分数,没有来源信息,模型回答出来像自己编的,用户根本不敢信。后来强制所有工具返回必须带source字段,答案可信度明显提升。
4. 实测效果与必须避开的坑
接入完成后,我花了两天时间做评测和调优。下面是实际测试的感受和一些很具体的避坑经验,这篇如果不写,读者后面大概率会自己踩一遍。
4.1 测试集设计:不要只拿几个好问题自欺欺人
我搭了一组覆盖三类问题的测试集,用来观察系统到底行不行:
| 类型 | 示例 | 判断标准 |
|---|---|---|
| 简单事实查询 | “S3 批量删除有没有限制?” | 答案必须包含文档中的关键数字 |
| 多跳推理查询 | “ECS 实例出现某种状态码,结合运维手册怎么排查?” | 是否能主动定位到两份不同文档 |
| 无答案问题 | “知识库里有没有提到 Kubernetes 集群迁移?” | 是否诚实回答没有相关内容,而不是硬编 |
实测下来,简单事实查询的正确率最高,多跳推理需要模型主动调用检索两次、甚至先查文档 A 再带上下文查文档 B。这个流程光靠一次检索很难,我后面给系统加了一个“允许模型连续多次调用工具”的配置,效果提升不少。
4.2 Embedding 选型:别只看榜单分数
Embedding 模型的选择直接决定召回质量。我的个人经验是:
- 通用领域闭源模型效果确实好,但企业内部文档如果专业术语多,做一个小的领域适配会更稳。
- 维度不是越高越好。高维度能表达更细粒度语义,但存储和检索成本也高,实际收益不一定大。
- 同一批文档做完 Embedding 后,建议抽样检查不同文档之间的相似度分布,如果所有文档相似度都偏高,说明 Embedding 模型区分度不够,需要换模型或调整清洗策略。
这次我用的模型对 AWS 服务简称理解得不够好,像 “EC2” 和 “弹性计算” 这类同义关系经常对不上。后来我在索引阶段给每个切片加了别名元信息,比如在文档里出现 “EC2” 的地方额外写入 “弹性计算云服务器”。这种小技巧,比换大模型立竿见影。
4.3 权限隔离:最容易被忽略又最致命的问题
前面说过,企业知识库必须有权限过滤。我在 MCP Server 里做了一层很实用的处理:工具调用时额外传入一个用户上下文,里面包含用户 ID、角色、文档权限级别。检索函数先根据这个上下文缩小可搜索的范围,再进行向量检索和关键词检索。
async def hybrid_search(query: str, user_ctx: UserContext): allowed_docs = await get_allowed_doc_ids(user_ctx.role, user_ctx.department) candidates = await vector_search(query, doc_filter=allowed_docs) keyword_hits = await keyword_search(query, doc_filter=allowed_docs) return rerank(merge(candidates, keyword_hits))这层权限过滤不能只放在前端、只把某些按钮隐藏,而应该在数据查询那一刻强制过滤。否则一旦模型通过某种方式直接调用工具或者绕过界面,数据就泄露了。我在服务端做了二次强制校验,即使前端漏传了用户上下文,请求也会被拒绝。
4.4 文档版本更新与失效索引
知识库最大的隐形敌人不是检索不准,而是内容过期。AWS 这类云服务更新频繁,如果文档已经改了,索引里还是旧版,AI 就会一本正经地给出过时答案。
我的同步策略很简单:
- 每次文档变更时触发增量更新,只重新 Embedding 变更的切片。
- 删除类变更,除了移除索引,还要记录一个“失效文档ID”列表。查询命中失效文档时,工具返回结果里直接标记为“已过期”,模型在回答里会提示用户“该信息可能已经过时,请以最新文档为准”。
- 定期跑一次全量比对,把漏掉的变更找出来。
我还给每个切片加了一个version字段,同一文档不同版本的切片是不同的向量。这样模型拿到旧版本片段时,至少能意识到这是某个历史版本的内容。
5. 从 Demo 到生产级:并发、审计和多 Server 治理
跑通一个 MCP Server 只在本地用,和要推给全团队使用,完全是两回事。最后这部分我说说从单点 Demo 到生产环境的改造清单。
5.1 并发抖动和缓存设计
内部知识库用的人数可能不算多,但 AI 场景有个特点:用户提问的时间高度集中。早上一上班、或者某个故障发生后的十分钟内,查询量会突然涌上来,检索服务如果没有保护机制,很容易被打挂。
我的处理办法是三层:第一,给向量检索和重排加超时控制和熔断;第二,对高频且重复的问题做语义缓存,比如“某个服务怎么计费”“某个功能怎么开启”这类问法稳定,命中缓存后完全不需要再跑向量检索;第三,对重排模型做排队池,避免瞬间并发拖垮显存。
5.2 可观测性:别等出问题再抓瞎
生产环境里,MCP Server 不能是一个黑盒。我至少会记录以下指标:
- 工具调用次数和失败率。
- 检索耗时和重排耗时。
- 召回结果为空的比例。这个数字如果偏高,说明知识库覆盖有问题。
- 用户对 AI 回答的反馈(点赞/点踩),这是判断检索质量最重要的信号。
日志方面,我会把每个查询对应的检索结果 Top3 和最终答案一起打出来。一旦回答出错,可以直接复盘是模型问题还是召回问题。
5.3 多 MCP Server 的治理思路
当团队里同时存在知识库 MCP、工单 MCP、监控 MCP 时,你就需要一个统一的能力目录,记录每个 Server 暴露了哪些 Tools、权限是谁审批的、版本是多少。这个目录不一定复杂,一个 JSON 文件也能用,但必须要有。否则半年之后,没人说得清哪个 Server 在跑、哪个工具已经废弃。
我在实践中还会给 MCP Server 加统一的前缀命名,比如知识库相关工具统一以kb_开头,工单相关统一以ticket_开头。这样 AI 在混杂环境下也能快速判断应该调用哪个能力,避免出现一个 Server 里堆了几百个工具、命名混乱到模型选错的情况。
5.4 接入层的前置体验设计
生产化之后还有一个很容易被忽略的细节:AI 知识库的“答案”和“原文”必须保持可追溯。我会让回答的末尾自动附上文档标题和链接。用户如果对 AI 的摘要不放心,可以跳回原文验证。这个设计看似简单,但极大提高了团队对新系统的信任度,尤其是那些本来就不信任“AI 生成的答案”的老工程师。
关于这一点我的体会很深:知识库系统上线后,第一批用户的接受度,往往取决于你能不能让他们在 30 秒内找到原始出处。AI 说得再漂亮,不如原文链接来得踏实。
最后分享一个个人观点:MCP Server 接入企业知识库这件事,真正的价值不在于把文档切块、向量化,而在于给“AI 访问企业数据”这件事,定义了一个干净、可控、可审计的边界。你不需要等一个完美的 RAG 框架,选一个好维护的数据源,先把 MCP Server 跑起来,再慢慢迭代检索质量。我自己就是这么一步步走过来的,先把 AWS 相关的几个文档源接进去,让 AI 能查到、能引用、能说“不知道”,后续的路就顺多了。