1. 传统知识库为什么总在“答非所问”上翻车
先说结论:绝大多数团队做 AI 知识问答,卡住的不是模型,而是检索。你手里有一堆产品手册、客服话术、故障处理记录,分散在飞书、Confluence、共享盘、甚至某个同事的本地 Word 里。用户问一句“PolarDB 存储满了怎么扩”,传统做法是人工翻文档、复制粘贴、再组织语言,响应延迟轻松超过五分钟。这不是模型不行,是知识根本没被组织成可检索的形态。
我见过最典型的三个坑。第一是文档孤岛,产品手册在一个平台、故障库在另一个平台、客服话术又是第三套系统,跨系统检索的时间占掉了整个响应流程的六成以上。第二是专业门槛,想自己写个检索服务,SQL 加 Python 定制开发起步两周,中小团队根本耗不起。第三是性能瓶颈,MySQL 原生全文索引在中文场景下召回率经常不到一半,关键词匹配出来的结果和用户真实意图差着十万八千里。
PolarDB MySQL 版内置向量引擎加 MCP 协议,解决的正是这三件事:把向量计算下沉到数据库存储层,省掉数据搬迁开销;用 MCP 把知识源统一成一个可配置的入口;再用自然语言到 SQL 的链路把问答跑通。你不需要自己搭一套向量数据库,也不需要写几百行检索代码,建表、灌数据、配 MCP、发请求,四步就能跑通端到端问答。这篇就按这个路径走,每一步都给可复制的配置和命令,最后附上我实际踩过的报错和排查方法。
适合谁看:手里有知识库但检索效果差的后端同学、想给客服或运维团队快速搭一个问答入口的技术负责人、以及正在评估“要不要单独买向量数据库”的架构决策者。全程不需要你精通向量算法,但需要你能跑 SQL 和改 JSON 配置。
2. 前置准备:PolarDB MySQL 实例与 TaoToken 模型接入
动手之前先把两件事备齐:一个开了向量能力的 PolarDB MySQL 实例,以及一个能调模型的服务入口。前者负责存知识向量和做相似度检索,后者负责把检索结果组织成自然语言答案。我这里用 TaoToken 做模型侧接入,原因是它同时提供 OpenAI 兼容接口和 Claude Code 这类编码工具的接入方式,后面配 MCP 服务时 Base URL 和 Key 一套就够,不用在多个平台之间来回切。
先看 PolarDB 侧。创建实例时关键参数是引擎版本和向量开关,控制台里选 PolarDB MySQL 8.0,规格按知识库规模来,十万条问答级别 2 核 8G 起步就够跑通验证。建库之后确认向量扩展可用,执行下面这条查一下:
SHOW VARIABLES LIKE 'polar_vector%';能看到polar_vector_enabled为 ON 就说明向量能力已就绪。如果为空,去实例参数里把向量引擎开关打开再重启生效。这一步别跳过,后面建向量索引全靠它。
模型侧到 TaoToken 官网注册后进控制台创建 API Key,地址是https://taotoken.net/api,注意这个 Base URL 不带任何路径后缀,OpenAI 兼容客户端直接填这个就行。Key 拿到后先别急着写代码,用 curl 验一下通不通:
curl https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o-mini", "messages": [{"role":"user","content":"回复 ok"}] }'返回里有choices[0].message.content就说明链路通了。这里有个细节:MCP 服务配置里填的 Base URL 必须是https://taotoken.net/api,不要自己拼/v1,客户端会自动补。我见过有人填成/api/v1导致 404,排查半天以为是 Key 的问题。
两样都备好之后,把下面三个值记在一个地方,后面配置反复要用:PolarDB 的连接串(host、port、user、password、database)、TaoToken 的 API Key、以及你打算用的模型 ID。模型 ID 建议选支持中文且上下文够长的,知识问答场景里检索回来的片段加上问题本身,很容易超过 8k token。
3. 可复制配置:建表、向量索引与 MCP 服务
这一节是全文的核心,配置写错后面全白搭。分三块:知识库表结构、向量索引参数、MCP 服务配置文件。
先建知识库表。核心思路是一行一条知识片段,存原文、向量、来源元数据。向量列的类型用 PolarDB 的向量类型,维度要和你选的 embedding 模型对齐,bge-m3 是 1024 维,OpenAI 的 text-embedding-3-small 是 1536 维,建表前确认好,维度不匹配插入直接报错。
CREATE TABLE kb_chunks ( id BIGINT AUTO_INCREMENT PRIMARY KEY, doc_id VARCHAR(64) NOT NULL, chunk_text TEXT NOT NULL, embedding VECTOR(1024) NOT NULL, source VARCHAR(255) DEFAULT NULL, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, KEY idx_doc (doc_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;建完表加向量索引。PolarDB 的向量索引支持 HNSW 和 IVF 两类,知识库规模在百万级以内优先 HNSW,召回率和延迟平衡更好。参数里M控制每层连接数,ef_construction控制建索引时的搜索宽度,这两个值越大索引越准但建得越慢。十万级数据用 M=16、ef_construction=200 是稳妥起点。
CREATE VECTOR INDEX idx_kb_embedding ON kb_chunks (embedding) WITH (type = 'hnsw', m = 16, ef_construction = 200, metric = 'cosine');metric选 cosine 还是 l2 取决于你的 embedding 模型,bge 系列用 cosine,OpenAI 的用 cosine 也行。建完索引用SHOW INDEX FROM kb_chunks确认索引状态是 visible。
接下来是 MCP 服务配置。MCP 的本质是把“知识源”和“检索动作”抽象成标准接口,让模型侧能按统一协议调用。下面这份 JSON 是我实际跑通的配置,路径和字段名照抄即可,只改连接串和 Key:
{ "mcpServers": { "polardb-kb": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-mysql"], "env": { "MYSQL_HOST": "your-polaradb-host", "MYSQL_PORT": "3306", "MYSQL_USER": "kb_reader", "MYSQL_PASSWORD": "your-password", "MYSQL_DATABASE": "qa_db", "EMBEDDING_BASE_URL": "https://taotoken.net/api", "EMBEDDING_API_KEY": "sk-your-taotoken-key", "EMBEDDING_MODEL": "bge-m3", "VECTOR_TABLE": "kb_chunks", "VECTOR_COLUMN": "embedding", "TEXT_COLUMN": "chunk_text", "TOP_K": "5" } } } }这份配置里三个值必须成对出现且一致:EMBEDDING_BASE_URL填https://taotoken.net/api,EMBEDDING_API_KEY填控制台创建的 Key,EMBEDDING_MODEL填你实际用的模型 ID。三者任何一个对不上,检索阶段就会报 401 或者维度不匹配。TOP_K控制每次召回几条片段,问答场景 5 条够用,太多会撑爆上下文。
如果你用的是 Claude Code 这类工具,MCP 配置放在~/.claude/settings.json或者项目级的.mcp.json里,结构同上。Cline 的话在 MCP Servers 面板里粘贴同样的 JSON 即可。Codex 用户注意auth.json里只放 TaoToken 的 Key,Base URL 和 Model ID 在 MCP 配置里单独写,别混在一起。
4. 灌数据与问答链路验证:从建表到拿到答案
配置就绪后开始灌知识。最省事的做法是写个脚本把文档切片、调 embedding、批量插入。切片策略直接影响召回质量,按语义切比按固定字数切好,但实现成本高。实操里我用折中方案:按段落切,单段超过 500 字再按句号二次切,每片保留前后各一句作为上下文重叠。
import requests, pymysql, json def embed(texts): resp = requests.post( "https://taotoken.net/api/v1/embeddings", headers={"Authorization": f"Bearer {API_KEY}"}, json={"model": "bge-m3", "input": texts} ) return [d["embedding"] for d in resp.json()["data"]] conn = pymysql.connect(host=HOST, user=USER, password=PWD, database="qa_db") cur = conn.cursor() chunks = load_and_split("/data/faq") # 你的切片函数 vectors = embed([c["text"] for c in chunks]) for c, v in zip(chunks, vectors): cur.execute( "INSERT INTO kb_chunks (doc_id, chunk_text, embedding, source) VALUES (%s,%s,%s,%s)", (c["doc_id"], c["text"], json.dumps(v), c["source"]) ) conn.commit()插入时 embedding 列传 JSON 数组字符串即可,PolarDB 会自动转成向量类型。灌完用SELECT COUNT(*) FROM kb_chunks确认条数对得上,再抽查一条看 embedding 列不为空。
验证问答链路分两步。第一步直接走 SQL 验证向量检索能不能召回正确片段:
SELECT chunk_text, source, COSINE_DISTANCE(embedding, '[0.012, -0.034, ...]') AS dist FROM kb_chunks ORDER BY dist ASC LIMIT 5;把用户问题的 embedding 填进去,看返回的前五条是不是相关。如果召回的全是无关内容,先查 embedding 模型是否和建表维度一致,再查索引是否生效。
第二步走 MCP 端到端。在支持 MCP 的客户端里发一句自然语言问题,比如“PolarDB 存储扩容怎么操作”,观察返回里是否带上了source字段和具体文档名。成功的标志是答案里引用了你灌进去的原文片段,而不是模型自己编的。我实测下来,十万条问答规模下,从发问到拿到带来源的答案,端到端在 1 秒上下,比传统人工检索快了一个数量级。
如果客户端里看不到 MCP 工具被调用,先确认 MCP 服务进程起来了,再确认客户端配置里的 server 名称和 JSON 里的 key 一致。这两处对不上是最常见的“配了但没生效”。
5. 常见报错排查:401、维度不匹配与 OAuth 失败
这一节按我实际遇到的报错顺序写,每条都给现象和修法。
401 Unauthorized。现象是灌数据或问答时返回 401,报错体里带invalid api key。九成是 Key 填错或者 Base URL 拼错。检查两点:Key 是不是从 TaoToken 控制台复制的完整串,有没有多空格;Base URL 是不是https://taotoken.net/api,有没有手贱加成/v1。改完重启 MCP 服务进程,环境变量不会热加载。
local proxy failed。这个报错通常出现在 MCP 客户端启动服务时,本质是客户端连不上你配置的 command。如果你用的是npx启动,先确认本机 Node 版本够,再手动在终端跑一遍npx -y @modelcontextprotocol/server-mysql看能不能起来。起不来就是包没装成,换 npm 源重试。能起来但客户端报这个错,检查客户端配置里command的路径是不是绝对路径。
reading choices 相关报错。现象是模型返回体解析失败,报cannot read property 'choices' of undefined。这说明请求根本没拿到正常响应,先看 HTTP 状态码。如果是 200 但体里是错误结构,多半是模型 ID 写错,服务端返回了错误对象。把EMBEDDING_MODEL换成控制台里确认存在的模型 ID 再试。
向量维度不匹配。报错信息里带expected 1024 dimensions, got 1536之类。这是建表时 VECTOR(1024) 和你实际用的 embedding 模型维度对不上。要么换模型,要么改表结构重建向量列和索引。改表之前先把数据备份,向量列改维度需要重建索引。
OAuth 相关失败。如果你在 Claude Code 里配 MCP 时看到 OAuth 报错,说明客户端在尝试走 OAuth 流程而你的服务是纯 Key 认证。检查配置文件里有没有多余的oauth字段,删掉,只保留env里的 Key。Claude Code 的 MCP 配置对字段名敏感,多一个不认识的字段就可能触发认证分支。
索引不生效导致全表扫描。现象是查询慢,EXPLAIN显示走了全表扫描。确认向量索引状态是 visible,且查询语句里ORDER BY用的距离函数和建索引时的metric一致。cosine 索引配 l2 距离函数,优化器不会用索引。
6. 把问答入口接到你的业务里
跑通验证之后,下一步是把它变成团队能用的东西。最轻量的做法是把 MCP 服务挂在一个内部 HTTP 网关后面,前端页面发问题、网关转 MCP 调用、返回带来源的答案。这样客服和运维同学不需要装任何客户端,打开网页就能问。
模型侧继续用 TaoToken 的 OpenAI 兼容接口,问答链路里两次调用模型:一次把问题转成 embedding 做检索,一次把检索片段加问题组织成答案。两次调用共用同一个 Key 和 Base URL,配置里不用改。如果你后面想把这套链路接到编码工具里做研发知识库,Coding Plan 的额度模型更适合长期高频调用,接入方式在控制台文档里有现成示例。
知识更新这块别偷懒。文档改了之后要重新切片、重新 embedding、更新对应 doc_id 的行,否则问答会返回旧答案。我建议在灌数据脚本里加一个按updated_at增量同步的逻辑,每次只处理变更的文档,十万条规模下增量更新几秒就完成。定期跑一次全量重建索引,保证 HNSW 图结构不退化。
最后留一个实用技巧:问答返回里一定要带source字段并在前端展示出来。用户看到答案来自哪份文档的第几段,信任度完全不一样,这也是这套方案相比纯模型生成的最大优势。检索召回不准的时候,用户还能顺着来源自己去翻原文,比对着一个没有出处的答案干瞪眼强得多。