【愚公系列】《MCP协议与AI Agent开发》004-大模型原理及MCP开发基础(LLM 在应用中的典型接口模式)
发布时间:2026/9/30 0:33:37 作者:尧图编辑部 阅读量:1,286
)
1. 从一次“模型不听话”说起LLM 在 MCP 与 AI Agent 中的典型接口模式如果你正在做 MCP 协议相关的开发大概率遇到过这种场景你明明在 system 里写了“必须调用 get_weather 工具”模型却回你一句“今天北京天气不错建议出门带伞”。这不是模型笨而是你没把 LLM 的接口模式用对。LLM 在应用中的典型接口模式说白了就是三件事怎么把任务说清楚消息结构、怎么让模型决定动手函数调用、怎么把结果接回来消息回传。这三件事串起来才构成 MCP 协议驱动 Agent 调用链的底层骨架。这篇内容面向想理解 MCP 协议如何驱动 Agent 调用链的开发者。我会从 Completion 与 Chat 两种接口模式讲起重点落在函数调用Function Calling的完整往返流程上给出可复制的接口配置片段并用一次真实的函数调用往返验证端到端联调。中间会用到 TaoToken 统一 Key/API 通道把模型调用、工具描述、消息回传这条链路跑通。你不需要有 Agent 框架经验只要能跑 Python 就能跟下来。先明确一个认知MCP 协议本身不负责“生成”它负责“调度”。真正决定 Agent 能不能正确调用工具的是 LLM 接口层对函数描述的理解与结构化输出能力。所以理解 LLM 的接口模式是写 MCP Server 和 Agent 编排逻辑的前置条件。下面从最基础的两种接口模式开始拆。2. Completion 与 Chat 接口模式对比MCP 协议下该选哪种接口大模型的服务交互主要围绕 Completion 与 Chat 两种接口模式展开。二者共享生成式语言建模的底层机制但在输入格式、交互结构和适用场景上有明显差异。理解这个差异直接决定你后面写 MCP 工具描述时的消息组织方式。Completion 接口接收一段连续的 Prompt 作为输入不包含角色结构与消息历史。它适合短文本续写、摘要生成、格式化输出这类线性任务。结构简单、响应快但缺乏对语义上下文的长期保持能力。你在做单次工具结果润色时可以用它但一旦涉及多轮工具调用它就会丢上下文。Chat 接口采用多轮消息体结构显式引入角色标签system、user、assistant、tool能够完整建模对话历史。它适合上下文保持、任务规划、多轮控制等复杂语义场景也是 MCP 推荐使用的核心模型接口形式。原因很直接MCP 的工具调用需要把“用户请求 → 模型决策 → 工具执行 → 结果回传 → 模型总结”这条链路上的每一环都记录成消息而只有 Chat 接口的消息数组能承载这种结构化历史。下面这张表把两种模式在 MCP 场景下的关键差异列清楚你可以对照自己的任务选型。对比维度Completion 接口Chat 接口输入结构单段 Prompt 字符串messages 数组含 role/content角色支持无system / user / assistant / tool上下文保持弱需手动拼接强原生多轮函数调用支持一般不直接支持原生支持 tools/functionsMCP 适用性仅用于结果润色工具调度主通道典型场景摘要、格式化Agent 规划、工具调用在工程实现上主流平台普遍提供兼容 OpenAI SDK 风格的标准接口支持统一调用语法、可选流式输出、灵活上下文组织。这意味着你写 MCP Server 时可以用同一套 SDK 语法对接不同模型只要改 base_url 和 model 字段。这也是后面我用 TaoToken 统一通道的原因把 Key 和 base_url 收敛到一处MCP 工具描述和消息回传逻辑不用跟着模型换而重写。一个最小的 Chat 接口调用长这样注意 messages 数组里 system 和 user 的分工from openai import OpenAI client OpenAI( api_key你的 TaoToken API Key, base_urlhttps://taotoken.net/api ) response client.chat.completions.create( modeldeepseek-chat, messages[ {role: system, content: 你是一个 MCP 工具调度助手只输出结构化调用意图}, {role: user, content: 帮我查一下北京今天的天气} ], streamFalse ) print(response.choices[0].message.content)这段代码体现的是 Chat 接口的结构化优势任务指令、上下文语义与用户输入分离组织。在 MCP 协议里system 通常承载工具清单和调用约束user 承载真实请求assistant 承载模型决策tool 承载执行结果。四类角色各司其职调用链才清晰。如果你把工具描述塞进 user 里模型很容易把它当成普通文本忽略掉这是新手最常见的坑之一。3. 可复制的函数调用配置片段tools 描述与消息回传结构函数调用是 MCP 驱动 Agent 调用链的核心。它的本质是模型不直接执行函数而是生成一个包含调用意图的结构化响应由外部系统负责执行并回传结果再由模型处理输出。这个“解耦”设计是 MCP 协议能标准化调度工具的前提。要让模型正确生成调用意图你必须提前提供函数名、参数定义及说明文档。下面是一份可直接复制的 tools 配置片段我把它写成 JSON 结构你可以直接放进请求体也可以转成 Python 字典{ model: deepseek-chat, messages: [ {role: system, content: 你是一个 MCP 工具调度助手。当用户请求涉及天气时必须调用 get_weather 工具不要自行编造天气。}, {role: user, content: 查询今天北京的天气} ], tools: [ { type: function, function: { name: get_weather, description: 查询指定城市的实时天气返回温度、天气状况和风力, parameters: { type: object, properties: { city: { type: string, description: 城市名称例如北京、上海 }, unit: { type: string, enum: [celsius, fahrenheit], description: 温度单位默认摄氏度 } }, required: [city] } } } ], tool_choice: auto }这份配置里有三个关键点直接决定 MCP 调用链能不能跑通。第一description 要写“什么时候用”而不只是“这是什么”。我见过太多人把 description 写成“天气查询函数”结果模型在用户问“今天出门穿什么”时不敢调用。正确的写法是描述触发条件比如“当用户请求涉及天气、温度、穿衣建议时调用”。第二parameters 里的 required 要精确。如果你把 city 标成 required模型就必须从用户话里抽出城市如果用户没说城市模型会先反问而不是瞎编。这个约束是 MCP 工具调用可靠性的来源。第三tool_choice 设为 auto 让模型自己判断设为具体函数名则强制调用。在 Agent 编排里我通常先用 auto 观察模型决策确认稳定后再按场景收紧。当模型决定调用时返回的消息结构里会多出一个 tool_calls 字段而不是普通的 content。这个结构就是 MCP 协议里“调用意图”的载体response client.chat.completions.create( modeldeepseek-chat, messages[ {role: system, content: 你是一个 MCP 工具调度助手。当用户请求涉及天气时必须调用 get_weather 工具。}, {role: user, content: 查询今天北京的天气} ], tools[{ type: function, function: { name: get_weather, description: 查询指定城市的实时天气, parameters: { type: object, properties: {city: {type: string}}, required: [city] } } }], tool_choiceauto ) tool_call response.choices[0].message.tool_calls[0] print(tool_call.function.name) # get_weather print(tool_call.function.arguments) # {city: 北京}拿到这个结构化响应后你的 MCP Server 或 Agent 执行器负责真正调用天气 API然后把结果以 roletool 的消息回传。回传时必须带上 tool_call_id否则模型无法把结果和之前的调用意图对应起来。这一步是消息回传衔接的关键漏了 id 就会出现“模型重复调用同一工具”的死循环。回传结构如下messages.append(response.choices[0].message) # 带上 assistant 的 tool_calls messages.append({ role: tool, tool_call_id: tool_call.id, content: {city: 北京, temp: 26, weather: 晴, wind: 3级} }) final client.chat.completions.create( modeldeepseek-chat, messagesmessages, toolstools ) print(final.choices[0].message.content)到这里一次完整的“用户请求 → 模型决策 → 工具执行 → 结果回传 → 模型总结”往返就闭合了。MCP 协议做的事情本质上是把这套消息结构标准化让不同工具、不同模型之间能互相识别。4. 端到端联调验证用 TaoToken 统一 Key 跑通一次函数调用往返前面讲的是结构这一节讲怎么真正跑起来。我用 TaoToken 作为统一 API 通道原因是它把 Key 管理和 base_url 收敛到一处MCP 工具描述和消息回传逻辑不用因为换模型而重写。你只需要在 https://taotoken.net/api 这个 base_url 下换 model 字段即可。先准备环境。安装 OpenAI SDK这是目前兼容性最好的调用方式pip install openai然后到 TaoToken 控制台创建一个 API Key。地址是 https://taotoken.net/api-keys 创建后复制保存。注意 Key 只在创建时完整显示一次丢了就重新建。接下来写一个完整的验证脚本把函数调用往返跑通。这个脚本模拟了 MCP Server 收到工具调用后执行并回传的全过程from openai import OpenAI import json client OpenAI( api_key你的 TaoToken API Key, base_urlhttps://taotoken.net/api ) tools [{ type: function, function: { name: get_weather, description: 查询指定城市的实时天气当用户询问天气、温度、穿衣建议时调用, parameters: { type: object, properties: { city: {type: string, description: 城市名称} }, required: [city] } } }] messages [ {role: system, content: 你是一个 MCP 工具调度助手涉及天气必须调用工具。}, {role: user, content: 查询今天北京的天气} ] # 第一轮模型决策 resp1 client.chat.completions.create( modeldeepseek-chat, messagesmessages, toolstools, tool_choiceauto ) msg resp1.choices[0].message print(模型决策:, msg.tool_calls[0].function.name, msg.tool_calls[0].function.arguments) # 模拟工具执行 args json.loads(msg.tool_calls[0].function.arguments) tool_result {city: args[city], temp: 26, weather: 晴, wind: 3级} # 第二轮回传结果 messages.append(msg) messages.append({ role: tool, tool_call_id: msg.tool_calls[0].id, content: json.dumps(tool_result, ensure_asciiFalse) }) resp2 client.chat.completions.create( modeldeepseek-chat, messagesmessages, toolstools ) print(最终回复:, resp2.choices[0].message.content)运行后你应该看到类似输出模型决策: get_weather {city: 北京} 最终回复: 北京今天天气晴气温 26 摄氏度风力 3 级适合外出。如果你看到的是模型直接编造天气而没有 tool_calls说明 system 约束不够强或 description 没写触发条件。如果你看到 tool_calls 但第二轮报错多半是 tool_call_id 没对上。这两个问题下一节展开。验证模型对话能力时你也可以直接在 https://taotoken.net/model-chat 里手动发一条“查询北京天气”看模型是否返回结构化调用意图用来快速判断是模型问题还是代码问题。长期做 Agent 编排的话Coding Plan 更适合持续调试地址是 https://taotoken.net/coding-plan 。5. 常见报错排查401、local proxy failed、reading choices、OAuth这一节按真实报错来。你在 MCP 联调时遇到的错误八成集中在这四类。401 Unauthorized。最常见的原因是 Key 没带对或 base_url 写错。检查两点api_key 是不是从 https://taotoken.net/api-keys 复制的完整值base_url 是不是 https://taotoken.net/api 注意不要多加 /v1 或漏掉协议头。如果你用的是环境变量确认变量名和代码里读的一致。401 不会因为模型名写错而触发所以看到 401 先查 Key 和地址别去改 model。local proxy failed。这个报错通常出现在你本地配了转发规则但目标地址不可达时。排查顺序先确认 base_url 能通用 curl 直接打一下 https://taotoken.net/api 看返回再检查你的 HTTP 客户端有没有被系统级设置劫持。如果你在容器里跑确认容器网络能出网。这个错误和模型无关纯粹是链路问题。reading choices 相关报错典型如KeyError: choices或NoneType has no attribute choices。这几乎都是响应体不是预期结构导致的。原因有两个一是请求被网关拦截返回了 HTML 错误页SDK 解析失败二是流式模式下你按非流式解析。如果你开了 streamTrue必须用 for chunk in stream 迭代不能直接取 choices。另外当模型返回的是 tool_calls 时message.content 可能是 None你如果直接 print content 会看到 None这不是报错但容易误判。正确做法是先判断 message.tool_calls 是否存在。OAuth 相关报错。如果你在 Claude Code 或类似客户端里配置 MCP Server 时看到 OAuth 失败通常是客户端把 MCP 的鉴权和服务端 API 鉴权混在一起了。MCP Server 自身的启动鉴权用客户端配置而调用 LLM 的 Key 用 TaoToken 的 API Key两者不要混。Claude Code 接入时Base URL 填 https://taotoken.net/api Key 填 TaoToken 的 KeyModel ID 填 deepseek-chat 或你实际使用的模型名。这三件套缺一不可只填 Key 不填 Base URL 会走到默认地址导致鉴权失败。为了让你对照排查我把四类错误整理成表报错关键词最可能原因排查动作401 UnauthorizedKey 错误或 base_url 错误核对 api-keys 与 /api 地址local proxy failed本地转发链路不通curl 直连 base_url 验证reading choices响应非预期结构或流式解析错检查 stream 用法与 tool_callsOAuth failed鉴权层混用分离 MCP 鉴权与 LLM Key还有一个隐蔽的坑模型返回 tool_calls 后你回传 tool 消息时 content 必须是字符串。如果你传了 dict某些 SDK 会静默丢弃或报序列化错误。统一用 json.dumps 转字符串最稳。另外 tool_call_id 必须和上一轮 assistant 消息里的 id 完全一致复制时别漏字符。如果你在 Cline 或 CC Switch 里配 MCP记得把 Base URL、Key、Model ID 三件套都填全。Cline 的 MCP 配置里模型通道走 TaoToken工具通道走你本地 MCP Server两者通过消息结构衔接不要试图让 MCP Server 自己去调模型。职责分清调用链才稳。6. 把接口模式用进 MCP 调用链接入文档与后续调试入口走到这里你应该已经跑通了一次完整的函数调用往返。回头看LLM 在 MCP 与 Agent 场景下的接口模式其实就三层Chat 接口负责承载多轮消息结构tools 描述负责把工具能力翻译成模型能理解的约束tool 消息回传负责把执行结果接回上下文。这三层对齐了MCP 协议驱动的调用链就顺了。实际项目里我建议你把工具描述当成接口契约来维护。每加一个 MCP 工具就同步更新 description 和 parameters并在 system 里写清楚调用约束。模型不会读你的代码它只读你给它的描述。描述写得越像“什么时候用”调用就越准。如果你要接着调试更多模型或工具组合接入文档在 https://taotoken.net/doc 里面有各语言 SDK 的 base_url 和参数说明。需要新建或轮换 Key 时到 https://taotoken.net/api-keys 。想快速验证某个模型对工具描述的理解能力直接去 https://taotoken.net/model-chat 发一条带工具意图的请求看返回结构。长期做 Agent 编排和 MCP Server 开发的话https://taotoken.net/coding-plan 更适合持续联调。最后留一个实用习惯每次改完 tools 描述先跑一遍本文第 4 节的验证脚本确认模型能稳定返回 tool_calls 再接入真实工具。这一步花两分钟能省掉后面大量“模型不调用工具”的排查时间。