💡

文章摘要

系统讲解 AI Agent 工具调用的完整技术栈:从 Function Calling 基础原理到 MCP 协议标准,从单工具调用到多工具自主编排。涵盖架构设计、错误处理、容错机制、实战代码,以及 2026 年最新进展。

一、为什么 Agent 需要工具调用?

LLM 的本质局限:强大的语言理解,但缺乏行动能力。 大语言模型LLM)在文本理解、推理、生成方面表现出色,但它们本质上只是"文字接龙"——根据输入预测下一个 token。这种架构决定了 LLM 有几个致命缺陷:

第一,知识截止问题。 LLM 的训练数据有截止日期,无法获取实时信息。GPT-4 的知识截止到 2023 年,Claude 3.5 的知识截止到 2024 年初。如果你问"今天北京的天气"或"最新的股票价格",纯 LLM 要么胡编乱造(幻觉),要么拒绝回答。

第二,无法执行动作。 LLM 可以告诉你如何发送邮件,但它自己不能发邮件。它可以写出查询数据库的 SQL,但它不能直接执行这条 SQL。LLM 被困在"只能说,不能做"的困境中。

第三,计算能力有限。LLM 计算 1234 × 5678,它可能会出错。让 LLM 分析一个 10MB 的 CSV 文件,它根本做不到。LLM 不擅长精确计算和大规模数据处理。

第四,幻觉问题。 LLM 会编造事实、虚构引用、捏造数据。在没有外部验证机制的情况下,LLM 的输出不可完全信任。

工具调用:让 LLM 从"能说"到"能做"。 工具调用Tool Use / Function Calling)是解决上述问题的关键机制。通过工具调用LLM 可以:

  • 获取实时信息:调用天气 API 获取当前天气,调用搜索引擎获取最新新闻
  • 执行动作:发送邮件、创建文件、调用支付接口、操作数据库
  • 精确计算:调用计算器、执行代码、运行数据分析脚本
  • 验证事实:查询数据库、检索知识库、调用事实核查工具

2026 年的 Agent 工具调用现状: 根据 Anthropic 2026 年 6 月发布的递归自我改进报告,Claude 在生产环境中已经承担了80% 的代码编写工作。这些代码编写任务不仅仅是生成代码片段,而是包括调用 API、操作数据库、部署服务等完整的工具链操作。工具调用已经成为 Agent 的核心能力,没有工具调用的 Agent 就像没有手脚的大脑——能思考,但无法改变世界。

图表加载中…

💡 一句话理解

设计 Agent 时,先列出 Agent 需要完成的任务,然后为每个任务设计对应的工具。工具是 Agent 与真实世界的桥梁。

⚠️ 常见踩坑

不要给 LLM 太多工具。工具过多会导致选择困难、调用错误、上下文爆炸。先从小而精的工具集开始,逐步扩展。

二、Function Calling 的演进

2023 年 6 月:OpenAI 开创 Function Calling OpenAI 在 GPT-3.5/4 API 中首次引入 Function Calling 机制。核心思想:开发者定义一组函数的 JSON Schema,LLM 在需要时输出函数调用请求,由外部执行后将结果返回给 LLM

OpenAI Function Calling 的工作流程:

  1. 开发者在 API 请求中定义可用函数(名称、描述、参数 Schema)
  2. LLM 根据对话上下文决定是否调用函数
  3. 如果需要调用,LLM 输出函数名和参数(JSON 格式)
  4. 外部系统执行函数,获取结果
  5. 将结果返回给 LLMLLM 基于结果生成最终回复

2023 年下半年:Anthropic 和 Google 跟进。 Anthropic 在 Claude 2/3 中引入 Tool Use,Google 在 Gemini 中引入 Function Calling。虽然名称不同,但核心机制类似:定义工具 Schema → LLM 决策调用 → 外部执行 → 结果返回。

关键差异:

  • OpenAI:支持并行函数调用(parallel function calling),可以一次调用多个函数
  • Anthropic:强调工具描述的重要性,提供更细粒度的工具控制
  • Google:将 Function Calling 与 Google Cloud 生态深度集成

2024-2025 年:开源生态爆发。 LangChainLlamaIndex、AutoGen 等框架提供了统一的工具调用接口,屏蔽了不同 LLM 提供商的差异。开发者可以编写一次工具定义,在 GPT-4、Claude、Gemini、Llama 之间无缝切换。

