ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

构建基于Serverless架构的向量检索MCP Server:TaoToken统一Key接入实践

构建基于Serverless架构的向量检索MCP Server:TaoToken统一Key接入实践 1. 为什么要在 Serverless 上跑向量检索 MCP Server如果你正在给 AI Agent 接一套语义检索能力大概率会遇到三个绕不开的问题向量库要常驻、MCP Server 要鉴权、模型调用要单独配 Key。传统做法是买一台常开的机器把 OpenSearch 客户端、嵌入模型调用、MCP 协议处理全塞进去流量低谷时资源空转流量高峰时又扛不住。Serverless 架构正好解决这个矛盾——请求来了才计费没人用就缩到零。MCP Server 是什么简单说它是 Model Context Protocol 的服务端实现把「工具」以标准协议暴露给 Claude、Cline、Codex 这类客户端。Agent 不需要知道你的向量库在哪、用什么嵌入模型只要按 MCP 协议发 JSON-RPC 请求就能拿到相似度检索结果。适合谁适合正在做 RAG、知识库问答、智能推荐的开发者尤其是希望零运维、按量付费的团队。向量检索这块OpenSearch 的 k-NN 插件支持 FAISS、NMSLIB、Lucene 三种引擎HNSW 和 IVF 算法都有knn_vector字段配上余弦、内积、欧氏距离几十亿向量也能做到毫秒级响应。把它放在 Lambda API Gateway 后面再挂一个 DynamoDB 管会话状态整套链路就是纯 Serverless 的。但这里有个容易被忽略的环节鉴权与模型通道。MCP Server 要调用嵌入模型把文本转成向量还要校验客户端传来的 token。如果每个环节都单独申请 Key、单独配环境变量联调成本会很高。我这次的做法是用 TaoToken 统一 Key 接入把模型调用和 MCP 鉴权收敛到一条 API 通道上本地联调和线上部署用同一套凭证省掉大量切换成本。下面从架构到可复制配置一步步拆。2. TaoToken 前置准备统一 Key 与 MCP 鉴权通道在动手写 Lambda 之前先把凭证体系理清楚。MCP Server 的鉴权分两层一层是客户端到 MCP Server 的 token 校验API Gateway 自定义授权器负责另一层是 MCP Server 内部调用嵌入模型时的 API Key。传统做法是这两层各管各的环境变量一堆本地调试还要单独 mock。用 TaoToken 的好处是模型调用走统一通道Key 只需要维护一份。TaoToken 是什么它是一个统一的大模型 API 接入通道兼容 OpenAI 风格的接口格式嵌入模型、对话模型都能通过同一个 Base URL 和 Key 调用。对 MCP Server 来说这意味着generate_embedding函数里的EMBEDDING_API_URL和Authorization头可以固定下来不用为每个模型单独适配。适合谁适合需要在一个项目里调用多种模型、又不想管理多套凭证的开发者。具体操作上你需要先拿到一个 API Key。访问控制台创建https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite创建完 Key 后在 API Keys 页面可以查看和管理https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite这里有个细节要注意MCP Server 的鉴权 token 和 TaoToken 的 API Key 是两个不同的东西。前者是你自己定义的、用于 API Gateway 授权器校验的字符串比如MCP_AUTH_TOKEN后者是调用嵌入模型时用的。不要混用也不要把 TaoToken 的 Key 直接当成 MCP 客户端的 auth token否则一旦客户端泄露模型额度也会被滥用。接入文档在这里建议先扫一遍接口格式https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite如果你打算长期跑编码类 Agent或者需要更稳定的调用配额可以了解下 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite实测下来把嵌入模型的调用统一到 TaoToken 后Lambda 环境变量从原来的五六个缩减到三个MCP_AUTH_TOKEN、TAOTOKEN_API_KEY、OPENSEARCH_ENDPOINT。本地联调时只需要在.env里填这三个部署时通过 SAM 参数注入不用改代码。还有一点MCP 协议本身不规定鉴权方式API Gateway 的自定义授权器是最常见的做法。授权器 Lambda 收到请求后从authorizationToken里取出客户端传来的 token和MCP_AUTH_TOKEN比对匹配就返回 Allow policy否则 Deny。这个逻辑很简单但要注意methodArn的构造写错了会导致所有请求都被拒。3. 可复制配置Serverless 函数、MCP 工具定义与 OpenSearch 索引映射这一节是核心直接给可复制的配置片段。先看 OpenSearch 索引映射这是向量检索的地基。knn_vector字段的dimension必须和嵌入模型输出维度一致BGE-M3 是 1024 维写错了索引创建会失败。{ settings: { index: { knn: true, knn.algo_param.ef_search: 100 } }, mappings: { properties: { document_id: { type: keyword }, text: { type: text }, metadata: { type: object, enabled: false }, embedding: { type: knn_vector, dimension: 1024, method: { name: hnsw, space_type: cosinesimil, engine: faiss, parameters: { ef_construction: 128, m: 16 } } } } } }用 curl 创建索引curl -X PUT https://your-opensearch-endpoint/vector-index \ -H Content-Type: application/json \ -u username:password \ -d index-mapping.json接下来是 Lambda 的 MCP 工具定义。基于LambdaMCPServer类用tool()装饰器注册两个工具一个负责索引文本一个负责相似度检索。注意类型提示要写全MCP 客户端靠它生成输入 schema。from typing import Dict from lambda_mcp_server import LambdaMCPServer mcp_server LambdaMCPServer() mcp_server.tool() def index_text_with_embedding( text: str, document_id: str None, metadata: str {} ) - Dict: 将文本转换为向量并索引到知识库中 embedding_result generate_embedding(text) if embedding_result[status] ! success: return {status: error, message: embedding_result[message]} doc { document_id: document_id or str(uuid.uuid4()), text: text, metadata: json.loads(metadata), embedding: embedding_result[embedding] } return opensearch_client.write_document(doc[document_id], doc) mcp_server.tool() def text_similarity_search( text: str, k: int 10, score: float 0.0 ) - Dict: 通过向量相似度搜索相关文档 embedding_result generate_embedding(text) if embedding_result[status] ! success: return {status: error, message: embedding_result[message]} return opensearch_client.search_documents( embedding_result[embedding], k, score )嵌入函数走 TaoToken 统一通道Base URL 和 Key 从环境变量读import os import requests TAOTOKEN_API_KEY os.environ.get(TAOTOKEN_API_KEY) EMBEDDING_API_URL https://taotoken.net/api/v1/embeddings DEFAULT_MODEL bge-m3 def generate_embedding(text: str, model: str None) - Dict: model_name model or DEFAULT_MODEL payload { model: model_name, input: text, encoding_format: float } headers { Authorization: fBearer {TAOTOKEN_API_KEY}, Content-Type: application/json } try: response requests.post( EMBEDDING_API_URL, jsonpayload, headersheaders, timeout30 ) response.raise_for_status() result response.json() return { status: success, embedding: result[data][0][embedding], model: model_name } except Exception as e: return {status: error, message: fAPI请求失败: {str(e)}}API Gateway 自定义授权器 Lambdaimport os def lambda_handler(event, context): token event.get(authorizationToken) expected_token os.environ.get(MCP_AUTH_TOKEN) if token expected_token: return generate_policy(Allow, event[methodArn]) return generate_policy(Deny, event[methodArn]) def generate_policy(effect, resource): return { principalId: mcp-client, policyDocument: { Version: 2012-10-17, Statement: [{ Action: execute-api:Invoke, Effect: effect, Resource: resource }] } }SAM 模板里把参数串起来部署时一次性注入Parameters: McpAuthToken: Type: String NoEcho: true TaoTokenApiKey: Type: String NoEcho: true OpenSearchEndpoint: Type: String Resources: McpFunction: Type: AWS::Serverless::Function Properties: Handler: app.lambda_handler Runtime: python3.11 Environment: Variables: MCP_AUTH_TOKEN: !Ref McpAuthToken TAOTOKEN_API_KEY: !Ref TaoTokenApiKey OPENSEARCH_ENDPOINT: !Ref OpenSearchEndpoint部署命令sam build sam deploy --guided \ --parameter-overrides \ McpAuthTokenyour-mcp-token \ TaoTokenApiKeyyour-taotoken-key \ OpenSearchEndpointyour-opensearch-endpoint这里有个坑NoEcho: true会让参数在 CloudFormation 控制台不显示明文但部署日志里仍可能打印建议用--parameter-overrides从环境变量读取不要硬编码在脚本里。4. 验证请求curl 跑通检索链路与成功结果部署完成后先别急着接 MCP 客户端用 curl 直接打 API Gateway 的 endpoint确认鉴权和检索链路都通。MCP 协议走的是 JSON-RPC 2.0请求体里method是tools/callparams里带工具名和参数。先测tools/list确认工具注册成功curl -X POST https://your-api-id.execute-api.region.amazonaws.com/prod/mcp \ -H Content-Type: application/json \ -H Authorization: your-mcp-token \ -d { jsonrpc: 2.0, id: 1, method: tools/list, params: {} }正常返回应该包含两个工具的定义{ jsonrpc: 2.0, id: 1, result: { tools: [ { name: index_text_with_embedding, description: 将文本转换为向量并索引到知识库中, inputSchema: { type: object, properties: { text: {type: string}, document_id: {type: string}, metadata: {type: string} }, required: [text] } }, { name: text_similarity_search, description: 通过向量相似度搜索相关文档, inputSchema: { type: object, properties: { text: {type: string}, k: {type: integer}, score: {type: number} }, required: [text] } } ] } }接着索引一条文档curl -X POST https://your-api-id.execute-api.region.amazonaws.com/prod/mcp \ -H Content-Type: application/json \ -H Authorization: your-mcp-token \ -d { jsonrpc: 2.0, id: 2, method: tools/call, params: { name: index_text_with_embedding, arguments: { text: 厄尔尼诺监测系统用于追踪太平洋海温异常, document_id: doc-001, metadata: {\source\:\test\} } } }返回result.content[0].text里应该有status: success和写入的 document_id。然后做相似度检索curl -X POST https://your-api-id.execute-api.region.amazonaws.com/prod/mcp \ -H Content-Type: application/json \ -H Authorization: your-mcp-token \ -d { jsonrpc: 2.0, id: 3, method: tools/call, params: { name: text_similarity_search, arguments: { text: 太平洋海温异常监测, k: 5, score: 0.5 } } }成功的话会返回匹配的文档列表包含document_id、text和score。如果score阈值设太高可能返回空数组这时候把score降到 0.3 再试。实测下来BGE-M3 对中文语义的区分度不错同义改写能拿到 0.7 以上的相似度。验证嵌入模型通道是否走通可以单独打一次 TaoToken 的接口curl -X POST https://taotoken.net/api/v1/embeddings \ -H Authorization: Bearer your-taotoken-key \ -H Content-Type: application/json \ -d { model: bge-m3, input: 测试文本, encoding_format: float }返回的data[0].embedding应该是 1024 维的浮点数组。如果这里报 401说明 TaoToken 的 Key 有问题如果 MCP 那边报 401说明MCP_AUTH_TOKEN不匹配。两者要分开排查。5. 本篇常见错排查401、local proxy failed 与 reading choices联调阶段最容易卡在几个报错上逐个说清楚。401 UnauthorizedMCP 层curl 返回{message:Unauthorized}说明 API Gateway 授权器拒绝了请求。先确认Authorization头有没有带值是不是和MCP_AUTH_TOKEN完全一致。注意授权器 Lambda 里比对的是event.get(authorizationToken)API Gateway 会把Authorization头的值原样传进来不会自动去掉Bearer前缀。如果你在客户端加了Bearer授权器里也要相应处理否则永远不匹配。401 UnauthorizedTaoToken 层Lambda 日志里出现API请求失败: 401 Client Error说明TAOTOKEN_API_KEY无效或过期。去控制台重新生成一个更新 SAM 参数后重新部署。注意环境变量更新后 Lambda 需要重新部署才生效改控制台环境变量也可以但容易和 SAM 模板不一致建议统一走部署流程。local proxy failed这个报错通常出现在本地用 MCP 客户端连远程 endpoint 时。原因是客户端配置了本地代理但代理没启动或端口不对。检查客户端的 proxy 设置把http_proxy、https_proxy环境变量清掉或者确认代理服务在运行。MCP 客户端连 API Gateway 走的是标准 HTTPS不需要额外代理。reading choices 报错这个一般出现在解析嵌入模型返回时。如果 TaoToken 返回的data数组为空result[data][0]会抛 IndexError日志里可能显示成reading choices之类的变体。根因是请求体格式不对比如input传了空字符串或者model名字写错。加一层防御data result.get(data, []) if not data: return {status: error, message: 嵌入返回为空检查 input 和 model}OAuth 相关报错如果你用的 MCP 客户端要求 OAuth 流程而你的 Serverless MCP Server 只做了 token 鉴权会报OAuth not supported或类似错误。解决办法是在客户端配置里选择 token 鉴权模式或者把 API Gateway 授权器改成支持 OAuth 的 Cognito 授权器。对于内部工具场景token 鉴权足够不用上 OAuth。CC Switch / Cline MCP / Codex auth.json 配置如果你用 Cline 或 Claude Code 这类客户端接 MCP Server配置里要写全三件套——Base URL、Key、Model ID。以 Cline 的 MCP 配置为例{ mcpServers: { vector-search: { url: https://your-api-id.execute-api.region.amazonaws.com/prod/mcp, headers: { Authorization: your-mcp-token } } } }Codex 的auth.json里则是{ mcp: { vector-search: { baseUrl: https://your-api-id.execute-api.region.amazonaws.com/prod/mcp, apiKey: your-mcp-token, model: bge-m3 } } }注意这里的model是嵌入模型 ID不是对话模型。写错了会导致检索时嵌入维度不匹配OpenSearch 直接报dimension mismatch。OpenSearch 连接超时Lambda 默认超时 3 秒OpenSearch 冷启动或网络抖动时容易超时。把 Lambda 超时调到 30 秒VPC 配置确认能访问 OpenSearch 终端节点。如果 OpenSearch 开了细粒度访问控制username和password要写对否则报 403。6. 语义一致 CTA把统一 Key 接入落到你的项目里整套链路跑通后你会发现最省心的部分其实是凭证收敛。MCP Server 的鉴权 token 自己定义嵌入模型的调用走 TaoToken 统一通道本地联调和线上部署用同一套环境变量不用在多个平台之间来回切换。对于需要频繁调试嵌入模型、又不想每次改代码的团队这个模式能省下不少时间。如果你还没拿到 Key从控制台开始https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite接口格式和参数说明在文档里嵌入模型的input、model、encoding_format都有示例https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite想先在网页上试一下模型对话效果可以直接开对话页https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite长期跑编码类 Agent 或需要稳定配额的话Coding Plan 更合适https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite最后留一个实用技巧Lambda 里调用嵌入模型时加一层本地缓存。同样的文本重复索引时直接读缓存省掉一次 API 调用。用functools.lru_cache或者 DynamoDB 做持久化缓存都行实测能减少三成左右的嵌入调用量。OpenSearch 的knn_vector字段一旦创建就不能改维度建索引前务必确认嵌入模型的输出维度BGE-M3 是 1024别的模型可能是 768 或 1536写错了只能删索引重建。
返回列表