mcpField
A
ai-patterns-tool-use-patterns

ai-patterns-tool-use-patterns

github.com/agents-incAgent 工作流2645 次安装2 天前更新
代码仓库

关于 ai-patterns-tool-use-patterns

面向实现 LLM 工具调用和 Agent 循环的开发者技能,涵盖模式定义、有限循环、输入校验、错误恢复与工具选择控制。

主要功能

  1. 01JSON Schema 工具定义
  2. 02设定上限的工具调用循环
  3. 03输入校验与结构化错误
  4. 04工具选择控制
  5. 05并行调用与审批机制
  6. 06TypeScript 类型安全模式

适用场景

  1. 01实现跨提供方的工具调用
  2. 02构建多步骤 Agent 工作流
  3. 03处理并行工具调用
  4. 04为高风险操作添加审批

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/agents-inc/skills --skill ai-patterns-tool-use-patterns

安装后在 Claude Code 中输入/ai-patterns-tool-use-patterns确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

metadata.yaml
reference.md
examples/
advanced.md
core.md

详细介绍

工具使用模式

快速指南: 工具使用(函数调用)允许大模型调用外部函数。通用模式为:定义工具架构(参数使用 JSON Schema)-> 将工具和消息发送给大模型 -> 检测响应中的 tool_use -> 在本地执行 -> 将结果返回给大模型 -> 重复此过程,直到模型以文本作答。每个循环都必须设置最大步数限制,执行前验证所有工具输入,并返回结构化错误,以便模型恢复。使用工具选择控制(auto、required、none、指定工具)来引导模型行为。


<critical_requirements>

关键:使用此技能前

所有代码都必须遵循项目在 CLAUDE.md 中的约定(kebab-case、命名导出、导入顺序、import type、命名常量)

(必须为每个工具循环设置最大步数限制——无界循环可能导致 API 调用无限进行和成本失控)

(必须在执行前验证所有工具输入参数——LLM 生成的参数是不可信输入)

(工具执行失败时,必须向模型返回结构化错误消息——绝不能悄无声息地吞掉错误或返回空结果)

(必须使用 JSON Schema 定义工具参数——所有主流提供商都要求使用此格式)

(必须将工具定义视为令牌成本——每次 API 调用都会发送每个工具架构,因此描述应简洁而准确)

</critical_requirements>


自动检测: 工具使用、函数调用、tool_calls、tool_use、工具调用循环、Agent 循环、工具定义、工具架构、toolChoice、tool_choice、并行工具调用、人在循环中、工具审批、Agentic 工作流、多步骤 Agent、工具结果、工具错误

适用场景:

  • 在任何提供商中实现 LLM 工具调用/函数调用
  • 构建迭代调用工具、直至任务完成的 Agent 循环
  • 处理并行工具调用(同一响应中的多个工具)
  • 将工具错误报告给模型,以便其恢复
  • 控制工具选择(auto、required、none、强制指定工具)
  • 在执行危险工具前添加人工审批关卡
  • 流式处理包含工具调用的响应

涵盖的关键模式:

  • 工具定义架构(参数使用 JSON Schema、描述)
  • 核心工具调用循环(发送 -> 检测 -> 执行 -> 返回 -> 重新发送)
  • 并行工具调用(处理同一响应中的多个调用)
  • 错误处理(将工具执行失败报告给模型)
  • 工具选择控制(auto、required、none、指定工具)
  • 包含对话状态的多步骤 Agent 工作流
  • 人在循环中的审批模式
  • TypeScript 中类型安全的工具定义
  • 安全性(输入验证、沙箱、最小权限)
  • 包含工具调用的流式处理

不适用场景:

  • 不涉及工具调用的简单文本生成——无需工具
  • 结构化输出/JSON 提取——改用提供商的结构化输出功能
  • 提供商特定的 SDK 模式——使用提供商的 SDK 技能获取特定于 SDK 的 API

详细资源:

  • examples/core.md -- 工具定义、工具调用循环、错误处理、类型安全的工具
  • examples/advanced.md -- 并行工具调用、多步骤 Agent、人在循环中、流式处理、安全性
  • reference.md -- 决策框架、提供商比较、反模式检查清单

## 理念

