news 2026/5/23 16:07:32

Tool Calling 与 Function Call 深度指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Tool Calling 与 Function Call 深度指南

本文档将带你深入理解 AI Agent 的核心机制:如何让大模型连接外部世界。我们将从最基础的概念出发,一步步揭示技术背后的原理。


1. 什么是 Tool Calling (工具调用)?

核心定义
Tool Calling 是指赋予大语言模型(LLM)使用外部工具的能力。
如果说 LLM 是一个博学的“大脑”,那么 Tool Calling 就是给它装上了“手”和“眼睛”。

1.1 为什么 LLM 需要工具?(The Why)

LLM 本质上是一个“文本生成器”,它有三大先天缺陷,必须通过使用工具来弥补:

  1. 无法获取实时信息
    • 痛点:它的知识截止于训练结束的那一天(比如不知道今天的股价)。
    • 工具:搜索引擎、天气 API、股票接口。
  2. 数学与逻辑短板
    • 痛点:它是基于概率预测下一个字的,做算术题经常瞎编(幻觉)。
    • 工具:计算器、Python 解释器。
  3. 无法与世界交互
    • 痛点:它只能生成文字,不能真正地“做事”(副作用)。
    • 工具:发邮件 API、数据库写入接口、智能家居控制器。

2. 实现 Tool Calling 的两种方式

既然 Tool Calling 是目的,那么我们该如何实现它呢?主要有两种技术路线:

方式 A:Prompt Engineering (提示词工程)

这是在早期模型(如 GPT-3)中常用的方法,也称为ReAct 模式

  • 做法:在 System Prompt 里写死规则:“如果你要用工具,请按这个格式输出:Action: search, Action Input: query”。
  • 缺点:非常不稳定。模型经常忘记格式,或者输出少个括号,导致程序无法解析。

方式 B:Function Calling (函数调用)

这是现代模型(如 GPT-4, Gemini)引入的原生能力

  • 做法:直接把工具的定义(JSON Schema)传给模型 API。
  • 优点:模型经过专门训练(Fine-tuning),能百分之百输出符合语法的结构化数据(JSON)。
  • 结论Function Call 是实现 Tool Calling 的最佳实践。

3. 深入解析:Function Call 技术原理

Function Call 本质上解决了“自然语言”到“机器指令”的翻译问题。

关键认知

LLM 永远不会自己执行代码。
当触发 Function Call 时,LLM 只是生成了一个文本指令(JSON)
真正执行代码的,依然是你的 Python/Java 后端。

3.1 核心工作流程 (Workflow)

这是一个“LLM - 代码 - LLM”的三明治结构。

UserLLM (Brain)App (Python Code)Tool (Function)AppLLMTool"What is 123 * 456?"1. App 把用户问题 + 工具定义(Schema) 发给 LLMPrompt + Tools Definition2. LLM 思考:这需要计算,我要调用 multiplyResponse (包含 tool_calls: multiply(123, 456))3. App 暂停生成,去执行本地代码执行 multiply(123, 456)返回结果 560884. App 把运行结果作为 "ToolMessage" 发回给 LLMUser Prompt + Tool Call + Tool Result(56088)5. LLM 看到结果,生成最终回答"The answer is 56088.""The answer is 56088."UserLLM (Brain)App (Python Code)Tool (Function)AppLLMTool

3.2 数据结构解密 (Under the Hood)

当我们说“定义工具”时,到底发给了 LLM 什么?通常是JSON Schema

发送给 LLM 的工具定义:

{"name":"multiply","description":"Multiplies two integers.","parameters":{"type":"object","properties":{"a":{"type":"integer"},"b":{"type":"integer"}},"required":["a","b"]}}

LLM 返回的调用指令:

{"tool_calls":[{"name":"multiply","arguments":"{\"a\": 123, \"b\": 456}"}]}

4. 技术演进与未来

  1. ReAct 时代:靠 Prompt 苦苦支撑,极不稳定。
  2. Native Function Calling:模型微调,稳定性大增,成为行业标准。
  3. Agents (智能体):能够自主规划、连续多轮调用工具,完成复杂任务。
  4. MCP (Model Context Protocol):未来的通用标准。它像 USB 协议一样,定义了数据和工具的接口标准,让 AI 能即插即用地连接任何系统。