2026 年的工具调用趋势:

  • 标准化工具描述:JSON Schema 成为事实标准,OpenAPI 规范被广泛用于定义工具
  • 工具市场(Tool Marketplace):出现专门的平台(如 Composio、Toolhouse)提供预构建的工具集成
  • 自主工具发现:Agent 可以根据任务需求自动搜索和加载合适的工具
  • 工具组合与编排:从单工具调用演进到多工具协同,支持复杂的工具链工作流
typescript
openai-function-calling.ts
import OpenAI from 'openai';

const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
});

// 1. 定义工具 Schema
const tools: OpenAI.Chat.Completions.ChatCompletionTool[] = [
  {
    type: "function",
    function: {
      name: "get_weather",
      description: "获取指定城市的当前天气信息",
      parameters: {
        type: "object",
        properties: {
          city: {
            type: "string",
            description: "城市名称,例如:北京、上海",
          },
          unit: {
            type: "string",
            enum: ["celsius", "fahrenheit"],
            description: "温度单位",
          },
        },
        required: ["city"],
      },
    },
  },
  {
    type: "function",
    function: {
      name: "search_flights",
      description: "搜索航班信息",
      parameters: {
        type: "object",
        properties: {
          from: { type: "string", description: "出发城市" },
          to: { type: "string", description: "到达城市" },
          date: { type: "string", description: "日期 YYYY-MM-DD" },
        },
        required: ["from", "to", "date"],
      },
    },
  },
];

// 2. 调用 LLM,传入工具定义
const response = await openai.chat.completions.create({
  model: "gpt-4-turbo",
  messages: [
    {
      role: "user",
      content: "北京今天天气怎么样?我想明天飞去上海。",
    },
  ],
  tools: tools,
  tool_choice: "auto", // LLM 自主决定是否调用工具
});

// 3. 处理工具调用请求
const message = response.choices[0].message;

if (message.tool_calls) {
  // LLM 请求调用工具
  for (const toolCall of message.tool_calls) {
    const functionName = toolCall.function.name;
    const functionArgs = JSON.parse(toolCall.function.arguments);
    
    // 4. 执行工具(这里调用真实的 API)
    const result = await executeTool(functionName, functionArgs);
    
    // 5. 将结果返回给 LLM
    const finalResponse = await openai.chat.completions.create({
      model: "gpt-4-turbo",
      messages: [
        ...messages,
        message,
        {
          role: "tool",
          tool_call_id: toolCall.id,
          content: JSON.stringify(result),
        },
      ],
      tools: tools,
    });
    
    console.log(finalResponse.choices[0].message.content);
  }
}

💡 一句话理解

工具描述(description)的质量直接影响 LLM 的调用准确性。写清楚工具的功能、参数含义、使用场景,就像写 API 文档一样。

⚠️ 常见踩坑

不同 LLM 提供商的 Function Calling 实现有细微差异(参数命名、并行调用支持、错误处理)。生产环境务必做充分的兼容性测试。

三、工具调用的核心架构

工具调用系统的四个核心组件: 一个完整的工具调用系统包括:Schema 定义层、路由决策层、执行引擎层、结果回传层。

1. Schema 定义层(Tool Definition)

Schema 是工具的"身份证",告诉 LLM:这个工具叫什么、能做什么、需要什么参数、返回什么结果。

Schema 设计原则:

  • 单一职责:每个工具只做一件事,不要设计"万能工具"
  • 语义化命名:工具名和参数名要自解释(get_weather 而非 func_001)
  • 完整描述:description 要详细说明功能、限制、使用场景
  • 参数验证:使用 JSON Schema 定义参数类型、必填项、枚举值、范围限制

2. 路由决策层(LLM Router)

LLM 充当"路由器",根据用户请求和上下文决定:

  • 是否需要调用工具?
  • 调用哪个工具?
  • 传递什么参数?
  • 是否需要并行调用多个工具?

路由策略:

  • 自主路由LLM 完全自主决定(tool_choice: "auto")
  • 强制路由:必须调用特定工具(tool_choice: {"type": "function", "function": {"name": "xxx"}})
  • 无路由:禁用工具调用(tool_choice: "none")