工具使用是将大模型从文本生成器转变为 Agent 的机制。模型无法执行代码、查询数据库或调用 API——它只能通过发出结构化的工具调用来_请求_你的代码执行这些操作。你的代码负责执行,模型负责规划。

核心原则:

  1. 模型负责规划,你负责执行——大模型会发出带有结构化参数的工具调用请求。你的代码负责验证、执行并返回结果。切勿让模型直接执行任意代码。
  2. Agent 是循环——每个 Agent,无论是简单的天气机器人,还是复杂的编码助手,都遵循相同的循环:大模型做出决策 -> 系统执行 -> 结果反馈 -> 重复。复杂性来自工具和状态,而非循环本身。
  3. 工具是 Schema——工具定义是一个 JSON Schema,用于告知模型有哪些函数、函数接受哪些参数,以及何时使用。更好的描述能带来更好的工具选择和参数质量。
  4. 错误是信息——工具失败时,向模型返回结构化错误消息。模型通常可以通过使用不同参数重试、选择其他工具或向用户说明失败原因来恢复。
  5. 纵深防御——大模型生成的参数是不可信输入。验证 Schema、强制执行类型、限制参数范围、对执行进行沙箱隔离,并要求对危险操作进行审批。

何时使用工具调用:

  • 任务需要模型不具备的现实世界数据(天气、数据库、API)
  • 任务需要产生副作用(发送电子邮件、创建记录、文件操作)
  • 任务需要结合中间数据查询进行多步推理
  • 任务需要模型无法可靠完成的计算(数学、代码执行)

何时不应使用工具调用:

  • 模型仅凭训练数据就能回答
  • 你只需要结构化 JSON 输出(改用结构化输出功能)
  • “工具”只是伪装成函数的提示词工程
  • 你需要确定性行为(工具调用会因模型的决策引入不确定性)

核心模式

模式 1:工具定义 Schema

每个工具定义都包含三个部分:名称、描述和参数 Schema。描述最为重要——它会引导模型决定是否调用工具,以及如何构造参数。

// Provider-agnostic tool definition shape
interface ToolDefinition {
  name: string; // ^[a-zA-Z0-9_-]{1,64}$
  description: string; // When and why to use this tool
  parameters: JsonSchema; // JSON Schema for input arguments
}
好的工具定义
// Good: precise description, constrained parameters, .describe() on each property
const VALID_UNITS = ["celsius", "fahrenheit"] as const;

const getWeatherTool: ToolDefinition = {
  name: "get_current_weather",
  description:
    "Get the current weather conditions for a specific city. " +
    "Returns temperature, humidity, and conditions. " +
    "Use this when the user asks about current weather, NOT forecasts.",
  parameters: {
    type: "object",
    properties: {
      location: {
        type: "string",
        description: "City name and optional country code, e.g. 'London, UK'",
      },
      unit: {
        type: "string",
        enum: VALID_UNITS,
        description: "Temperature unit. Defaults to celsius if not specified.",
      },
    },
    required: ["location"],
  },
};

为什么好: 描述说明了工具会返回什么、何时使用,以及何时不应使用。参数带有描述和约束(枚举)。有效值使用了命名常量。

不好的工具定义
// Bad: vague description, no parameter descriptions, no constraints
const weatherTool: ToolDefinition = {
  name: "weather",
  description: "Gets weather", // BAD: too vague
  parameters: {
    type: "object",
    properties: {
      loc: { type: "string" }, // BAD: no description, cryptic name
      u: { type: "string" }, // BAD: no enum constraint
    },
  },
};

为什么不好: 描述含糊会导致工具选择错误;缺少参数描述会导致参数格式错误;缺少枚举约束会导致值无效;晦涩的参数名称会让模型困惑。


模式 2:核心工具调用循环

这是工具使用的基本模式。发送包含工具定义的消息,检查响应是否包含工具调用,执行这些调用,返回结果,然后让模型生成最终响应。完整实现请参阅 examples/core.md。

const MAX_TOOL_STEPS = 10;

