工具调用:FunctionCall与ToolUse
工具调用:Function Call 与 Tool Use
工具调用是 Agent 的「手」,让大模型能操作外部世界。这篇讲 Function Calling 的原理、工具怎么定义、模型怎么选工具、参数怎么传、常见的工具类型,以及开发中的最佳实践。
大家好,我是黒漂技术佬。
大模型本身只能输出文字,知识有截止日期、不会算精确数学、不能查数据库、发不了邮件。接上工具之后,能力边界大大扩展。
Function Calling(函数调用,也叫 Tool Use)就是大模型调用外部工具的标准方式。现在主流模型都原生支持,是 Agent 开发的核心技术。
这篇讲工具调用的原理、工具定义、多工具选择、参数传递、常见工具类型、最佳实践。
一、什么是 Function Calling?
概念
你把工具(函数)的描述告诉大模型,模型根据用户问题判断:
- 需不需要调用工具
- 调用哪个工具
- 参数填什么
然后模型输出结构化的调用指令,你的程序执行这个函数,把结果返回给模型继续处理。
为什么不用普通对话?
普通对话里你也可以让模型输出「调用搜索(xxx)」,但格式不稳定、容易错。Function Calling 是模型专门训练过的,输出是标准 JSON 格式,稳定可靠。
交互流程
1. 你:用户问题 + 工具定义 → 发给模型
2. 模型:判断要调用工具 → 返回 tool_calls(JSON格式)
3. 你:执行函数,拿到结果
4. 你:把结果发给模型
5. 模型:根据结果生成最终回答
多轮的话就是 2-3-4 循环,跟 ReAct 对应。
二、工具怎么定义?
JSON Schema 格式
每个工具用 JSON Schema 描述:名字、描述、参数类型、参数说明。
{
"type": "function",
"function": {
"name": "search_web",
"description": "搜索互联网获取实时信息,回答不知道的问题",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "搜索关键词"
}
},
"required": ["query"]
}
}
}
字段说明
| 字段 | 作用 |
|---|---|
| name | 函数名,模型调用时返回这个名字 |
| description | 函数说明,模型靠这个判断什么时候用 |
| parameters | 参数定义,JSON Schema 格式 |
| properties | 每个参数的名字、类型、描述 |
| required | 必填参数列表 |
复杂参数
参数可以是嵌套对象、数组、枚举:
{
"name": "send_email",
"description": "发送邮件给指定收件人",
"parameters": {
"type": "object",
"properties": {
"to": {
"type": "string",
"description": "收件人邮箱地址"
},
"subject": {
"type": "string",
"description": "邮件主题"
},
"body": {
"type": "string",
"description": "邮件正文"
},
"priority": {
"type": "string",
"enum": ["normal", "high", "low"],
"description": "优先级"
}
},
"required": ["to", "subject", "body"]
}
}
多个工具
一次可以传多个工具定义,模型自己选:
tools = [
{ "type": "function", "function": { "name": "search_web", ... } },
{ "type": "function", "function": { "name": "calculator", ... } },
{ "type": "function", "function": { "name": "get_weather", ... } },
]
三、调用过程详解
第一步:用户提问,带上工具定义
messages = [
{"role": "user", "content": "北京今天天气怎么样?"}
]
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "查询指定城市的实时天气",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "城市名"}
},
"required": ["city"]
}
}
}
]
response = llm.chat(messages=messages, tools=tools)
第二步:模型返回工具调用
模型判断需要调用工具,返回:
{
"role": "assistant",
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\": \"北京\"}"
}
}
]
}
tool_calls 就是模型要调用的工具列表。arguments 是 JSON 字符串。
第三步:执行工具函数
import json
tool_call = response.tool_calls[0]
func_name = tool_call.function.name
args = json.loads(tool_call.function.arguments)
# 执行对应的函数
if func_name == "get_weather":
result = get_weather(**args)
第四步:把结果传回模型
messages.append(response) # 把模型的tool_call消息加回去
messages.append({
"role": "tool",
"tool_call_id": "call_abc123",
"content": result # 比如"北京今天晴,25-32℃"
})
final_response = llm.chat(messages=messages, tools=tools)
第五步:模型生成最终回答
模型拿到工具结果,整理成自然语言回答:
北京今天天气晴朗,气温25到32摄氏度,空气质量良,适合出行。
多工具多轮
如果需要多个工具,模型可能返回多个 tool_call,或者一轮一轮调用,跟 ReAct 循环一样。
四、模型怎么选工具?
靠 description
模型主要看函数的 description 判断什么时候用。描述写得清楚,模型就选得准。
好的 description 怎么写?
坏例子:
"description": "搜索"
太简略,模型不知道什么时候用。
好例子:
"description": "当需要回答实时信息、新闻、未知事实时,调用此工具搜索互联网。
不要用已有知识回答时效性问题。"
说明清楚:什么时候用、什么场景用、什么情况不要用。
最佳实践
- 名字直观:函数名要见名知意
- 描述详细:说明用途、适用场景、不适用场景
- 参数说明清楚:每个参数是什么、格式要求、例子
- 工具数量适中:太多了模型容易选错,一般 5-10 个比较合适
- 功能不重叠:两个工具功能差不多的话模型会纠结
五、常见的工具类型
1. 搜索类
- 网页搜索(Google、Bing、Tavily)
- 知识库检索(企业内部 RAG)
- 文档搜索
2. 计算类
- 数学计算器
- Python 代码执行(最强大,能算能画图能处理数据)
- 单位换算
3. 信息查询类
- 天气查询
- 股票行情
- 航班/火车查询
- 数据库查询(自然语言转 SQL)
4. 操作执行类
- 发送邮件
- 创建日历日程
- 发送消息(飞书/钉钉/微信)
- 创建工单
- 文件读写
5. 代码类
- 执行 Python 代码
- 代码审查
- 运行测试
6. 浏览器类
- 打开网页
- 截图
- 点击/填写表单
六、并行工具调用
是什么
模型一次返回多个 tool_call,同时调用多个工具,不用等一个完了再调下一个。
"tool_calls": [
{"name": "get_weather", "arguments": {"city": "北京"}},
{"name": "get_weather", "arguments": {"city": "上海"}}
]
好处
- 减少轮次,更快
- 独立的查询可以并行执行,省时间
什么时候并行
- 两个查询互相不依赖
- 需要对比多个信息的时候
比如「对比北京和上海的天气」,就可以并行查两个城市。
七、工具调用的最佳实践
1. 工具描述要精准
description 是模型选工具的唯一依据,一定要写清楚:
- 这个工具是干嘛的
- 什么情况下用
- 什么情况下不要用
- 参数的格式和含义
2. 参数校验
模型传的参数不一定对,一定要校验:
- 必填参数有没有
- 类型对不对
- 枚举值在不在范围内
- 格式合不合法(邮箱、日期等)
不对就返回错误信息,让模型重试。
3. 错误处理要友好
工具执行失败,返回给模型的错误信息要清楚,方便它修正:
错误:城市名「火星」不支持,支持的城市列表:北京、上海、广州...
模型看到就知道换个城市名再试。
4. 结果要简洁
工具返回的结果不要太长,太长占 token 还干扰模型。做摘要再返回:
- 搜索结果只返回前 3 条摘要
- 数据库查询只返回关键字段
- 长文本先总结再给模型
5. 控制工具数量
工具太多模型容易选错。按场景给工具:
- 客服 Agent 给知识库+工单工具
- 数据分析 Agent 给 SQL+画图工具
- 不要把所有工具都塞进去
6. 敏感操作要确认
发邮件、删数据、下单这种有副作用的操作,执行前要人确认:
- Agent 生成调用请求
- 弹给用户确认
- 用户点确认了再真正执行
7. 工具结果要标记来源
RAG 搜索的结果要标来源链接,方便用户查证,也能追责。
八、常见坑
坑 1:工具描述太模糊
"搜索工具" → 模型不知道什么时候该搜。要写成"需要实时信息、最新新闻时使用"。
坑 2:参数没校验
模型传了个不存在的参数值,程序直接崩。一定要校验参数。
坑 3:工具返回结果太长
搜索返回整篇文章,token 爆了还干扰判断。要截断、摘要。
坑 4:工具功能重叠
两个工具都能查信息,模型不知道选哪个。合并或者区分清楚场景。
坑 5:循环调用失败
工具报错了,模型用同样的参数反复调。要加错误提示和重试限制。
坑 6:安全问题
Agent 能调用删数据、发邮件这种危险操作,没有确认机制。敏感操作必须人在回路。
坑 7:arguments 是字符串
注意 tool_calls 里的 arguments 是 JSON 字符串,不是对象,要 json.loads 解析一下。
九、一个完整的多工具示例
工具定义
tools = [
{
"type": "function",
"function": {
"name": "search_company",
"description": "查询公司的基本信息,包括成立时间、创始人、主营业务",
"parameters": {
"type": "object",
"properties": {
"company_name": {"type": "string", "description": "公司名称"}
},
"required": ["company_name"]
}
}
},
{
"type": "function",
"function": {
"name": "get_stock_price",
"description": "查询上市公司的当前股价和市值",
"parameters": {
"type": "object",
"properties": {
"stock_code": {"type": "string", "description": "股票代码"}
},
"required": ["stock_code"]
}
}
}
]
对话过程
用户: 字节跳动上市了吗?市值多少?
模型(第一轮):
先查字节跳动的基本信息,看有没有上市、股票代码是什么。
tool_call: search_company("字节跳动")
工具返回:
字节跳动成立于2012年,创始人张一鸣,旗下产品有抖音、TikTok等。
目前未上市,暂无股票代码。
模型(第二轮):
字节跳动还没上市,没有股票代码,所以查不到市值。直接回答用户。
最终回答:
字节跳动目前尚未上市,因此没有公开的市值数据。
公司成立于2012年,旗下核心产品包括抖音、TikTok等。
十、本篇小结
- Function Calling:模型原生支持的工具调用机制,输出结构化的 JSON 调用指令
- 工具定义:name + description + parameters(JSON Schema)
- 调用流程:用户问题+工具定义 → 模型返回 tool_calls → 执行函数 → 结果传回 → 模型生成答案
- 模型靠 description 判断选哪个工具,描述写得准不准很重要
- 支持并行调用:一次返回多个 tool_call,独立任务并行执行更快
- 常见工具类型:搜索、计算、信息查询、操作执行、代码、浏览器
- 最佳实践:描述精准、参数校验、错误友好、结果简洁、控制数量、敏感操作要人确认
- 常见坑:描述模糊、不校验参数、结果太长、功能重叠、安全问题
下一篇讲记忆系统:短期记忆、长期记忆、向量记忆,Agent 怎么记住用户和历史。
我是黒漂技术佬。

浙公网安备 33010602011771号