3. 执行引擎层(Tool Executor)

执行引擎负责:

  • 解析 LLM工具调用请求
  • 验证参数合法性
  • 调用真实的外部 API/函数
  • 处理超时、重试、错误

执行模式:

  • 同步执行:等待工具返回结果(适合快速 API)
  • 异步执行:立即返回任务 ID,稍后查询结果(适合耗时操作)
  • 流式执行:工具执行过程中实时返回部分结果(适合长任务)

4. 结果回传层(Result Formatter)

工具执行结果需要格式化后返回给 LLM

  • 结构化结果:JSON 格式,便于 LLM 解析
  • 错误处理:工具失败时返回错误信息,而非抛出异常
  • 结果截断:如果结果过大,需要截断或摘要,避免超出上下文窗口
图表加载中…
typescript
tool-schema.ts
// 工具 Schema 定义最佳实践
const weatherToolSchema = {
  type: "function",
  function: {
    name: "get_weather",
    description: "获取指定城市的当前天气信息,包括温度、湿度、风速、天气状况。支持实时天气和未来 7 天预报。",
    parameters: {
      type: "object",
      properties: {
        city: {
          type: "string",
          description: "城市名称,支持中文和英文,例如:北京、Beijing、上海",
        },
        unit: {
          type: "string",
          enum: ["celsius", "fahrenheit"],
          description: "温度单位:celsius(摄氏度)或 fahrenheit(华氏度),默认 celsius",
          default: "celsius",
        },
        forecast_days: {
          type: "integer",
          description: "预报天数,0 表示当前天气,1-7 表示未来天数",
          minimum: 0,
          maximum: 7,
          default: 0,
        },
      },
      required: ["city"],
      additionalProperties: false, // 禁止未定义参数
    },
  },
};

💡 一句话理解

使用 JSON Schema 的完整特性(type、enum、minimum、maximum、required)来约束参数,减少 LLM 输出非法参数的概率。

⚠️ 常见踩坑

工具执行引擎必须做参数验证和沙箱隔离。LLM 可能被提示注入攻击诱导调用危险工具或传递恶意参数。

四、MCP(Model Context Protocol):2026 年的工具标准化

什么是 MCP MCPModel Context Protocol)是 Anthropic 在 2024 年底推出的开放标准协议,旨在统一 LLM 与外部工具/数据源的交互方式。可以把 MCP 理解为"AI 工具的 USB 接口"——无论什么工具,只要遵循 MCP 协议,就能被任何支持 MCP 的 Agent 使用。

为什么需要 MCPMCP 出现之前,每个 LLM 提供商都有自己的工具调用格式,每个工具集成都需要定制开发。这导致:

  • 工具碎片化:为 GPT-4 开发的工具不能直接用于 Claude
  • 重复开发:每个 Agent 框架都要重新实现工具集成
  • 互操作性差:不同系统之间的工具无法共享

MCP 的核心架构:

MCP 采用客户端-服务器架构:

  • MCP Client(客户端)嵌入在 Agent/LLM 应用中,负责连接 MCP Server
  • MCP Server(服务器):封装具体的工具实现,对外暴露标准化的接口
  • Resources(资源)MCP Server 提供的数据源(文件、数据库、API)
  • Tools(工具)MCP Server 提供的可调用函数
  • Prompts(提示词模板)MCP Server 提供的预定义提示词

MCP 的工作流程:

  1. Agent 启动时,连接到配置的 MCP Server
  2. MCP Server 暴露可用的工具和资源列表
  3. Agent 根据任务需求,通过 MCP 协议调用工具
  4. MCP Server 执行工具,返回标准化结果
  5. Agent 基于结果继续推理或生成回复

2026 年 MCP 生态现状:

  • 主流 LLM 支持:Claude、GPT-4、Gemini、Llama 都已支持 MCP
  • 工具市场:出现 MCP 工具市场(如 mcp.so),提供数千个预构建的 MCP Server
  • 企业采用:越来越多的企业将内部系统封装为 MCP Server,供 Agent 调用
  • 框架集成LangChainLlamaIndex、AutoGen 都提供 MCP 客户端实现

MCP vs Function Calling

