ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

ChatBI应用之本地Qwen3大模型如何使用mysql-mcp-server-sse调用mysql数据库生成高考志愿推荐表

ChatBI应用之本地Qwen3大模型如何使用mysql-mcp-server-sse调用mysql数据库生成高考志愿推荐表 1. 本地 Qwen3 mysql-mcp-server-sse 到底解决什么问题ChatBI 这个词听起来玄乎落到工程上其实就一句话让用户用自然语言问数据系统自动生成 SQL、执行、再把结果整理成人能看的表格。高考志愿推荐表是个特别典型的场景——家长和考生不会写 SQL但他们能说清楚浙江 600 分想学计算机、电子信息、机械、自动化、土木帮我看看能报哪些学校。传统做法是把 SQL 写死在代码里或者让模型直接连数据库。前者不灵活后者风险大模型幻觉出来的 SQL 可能删库、可能全表扫描拖垮生产库。mysql-mcp-server-sse 的价值就在于它把执行 SQL这件事封装成 MCP 工具模型只能通过工具调用接口来查权限、连接池、风险等级都在服务端控制。再配合本地 vllm 部署的 Qwen3-32B-FP8整个链路数据不出内网适合对数据敏感的教育、政务类 ChatBI 场景。这套方案适合谁我总结下来是三类人一是做垂直行业 ChatBI 的开发者需要快速验证自然语言转 SQL的可行性二是手里有本地 GPU比如 H20 95G 这种想跑私有化大模型又不想从零搭工具链的团队三是已经用 vllm 部署了 Qwen3想给它接上数据库能力的工程师。核心检索词就是ChatBI、Qwen3、mysql-mcp-server-sse、vllm下面我按实际踩过的流程一步步拆。先说整体架构避免后面看代码时迷路。链路是用户输入 → Python 客户端把考生信息发给 vllm 上的 Qwen3 → Qwen3 按 system prompt 生成一条 SELECT → 客户端通过 SSE 连到 mysql-mcp-server-sse → 服务端用 mysql_query 工具执行 → 结果回传 → 客户端再把结果喂给 Qwen3 生成最终志愿报告。两个模型调用点一个 MCP 调用点职责清晰。环境我实测用的是mysql-mcp-server-sse 源码部署、Qwen3-32B-FP8、vllm 0.8.5、H20 95G、mysql:8.0.39。版本不用完全一致但 vllm 建议 0.8 以上Qwen3 的 tool call 解析在旧版本上偶发不稳定。2. 前置准备vllm 起 Qwen3 与 mysql-mcp-server-sse 部署2.1 vllm 启动 Qwen3 并开启工具调用Qwen3 要能生成结构化 SQL靠的是它本身的指令遵循能力不一定非要走 function calling 协议——我这里的做法是让模型直接输出 SQL 文本客户端用正则提取。这样对 vllm 的启动参数要求更宽松不用折腾 tool parser。启动命令大致如下vllm serve /mnt/models/Qwen3-32B-FP8 \ --served-model-name Qwen3-32B-FP8 \ --host 0.0.0.0 \ --port 9898 \ --tensor-parallel-size 1 \ --max-model-len 32768 \ --gpu-memory-utilization 0.90 \ --trust-remote-code几个参数值得说清楚。--served-model-name决定后面请求里model字段填什么必须和客户端MODEL_NAME一致否则会报 model not found。--max-model-len 32768是因为志愿报告那一步 prompt 里要塞几十条查询结果上下文短了会被截断。--gpu-memory-utilization 0.90在 H20 95G 上跑 FP8 的 32B 模型比较稳显存留一点余量给 KV cache 波动。启动成功后访问http://127.0.0.1:9898/v1/models应该能看到模型列表。这一步不通后面全是白搭所以先单独用 curl 验证curl http://127.0.0.1:9898/v1/chat/completions \ -H Content-Type: application/json \ -d {model:Qwen3-32B-FP8,messages:[{role:user,content:你好}]}2.2 部署 mysql-mcp-server-sse源码方式最直接方便改 server.pycd /mnt/program git clone https://github.com/mangooer/mysql-mcp-server-sse.git cd mysql-mcp-server-sse pip install -r requirements.txt chmod -R 777 /mnt/program/mysql-mcp-server-sse cp .env.example .env.env里要改的是数据库连接信息重点字段MYSQL_HOST127.0.0.1 MYSQL_PORT3306 MYSQL_USERchatbi_reader MYSQL_PASSWORDyour_password MYSQL_DATABASEgkzy SERVER_HOST127.0.0.1 SERVER_PORT9100这里有个安全细节给 ChatBI 用的数据库账号一定要只读只授SELECT权限。MCP 服务端虽然有风险等级控制ALLOWED_RISK_LEVELS但账号层面的最小权限才是最后一道闸。我见过有人图省事用 root结果模型生成了一条带DELETE的语句虽然被风险等级拦了但日志里那一下心跳还是够吓人的。server.py 里我做了二次优化主要是自动注册工具和连接池回收。核心逻辑是遍历src.tools目录把所有register_*tool函数自动挂到 FastMCP 实例上def auto_register_tools(mcp): import src.tools package src.tools for finder, name, ispkg in pkgutil.iter_modules(package.__path__, package.__name__ .): if ispkg: continue module importlib.import_module(name) for func_name, func in inspect.getmembers(module, inspect.isfunction): if (func_name.startswith(register_) and func_name.endswith(tool)) or func_name.endswith(tools): try: func(mcp) logger.info(f自动注册工具: {name}.{func_name}) except Exception as e: logger.error(f自动注册工具失败: {name}.{func_name} - {e})这样加新工具只要往src/tools丢文件不用改 server.py。启动服务cd /mnt/program/mysql-mcp-server-sse python -m src.server正常日志会看到Uvicorn running on http://127.0.0.1:9100以及自动注册了mysql_query、mysql_show_tables、mysql_describe_table等工具。SSE 端点是http://127.0.0.1:9100/sse注意不是根路径。2.3 把模型请求 endpoint 切到 TaoToken 统一通道本地 vllm 适合验证但如果你要长期跑、或者团队里多人共用、又或者本地 GPU 资源紧张可以把模型请求 endpoint 换成 TaoToken 的统一通道好处是 Key 集中管理、模型切换不用改代码。做法很简单把客户端里的MODEL_API_URL从http://127.0.0.1:9898/v1/chat/completions改成https://taotoken.net/api对应的 chat completions 路径MODEL_NAME换成通道里支持的模型 ID请求头加上Authorization: Bearer 你的Key。Key 在控制台创建接入文档里有各语言的示例。这样本地只保留 mysql-mcp-server-sse模型侧走统一入口运维成本低很多。3. 可复制配置客户端脚本与 MCP 工具注册3.1 客户端配置区整个客户端脚本的配置集中在头部改这几个常量就能跑MODEL_API_URL http://127.0.0.1:9898/v1/chat/completions MODEL_NAME Qwen3-32B-FP8 BASE_URL http://127.0.0.1:9100 SSE_ENDPOINT f{BASE_URL}/sse HEADERS {Content-Type: application/json} SSE_HEADERS { Accept: text/event-stream, Cache-Control: no-cache, Connection: keep-alive, } CONNECTION_TIMEOUT 30如果切到 TaoTokenMODEL_API_URL改成统一通道地址HEADERS里补上Authorization。注意 SSE 那组头是给 MCP 服务端用的和模型请求头分开别混。3.2 System Prompt 设计这是整个方案里最影响效果的部分。Qwen3 生成 SQL 的质量八成取决于 system prompt 约束得够不够死。我的 prompt 核心是明确表名gkzy.zj_data、明确字段名、明确冲稳保难的分值规则、明确输出格式只给 SQL。你是一位经验丰富的高考志愿规划师精通各省市招生数据与行业动态。 现在我会给你一段考生的基础信息。请你根据这些信息输出一个 SQL 查询语句 查询 gkzy.zj_data 表中符合以下条件的数据 - 地区字段与考生所在地区对应 - 专业名称字段与考生专业倾向对应 - 专业分数字段是冲、稳、保、难规则生成的成绩 并按专业分数冲、稳、保、难规则去构造 sql 语句并查询出结果限制总共结果最多 50 条。冲稳保难的规则我写得很具体冲刺比考生成绩高约 5 到 10 分稳妥持平或低不超过 5 分保底低 10 分以上难度高 10 分以上。还特别强调不要在 SQL 末尾加分号字段名称必须与 gkzy.zj_data 中的列名一致有些学校的分数是 -查询前直接排除掉。这些约束每一条都是踩坑换来的——不加分号是因为 MCP 工具对分号敏感字段名不一致会直接报 Unknown column。3.3 MCP 工具注册与 SSE 握手MCP 走的是 JSON-RPC over SSE握手顺序不能乱先 GET/sse拿到 endpoint 事件再 POSTinitialize收到响应后 POSTnotifications/initialized然后tools/list最后tools/call。少一步都会卡住。核心代码def run_sql_via_sse(sql: str): session requests.Session() initial_url f{SSE_ENDPOINT}?sql{quote_plus(sql)} resp session.get(initial_url, headersSSE_HEADERS, streamTrue, timeoutNone) resp.raise_for_status() session_endpoint None post_url None initialize_done False tools_list_done False query_sent False results [] # ... 事件循环处理tools/call的请求体长这样工具名固定mysql_query参数是query{ jsonrpc: 2.0, id: 3, method: tools/call, params: { name: mysql_query, arguments: { query: SELECT 院校名称 AS school, 专业名称 AS major FROM gkzy.zj_data WHERE 地区 浙江 LIMIT 50 } } }返回结果里content数组可能包含type: table的段里面有fields和rows直接按索引拼成字典就行。如果只有type: text就尝试解析里面的 JSON。4. 验证请求从自然语言到志愿推荐表全流程4.1 第一步模型生成 SQL用户输入考生信息年份2025 地区浙江 高考成绩600分省内位次约53964名 意向院校类型双一流、985、211 专业倾向工科计算机、电子信息、机械、自动化、土木 经济条件无特别限制 地域喜好不限客户端把这段和 system prompt 一起发给 Qwen3temperature 设 0.6、top_p 0.95、top_k 20。模型返回的原始内容里可能带 think 标签用正则提取第一个SELECT ... LIMIT npattern re.compile(r(SELECT[\s\S]?LIMIT\s\d), re.IGNORECASE) match pattern.search(raw)实测生成的 SQL 类似SELECT 院校名称 AS school, 专业名称 AS major, 地区 AS region, 专业分数 AS score FROM gkzy.zj_data WHERE 地区 浙江 AND 专业名称 IN (计算机,电子信息,机械,自动化,土木) AND 专业分数 IS NOT NULL AND 专业分数 0 AND ( (专业分数 BETWEEN 605 AND 610) OR (专业分数 BETWEEN 595 AND 600) OR (专业分数 590) OR (专业分数 610) ) ORDER BY 专业分数 DESC LIMIT 50注意模型自己把冲稳保难翻译成了分数区间这正是 system prompt 里规则写清楚的效果。4.2 第二步SSE 执行 SQL客户端连上http://127.0.0.1:9100/sse日志会依次打印[] 收到会话 endpoint: /messages/?session_id5cd4460371e04bde939ddb429ab93879 [←] 收到 initialize 响应 [←] 收到 tools/list 响应工具列表 - mysql_query: 执行MySQL查询并返回结果 - mysql_show_tables: 获取数据库中的表列表 ...然后tools/call执行返回的查询结果节选[ {school: 中国海洋大学, major: 自动化, region: 浙江, score: 654}, {school: 中国石油大学北京, major: 自动化, region: 浙江, score: 643}, {school: 上海电力大学, major: 自动化, region: 浙江, score: 634}, {school: 中国石油大学北京克拉玛依校区, major: 自动化, region: 浙江, score: 600}, {school: 中国计量大学, major: 自动化, region: 浙江, score: 599}, {school: 丽水学院, major: 自动化, region: 浙江, score: 536} ]4.3 第三步生成最终志愿报告把考生信息和查询结果一起塞进第二个 prompt让 Qwen3 按 Markdown 结构输出。这里有个关键约束明确告诉模型不要再生成 SQL、不要再调工具否则它会陷入我再查一下的循环。prompt 里写请不要继续生成任何SQL查询语句或调用数据库工具仅基于已有信息生成最终报告。最终输出是带冲稳保难分区的志愿表每行包含概率、建议、院校名称、层次、专业、历年分数。实测从提问到出报告约 2 分钟其中模型生成 SQL 约 20 秒SSE 查询约 3 秒报告生成约 90 秒报告 prompt 长输出也长。5. 本篇常见错排查5.1 401 Unauthorized 与 local proxy failed如果切到 TaoToken 后报 401先检查Authorization头是不是Bearer开头、Key 有没有多余空格。报local proxy failed通常是本地网络出口问题不是 Key 的问题检查一下请求地址有没有写错路径。本地 vllm 场景下如果报连接拒绝确认--host 0.0.0.0起了没以及防火墙放行 9898。5.2 reading choices 报错KeyError: choices或reading choices这类错误九成是模型返回体不是标准 OpenAI 格式。本地 vllm 正常返回一定有choices如果拿不到先打印resp.text看原始内容。常见原因是MODEL_NAME和--served-model-name不一致vllm 返回了 error 对象而不是正常响应。5.3 OAuth 与鉴权类报错MCP 服务端本身不带 OAuth如果你在客户端看到 OAuth 相关报错多半是误用了某个需要鉴权的 MCP 客户端配置。mysql-mcp-server-sse 走的是裸 SSE JSON-RPC不需要 token。如果确实要给 MCP 加鉴权在 server.py 的 FastMCP 初始化处加中间件别在客户端瞎配。5.4 工具名找不到 / Unknown tooltools/list返回里没有mysql_query或者tools/call报 unknown tool检查src/tools目录下的文件是否被正确 import。自动注册依赖函数名以register_开头且以tool或tools结尾命名不对就不会被挂上。启动日志里会打印自动注册工具: xxx对着看哪个没注册上。5.5 SQL 字段名不一致Unknown column xxx in field list是最常见的。Qwen3 有时会自作主张把院校名称写成学校名称。解决办法是在 system prompt 里把表结构写死或者先调mysql_describe_table工具拿到真实列名再拼进 prompt。我倾向于后者更稳。5.6 连接池耗尽长时间跑批量查询会看到Too many connections。server.py 里连接池默认 min 5 / max 20回收时间 300 秒。如果并发高调大ConnectionPoolConfig里的 maxsize同时确认 MySQL 的max_connections够用。另外那个后台回收线程每 5 分钟跑一次_cleanup_unused_pools别把它关了。6. 把模型请求接到 TaoToken 统一通道本地 vllm 验证通过后生产环境我建议把模型侧切到 TaoToken。原因有三个一是 Key 集中管理不用每台机器配一遍二是模型可以随时换今天 Qwen3 明天换别的客户端只改MODEL_NAME三是本地 GPU 可以只留给必须私有化的部分。切换步骤在控制台创建 API Key把客户端MODEL_API_URL指向统一通道的 chat completions 地址HEADERS加上Authorization: Bearer KeyMODEL_NAME填通道支持的模型 ID。MCP 服务端不用动它只连本地 MySQL。这样架构变成模型走统一通道 数据留本地既省运维又保数据安全。如果你要长期跑编码类或 Agent 类任务可以看下 Coding Plan额度模型更适合持续调用只是验证模型效果的话模型对话页面直接试就行。接入细节和参数说明在接入文档里Key 在 API Keys 页面管理。实测下来把 endpoint 切过去之后客户端代码改动不超过 5 行其余逻辑完全复用。
返回列表