for (let step = 0; step < MAX_TOOL_STEPS; step++) {
  const response = await callLLM({ messages, tools });
  if (!response.toolCalls?.length) return response.text;
  messages.push({ role: "assistant", toolCalls: response.toolCalls });
  for (const tc of response.toolCalls) {
    messages.push({
      role: "tool",
      toolCallId: tc.id,
      content: JSON.stringify(await executeTool(tc)),
    });
  }
}

要点: 始终使用带有命名常量(MAX_TOOL_STEPS)的有界 for 循环——切勿使用 while (true)。始终先追加助手消息(包含工具调用),再追加工具结果。达到步数上限时,确保能够正常终止。

### 模式 3:带错误处理的工具执行

工具执行失败时,应向模型返回结构化错误,而不是直接崩溃。模型通常可以通过重试、选择其他方法或说明失败原因来恢复。完整的验证流程请参阅 examples/core.md。

// Four-step validation: tool exists -> JSON parses -> schema validates -> execution succeeds
async function executeTool(toolCall: ToolCall): Promise<ToolResult> {
  const handler = toolRegistry[toolCall.name];
  if (!handler)
    return { success: false, error: `Unknown tool: "${toolCall.name}"` };
  const validated = handler.schema.safeParse(toolCall.arguments);
  if (!validated.success)
    return { success: false, error: `Invalid args: ${validated.error}` };
  try {
    return { success: true, data: await handler.execute(validated.data) };
  } catch (error) {
    return {
      success: false,
      error: `Tool failed: ${error instanceof Error ? error.message : "Unknown"}`,
    };
  }
}

要点: 始终验证工具是否存在、解析 JSON 参数、根据架构进行验证,并捕获执行错误。返回结构化的 { success, data?, error? } 结果,以便模型恢复。工具出错时,绝不要让循环崩溃。


模式 4:工具选择控制

控制模型是否以及如何使用工具。所有主流提供商都支持四种模式。

type ToolChoice =
  | "auto" // Model decides whether to call tools (default)
  | "required" // Model MUST call at least one tool
  | "none" // Model MUST NOT call any tools
  | { name: string }; // Model MUST call this specific tool
各模式的适用场景
// auto (default) -- let the model decide
const response = await callLLM({
  messages,
  tools,
  toolChoice: "auto",
});

// required -- force tool use (e.g., first step of an agent must act)
const response = await callLLM({
  messages,
  tools,
  toolChoice: "required",
});

// none -- disable tools (e.g., final response must be text only)
const response = await callLLM({
  messages,
  tools,
  toolChoice: "none",
});

// specific tool -- force a particular tool (e.g., classification step)
const response = await callLLM({
  messages,
  tools,
  toolChoice: { name: "classify_intent" },
});

选择建议:

模式适用场景
auto通用 Agent——模型根据上下文自行决定
requiredAgent 的第一步必须始终调用工具(例如,查询数据)
none生成最终回复——不允许再调用工具
{ name }必须使用指定工具的工作流步骤(例如,提取、分类)

适用场景: 需要在工作流的特定环节限制或确保工具行为时


模式 5:TypeScript 中类型安全的工具定义

使用带有 Zod 架构的类型化注册表模式,为工具定义提供编译时安全性,并在执行时进行运行时验证。完整的 ToolRegistry 类实现请参阅 examples/core.md。

import { z } from "zod";

// Define tool with schema + execute function -- TypeScript infers argument types
function defineTool<T extends z.ZodType>(config: {
  name: string;
  description: string;
  schema: T;
  execute: (args: z.infer<T>) => Promise<unknown>;
}) {
  return config;
}

适用场景: 任何实现工具调用的 TypeScript 项目——Zod 会在运行时进行验证,TypeScript 则会在编译时发现架构与处理程序不匹配的问题


<decision_framework>

决策框架

你需要工具调用吗?
Does the task require information the model doesn't have?
+-- YES -> Tool calling (fetch data from APIs, databases, files)
+-- NO -> Does the task require side effects?
    +-- YES -> Tool calling (send email, create record, execute code)
    +-- NO -> Do you need structured JSON output?
        +-- YES -> Use structured output features (NOT tool calling)
        +-- NO -> Plain text generation, no tools needed
