前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
返回 AI 情报前线
All News · 全部资讯9915
  • Apigee 支持在自有集群运行 MCP 工具
  • 编程 Agent 源码研究:架构比功能清单耐用
  • 把 GPU 内核调优变成夜间并行搜索
  • AI 生成的 Supabase 权限代码可能允许提权
  • Agent 成本控制要先预留并发调用预算
  • 全 AI 编码项目如何靠质量关卡持续交付
  • AI反编译加速老游戏移植,也带来风险
  • 用 AgentCore 构建可适应新格式的文档处理
  • 用 Issue 标签驱动 AI 分析与提 PR
  • AI 辅助六天串起 Chrome 漏洞利用链
  • Claude Code 改造电商站的操作与复盘
  • Agent 平台缺少身份配置却开放管理权限
  • 英特尔将至强推理调优封装进容器
  • 用 AgentCore 将企业文档生成演示文稿
  • 用 Bedrock Agent 编排云安全修复
  • Orion-1 面向网络防御,试补通用模型限制
  • 用 JumpStart 完成模型部署与性能测试
  • GPT-4 系列部分 API 据报将停用
  • 工厂智能排产应先建约束模型
  • 法律 RAG 的核心是有据回答与可靠拒答
  • Agent 如何调用工具:从函数接口到 MCP
  • 给 AI 功能算清每月 API 账单
  • 用可验证进度训练长任务 Agent
  • Agent 数据架构怎么选:五种方案对照
  • 终端编程助手实测:正确率之外的差异
  • curl 安全更新揭示 AI 漏洞研究成效
  • 视频编辑智能体如何准确操作时间线
  • Fish Audio 新语音模型开放权重,部署门槛仍高
  • n8n 节点改名后,副作用策略为何失效
  • Gemini 4 Argon 迁移需查参数兼容与计费
  • 智能体审批如何防止越权与重复执行
  • XBOW 披露 AI 辅助 Chrome 漏洞研究
  • Liquid AI 推出面向本地智能体的 MoE
  • 让编程 Agent 从源码开始排查故障
  • 把团队交付流程写成 Agent 技能
  • 多步 Agent 提分,未必是规划有效
  • Vercel沙箱挑战揭示内核隔离风险
  • AI实验室如何让万级代理持续运行
  • Gemini 4 Argon正式可用,评测跟进
  • Gemini 4 Argon 据报支持百万输出
  • TokenRouter 通过逐词元路由提升推理吞吐
  • 用结构化证据账本改善长视频推理
  • 小模型运行 Anatomy:检查通过却产出简陋
  • 多家 LLM 接口容错的限流处理经验
  • 给 AI 测试提示补上意图与行为清单
  • Agent 决策之外,还需可靠工作流
  • DeepMind 与新加坡探索端侧反诈模型
  • Vercel免费接入Nemotron长上下文模型
  • AI Studio应用可私密部署到安卓
  • AI重构通过测试,仍可能破坏接口契约
  • 智能体注册测试需明确邮件领取规则
  • 已加载 51 / 9915
8.0
热点
AI SCORE
技术实践2026-10-14 21:00

Agent 如何调用工具:从函数接口到 MCP

dev.to · AI#Agent#MCP#工具调用
Editor brief · 编辑速览

文章以邮件助手为例,解释 Agent 如何通过结构化函数调用访问外部服务,以及 MCP 如何统一工具接入。重点强调工具的自然语言描述会影响 Agent 对调用时机和参数的判断。

文章思维导图
Knowledge map
拖拽缩放
Full translation

完整中文译文

在 AI 系统设计系列中,我们始终以 Mailmind 为例。这是一款虚构的 AI 邮件助手,可以读取收件箱、对邮件进行分类处理,并起草回复。

LLM(大语言模型)智能体向外部服务发送结构化的函数调用。这些调用就是工具调用。智能体输出工具名称和一组参数。工具在 LLM 外部运行,并返回文本结果,作为一条观察信息。MCP(Model Context Protocol,模型上下文协议)将这一过程标准化,让任何智能体都能使用任何工具服务器,而无需编写定制的集成代码。

工具最重要的部分不是代码,而是智能体用来判断何时以及如何使用它的自然语言描述。描述写错了,工具就成了累赘;描述写对了,智能体就能在恰当的时机使用它。

在第 3 部分中,你已经看到记忆如何让智能体持续保有对过去的认知。现在,我们转向当下:智能体如何操作自身上下文窗口之外的世界。我们将继续以基于收件箱的 AI 助手 Mailmind 为例贯穿全文。

LLM 智能体内部的工具调用是如何工作的?