图表加载中…
typescript
mcp-server.ts
import { Server } from '@modelcontextprotocol/sdk/server';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
} from '@modelcontextprotocol/sdk/types';

// 创建 MCP Server
const server = new Server(
  {
    name: "weather-mcp-server",
    version: "1.0.0",
  },
  {
    capabilities: {
      tools: {}, // 声明支持工具调用
    },
  }
);

// 注册可用工具列表
server.setRequestHandler(ListToolsRequestSchema, async () => {
  return {
    tools: [
      {
        name: "get_weather",
        description: "获取指定城市的当前天气信息",
        inputSchema: {
          type: "object",
          properties: {
            city: {
              type: "string",
              description: "城市名称",
            },
            unit: {
              type: "string",
              enum: ["celsius", "fahrenheit"],
              description: "温度单位",
            },
          },
          required: ["city"],
        },
      },
      {
        name: "get_forecast",
        description: "获取未来 7 天天气预报",
        inputSchema: {
          type: "object",
          properties: {
            city: { type: "string", description: "城市名称" },
            days: {
              type: "integer",
              description: "预报天数(1-7)",
              minimum: 1,
              maximum: 7,
            },
          },
          required: ["city"],
        },
      },
    ],
  };
});

// 处理工具调用请求
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const { name, arguments: args } = request.params;
  
  if (name === "get_weather") {
    // 调用真实的天气 API
    const weather = await fetchWeatherData(args.city, args.unit);
    return {
      content: [
        {
          type: "text",
          text: JSON.stringify({
            city: args.city,
            temperature: weather.temp,
            humidity: weather.humidity,
            condition: weather.condition,
          }),
        },
      ],
    };
  }
  
  if (name === "get_forecast") {
    const forecast = await fetchForecast(args.city, args.days);
    return {
      content: [
        {
          type: "text",
          text: JSON.stringify(forecast),
        },
      ],
    };
  }
  
  throw new Error(`Unknown tool: ${name}`);
});

// 启动服务器
async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error("Weather MCP Server is running...");
}

main().catch(console.error);

💡 一句话理解

使用 MCP 可以大幅提升工具的可复用性。一次开发 MCP Server,可以在多个 Agent 应用中复用,无需重复集成。

⚠️ 常见踩坑

MCP Server 暴露了外部系统的访问接口,必须做好认证、授权、速率限制。不要让 MCP Server 成为安全漏洞。

五、多工具编排模式

从单工具到多工具:编排的必要性。 简单的 Agent 可能只需要调用一个工具就能完成任务。但复杂的现实场景往往需要多个工具协同工作。例如:"帮我查一下北京明天的天气,如果下雨就取消航班,否则预订最早的航班"——这个任务需要天气查询、条件判断、航班搜索、预订系统四个工具的协同。

多工具编排的四种模式:

1. 串行编排(Sequential Orchestration)

工具按固定顺序依次调用,前一个工具的输出作为后一个工具的输入。

适用场景:工作流固定的任务,如 ETL(提取-转换-加载)、数据处理管道。

优点:逻辑清晰、易于调试、可预测。

缺点:灵活性差、无法根据中间结果动态调整。

示例:获取天气 → 判断是否适合出行 → 查询航班 → 预订酒店

2. 并行编排(Parallel Orchestration)

多个工具同时调用,结果汇总后统一处理。

适用场景:多个独立子任务可以同时执行,如同时查询多个数据源。

优点:提升执行效率、减少总耗时。

缺点:需要处理结果合并、冲突解决。

示例:同时查询天气、航班、酒店价格 → 汇总后推荐最优方案

3. 条件分支编排(Conditional Branching)

根据某个工具的执行结果,决定下一步调用哪个工具。

适用场景:需要根据中间结果做决策的任务。

优点:灵活、能处理复杂逻辑。

缺点:分支逻辑复杂时难以维护。

示例:如果天气好 → 查询户外景点;如果天气差 → 查询室内景点

4. 动态选择编排(Dynamic Selection)

LLM 根据当前上下文自主决定调用哪个工具、调用顺序。

适用场景:任务目标明确但执行路径不固定,需要 Agent 自主决策。

优点:最灵活、能处理开放式任务。

缺点LLM 可能做出错误决策、难以保证执行质量。