5. 实战示例对比

我们在src/examples/function_call/下提供了三种实现方式的对比。以下是每种方式的完整代码及深度解析。

示例 1: LangChain Native 方式 (推荐)

这是现代 AI 应用开发的标准做法,利用 LangChain 封装好的接口,代码最简洁,兼容性最好。

文件:src/examples/function_call/demo_with_function_call.py

importsrc.configs.configfromloguruimportloggerfromlangchain_core.toolsimporttoolfromsrc.llm.gemini_chat_modelimportget_gemini_llm# 1. 定义工具 (使用 @tool 装饰器)@tooldefmultiply(a:int,b:int)->int:"""Multiplies two integers."""returna*b# 2. 初始化 LLMllm=get_gemini_llm()# 3. 绑定工具 (Native Function Calling)# 这会把工具的 schema 转换为 Gemini API 能理解的格式 (Function Declaration)llm_with_tools=llm.bind_tools([multiply])# 4. 执行logger.info("=== Demo: Native Function Calling (bind_tools) ===")query="What is 123 multiplied by 456?"logger.info(f"User Question:{query}")response=llm_with_tools.invoke(query)logger.info(f"LLM Response Type:{type(response)}")logger.info(f"LLM Response Content:{response.content}")# 5. 检查是否触发了 Function Callifresponse.tool_calls:logger.info("Tool Call Detected!")fortool_callinresponse.tool_calls:logger.info(f"Tool Name:{tool_call['name']}")logger.info(f"Arguments:{tool_call['args']}")# 执行工具 (可选)iftool_call['name']=='multiply':result=multiply.invoke(tool_call['args'])logger.info(f"Tool Execution Result:{result}")else:logger.info("No tool call detected.")
深度代码解析
  • @tool:
    • 作用: LangChain 提供的装饰器。它不仅定义了函数,还会自动提取函数的docstring(文档字符串)和类型注解(Type Hints),将其转换为 LLM 能理解的JSON Schema描述。
    • 重要性: 文档字符串写得越好,LLM 越能准确理解何时调用这个工具。
  • llm.bind_tools([multiply]):
    • 作用: 这是 LangChain 的核心适配器方法。
    • 功能: 它将 Python 函数列表转换为特定 LLM 提供商(这里是 Google Gemini)所需的 API 格式(如FunctionDeclaration)。对于 OpenAI,它会转换为functionstools参数格式。
    • 返回值: 返回一个新的Runnable对象,这个对象已经“知道”了这些工具的存在。
  • response.tool_calls:
    • 作用:AIMessage对象的一个属性。如果模型决定调用工具,这个列表会包含调用信息。
    • 结构:[{'name': 'multiply', 'args': {'a': 123, 'b': 456}, 'id': '...'}]
    • 优势: 这是一个标准的 Python 字典/对象,不需要你去解析原始的 JSON 字符串,LangChain 已经帮你解析好了。

示例 2: Manual / ReAct 方式 (手动 Prompt)

这是在 Function Calling 技术出现之前的做法。虽然现在不推荐用于新模型,但了解它有助于理解 Function Call 的本质。

文件:src/examples/function_call/demo_no_function_call.py