应选择哪种循环模式?
How many tools might the model call?
+-- Single tool call per request
|   +-- Simple request-response with one tool execution
|   +-- No loop needed, just one round-trip
+-- Multiple sequential tool calls
|   +-- Use the bounded tool call loop (Pattern 2)
|   +-- Set MAX_TOOL_STEPS based on task complexity
+-- Multiple parallel tool calls in one response
|   +-- Execute all tool calls concurrently (Promise.all)
|   +-- Then return all results and loop
+-- Complex multi-step agent
    +-- Use the bounded loop with conversation state
    +-- Add human-in-the-loop for dangerous operations
    +-- Consider per-step tool filtering
如何处理工具错误?
Tool execution failed. What to do?
+-- Return structured error to the model
|   +-- Include error message and context
|   +-- Model can retry, choose alternative, or explain failure
+-- NEVER: silently return empty result
+-- NEVER: crash the loop
+-- NEVER: retry automatically without telling the model
    (the model should decide whether to retry)

</decision_framework>


<red_flags>

危险信号

高优先级问题:

  • 使用没有步骤计数器的无界工具循环(while (true))——有无限调用 API 和成本失控的风险
  • 未验证就执行工具参数——大模型生成的参数是不可信输入,应像处理用户输入一样处理
  • 默默吞掉工具错误(返回 null 或 {})——模型不知道发生了故障,因此无法恢复
  • 未进行沙箱隔离就允许通过工具参数执行任意代码——提示词注入可能升级为代码执行
  • 工具描述写着“获取数据”——模糊的描述会导致工具选择错误、参数格式不正确

中优先级问题:

  • 每次 API 调用都发送所有工具,而不是只发送相关工具——浪费令牌,还会使模型困惑
  • 在工具结果之前,未将助手消息(包含工具调用)加入对话历史——会破坏消息顺序
  • 使用 tool_choice: "required",却未提供没有合适工具时的回退方式——会强制进行无意义的工具调用
  • 将原始数据库行或完整 API 响应作为工具结果返回——无关数据会淹没上下文;应进行摘要或截断
  • 不记录工具调用和结果——无法调试生产环境中的 Agent 行为

常见错误:

  • 忘记工具调用参数是 JSON 字符串,而不是已解析的对象——使用前务必调用 JSON.parse()
  • 认为只要有可用工具,模型就一定会调用——在 auto 模式下,模型可能会直接以文本作答
  • 将工具调用视为结构化输出——两者解决的是不同问题(执行操作与提取数据)
  • 将业务逻辑写进工具描述,而不是工具实现中——描述用于引导工具选择,而非执行

注意事项与边缘情况:

  • 每次 API 调用都要为工具定义消耗令牌——10 个包含详细架构的工具,每次请求可能消耗 1000 多个令牌
  • 并行工具调用可能以任意顺序到达——切勿假定执行顺序与定义顺序一致
  • 有些模型会臆造与任何定义都不匹配的工具名称或参数——务必验证工具名称是否存在于注册表中
  • 包含工具调用的流式响应需要先累积部分 JSON 数据块,再进行解析——参数是逐步到达的,并非一次性完整到达
  • 返回过大的工具结果(>4000 个令牌)可能导致对话超出上下文限制——应截断或摘要大型结果
  • 模型会保留完整对话上下文,包括所有工具调用和结果——长时间运行的 Agent 会累积大量令牌用量
  • 不同提供方的工具结果消息格式不同(role: "tool" 与内容块)——应在 callLLM 封装器中统一处理

</red_flags>


<critical_reminders>

关键提醒

所有代码都必须遵循 CLAUDE.md 中的项目约定(kebab-case、命名导出、导入顺序、import type、命名常量)

(必须为每个工具循环设置最大步骤限制——无界循环有无限调用 API 和成本失控的风险)

(必须在执行前验证所有工具输入参数——大模型生成的参数是不可信输入)

(工具执行失败时,必须向模型返回结构化错误消息——绝不能默默吞掉错误或返回空结果)

(必须使用 JSON Schema 定义工具参数——所有主流提供方都要求采用此格式)

(必须将工具定义视为令牌成本——每次 API 调用都会发送每个工具架构,因此描述应简洁准确)

不遵循这些规则会导致 Agent 在无限循环中耗费大量 API 成本、执行未经验证的输入,或发生静默故障且模型无法恢复。

</critical_reminders>

评论

支持 Markdown