示例:用户说"帮我规划北京三日游",Agent 自主决定查询景点、餐厅、交通、天气等工具的顺序和内容。

2026 年的编排趋势:

  • 混合编排:结合多种模式,如"串行 + 并行"、"条件分支 + 动态选择"
  • 可视化编排:出现拖拽式编排工具(如 n8n、Make、Zapier 的 AI 版本)
  • 自适应编排:Agent 根据任务复杂度自动选择编排策略
  • 编排优化:通过强化学习优化编排策略,减少工具调用次数和总耗时
图表加载中…
typescript
tool-orchestration.ts
// 多工具编排示例:旅行规划 Agent

interface ToolResult {
  success: boolean;
  data?: any;
  error?: string;
}

// 1. 串行编排:依次调用工具
async function sequentialOrchestration(
  tools: Array<{ name: string; args: any }>,
  executeTool: (name: string, args: any) => Promise<ToolResult>
): Promise<ToolResult[]> {
  const results: ToolResult[] = [];
  
  for (const tool of tools) {
    const result = await executeTool(tool.name, tool.args);
    results.push(result);
    
    if (!result.success) {
      // 如果某个工具失败,可以选择中断或继续
      break;
    }
  }
  
  return results;
}

// 2. 并行编排:同时调用多个工具
async function parallelOrchestration(
  tools: Array<{ name: string; args: any }>,
  executeTool: (name: string, args: any) => Promise<ToolResult>
): Promise<ToolResult[]> {
  const promises = tools.map(tool => executeTool(tool.name, tool.args));
  return await Promise.all(promises);
}

// 3. 条件分支编排:根据结果决定下一步
async function conditionalOrchestration(
  executeTool: (name: string, args: any) => Promise<ToolResult>
): Promise<ToolResult> {
  // 第一步:查询天气
  const weatherResult = await executeTool("get_weather", { city: "北京" });
  
  if (!weatherResult.success) {
    return weatherResult;
  }
  
  // 根据天气条件决定下一步
  if (weatherResult.data.condition === "rain") {
    // 如果下雨,查询室内景点
    return await executeTool("search_attractions", {
      city: "北京",
      type: "indoor",
    });
  } else {
    // 如果天气好,查询户外景点
    return await executeTool("search_attractions", {
      city: "北京",
      type: "outdoor",
    });
  }
}

// 4. 动态选择编排:由 LLM 自主决策
async function dynamicOrchestration(
  userRequest: string,
  availableTools: string[],
  llmDecide: (context: string, tools: string[]) => Promise<{
    tool: string;
    args: any;
    shouldContinue: boolean;
  }>,
  executeTool: (name: string, args: any) => Promise<ToolResult>
): Promise<ToolResult[]> {
  const results: ToolResult[] = [];
  let context = userRequest;
  let shouldContinue = true;
  
  while (shouldContinue) {
    // LLM 根据当前上下文决定下一步
    const decision = await llmDecide(context, availableTools);
    
    // 执行选定的工具
    const result = await executeTool(decision.tool, decision.args);
    results.push(result);
    
    // 更新上下文
    context += `\n工具 ${decision.tool} 执行结果: ${JSON.stringify(result)}`;
    shouldContinue = decision.shouldContinue;
  }
  
  return results;
}

💡 一句话理解

选择合适的编排模式:任务路径固定用串行,独立子任务用并行,需要决策用条件分支,开放式任务用动态选择。

⚠️ 常见踩坑

动态选择编排虽然灵活,但 LLM 可能陷入无限循环或做出低效决策。设置最大工具调用次数和超时机制,防止 Agent 失控。

六、工具调用的错误处理与容错

工具调用失败是常态,而非例外。 在真实的生产环境中,工具调用可能因为各种原因失败:网络超时、API 限流、参数错误、权限不足、服务宕机。一个健壮的 Agent 系统必须能够优雅地处理这些错误,而不是崩溃或给出误导性结果。

工具调用失败的常见类型:

1. 参数错误(Parameter Error)

  • LLM 传递了非法参数(类型错误、缺少必填项、超出范围)
  • 原因:LLM 理解错误、Schema 定义不清晰
  • 处理:参数验证、返回清晰的错误提示、引导 LLM 修正参数