智能体的推理循环在思考、行动和观察之间不断轮转。行动就是一次工具调用。智能体先思考自己需要什么,然后针对某个具体工具输出结构化请求,并提供具体输入。系统执行该工具,再将结果作为观察信息送回智能体的上下文,供下一轮思考使用。

这就是你在第 1 部分中已经了解的 ReAct 循环。这里新增的内容是工具本身。每个工具都有三个组成部分:名称、自然语言描述,以及输入模式(通常是 JSON(JavaScript Object Notation,JavaScript 对象表示法)Schema)。智能体会读取描述。它告诉模型何时使用工具、工具能做什么、需要哪些输入,以及会返回什么。好的描述还会说明何时不该使用该工具。

下面是 Mailmind 的 get_email_info 的一个糟糕描述:

Gets email information.
Retrieve the full content and metadata of a single email by its ID.
Use this when you need to read the body, headers, or attachments of one specific message.
Do not use this to search across threads. Use search_inbox for that.
Returns the subject, sender, recipients, date, body text, and a list of attachment IDs.

两者的差别非常大。读到好描述的智能体会在正确的时机调用正确的工具。读到糟糕描述的智能体只能猜测,而且经常猜错。

Mailmind 中的流程如下:

flowchart LR
    A[Thought: Need to find the invoice] --> B[Tool call: search_inbox("invoice from last week")]
    B --> C[External system: IMAP search]
    C --> D[Observation: list of matching email IDs]
    D --> E[Thought: Retrieve the most recent one]
    E --> F["Tool call: get_email_info(id=...)"]
    F --> G[Observation: full email content]
    G --> H[Thought: Extract the invoice number]

智能体从未直接接触真正的邮件服务器。它只能看到模式定义和返回的文本。

为什么工具描述比代码更重要?

描述是智能体理解工具语义的唯一接口。模型无法访问实际实现,不能检查代码,也无法从副作用中学习。它完全依据提示词和历史观察信息进行推理。如果描述含糊不清,智能体就会臆想出工具并不具备的用途。如果描述只说“获取邮件”,智能体可能会尝试用它来搜索、删除邮件,或者获取整个邮件会话。这些尝试都会失败,而且失败的方式让人摸不着头脑。

描述也是防止误用最有效的地方。你可以写入诸如“切勿将此工具用于批量操作”或“此工具较慢,优先使用缓存结果”这样的规则。只要在提示词中清楚地表达这些规则,智能体就会遵循。关键不是寄希望于模型表现良好,而是给它正确的指令。

输入模式是第二个组成部分。它定义参数的结构。但仅凭模式,并不能告诉智能体何时使用工具,描述才可以。这就是为什么资深工程师花在描述上的时间比花在函数体上的时间更多。

智能体如何处理工具故障,避免打乱对话?

工具调用可能因多种原因失败:网络可能中断,API(application programming interface,应用程序编程接口)可能返回 500,输入格式也可能不正确。智能体必须妥善处理这些故障,否则,一次暂时性的错误就可能污染整个上下文窗口。

系统将故障分为三类。暂时性故障(超时、速率限制、临时服务器错误)会采用指数退避进行重试。永久性故障(未找到资源、参数无效、身份验证错误)不会重试。系统会将错误告知智能体,让它思考替代方案。部分结果(例如搜索返回了一些条目,同时附带一条警告)会被封装为结构化的观察信息,并标明结果不完整。随后,智能体可以决定是利用现有结果继续,还是尝试其他策略。

外部服务不可用时,会触发熔断器。连续失败次数达到可配置的阈值后,熔断器进入打开状态,智能体停止调用该工具。系统会告知智能体工具不可用,并在存在备用方案时提供该方案。在 Mailmind 中,如果 send_confirmation 工具持续失败,智能体可能会退而求其次,在用户的仪表盘上添加一条说明,而不是发送邮件。用户会看到警告,而不是遭遇静默失败。

关键在于,智能体从不盲目重试。它总能获得失败原因,并据此决定下一步做什么。

graph TD
    A[Tool call fails] --> B{Failure type?}
    B -->|Transient| C[Retry with backoff]
    B -->|Permanent| D[Return error reason]
    B -->|Dead service| E[Open circuit breaker]
    C --> F[Agent decides next step]
    D --> F
    E --> F

如何避免工具输出挤爆上下文窗口?

工具返回的每个 token 都会占用模型有限的注意力预算。一次搜索如果返回 200 封邮件的摘要,就可能瞬间撑爆上下文窗口。智能体随后会忘记对话中更早的步骤。这是一种灾难性的失败。