importsrc.configs.configfromloguruimportloggerfromlangchain_core.promptsimportChatPromptTemplatefromsrc.llm.gemini_chat_modelimportget_gemini_llm# 1. 定义工具 (Tools)defmultiply(a:int,b:int)->int:"""Multiplies two integers."""returna*bdefmain():# 2. 初始化 LLMllm=get_gemini_llm()# 3. 定义 ReAct 风格的 Prompt (手动教 LLM 如何调用工具)# 我们不使用 bind_tools,而是把工具描述写在 Prompt 里react_system_prompt=""" You are a helpful assistant. You have access to the following tools: 1. multiply: Multiplies two integers. Input should be two numbers separated by a comma. To use a tool, please use the following format exactly: Thought: Do I need to use a tool? Yes Action: multiply Action Input: 5, 4 Observation: [Tool output will be placed here] If you do not need to use a tool, just answer the question directly. """prompt=ChatPromptTemplate.from_messages([("system",react_system_prompt),("user","{input}")])chain=prompt|llm# 4. 执行logger.info("=== Demo: Manual Tool Usage (No Function Call) ===")query="What is 123 multiplied by 456?"logger.info(f"User Question:{query}")response=chain.invoke({"input":query})content=response.content logger.info(f"LLM Response (Raw Content):\n{content}")# 5. 手动解析并执行 (模拟 Agent 的工作)# 这是一个非常简化的解析器ifisinstance(content,str)and"Action: multiply"incontent:try:# Extract input (Assuming format "Action Input: x, y")lines=content.split('\n')action_input_line=next(lineforlineinlinesifline.strip().startswith("Action Input:"))input_str=action_input_line.split(":")[1].strip()args=[int(x.strip())forxininput_str.split(",")]logger.info(f"Detected Tool Call: multiply with args{args}")# Execute toolresult=multiply(args[0],args[1])logger.info(f"Tool Execution Result:{result}")exceptExceptionase:logger.error(f"Failed to parse or execute tool:{e}")else:logger.info("No tool call detected or format incorrect.")if__name__=="__main__":main()
深度代码解析
  • Prompt Engineering (react_system_prompt):
    • 作用: 我们必须在 System Prompt 中手动写一段很长的说明书,教 LLM:“如果你要用工具,请必须按这个格式写…”。
    • 痛点: 这非常消耗 Token,而且模型很容易不遵守格式(比如把Action Input写成了Input),导致解析失败。
  • 手动解析逻辑 (Parsing Logic):
    • 作用: 代码中的if "Action: multiply" in content:content.split('\n')部分。
    • 痛点: 这是非常脆弱的。我们必须用字符串匹配和正则表达式去“猜测”模型的意图。如果模型多输出一个空格或换行,解析器可能就挂了。这正是Function Call致力解决的问题——把非结构化的文本解析变成结构化的 API 调用。

示例 3: Google Native SDK 方式

如果你不想引入 LangChain 的复杂性,想直接使用 Google 官方 SDK,这是最轻量级的选择。

文件:src/examples/function_call/demo_gemini_native.py

importosfromdotenvimportload_dotenvfromgoogleimportgenaifromgoogle.genaiimporttypes# 1. 加载环境变量load_dotenv()api_key=os.getenv("GOOGLE_API_KEY")ifnotapi_key:print("Error: GOOGLE_API_KEY not found in .env")exit(1)# 2. 定义工具函数defmultiply(a:int,b:int)->int:"""Multiplies two integers."""returna*b# Wrapper 类定义 (省略,见完整代码) ...defmain():# 3. 初始化 Clientclient=genai.Client(api_key=api_key)# 4. 配置模型参数 (预定义配置)my_config=types.GenerateContentConfig(tools=[multiply],tool_config=types.ToolConfig(function_calling_config=types.FunctionCallingConfig(mode=types.FunctionCallingConfigMode.ANY# 强制模型使用工具)))# 5. 实例化封装后的 Modelprint("=== Demo: Native Gemini SDK (Wrapped Style) ===")my_model=GeminiModelWrapper(client,"gemini-2.5-pro",my_config)# 6. 调用query="What is 123456 multiplied by 6854321?"print(f"User Question:{query}")response=my_model.generate(query)# 7. 解析结果print(f"\nResponse Text:{response.text}")# 检查 Function Callsif(response.candidatesandresponse.candidates[0].contentandresponse.candidates[0].content.parts):forpartinresponse.candidates[0].content.parts:ifpart.function_call:fc=part.function_callprint(f"\nFunction Call Detected:")print(f" Name:{fc.name}")print(f" Args:{fc.args}")# 增加对 fc.args 的非空检查以满足静态类型检查args=fc.argsiffc.name=="multiply"andargsisnotNone:result=multiply(int(args['a']),int(args['b']))print(f" Execution Result:{result}")if__name__=="__main__":main()
深度代码解析
  • genai.Client:
    • 作用: Google GenAI V1 SDK 的核心入口。
  • types.GenerateContentConfig:
    • 作用: 用于配置生成请求的参数。
    • tools=[multiply]: 这里的亮点是,Google SDK 能够直接接受 Python 函数作为工具列表,它内部会自动反射生成 JSON Schema,无需手动编写 Schema。
  • mode=types.FunctionCallingConfigMode.ANY:
    • 作用:强制模式。这告诉模型:“不管用户问什么,你必须调用一个工具,不准直接回答文本”。
    • 场景: 当你明确知道这一步必须执行动作(如计算、查询)时使用。默认是AUTO(模型自己决定用不用)。
  • part.function_call:
    • 作用: SDK 将返回的 JSON 指令反序列化为 Python 对象。
    • 属性: 包含name(函数名) 和args(参数字典)。这使得参数提取变得非常安全和简单。