2. 执行超时(Timeout)

  • 工具执行时间过长,超过设定的超时阈值
  • 原因:外部 API 响应慢、网络问题、工具实现低效
  • 处理:设置合理的超时时间、重试机制、异步执行

3. API 限流(Rate Limiting)

  • 调用频率超过 API 提供商的限制
  • 原因:短时间内大量调用、未做速率控制
  • 处理:实现指数退避重试、缓存结果、批量调用

4. 权限错误(Permission Error)

  • Agent 没有调用某个工具的权限
  • 原因:未配置访问令牌、权限范围不足
  • 处理:检查权限配置、提示用户授权、降级到无需权限的工具

5. 服务不可用(Service Unavailable)

  • 外部服务宕机或维护
  • 原因:服务提供商故障、网络问题
  • 处理:重试机制、备用服务切换、优雅降级

容错策略:

1. 重试机制(Retry)

  • 对于临时性错误(网络超时、API 限流),自动重试
  • 使用指数退避策略:第一次等待 1 秒,第二次 2 秒,第三次 4 秒
  • 设置最大重试次数,避免无限重试

2. 降级策略(Fallback)

  • 如果主工具失败,切换到备用工具
  • 例如:主天气 API 失败 → 切换到备用天气 API → 返回缓存的天气数据

3. 错误隔离(Error Isolation)

  • 单个工具失败不应导致整个任务失败
  • 对于多工具任务,标记失败的工具,继续执行其他工具

4. 用户反馈(User Feedback)

  • 工具失败时,向用户清晰说明原因和可选方案
  • 不要隐藏错误或编造结果

5. 上下文管理(Context Management)

  • 工具失败后,将错误信息返回给 LLM
  • LLM 可以基于错误信息调整策略或向用户解释

生产环境的最佳实践:

  • 日志记录:记录所有工具调用的输入、输出、耗时、错误
  • 监控告警:监控工具调用的成功率、延迟、错误率
  • 熔断机制:如果某个工具连续失败,暂时禁用该工具
  • 成本监控:监控工具调用的 API 成本,设置预算上限

💡 一句话理解

为每个工具实现统一的错误处理包装器(wrapper),标准化错误格式和重试逻辑,避免在每个地方重复编写错误处理代码。

⚠️ 常见踩坑

不要让 LLM 看到原始的堆栈跟踪或技术错误信息。将错误转换为 LLM 能理解的描述性文本,避免 LLM 被技术细节干扰。

七、实战:构建一个多工具 Agent

实战目标:构建一个旅行规划 Agent。 这个 Agent 能够:

  • 查询目的地天气
  • 搜索航班和酒店
  • 推荐景点和餐厅
  • 根据用户偏好和预算做出个性化推荐
  • 处理工具调用失败和异常情况