第一道防线是设计工具时,只返回智能体需要的内容。Mailmind 中的 search_inbox 工具不会返回完整的邮件正文,而是返回 ID 列表,并为每条结果附上一小段内容片段。随后,智能体只需对真正想阅读的邮件调用 get_email_info。

{
  "type": "comparison",
  "title": "Context Window Budget",
  "caption": "Returning only essential fields keeps the context window lean. Illustrative token counts.",
  "before": {
    "label": "Before Optimization",
    "points": [
      "Full email bodies returned",
      "200 emails = 25,000 tokens",
      "Agent hits context limit quickly",
      "Relevant details buried"
    ]
  },
  "after": {
    "label": "After Optimization",
    "points": [
      "Only ID, subject, sender, date",
      "200 emails = 800 tokens",
      "Plenty of room for reasoning",
      "Agent sees only what it needs"
    ]
  }
}

第二道防线是分页。工具返回一页结果和一个游标。智能体可以在需要时逐页获取结果,但应鼓励它尽早停止。描述中会写明:“最多返回 10 条结果。如果需要更多,请使用游标,但先考虑是否真的需要全部结果。”

第三道防线是摘要。当工具返回大量结果后,可以通过后处理步骤对输出进行摘要,再将其放入智能体的上下文。这样做有风险,因为摘要可能遗漏重要细节。只有在输出确实过大,而且智能体不需要逐字读取时,才应使用这种方法。

第四道防线是缓存。在智能体的单次运行中,参数相同的同一工具调用应返回同一份缓存结果。这样既能避免重复工作,也能节省 token。缓存的作用范围限于本次运行,并在对话结束时清除。

涉及多个系统的工具调用失败时,会发生什么?

这是大多数工程师会忽略的问题。Mailmind 本应归档一个邮件会话、更新用户的任务列表,再发送确认通知。前两次调用成功了,第三次却静默失败。用户看到任务列表已更新,却没有收到确认通知。系统此时就处于不一致的状态。

绝不要回滚已经在不同系统中完成的更新。归档操作已经写入邮件服务器,任务更新已经写入任务数据库,两者都属于外部系统。回滚是另一次操作,也可能失败。很快,你就会制造出一堆补偿事务,而这些事务本身又需要重试。系统会变得一团糟。

应改用发件箱模式(outbox pattern)。智能体将计划执行的操作写入本地发件箱表。每个操作对应一行记录:“向用户 X 发送内容为 Y 的确认邮件。”智能体写入发件箱的工具调用是同步的,而且很快。随后,智能体向用户报告成功。后台工作进程读取发件箱,并在真实的外部系统中执行这些操作。它会独立重试每个操作,直到成功。如果某个操作在多次重试后仍然失败,就会被移入死信队列,并触发告警。此时可以由人工介入。

发件箱模式将智能体跨多个系统的操作转化为一次原子写入。剩下的部分通过最终一致性来完成。智能体的上下文不会看到那些暂时性故障。用户最终会收到确认。系统会自行恢复。

所有可靠的分布式系统都在使用同一种模式。智能体只是另一个客户端。

MCP 如何实现工具集成的标准化?

在 MCP 出现之前,每次智能体集成都需要编写一套定制的胶水代码。你先写一个调用 Gmail API 的 Python 函数,再按照智能体框架要求的特定格式封装它。对于 Calendar、Notion、Slack,也要重复同样的工作。每个工具都有各自的发现机制、错误处理方式和身份验证方式。工程成本很高,最终的集成也很脆弱。

MCP(Model Context Protocol,模型上下文协议)就像 AI 工具的 USB-C。它定义了智能体发现和调用工具的标准方式。智能体是客户端,会连接到一个或多个 MCP 服务器。每个服务器暴露一组工具。智能体向服务器请求可用工具列表,服务器则返回每个工具的名称、描述和输入模式。随后,智能体发送包含工具名称和参数的 tools/call 请求来调用工具。服务器执行工具并返回结果。

该协议不依赖特定的传输方式。对于本地工具,它可以通过 stdio 运行;对于远程工具,可以通过结合 Server-Sent Events 的 HTTP 运行,也可以通过 WebSockets 运行。同一个服务器可以供任何 MCP 客户端使用,同一个客户端也可以使用任何 MCP 服务器。针对每个外部系统,服务器作者只需完成一次集成工作。智能体开发者只需让客户端连接到相应的服务器。

在 Mailmind 中,你可以分别为 Gmail、任务管理器和日历配置一个 MCP 服务器。智能体连接这三个服务器,工具列表会自动构建。无需编写任何定制代码,智能体就能看到一组统一的工具。

MCP 底层究竟如何工作?

