基于MCP协议构建企业智能问数助手:MaxKB与SQLBot的集成实践
1. 项目概述当知识库遇上企业数据湖最近在做一个挺有意思的智能问答项目核心目标是把一个开源的本地知识库系统 MaxKB和我们自己内部的数据查询工具 SQLBot 打通通过 MCPModel Context Protocol协议构建一个能直接“理解”业务问题、自动查询数据库并给出答案的“智能问数”助手。简单来说就是让非技术同事也能像问同事一样用自然语言问出“上个月华东区的销售额前三名产品是什么”然后系统能自动理解意图、生成SQL、执行查询、分析结果最后用大白话把答案和图表给出来。这背后的驱动力很实际公司里数据越来越多躺在数据仓库和业务系统里但能熟练写SQL、做分析的人永远是少数。业务、运营、市场部门的同事每天都有大量的数据疑问要么得写邮件提需求给数据团队排队要么自己硬着头皮去学复杂的BI工具效率低还容易出错。我们想做的就是拆掉这堵墙让数据真正“说话”而且是用每个人都能听懂的语言。MaxKB 本身是个优秀的开源知识库问答系统能基于本地文档构建智能问答但它的知识更多是静态的、文本型的。而 SQLBot 是我们内部一个封装了数据库连接、权限控制和SQL生成能力的服务。MCP 协议则是连接这两者的“万能胶”和“翻译官”。这个项目的核心就是如何让 MaxKB 这个“大脑”不仅能调用自己的知识库还能通过 MCP 去“指挥” SQLBot 这个“双手”从动态的企业数据库中抓取实时数据完成一次完整的“问数”任务。整个过程涉及自然语言理解、意图识别、SQL生成与校验、数据安全以及结果的可解释性是一个典型的 AI 应用工程化落地案例。2. 核心架构设计与技术选型考量2.1 为什么是 MaxKB SQLBot MCP 的组合这个技术栈不是拍脑袋定的是经过几轮 POC 和团队讨论后的结果。我们先拆开看每个组件扮演的角色。MaxKB作为智能体“中枢”与交互界面。我们评估过直接基于 LangChain 或 LlamaIndex 从头搭建但考虑到快速落地和降低维护成本选择了 MaxKB。它的优势在于开箱即用的问答前端、知识库管理、多模型支持正好对接我们内部的 ChatGLM、Qwen 等模型以及相对清晰的插件扩展机制。它负责接收用户的自然语言问题进行初步的意图理解并判断这个问题应该查询本地知识库还是需要转向外部工具即我们的 SQLBot来获取数据。SQLBot作为专业的数据查询“执行器”。这不是一个开源产品而是我们数据平台团队自研的一个服务。它的核心价值在于1)统一数据服务层对接了公司多个数据源MySQL, PostgreSQL, ClickHouse, Hive提供了统一的鉴权、连接池和查询接口2)SQL生成与优化内置了一些基于模板和规则的 SQL 生成能力3)安全沙箱所有查询都在受控的数据库只读账号下执行并有查询超时、行数限制等防护4)结果格式化能将查询结果标准化为 JSON 或表格。我们需要做的不是替代它而是让 MaxKB 能更好地调用它。MCP (Model Context Protocol)关键的“协议层”与“适配器”。这是本项目技术上的核心。MCP 是一种新兴的、用于标准化 AI 应用与工具如数据库、搜索引擎、API之间通信的协议。它的核心思想是让 AI 模型或像 MaxKB 这样的应用能够以声明式的方式发现、描述和调用外部工具而无需关心工具的具体实现。选择 MCP 主要基于以下几点解耦与标准化MaxKB 不需要硬编码调用 SQLBot 的 API。它只需要实现 MCP Client就能通过协议动态发现 SQLBot作为 MCP Server暴露了哪些“工具”比如execute_query,get_table_schema。未来如果要增加新的数据工具如调用某个 BI 系统的 API只需要让新工具也实现 MCP Server 即可MaxKB 端几乎无需改动。丰富的工具描述MCP 允许 Server 详细描述每个工具的输入参数名称、类型、描述、是否必填、输出格式以及使用示例。这相当于给 MaxKB 的 LLM 提供了一个清晰的“工具说明书”极大提高了工具调用的准确率。生态趋势虽然 MCP 相对较新但得到了 Anthropic、Google 等公司的支持在 AI Agent 开发领域正在成为连接模型与工具的事实标准之一采用它可以保证技术栈的前瞻性。注意技术选型时我们曾纠结是否用更常见的“自定义 API Function Calling”模式。但考虑到未来工具的扩展性和维护成本MCP 提供的标准化接口和动态发现能力更具优势。不过初期需要投入时间理解协议和进行适配开发。2.2 整体数据流与架构图景整个系统的运行流程可以概括为以下几个步骤我画个简单的逻辑图帮助理解用户提问业务用户在 MaxKB 的 Web 界面输入“对比一下今年Q1和去年Q1我们产品A在线上和线下渠道的销售额占比。”意图判断与路由MaxKB 内置的 LLM 首先分析问题。它发现关键词“销售额”、“占比”、“对比”、“Q1”、“渠道”判断这是一个需要复杂计算和实时数据的统计分析问题超出了本地知识库的范围。于是它决定调用外部工具。工具发现与调用MaxKB 作为 MCP Client向已注册的 SQLBot MCP Server 查询可用的工具。它发现了query_data_warehouse这个工具并获得了其详细的输入参数描述如question-自然语言问题business_domain-可选业务域。参数组装与请求MaxKB 的 LLM 根据工具描述将用户问题转化为结构化的调用请求例如{“question”: “对比今年Q1和去年Q1产品A在线上和线下渠道的销售额占比”, “business_domain”: “sales”}通过 MCP 协议发送给 SQLBot。SQL 生成与执行SQLBot 收到请求后其内部的 LLM或规则引擎将自然语言问题解析成具体的 SQL 查询语句。这个过程可能涉及查询元数据获取表结构、识别“销售额”对应的字段、理解“Q1”的时间范围、区分“线上/线下”渠道标识。生成 SQL 后在安全沙箱中执行。结果处理与返回SQLBot 获取数据库返回的原始数据进行必要的聚合、计算如计算占比并将结果格式化为清晰的表格数据或简单的 JSON 结构。然后通过 MCP 协议将结果返回给 MaxKB。答案合成与呈现MaxKB 收到 SQLBot 返回的结构化数据再次调用其 LLM将数据“翻译”成通顺的自然语言答案并可以指示前端生成简单的图表如饼图。最终用户看到的是“根据查询今年Q1产品A的销售额中线上渠道占比65%线下渠道占比35%去年Q1则为线上58%线下42%。可见线上渠道占比提升了7个百分点。” 同时附上一个占比对比图。这个流程中MCP 协议主要在第3、4、6步发挥作用确保了 MaxKB 和 SQLBot 之间通信的标准化和灵活性。3. MCP 服务端SQLBot的实现细节要让 SQLBot 成为 MaxKB 可用的工具我们需要将其包装成一个 MCP Server。这里面的门道不少。3.1 工具Tools的设计与暴露MCP Server 的核心是向客户端声明自己提供了哪些“工具”。对于 SQLBot我们至少需要暴露两个核心工具get_table_schema(可选但强烈推荐)用于让 LLM 了解数据库中有哪些表、表结构、字段含义和关系。这能极大提升后续 SQL 生成的准确性。这个工具可以返回指定数据源或业务域下的表信息。execute_query(核心)接收自然语言问题执行查询并返回结果。这是主工具。工具的定义需要非常细致。以execute_query为例在实现时我们是这样定义的以伪代码示意 MCP 的tools声明{ name: execute_query, description: 根据自然语言问题查询企业数据仓库返回结构化的数据结果。适用于销售、财务、用户行为等领域的统计分析问题。, inputSchema: { type: object, properties: { question: { type: string, description: 用中文清晰描述你的数据问题例如‘上个月销售额最高的三个省份是哪里’、‘对比近30天新老用户的活跃度’ }, business_domain: { type: string, description: 指定问题所属的业务域可选值sales销售 finance财务 user用户 product产品。这有助于更精准地选择数据源和理解业务术语。, enum: [sales, finance, user, product, general] }, result_format: { type: string, description: 期望的结果格式默认为 table。, enum: [table, json], default: table } }, required: [question] } }为什么设计这些参数question是核心描述越清晰越好。business_domain是一个重要的优化点。直接让 LLM 从所有表中找答案很难通过限定业务域我们可以提前过滤元数据缩小表范围同时给 LLM 提供业务上下文例如在“销售”域下“GMV”可能对应sales_order表中的amount字段。result_format给了客户端一些灵活性。实操心得工具的描述description和参数描述properties里的description至关重要它们直接作为提示词的一部分给到 MaxKB 的 LLM。写得模糊LLM 就调用得不准。我们的经验是描述要具体包含明确的示例和边界说明。例如注明“本工具适用于统计查询不支持数据修改操作”。3.2 自然语言到 SQL 的转换核心这是 SQLBot 的“大脑”。我们采用了“LLM 规则校验 元数据引导”的混合策略。第一步问题澄清与增强。直接拿用户问题去生成 SQL 风险很高。我们首先用一个轻量级 LLM例如 Qwen-7B对输入的问题进行解析和澄清。例如用户问“今年销量怎么样”LLM 会尝试补全“您是指‘截至当前日期本年度所有产品的总销售数量吗’还是‘分月度的销量趋势’”。在自动问答场景我们通常默认选择最常见解释但会记录这个不确定性。更优的做法是设计一个交互澄清流程但这会增加复杂度。第二步检索相关元数据。根据business_domain或从问题中提取的关键词如“订单”、“用户”从我们维护的“数据地图”中检索相关的表名、字段名、字段注释、以及重要的关联关系。这些元数据会作为上下文提供给主 SQL 生成 LLM。第三步SQL 生成与安全校验。使用一个更强大的 LLM如 GLM-4结合澄清后的问题和元数据上下文生成 SQL 语句。Prompt 工程是关键我们的模板大致如下你是一个资深的数据分析师。请根据以下问题和企业数据库元数据生成一条安全、高效的 {数据库类型} SQL 查询语句。 # 问题 {澄清后的问题} # 相关表结构 {table_schema_info} # 重要规则 1. 只使用提供的表不要假设不存在的表或字段。 2. 务必使用 AS 为计算字段和聚合结果起一个清晰的别名例如 SUM(amount) AS total_sales。 3. 如果问题涉及“占比”、“增长率”需在SQL中完成计算。 4. 查询必须是只读的 SELECT 语句绝对禁止出现 INSERT, UPDATE, DELETE, DROP 等关键字。 5. 如果问题中涉及“最近7天”、“上个月”等时间范围请使用数据库中的日期字段和合适的日期函数。 6. 结果集限制在1000行以内。 请直接输出SQL语句不要有任何解释。生成 SQL 后必须经过一道严格的安全和语法校验层关键字黑名单检查是否包含危险操作关键字。语法检查使用 SQL 解析器如 sqlglot进行初步语法验证。执行计划预览针对大数据量对于可能消耗大量资源的查询通过EXPLAIN估算成本如果超过阈值则拒绝执行并返回提示“查询过于复杂请缩小范围”。第四步执行与后处理。SQL 在配置了只读权限、资源限制的数据库用户下执行。获取原始数据后根据result_format进行格式化。对于“table”格式我们会将数据转为 Markdown 表格字符串这对于 MaxKB 的 LLM 来说可读性最好。4. MaxKB 客户端的集成与配置MaxKB 侧的工作主要是使其能够作为 MCP Client发现并调用我们部署好的 SQLBot MCP Server。4.1 MCP Client 的接入方式MaxKB 本身没有原生支持 MCP因此我们需要通过其插件系统或修改后端代码来集成一个 MCP 客户端库。我们选择在 MaxKB 的后端服务Python中集成mcp官方 SDK 或社区客户端库。核心步骤包括建立连接配置 SQLBot MCP Server 的地址例如ssht://sqlbot-server:8000和认证信息如果启用。工具发现启动时或定期调用list_tools()方法从 Server 获取工具列表及其模式并缓存在本地。请求转发当 MaxKB 的对话逻辑判断需要调用外部工具时将当前对话历史、用户问题以及缓存的工具描述一起提交给 MaxKB 内置的 LLM要求其决定调用哪个工具并生成调用参数。调用与响应使用 MCP Client 的call_tool()方法向 Server 发起请求并等待结果。结果整合将工具返回的结构化数据如 Markdown 表格插入到对话上下文中再次调用 LLM让其根据工具结果生成最终面向用户的自然语言回复。4.2 MaxKB 中的提示词工程要让 MaxKB 的 LLM 用好 SQLBot 工具需要在系统提示词System Prompt中进行精心设计。我们的提示词包含了以下几个部分角色定义“你是一个集成了企业数据库查询能力的智能助手。你可以回答基于文档的知识问题也可以查询实时数据库来解答数据统计类问题。”工具介绍清晰列出 SQLBot 提供的工具名称、描述以及调用时机。例如“当你遇到需要计算、统计、对比、查询具体数值如销售额、用户数、增长率、排名的问题特别是问题中带有时间、地区、产品等维度时应优先考虑使用execute_query工具。”调用格式明确告诉 LLM 如何生成调用请求最好给出示例。结果处理指令“当你收到工具返回的表格数据后你需要1) 用通俗的语言总结核心发现2) 指出数据中的关键趋势或异常点3) 如果用户问题隐含对比或判断请基于数据给出明确结论。”踩坑记录最初我们只是简单列出了工具但 LLM 经常“偷懒”不愿意调用工具而是试图用自己的知识猜测答案或者说“我无法查询实时数据”。后来在提示词中强调了“应优先考虑使用”并给出了明确的场景描述调用率才大幅提升。同时需要控制工具调用的“野心”对于明显是知识型的问题如“公司的成立时间是什么”应禁止其调用查询工具转而使用本地知识库。5. 安全、权限与运维考量企业级应用安全是生命线。这个智能问数体涉及数据访问必须慎之又慎。5.1 多层权限控制体系用户身份继承MaxKB 用户登录后其身份信息如工号、部门需要传递给 SQLBot。SQLBot 不应有自己的用户体系而应信任来自 MaxKB 的认证通过 JWT Token 等方式实现单点登录。数据权限映射在 SQLBot 侧维护一个“用户/部门-数据视图”的映射关系。当收到查询请求时SQLBot 不仅生成 SQL还要根据用户身份在 SQL 的 WHERE 条件中自动注入权限过滤条件。例如销售经理只能看到其负责区域的销售数据。这部分通常通过改写 SQL 或让查询指向预定义的数据视图来实现。MCP 传输安全MCP 连接应使用 TLS/SSL 加密。Server 端需要对 ClientMaxKB进行认证防止未授权的服务调用。5.2 查询防护与审计资源限制严格执行查询超时如 30 秒和最大返回行数限制如 1 万行防止恶意或低效查询拖垮数据库。SQL 注入防御虽然 LLM 生成的 SQL 参数化程度低但必须在执行前进行严格的语法和模式检查确保查询只涉及允许的表和字段。绝对禁止将用户输入的任何部分直接拼接到 SQL 中。全链路审计记录每一次工具调用的日志包括用户 ID、原始问题、生成的 SQL、执行数据库、执行时间、返回行数。这既用于安全审计也为后续优化提供数据支持。5.3 性能与稳定性优化缓存策略对于常见的、计算成本高的查询如“昨日核心大盘数据”可以在 SQLBot 侧引入结果缓存设置合理的过期时间如 5 分钟避免重复冲击数据库。异步处理对于可能耗时的复杂查询可以考虑采用异步模式。MCP 调用立即返回一个任务 IDMaxKB 轮询或通过回调获取结果。这能避免 HTTP 请求超时。降级方案当 SQLBot 服务或底层数据库不可用时MaxKB 应能优雅降级提示用户“数据查询服务暂不可用您可以尝试询问文档知识类问题”。6. 效果评估与迭代方向项目上线初期我们设定了几个核心评估指标SQL 生成准确率随机采样一批问题人工评估生成的 SQL 是否能正确回答问题。目标 85%。端到端回答满意度邀请真实业务用户测试对答案的准确性、可读性和有用性进行评分。工具调用准确率统计 LLM 是否正确判断了何时该调用 SQLBot避免误调用或漏调用。在实际运行中我们发现了一些典型问题及应对策略问题歧义问题处理不佳。如“分析一下我们的产品”LLM 可能不知道具体指哪个产品线。策略在 MaxKB 前端增加交互澄清环节当检测到问题关键信息模糊时主动反问用户“请问您想分析哪个产品线例如产品A、产品B 还是全部” 将用户选择作为参数传递给 SQLBot。问题复杂逻辑 SQL 生成错误。如涉及多层嵌套子查询、窗口函数等复杂分析。策略在 SQLBot 的“数据地图”中预定义一些常用的、复杂的“分析视图”或“指标定义”。当 LLM 识别出问题匹配某个预定义指标时直接调用对应的固化查询模板而非完全从零生成 SQL。问题结果解释过于机械。LLM 只是复述数据缺乏洞察。策略优化 MaxKB 的结果合成提示词要求其扮演“数据分析师”角色不仅要报告数字还要尝试解释“为什么”基于常识和上下文并指出“所以呢”业务启示。例如不仅说“销售额下降了10%”还可以补充“这可能与近期市场竞争加剧有关建议关注客户流失率”。未来的迭代方向多轮对话支持上下文相关的连续追问。例如用户问“Q1 销售额多少”接着问“那利润率呢”系统应能理解“那”指的是上一个查询的同一范围Q1。可视化增强让 SQLBot 返回的数据不仅包含表格还能附带一个简单的图表类型建议如chart_type: “bar”MaxKB 前端据此渲染图表。知识库与数据联动实现更深的融合。例如用户问“根据某某营销方案文档预计能提升多少销售额”系统可以先从知识库提取方案中的关键假设如预计转化率提升5%再调用 SQLBot 查询当前基线数据最后计算出预估数值。这个项目让我深刻体会到构建一个可用的企业级智能问数体技术集成只是第一步更难的是对业务的理解、对数据安全的把控以及对异常情况的周全处理。从“能用”到“好用”还有很长的路要走但每解决一个实际问题看到业务同事能更自主地获取数据洞察就觉得这些折腾都值了。