技术栈选择:

  • LLM:Claude 3.5 Sonnet(支持 Tool Use
  • 框架LangChain(提供工具编排和错误处理)
  • 工具:天气 API、航班搜索 API、酒店预订 API、景点推荐 API
  • 语言:TypeScript

架构设计:

第一步:定义工具 Schema

为每个外部 API 定义清晰的工具 Schema,包括名称、描述、参数。

第二步:实现工具执行引擎

封装每个 API 调用,处理认证、错误、重试。

第三步:实现编排逻辑

使用条件分支 + 动态选择的混合编排:

  • 先查询天气(串行)
  • 根据天气决定景点类型(条件分支)
  • 并行查询航班和酒店(并行)
  • LLM 根据结果生成最终推荐(动态选择)

第四步:实现错误处理

为每个工具添加重试、降级、错误隔离。

第五步:集成测试

模拟各种场景:正常流程、工具失败、超时、权限错误。

关键代码实现:

下面的代码展示了一个完整的多工具 Agent 实现,包括工具定义、编排逻辑、错误处理。

typescript
travel-agent.ts
import Anthropic from '@anthropic-ai/sdk';
import { z } from 'zod';

const anthropic = new Anthropic();

// 1. 定义工具 Schema
const tools = [
  {
    name: "get_weather",
    description: "查询指定城市的天气信息,包括温度、天气状况、未来 3 天预报。",
    input_schema: {
      type: "object",
      properties: {
        city: { type: "string", description: "城市名称" },
        date: { type: "string", description: "日期 YYYY-MM-DD,默认为今天" },
      },
      required: ["city"],
    },
  },
  {
    name: "search_flights",
    description: "搜索航班信息,返回价格和时刻表。",
    input_schema: {
      type: "object",
      properties: {
        from: { type: "string", description: "出发城市" },
        to: { type: "string", description: "到达城市" },
        date: { type: "string", description: "出发日期 YYYY-MM-DD" },
        budget: { type: "number", description: "预算上限(元)" },
      },
      required: ["from", "to", "date"],
    },
  },
  {
    name: "search_hotels",
    description: "搜索酒店信息,返回价格、评分、设施。",
    input_schema: {
      type: "object",
      properties: {
        city: { type: "string", description: "城市名称" },
        checkin: { type: "string", description: "入住日期 YYYY-MM-DD" },
        checkout: { type: "string", description: "退房日期 YYYY-MM-DD" },
        budget_per_night: { type: "number", description: "每晚预算上限(元)" },
      },
      required: ["city", "checkin", "checkout"],
    },
  },
  {
    name: "recommend_attractions",
    description: "推荐景点,支持按类型筛选(户外/室内/文化/美食)。",
    input_schema: {
      type: "object",
      properties: {
        city: { type: "string", description: "城市名称" },
        type: {
          type: "string",
          enum: ["outdoor", "indoor", "culture", "food"],
          description: "景点类型",
        },
        count: {
          type: "integer",
          description: "推荐数量,默认 5",
          default: 5,
        },
      },
      required: ["city"],
    },
  },
];

// 2. 工具执行引擎(带错误处理和重试)
async function executeTool(
  name: string,
  args: any,
  maxRetries = 3
): Promise<{ success: boolean; data?: any; error?: string }> {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      // 根据工具名调用对应的 API
      switch (name) {
        case "get_weather":
          return await executeWeatherAPI(args);
        case "search_flights":
          return await executeFlightAPI(args);
        case "search_hotels":
          return await executeHotelAPI(args);
        case "recommend_attractions":
          return await executeAttractionAPI(args);
        default:
          return { success: false, error: `Unknown tool: ${name}` };
      }
    } catch (error: any) {
      console.error(`Tool ${name} failed (attempt ${attempt}): ${error.message}`);
      
      if (attempt === maxRetries) {
        return { success: false, error: `Tool ${name} failed after ${maxRetries} attempts: ${error.message}` };
      }
      
      // 指数退避重试
      await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, attempt - 1)));
    }
  }
  
  return { success: false, error: "Unexpected error" };
}

// 3. 主循环:Agent 推理与工具调用
async function travelAgent(userRequest: string): Promise<string> {
  const messages: Anthropic.MessageParam[] = [
    {
      role: "user",
      content: userRequest,
    },
  ];
  
  const maxIterations = 10; // 防止无限循环
  let iteration = 0;
  
  while (iteration < maxIterations) {
    iteration++;
    
    // 调用 Claude
    const response = await anthropic.messages.create({
      model: "claude-3-5-sonnet-20241022",
      max_tokens: 4096,
      tools: tools,
      messages: messages,
    });
    
    // 检查是否需要调用工具
    const toolUses = response.content.filter(
      (block): block is Anthropic.ToolUseBlock => block.type === "tool_use"
    );
    
    if (toolUses.length === 0) {
      // 没有工具调用,返回最终回复
      const textBlock = response.content.find(
        (block): block is Anthropic.TextBlock => block.type === "text"
      );
      return textBlock?.text || "Sorry, I couldn't generate a response.";
    }
    
    // 执行所有工具调用
    const toolResults: Anthropic.ToolResultBlockParam[] = [];
    
    for (const toolUse of toolUses) {
      console.log(`Calling tool: ${toolUse.name} with args:`, toolUse.input);
      
      const result = await executeTool(toolUse.name, toolUse.input);
      
      toolResults.push({
        type: "tool_result",
        tool_use_id: toolUse.id,
        content: JSON.stringify(result),
      });
    }
    
    // 更新消息历史
    messages.push({
      role: "assistant",
      content: response.content,
    });
    
    messages.push({
      role: "user",
      content: toolResults,
    });
  }
  
  return "Sorry, I couldn't complete the task after multiple attempts.";
}

