AI实践(7)工具函数调用

AI实践(7)工具函数调用
AI实践(8)工具函数调用


Author: Once Day Date: 2026年3月2日



一位热衷于Linux学习和开发的菜鸟,试图谱写一场冒险之旅,也许终点只是一场白日梦…



漫漫长路,有人对你微笑过嘛…



全系列文章可参考专栏:
AI实践成长_Once-Day的博客-ZEEKLOG博客



参考文章:Prompt Engineering Guide提示词技巧 – Claude 中文 - Claude AI 开发技术社区Prompting strategies for financial analysis | ClaudeDocumentation - Claude API DocsOpenAI for developers在LLM中调用函数 | Prompt Engineering GuideAI大模型Function Call技术教程:从入门到精通-ZEEKLOG博客详解 OpenAI 函数调用(Function Calling):让模型具备数据获取与行动能力 - 大A就是我 - 博客园大模型函数调用(Function Calling)完全指南 - 知乎使用 Claude 的工具 - Claude API 文档 — Tool use with Claude - Claude API DocsHow to implement tool use - Claude API Docs

文章目录

1. 概述
1.1 什么是工具函数调用(Tool Use / Function Calling)

大语言模型(LLM)本身是一个纯文本生成系统,其能力边界局限于训练数据所覆盖的知识范围。当用户提出诸如"今天北京天气如何"或"帮我查一下订单状态"这类需要实时数据的问题时,模型仅凭自身参数无法给出准确回答。工具函数调用(Tool Use / Function Calling)正是为了突破这一限制而提出的能力,它在 LLM 与外部系统之间建立了一条可靠的交互通道,使模型能够在对话过程中主动调用预先定义好的外部函数,从而获取实时信息或执行特定操作。

从技术实现上看,Function Calling 并非让模型直接执行代码,而是让模型在理解用户意图后,输出一段结构化的 JSON 数据,其中包含需要调用的函数名称及对应的参数。这一能力依赖于模型在训练阶段的专项微调——经过微调的 LLM 能够准确判断当前对话是否需要借助外部工具,并严格按照预定义的函数签名(function schema)生成符合格式要求的调用请求。整个过程中,模型扮演的是"决策者"角色,而实际的函数执行则由外部程序完成。

这种机制带来了极大的灵活性。开发者可以在单次请求中定义多个工具函数,例如同时提供天气查询、数据库检索、日程管理等不同功能。模型会根据用户的具体意图,自行选择合适的函数进行调用,甚至在复杂场景下进行多次连续调用以完成一个完整的任务链。以下是一个工具定义的基本结构示意:

{"name":"get_weather","description":"获取指定城市的当前天气信息","parameters":{"type":"object","properties":{"city":{"type":"string","description":"城市名称,例如:北京"}},"required":["city"]}}

上述 JSON 结构定义了一个名为 get_weather 的工具函数,包含函数名、功能描述以及参数的类型约束。模型在接收到用户关于天气的提问后,会解析意图并输出类似 {"name": "get_weather", "arguments": {"city": "北京"}} 的结构化调用指令,由客户端或服务端程序负责实际执行并将结果回传给模型,最终模型基于返回结果生成自然语言回复。

目前,主流的 LLM 服务商均已支持 Function Calling 能力,包括 OpenAIAnthropicClaude)、GoogleGemini)等。尽管各平台在 API 设计和参数格式上存在差异,但核心思路一致:通过预定义工具描述,让模型具备调用外部能力的"接口意识"。

1.2 为什么需要工具函数调用

LLM 本质上是一个基于概率的文本生成模型,其知识来源于训练语料的静态快照。这意味着模型无法感知训练截止日期之后发生的事件,也无法直接访问私有数据库、调用第三方服务或执行任何具有副作用的操作。在构建 LLM 驱动的聊天机器人或智能代理(Agent)时,这一局限尤为突出——用户的实际需求往往涉及实时数据查询、业务流程触发等模型自身无法完成的任务。函数调用机制正是为弥合这一鸿沟而设计的核心能力,它将自然语言理解与外部系统执行有机地连接在一起。