7. 常见误区 (Myth Busting)

  1. 误区:LLM 只要联网就能自己调用工具。

    • 真相:LLM 必须由开发者显式地提供工具定义,并在代码中显式地执行工具。它自己没有手。
  2. 误区:Function Call 只能调用 Python 函数。

    • 真相:Function Call 只是输出文本指令。你的后端可以用 Python、Java、Go 甚至 Shell 脚本来执行这个指令。
  3. 误区:模型会无条件执行任何工具。

    • 真相:模型会根据 Prompt 和上下文判断。但这也带来了安全风险(Prompt Injection)。千万不要给 LLM 一个delete_database()的工具而不加权限控制!
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/21 5:15:16

零门槛上手!AcFunDown:小白也能秒懂的A站视频下载神器

还在为喜欢的AcFun视频无法离线保存而烦恼吗?AcFunDown作为一款完全免费的A站视频下载工具,凭借其简单易用的图形界面和强大的下载功能,让视频收藏变得前所未有的简单。无论你是想保存精彩瞬间还是备份学习资料,这款开源神器都能完…

作者头像 李华
网站建设 2026/5/15 16:50:47

ARM64与x64启动流程对比:系统移植深度剖析

ARM64与x64启动流程对比:从加电到内核的系统移植实战解析你有没有遇到过这样的情况:把一个在 x86_64 上跑得好好的 Linux 系统镜像,直接烧录到一块新的 ARM64 开发板上,结果——黑屏、串口无输出、CPU卡死?别急&#x…

作者头像 李华
网站建设 2026/5/22 21:27:22

Patreon内容备份终极指南:5分钟快速上手教程

Patreon内容备份终极指南:5分钟快速上手教程 【免费下载链接】PatreonDownloader Powerful tool for downloading content posted by creators on patreon.com. Supports content hosted on patreon itself as well as external sites (additional plugins might be…

作者头像 李华
网站建设 2026/5/23 10:53:54

STM32硬件SPI驱动ST7789显示屏:高效图形渲染终极方案

STM32硬件SPI驱动ST7789显示屏:高效图形渲染终极方案 【免费下载链接】ST7789-STM32 using STM32s Hardware SPI to drive a ST7789 based IPS displayer 项目地址: https://gitcode.com/gh_mirrors/st/ST7789-STM32 STM32 ST7789驱动项目通过硬件SPI接口配合…

作者头像 李华
网站建设 2026/5/22 13:34:44

AcFunDown终极指南:2025年最简单快速的A站视频批量下载方案

还在为无法保存AcFun精彩视频而烦恼吗?AcFunDown作为一款专为A站用户设计的免费视频下载工具,能够让你轻松实现视频批量下载、多格式支持和断点续传功能。无论你是想收藏UP主的精彩作品,还是备份学习资料,这款基于Java开发的图形界…

作者头像 李华
网站建设 2026/5/21 16:49:38

网购平台信息管理系统源码-SpringBoot后端+Vue前端+MySQL【可直接运行】

摘要 随着互联网技术的快速发展和电子商务的普及,网购平台已成为人们日常生活中不可或缺的一部分。传统的线下购物模式逐渐被线上购物所取代,消费者对网购平台的便捷性、安全性和用户体验提出了更高要求。然而,许多中小型电商平台在信息管理…

作者头像 李华