// 使用示例
async function main() {
  const userRequest = `
    我计划下周一从上海去北京出差 3 天。
    预算:机票 2000 元以内,酒店每晚 800 元以内。
    请帮我查一下北京天气,搜索航班和酒店,推荐一些适合的餐厅。
  `;
  
  const response = await travelAgent(userRequest);
  console.log(response);
}

main().catch(console.error);

💡 一句话理解

在开发阶段,为每个工具添加详细的日志输出(工具名、参数、结果、耗时),便于调试和优化。

⚠️ 常见踩坑

生产环境必须设置最大迭代次数(maxIterations)和超时机制,防止 Agent 陷入无限循环或长时间运行。

八、2026 年最新进展与未来方向

2026 年工具调用领域的关键趋势:

1. 自主工具发现与加载(Autonomous Tool Discovery)

2026 年的 Agent 不再局限于预定义的工具集,而是能够根据任务需求自主搜索和加载合适的工具。

实现方式

  • Agent 连接到工具注册中心(Tool Registry)
  • 根据任务描述,语义搜索匹配的工具
  • 动态加载工具 Schema 和使用文档
  • 评估工具的可信度和质量

示例:用户说"帮我分析一下这个网站的 SEO",Agent 自主搜索并加载 SEO 分析工具、网站爬虫工具、性能测试工具。

2. 工具组合与链式调用(Tool Composition)

Agent 能够将多个简单工具组合成复杂的工作流。

实现方式

  • 工具之间通过标准化的数据格式传递
  • Agent 理解工具的输入输出,自动构建调用链
  • 支持条件分支、循环、并行执行

示例:网站监控 Agent = 网站爬虫 + 性能测试 + SEO 分析 + 报告生成

3. 工具学习与优化(Tool Learning)

Agent 能够从历史工具调用中学习,优化未来的调用策略。

实现方式

  • 记录每次工具调用的输入、输出、耗时、成功率
  • 分析哪些工具组合最有效
  • 优化工具调用的参数和顺序
  • 识别并避免低效或失败的工具

示例:Agent 发现"先查天气再查航班"的成功率高于"同时查询",自动调整编排策略。

4. 跨 Agent 工具共享(Tool Sharing)

多个 Agent 之间可以共享工具和工具调用经验。

实现方式

  • 工具封装为标准化的 MCP Server
  • Agent 之间通过 A2A(Agent-to-Agent)协议共享工具
  • 工具调用经验在 Agent 网络中传播

示例:旅行规划 Agent 发现了一个好用的汇率转换工具,推荐给其他 Agent。

5. 安全与隐私保护(Security & Privacy)

随着工具调用能力的增强,安全问题也日益突出。

关键挑战

  • 工具调用可能被提示注入攻击利用
  • 工具可能访问敏感数据
  • 工具调用可能产生不可逆的操作

解决方案

  • 工具调用沙箱化
  • 细粒度的权限控制
  • 操作审计和回滚机制
  • 用户确认机制(对于敏感操作)

未来方向:

1. 通用工具智能(General Tool Intelligence)

未来的 Agent 将具备通用的工具使用能力,能够:

  • 理解任何工具的 Schema 和文档
  • 自主探索工具的功能和限制
  • 创造性地组合工具解决新问题

2. 工具生态标准化

MCP 等标准协议的普及将带来:

  • 工具的可移植性(一次开发,到处使用)
  • 工具市场的繁荣(类似 App Store)
  • 工具质量的提升(社区评审和评级)

3. 人机协作工具调用

Agent 与人类协作完成复杂任务:

  • Agent 负责工具调用和数据收集
  • 人类负责决策和审核
  • 双向反馈优化

总结: 工具调用AI Agent 从"能说"到"能做"的关键能力。从 2023 年 OpenAI 的 Function Calling 到 2026 年的 MCP 生态和自主工具编排,这个领域正在快速发展。掌握工具调用的核心架构、编排模式、错误处理,是构建生产级 Agent 的必备技能。

💡 一句话理解

持续关注 MCP 生态和工具市场的发展,及时采用新的工具和最佳实践。工具调用领域变化很快,保持学习的心态。

⚠️ 常见踩坑

工具调用能力越强,安全风险越大。始终遵循最小权限原则,对敏感操作做二次确认,定期审计工具调用日志。