从功能定位来看,函数调用主要解决两类问题。

  • 数据获取,即获取模型自身不具备的实时信息,例如当前天气、数据库记录、知识库文档等,弥补训练数据时效性不足的缺陷。
  • 执行行动,即触发具有实际效果的外部操作,例如发送邮件、创建工单、提交表单、调用第三方 API 等,使模型从"被动回答"升级为"主动行动"。

借助这两类能力,开发者可以构建出多种形态的应用:

  • 能够调用外部工具回答问题的对话代理,例如将"伯利兹的天气如何"转换为 get_current_weather(location="Belize", unit="celsius") 的函数调用。
  • 用于从非结构化文本中提取和标记数据的 LLM 驱动方案,例如从百科文章中抽取人名、地名等实体。
  • 将自然语言转换为有效 API 调用或数据库查询语句的中间层应用。
  • 与企业知识库交互的对话式检索引擎,支持多轮问答与上下文关联。

目前主流大模型平台均已将函数调用作为标准能力提供支持,但在 API 设计、参数格式和调用模式上各有差异。下表对几个代表性平台进行了简要对比:

平台能力名称工具定义格式并行调用强制调用
OpenAIGPT-4oFunction CallingJSON Schema支持支持(tool_choice
AnthropicClaudeTool UseJSON Schema支持支持(tool_choice
GoogleGeminiFunction CallingOpenAPI Schema支持支持(function_calling_config

各平台的核心工作流程基本一致:开发者通过 JSON Schema 定义工具函数的名称、描述和参数约束,模型根据用户输入和系统提示自主决定是否调用以及调用哪些函数,最终在获取函数执行结果后整合生成自然语言响应。

在具体实现层面,工具通常被划分为客户端工具和服务端工具两类:

  • 客户端工具的执行发生在调用方(即开发者的应用程序)一侧,由应用程序接收模型输出的调用指令后自行完成函数执行并回传结果;
  • 服务端工具则由模型服务商在其基础设施上直接执行,开发者无需处理中间的调用与回传过程。两种模式适用于不同的场景,后续章节将以 Claude 为例分别进行详细实践。
2. 函数实例(获取天气)

以一个典型的天气查询场景为例,展示函数调用的完整实现过程。下面的代码基于 OpenAIPython SDK,定义了一个名为 get_weather 的工具函数,用于获取指定地点的当前温度,位置参数需要以"城市+国家"的格式传入。开发者通过 tools 参数将函数的 schema 描述提交给模型,模型在理解用户意图后自主决定是否触发调用:

from openai import OpenAI # 配置 API 中转站端点 client = OpenAI( base_url="https://api.aaaaapi.com/v1", api_key="your-api-key"# 替换为你的 API 密钥)# 定义工具(函数) tools =[{"type":"function","name":"get_weather","description":"获取指定地点的当前温度。","parameters":{"type":"object","properties":{"location":{"type":"string","description":"城市和国家,例如:波哥大,哥伦比亚"}},"required":["location"],"additionalProperties":False}}]# 调用模型,触发函数调用 response = client.responses.create( model="gpt-4.1",input=[{"role":"user","content":"今天巴黎的天气怎么样?"}], tools=tools )print(response.output)

在上述代码中,tools 列表内的每个元素对应一个工具定义,其中 type 固定为 "function"namedescription 分别描述函数名称与用途,parameters 则使用标准的 JSON Schema 格式约束参数类型和必填项。当用户输入"今天巴黎的天气怎么样"时,模型会解析出意图与天气查询相关,并从对话内容中提取出地点信息,随后生成一条结构化的函数调用指令,而非直接返回文本回复。

模型返回的 response.output 中会包含一个 function_call 类型的输出项,其内容类似于 {"name": "get_weather", "arguments": "{\"location\": \"巴黎, 法国\"}"}。可以看到,模型不仅识别了用户提到的"巴黎",还自动补全了国家信息以符合参数描述中的格式要求。这一结果本身并不包含天气数据,而是一条等待执行的调用指令。

函数调用的完整交互流程可以概括为五个步骤:

在这里插入图片描述

首先,开发者通过 tools 参数传入函数 schema,告知模型可用工具的用途和参数格式。其次,模型根据用户输入判断是否需要调用工具,若需要则生成包含函数名和参数的调用指令。随后,应用程序解析模型输出,调用对应的自定义代码(如请求天气 API 获取实时数据)。接着,将函数执行的返回值以 tool result 的形式回传给模型。最后,模型基于原始问题和函数返回结果,生成一段完整的自然语言回答呈现给用户,例如"巴黎今天的气温为 18°C,多云"。

需要注意的是,整个流程中模型本身并不执行任何函数代码,它的职责仅限于意图识别、参数提取和结果整合。实际的 API 调用、数据库查询等操作均由开发者的应用程序负责完成。

3. 函数调用工作流

函数调用的起点是向模型提供准确且完整的函数 schema 定义。这份 schema 是模型理解工具能力边界的唯一依据——模型并不知道函数的实际实现逻辑,它完全依赖 schema 中的描述信息来判断何时调用、如何构造参数。因此,schema 的质量直接决定了函数调用的准确率。一个标准的函数 schema 需要包含以下核心字段:

字段说明
type固定为 "function",标识工具类型
name函数名称,如 get_weather,应简洁且语义明确
description函数用途和适用场景的自然语言描述,需详细清晰
parameters函数参数的 JSON Schema 定义,包含类型、描述、约束等
strict是否启用严格模式,推荐设为 true 以确保输出严格符合 schema

其中 description 字段的撰写尤为关键。模糊的描述会导致模型在不恰当的时机触发调用,或者在应该调用时选择了错误的函数。例如,将天气查询函数的描述写为"获取天气"远不如"获取指定城市当前的实时气温和天气状况,适用于用户询问某地天气的场景"来得准确。当系统中同时存在多个功能相近的工具时,清晰的 description 能够帮助模型做出正确的路由决策。

在参数定义层面,遵循一些最佳实践能够显著提升调用的可靠性。首先,应为每个参数添加详细的 description 说明,明确格式要求和示例值,例如 location 参数注明"城市和国家,例如:巴黎,法国"。其次,必须通过 type 字段指定参数的数据类型(stringnumberobject 等),避免类型歧义。对于有限取值的参数,应使用 enum 进行约束,如温度单位限定为 ["celsius", "fahrenheit"]。最后,通过 required 数组显式声明必填参数,确保模型不会遗漏关键信息。以下是一个遵循最佳实践的参数定义示例:

"parameters":{"type":"object","properties":{"location":{"type":"string","description":"城市和国家,例如:巴黎,法国"},"unit":{"type":"string","enum":["celsius","fahrenheit"],"description":"温度单位,默认为 celsius"}},"required":["location"],"additionalProperties":false}

当模型决定调用函数后,应用程序需要按照规范的流程处理调用指令并完成结果回传。首先从 response.output 中解析出函数调用信息,提取 name(函数名)、arguments(参数 JSON 字符串)和 call_id(调用标识符)三个关键字段。然后根据函数名路由到对应的实现代码,将 arguments 反序列化为实际参数并执行。函数执行完成后,将返回值格式化为字符串(支持纯文本或 JSON 格式),通过 function_call_output 类型的消息回传给模型,其中必须携带对应的 call_id 以便模型正确关联请求与结果。

在这里插入图片描述

在实际工程中,还需要对异常情况进行处理。函数执行可能因网络超时、参数非法、服务不可用等原因失败,此时应将错误信息以结构化的方式回传给模型,而非直接抛出异常。模型在接收到错误描述后,通常能够生成合理的兜底回复,例如告知用户"当前天气服务暂时不可用,请稍后再试",从而保证对话体验的连贯性。

4. 客户端工具实践

客户端工具(Client Tools)是 Claude 工具调用体系中最常用的模式。其核心特征在于:工具的实际执行发生在开发者的应用程序一侧,Claude 仅负责理解用户意图、决定是否调用工具并生成结构化的调用指令,而具体的函数逻辑、API 请求、数据处理等工作均由客户端代码完成。这种设计赋予开发者对工具执行过程的完全控制权,适用于需要访问本地资源、私有数据库或自定义业务逻辑的场景。

客户端工具的完整交互流程分为四个阶段。首先,开发者在 API 请求中通过 tools 参数定义可用工具的名称、描述和输入参数的 JSON Schema,同时传入用户的自然语言消息。随后,Claude 评估当前对话是否需要借助工具来完成回答,若判断需要,则构造一条格式规范的工具调用请求,此时 API 响应中的 stop_reason 字段值为 tool_use,明确表示模型希望执行工具调用而非直接生成文本回复。

在这里插入图片描述

以下是一个基于 Claude API 的客户端工具调用完整示例,实现了天气查询功能:

import os import anthropic import json API_KEY = os.getenv("CLAUDE_API_KEY") API_BASE = os.getenv("CLAUDE_API_BASE") client = anthropic.Anthropic( api_key=API_KEY, base_url=API_BASE,) TOOLS =[{"name":"get_weather","description":"获取指定城市的当前天气信息,当用户询问某地天气时使用。","input_schema":{"type":"object","properties":{"location":{"type":"string","description":"城市名称,例如:San Francisco, CA",}},"required":["location"],},}]# 第一步:定义工具并发送用户消息 response = client.messages.create( model="claude-sonnet-4.6", max_tokens=10000, tools=TOOLS, messages=[{"role":"user","content":"What's the weather in San Francisco?"}],)# 第二步:检查模型是否请求工具调用if response.stop_reason =="tool_use":# 提取工具调用信息 tool_block =next(b for b in response.content if b.type=="tool_use") tool_name = tool_block.name tool_input = tool_block.input tool_use_id = tool_block.id# 第三步:执行本地工具函数defget_weather(location:str)->dict:# 实际场景中此处调用天气 APIreturn{"temperature":"15°C","condition":"partly cloudy"} result = get_weather(tool_input["location"])# 第四步:将结果回传给 Claude final_response = client.messages.create( model="claude-sonnet-4.6", max_tokens=10000, tools=TOOLS, messages=[{"role":"user","content":"What's the weather in San Francisco?"},{"role":"assistant","content": response.content},{"role":"user","content":[{"type":"tool_result","tool_use_id": tool_use_id,"content": json.dumps(result),}],},],)print(final_response.content[0].text)

上述代码中有几个关键细节值得注意。Claude 的工具定义使用 input_schema 字段(而非 OpenAIparameters),这是两个平台在 API 设计上的主要差异之一。当 stop_reasontool_use 时,response.content 中会包含一个 typetool_use 的内容块,其中的 id 字段是调用标识符,在回传结果时必须通过 tool_use_id 进行关联,否则 Claude 无法正确匹配调用请求与返回结果。

在结果回传阶段,需要构造完整的对话历史传入第二次 API 调用,包括原始用户消息、Claude 的工具调用响应(作为 assistant 消息)以及工具执行结果(作为 user 消息中的 tool_result 内容块)。Claude 在接收到工具返回的数据后,会将其与原始问题结合分析,生成一段完整的自然语言回答,例如"旧金山目前的天气为 15°C,局部多云"。整个过程中,模型始终不直接执行任何代码,所有工具逻辑的安全性和正确性由开发者自行保障。

5. 服务端工具实践

与客户端工具不同,服务端工具(Server Tools)的执行完全发生在 Anthropic 的基础设施上,开发者无需编写任何工具实现代码,也无需处理调用指令的解析与结果回传。Claude 在判断需要使用服务端工具后,会在服务端自动完成工具执行,并将结果直接整合到最终响应中。这种模式极大地简化了开发流程,尤其适用于 Anthropic 已经内置支持的通用能力场景。

目前 Claude 提供的服务端工具主要包括 web_search(网页搜索)和 web_fetch(网页内容获取)两种。前者允许模型在对话过程中主动检索互联网上的实时信息,后者则可以抓取指定 URL 的页面内容进行分析。开发者只需在 tools 参数中声明这些工具,无需定义 input_schema,因为服务端工具的参数结构由 Anthropic 预先规定。

import os import anthropic API_KEY = os.getenv("CLAUDE_API_KEY") API_BASE = os.getenv("CLAUDE_API_BASE") client = anthropic.Anthropic(api_key=API_KEY, base_url=API_BASE)# 使用服务端工具 - web_search response = client.messages.create( model="claude-sonnet-4.6", max_tokens=10240, tools=[{"type":"web_search_20260209","name":"web_search","max_uses":5,# 限制单次对话中的最大搜索次数}], messages=[{"role":"user","content":"搜索一下 2026年3月13日 GitHub 热门项目, 简要介绍它们。"}],)print(response.content)

上述代码的结构相比客户端工具明显更为简洁。服务端工具通过特定的 type 标识(如 web_search_20260209)进行声明,而非使用通用的 "function" 类型。max_uses 参数用于控制工具在单次对话中的调用上限,防止模型过度依赖搜索而消耗过多资源。整个过程中,开发者只需发起一次 API 调用即可获得最终结果,不存在客户端工具中"接收调用指令—执行函数—回传结果"的多轮交互。

在这里插入图片描述

服务端工具的一个重要特性是支持内部采样循环(sampling loop)。当一个问题需要多步检索才能获得完整答案时,Claude 可以在服务端连续执行多次工具调用——例如先搜索关键词获取相关链接,再抓取特定页面提取详细内容——整个过程对开发者完全透明,最终返回的响应已经是经过多轮工具调用后综合整理的结果。

下表对客户端工具与服务端工具的核心差异进行了对比:

特性客户端工具服务端工具
执行位置开发者应用程序Anthropic 服务端
需要回传结果是,需构造 tool_result否,自动整合
自定义逻辑完全自定义仅限预置工具能力
多轮调用处理开发者手动编排服务端自动循环
典型场景数据库查询、业务 API、本地计算网页搜索、URL 内容分析

在实际项目中,客户端工具与服务端工具通常会混合使用。例如一个智能客服系统可以同时配置 web_search 用于检索公开信息,以及自定义的 query_order 客户端工具用于查询内部订单数据库。Claude 会根据用户的具体问题自主选择合适的工具组合,开发者只需确保客户端工具的调用链路正确处理即可。






Alt

Once Day

也信美人终作土,不堪幽梦太匆匆......

如果这篇文章为您带来了帮助或启发,不妨点个赞👍和关注!

(。◕‿◕。)感谢您的阅读与支持~~~

Read more

JavaScript DOM 核心操作:从内容到节点的实战指南

JavaScript DOM 核心操作:从内容到节点的实战指南

DOM(文档对象模型)是前端开发中操作页面结构、内容和样式的核心,本文聚焦 DOM 中元素内容、属性、样式的读写修改,以及节点的增删改,结合实战示例讲解核心用法与最佳实践。 一、操作元素内容 元素内容操作分为纯文本处理和带 HTML 结构的处理,核心使用 innerText 和 innerHTML 两个属性,二者特性对比如下: 方法识别 HTML 标签保留换行 / 空格标准性适用场景innerText❌❌非标准(IE)仅读取 / 修改纯文本innerHTML✅✅W3C 标准读取 / 修改带 HTML 结构的内容 1. innerText:纯文本操作 仅处理文本内容,会忽略 HTML 标签和源码中的换行 / 空格,适合简单文本读写。 // 读操作:获取元素纯文本内容 var text = element.innerText;

By Ne0inhk
Java SpringBoot+Vue3+MyBatis 图书管理系统系统源码|前后端分离+MySQL数据库

Java SpringBoot+Vue3+MyBatis 图书管理系统系统源码|前后端分离+MySQL数据库

摘要 随着信息技术的快速发展,传统图书管理模式逐渐暴露出效率低下、数据冗余等问题,亟需通过数字化手段提升管理效率。图书管理系统作为图书馆和书店的核心工具,能够实现图书信息的快速录入、检索、借阅和归还,大幅降低人工操作的错误率。基于此,本研究设计并实现了一套基于前后端分离架构的图书管理系统,旨在通过现代化的技术手段优化图书管理流程,提高用户体验和管理效率。关键词:图书管理、数字化、效率优化、前后端分离、MySQL。 本系统采用Java SpringBoot作为后端框架,结合Vue3构建前端界面,利用MyBatis实现数据持久化操作,数据库选用MySQL以确保数据的高效存储与查询。系统主要功能包括用户登录与权限管理、图书信息增删改查、借阅与归还记录管理、数据统计与分析等模块。前后端分离的设计模式提升了系统的可维护性和扩展性,同时RESTful API接口确保了前后端数据交互的高效性。系统通过响应式布局和动态路由优化了用户体验,管理员可通过可视化界面实时监控图书流通情况。关键词:SpringBoot、Vue3、MyBatis、MySQL、权限管理、数据统计。 数据表设计 用户信息数据

By Ne0inhk
我的世界Java下载——MC启动的基石【2025年MC下的Java下载配置教程】

我的世界Java下载——MC启动的基石【2025年MC下的Java下载配置教程】

一、从Mc的角度简述Java     ·游戏本体就是 Java 写的:Notch 最早用 Java 开发 MC,使其天然跨平台,PC、Mac、Linux 都能玩。     ·模组生态靠 Java:Forge、Fabric 等 API 和无数 Mod 都是 Java 字节码;玩家拖进 mods 文件夹就能被 JVM 动态加载,无需重新编译游戏。     ·插件与服务端同理:Bukkit、Spigot、Paper 等服务器核心也是 Java 程序,插件 jar 直接热插拔,让小游戏、 经济、地皮等功能即刻生效。     ·启动器只是“入口”:PCL2、HMCL、官方启动器都负责下载

By Ne0inhk
LangChain v1.0:从零开始构建企业级AI智能体全攻略!

LangChain v1.0:从零开始构建企业级AI智能体全攻略!

2025年9月,LangChain正式发布v1.0版本,标志着这个曾经被开发者戏称为"玩具框架"的工具包,终于完成了向生产级解决方案的关键一跃。作为深耕LangChain生态两年的开发者,我见证了从v0.1到v1.0的艰难蜕变。这次更新绝非简单的版本迭代——通过create_agent新接口、content_blocks标准化内容处理和命名空间简化三大核心改进,LangChain第一次真正解决了企业级Agent开发的痛点:开发效率提升40%、系统稳定性提高65%、模型切换成本降低80%。 版本解析:三大核心改进重构Agent开发范式 create_agent:一行代码构建生产级智能体 告别200行模板代码——这是create_agent接口给开发者最直观的感受。在v1.0之前,构建一个基础的ReAct Agent需要导入langgraph.prebuilt.create_react_agent,手动配置提示词模板,处理工具调用格式,至少编写50行以上的样板代码。而现在,一切都变得无比简单: from langchain.agents import create_agentfr

By Ne0inhk