客户端向服务器发起连接,双方协商协议版本。服务器发送能力响应,列出其支持的工具,以及能够提供的资源(结构化数据)和提示词(模板化消息)。随后,客户端可以调用 tools/list 获取完整的工具元数据。服务器返回一个包含工具定义的 JSON 数组,客户端据此构建智能体的提示词。

当智能体发出工具调用时,客户端会发送 tools/call 请求。请求包含工具名称,以及一个由参数组成的 JSON 对象。服务器根据模式校验参数。如果参数有效,服务器就执行工具,并以包含 content 字段的 JSON 响应返回结果。客户端将结果封装为一条观察信息,提供给智能体。

流程如下:

flowchart TD
    A[Agent client] -->|connect| B[MCP server]
    B -->|capabilities + tools| A
    A -->|tools/call| B
    B -->|executes tool| C[External system]
    C -->|result| B
    B -->|content response| A
    A -->|observation| D[Agent context]

协议有意保持简单。真正的复杂性在于服务器的实现。客户端只是一个轻量的转换层,负责在智能体的内部表示与 MCP 的传输格式之间进行转换。这种分离意味着,服务器的改进,例如更完善的错误处理、身份验证和重试机制,对智能体而言都是透明的。智能体看到的始终是稳定的工具接口。

常见问题

问:我不能直接使用 LLM 内置的函数调用 API 吗?为什么还需要 MCP?

内置的函数调用 API 定义了调用的格式,却没有定义工具发现机制或传输方式。你仍然需要编写代码,将调用连接到真实系统。MCP 将这层连接标准化,提供了任何智能体都能使用的可复用服务器。LLM 的 API 负责最后一环,MCP 处理其余部分。

问:如何测试与第三方 API 交互的工具?

先单独测试工具与外部系统的集成,再使用返回受控响应的模拟服务器,测试智能体对工具的使用。测试智能体的推理能力时,可以给它设置场景,并检查它是否使用正确的参数调用了正确的工具。测试工具本身的正确性时,则让它连接到沙箱 API。

问:对于简单的智能体,发件箱模式是不是小题大做?

如果智能体只操作一个系统,就没有必要使用发件箱。一旦它需要写入两个或更多相互独立的系统,发件箱就是唯一安全的模式。对于演示项目,丢失一条确认消息的代价很低。对于用户依赖的生产系统,这是一项硬性要求。

问:MCP 如何处理身份验证?

MCP 没有定义身份验证标准。服务器和客户端在协议之外协商身份验证。常见配置使用环境变量、OAuth 令牌或 sidecar 代理。协议本身不进行身份验证。安全边界由传输层和服务器的访问控制构成。

问:工具能调用另一个工具吗?递归循环又该如何处理?

工具可以在内部调用另一个工具。这只是服务器内部的函数组合,智能体只能看到最终结果。也可能出现这样的递归循环:智能体调用某个工具,而该工具又触发另一次智能体调用。但这很危险。系统必须强制限制最大调用深度和 token 预算。没有这些限制,智能体可能会永远循环下去。

Mailmind 的 send_confirmation 工具总是返回成功状态,但用户反馈有时没有收到确认邮件。工具代码调用第三方邮件 API,并检查 HTTP 状态码,将任何 2xx 响应都视为成功。最可能的根本原因是什么?你会如何修复?

答案:该工具混淆了 API 接受请求与邮件实际送达。邮件服务商返回 2xx 响应,意味着请求已被接受,并不意味着邮件已经送达。服务商可能先将邮件放入队列,随后又悄无声息地丢弃它。工具发出的成功信号过早。修复方法是记录服务商返回的消息 ID,并异步验证是否送达。send_confirmation 工具写入一条包含消息 ID 的发件箱记录。后台进程使用服务商的事件 API 轮询投递状态。如果在 30 分钟内仍未确认送达,就将该记录移入死信队列并触发告警。随后,智能体可以改用其他渠道,或告知用户。关键是,在确认送达之前,都应将该操作视为仍在处理中,而不能在 API 返回“OK”时就认定操作已经完成。

如果你想设计能够可靠执行实际操作的智能体,而不只是谈论这些操作,可以在 internalsdecoded.com 订阅 Internals Decoded。下一篇将介绍:智能体如何规划一系列工具调用,以实现复杂的多步骤目标。

本文最初发表于 Internals Decoded。以通俗的对话方式讲解 AI 内部原理。

如需采取进一步措施,你可以考虑屏蔽此人和/或举报滥用行为。

Original source

本文由 AI 翻译整理自 dev.to · AI,原文版权归原作者所有。

阅读英文原文
上一篇
法律 RAG 的核心是有据回答与可靠拒答
下一篇
给 AI 功能算清每月 API 账单