AI Agent 原理与实现(二):工具调用
在之前的文章里我介绍过大语言模型本质上是一个文本生成模型,我们向它输入一段文本,它根据已有的上下文不断预测下一个字符,最终生成一段回答。模型非常擅长发现文本中的模式,并且按照这些模式生成新的文本。它可以帮你写文章、写代码、总结资料、回答问题。模型在训练阶段接触了大量的文本数据,学会了如何从上下文中预测下一个字符。但大语言模型也存在短板,它只能根据当前用户输入和自己在训练阶段学到的知识来生成回答,它无法访问实时信息。另外一个短板是,模型只能“说”,不能实际动手“做”。
几年前我们在和大模型交互时,询问现在是什么时间,模型可能会回答一个过去的时间,比如 2023 年。当我询问今天北京的天气时,它可能会胡乱编造一个天气。这里本质的原因是,模型的参数里存储的是训练阶段学到的知识,而这些知识是静态的、过时的。模型无法访问实时信息,它只能根据用户输入来生成文本。
另外,当你请求大模型帮你做一件具体的事情时,大模型常常是无能为力的。比如下面是我和某大语言模型的对话:
帮我订咖啡
我没法真的帮你下单买咖啡——我没有接入任何外卖/咖啡平台的账号或支付能力,也没法替你完成付款和配送。
不过我可以帮你做这些:
搜咖啡店:告诉我你所在的城市或区域,我可以帮你查附近的咖啡店、评分和营业时间。 写订单:告诉我你想喝什么(比如拿铁、美式、燕麦拿铁、甜度/温度偏好),我帮你整理成一段可以直接复制发给店员或粘贴到外卖 App 的文字。 比价:如果你在几个店之间犹豫,我可以帮你对比价格和评价。 你想要哪种帮助?或者直接告诉我你想喝什么、在哪个位置,我帮你把订单内容准备好。
模型完全能理解你的意思,但它实在没办法帮你完成订咖啡的操作。因为它接触不到外部世界的实时信息,也没有能力去执行支付的操作,它只能给你一些建议。
本文将要介绍的工具调用则是赋予大语言模型“动手能力”的关键技术。通过工具调用,模型可以在需要时调用外部程序来获取实时信息、执行计算、操作外部系统,从而完成用户的请求。
什么是工具调用
工具调用并不是让大模型真的去使用一个工具,而是让大模型学会在需要时生成一条结构化的“调用请求”,告诉外部程序应该调用哪个函数、传入什么参数。外部程序接收到这个请求后,执行相应的操作,并将结果返回给模型,模型再根据结果生成最终回答。
比如,你的应用程序里存在这样一个函数:
def get_weather(city: str, date: str):
...
开发者可以把这个函数的名称、用途和参数格式告诉模型,就像递给模型一张"工具使用说明书"。这时候,用户问"北京明天会下雨吗?",输入给模型的上下文里除了这个问题,还包含了 get_weather 的使用说明。输入给模型的上下文大致变成:
可用工具:
- get_weather(city, date):查询指定城市在指定日期的天气
- city:需要查询的城市
- date:需要查询的日期
- web_search(query):在互联网上搜索信息
- query:搜索关键词
- send_email(to, subject, body):发送电子邮件
- to:收件人邮箱地址
- subject:邮件主题
- body:邮件正文
用户:北京明天会下雨吗?
模型不再胡乱编造一个天气信息,而是选择使用 get_weather 这个工具去获取当前的实时信息。于是大模型输出如下内容:
<tool_call>
{
"name": "get_weather",
"arguments": {
"city": "北京",
"date": "明天"
}
}
</tool_call>
Agent 收到这条请求后,会使用模型给出的参数调用 get_weather 函数,获取到真实的天气信息,再把查询结果返回给模型。
此时,输入给模型的上下文大致变成:
可用工具:
- get_weather(city, date):查询指定城市在指定日期的天气
- city:需要查询的城市
- date:需要查询的日期
- web_search(query):在互联网上搜索信息
- query:搜索关键词
- send_email(to, subject, body):发送电子邮件
- to:收件人邮箱地址
- subject:邮件主题
- body:邮件正文
用户:北京明天会下雨吗?
助手决定调用:get_weather(city="北京", date="明天")
工具返回:
{
"condition": "阵雨",
"temperature_min": 25,
"temperature_max": 32,
"precipitation_probability": 70
}
模型看到历史对话信息,了解到此前用户想要查询北京明天的天气,也知道自己请求调用了 get_weather 工具,并且收到了工具返回的结果。于是模型就可以根据这些信息生成最终的回答:
北京明天预计有阵雨,降水概率约为 70%,建议随身携带雨伞。气温在 25~32℃之间,体感可能比较闷热。
所以,工具调用准确的定义是这样的——大模型根据用户意图,在开发者提供的工具集合中选择合适的工具,并生成符合指定结构的调用参数。外部程序负责执行工具,并将结果返回给模型,模型再根据结果生成最终回答。
工具调用的完整流程
这里我仍以查询天气为例,说明工具调用的完整流程。
第一步:定义工具
首先我们需要定义一系列可以被大语言模型使用的工具,说是工具,实际上其实就是一些函数。我们预先并不知道大语言模型会处理什么任务,所以我们通常会提供多个可能被大语言模型使用到的工具。
比如我们提供了如下工具:
- get_weather(city: str, date: str):查询指定城市在指定日期的天气
- web_search(query: str):在互联网上搜索信息
- send_email(to: str, subject: str, body: str):发送电子邮件
- download_file(url: str, save_path: str):下载文件到本地
- ...
这里每一个工具都是一个函数,可以使用 Python 定义:
def get_weather(city: str, date: str) -> dict:
return weather_service.query(city=city, date=date)
我们需要详细地描述每个工具的作用,以及它的每个参数的含义,大语言模型才能够判断应该使用哪个工具,以及如何使用此工具。下面是 get_weather 工具的详细描述:
{
"type": "function",
"function": {
"name": "get_weather",
"description": "查询某个城市在指定日期的天气",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "需要查询的城市"
},
"date": {
"type": "string",
"description": "需要查询的日期"
}
},
"required": ["city", "date"]
}
}
}
工具描述中说明了该工具的名称、作用、参数的类型和含义,以及哪些参数是必填的。这个工具描述,就相当于一份专门写给模型看的文档。模型并不关心某个工具具体是怎么实现的,只需要知道这个工具能干什么,以及如何使用它。
第二步:把工具描述和用户请求发送给模型
因为我们预先并不知道用户会问什么问题,所以我们需要把所有可用的工具描述都发送给模型。这些工具会通过结构化的形式发送给模型,格式大致如下:
<tools>
{"type": "function", "function": {"name": "get_weather", "description": "查询某个城市在指定日期的天气", "parameters": {"type": "object", "properties": {"city": {"type": "string", "description": "需要查询的城市"}, "date": {"type": "string", "description": "需要查询的日期"}}, "required": ["city", "date"]}}}
{"type": "function", "function": {"name": "web_search", "description": "在互联网上搜索信息", "parameters": {"type": "object", "properties": {"query": {"type": "string", "description": "搜索关键词"}}, "required": ["query"]}}}
...
</tools>
这里使用了 XML 标签 <tools> 来包裹所有可用的工具,每一行都是一个工具的 JSON 描述。模型可以解析这些工具描述,理解每个工具的作用和参数。
当用户提问:
北京明天会下雨吗?
发送给模型的内容会包含所有的工具定义和用户当前的问题。
第三步:模型决定是否调用工具
对于普通的问题,比如"北京是哪个国家的首都?",模型可能直接生成文本回答:
北京是中国的首都。
对于依赖外部信息的问题,比如"北京明天会下雨吗?",模型就会生成一个特殊的工具调用结构。不同模型输出的格式不太一样,比如 Qwen3 模型的输出可能是:
<tool_call>
{"name":"get_weather","arguments":{"city":"北京","date":"明天"}}
</tool_call>
第四步:Agent 执行工具调用
Agent 接收到大语言模型返回的工具调用内容后,首先会验证参数是否合理。如果参数有误,会拒绝执行,并将错误信息返回给大语言模型。参数验证通过之后,Agent 执行真实代码:
result = get_weather(
city="北京",
date="明天"
)
假设天气服务返回了这样的结果:
{
"city": "北京",
"date": "2026-07-30",
"condition": "阵雨",
"temperature": {
"min": 25,
"max": 32
},
"precipitation_probability": 70
}
到这一步为止,模型自己并没有参与天气查询。它只是提出了一个调用请求。真正去访问网络、发送 HTTP 请求、解析数据的,是 Agent 程序,模型拥有的是工具选择能力,而不是工具执行能力。
第五步:模型生成最终回答
Agent 会将工具执行的结果拼接到上下文中,然后再发送给模型。模型看到工具的执行结果后,就可以继续生成下一轮的输出。模型看到了工具调用的结果之后,会生成最终的回复。
北京明天预计有阵雨,降水概率约为 70%,建议随身携带雨伞。气温在 25~32℃之间,体感可能比较闷热。
至此,一次完整的工具调用流程就结束了。
模型如何知道应该调用哪个工具
你可能会好奇:模型到底是怎么在那么多工具里挑中正确的那一个的?
假设模型同时拥有下面三个工具:
get_weather:查询天气
search_document:搜索文档
send_email:发送邮件
当用户说:
帮我查一下上海明天的天气。
模型需要选择 get_weather。
当用户说:
查一下项目文档中关于故障恢复的说明。
模型需要选择 search_document。
其实这里面依然没有脱离大语言模型的工作原理,始终是基于当前上下文和训练数据来预测下一个字符,这里的上下文就是工具描述和用户输入的指令。在模型的训练过程中,有大量的工具选择类的训练样本。这些训练数据中包含一系列的工具描述和用户请求,以及对应的正确工具选择。模型通过学习这些样本,学会了在面对新的用户请求时,如何根据工具描述和上下文来选择最合适的工具。
不同商业模型的具体训练配方通常不会完全公开,毕竟那是各家公司的看家本领。但从公开的研究来看,大模型学习工具调用的核心目标是:
让模型学习在什么位置调用什么工具、使用什么参数,以及如何利用工具返回的结果继续生成。
训练数据里可以包含类似下面这样的完整轨迹:
用户:
37 × 492 等于多少?
助手:
<tool_call>
{"name":"calculator","arguments":{"expression":"37*492"}}
</tool_call>
工具:
18204
助手:
37 × 492 = 18204。
模型通过这些样本,会学习几件事情:
- 精确计算应该使用计算器;
- 调用计算器时需要生成表达式参数;
- 工具返回结果后,应根据结果回答用户;
- 工具调用不是最终回答,而是中间步骤。
所以这里面并没有很复杂的流程,大模型并没有去做用户的意图的识别,然后再基于意图去选择工具。大语言模型能够正确地选择工具,并且提供正确的参数。这完全是因为在训练过程中见过了大量的样本,以至于大语言模型能够自动完成工具的选择,以及从用户输入的内容中提取出调用工具所需的参数。大语言模型像是将此前需要做的分类以及实体识别等操作都内化了,直接在生成文本的过程中就完成了工具选择和参数提取。
完整的工具调用例子
在接下来的这个例子中,我提供了一些工具给大语言模型,让它知道有哪些工具可以调用。然后我输入了一个数学问题给大语言模型,让它计算两个数的平方根,然后将结果相加。
下面是我输入给大语言模型的内容:
# Tools
You may call one or more functions to assist with the user query.
You are provided with function signatures within <tools></tools> XML tags:
<tools>
{"type": "function", "function": {"name": "sqrt", "description": "计算平方根", "parameters": {"type": "object", "properties": {"value": {"type": "number", "description": "要计算平方根的数值"}}, "required": ["value"]}}}
{"type": "function", "function": {"name": "add", "description": "计算两个数的和", "parameters": {"type": "object", "properties": {"a": {"type": "number", "description": "第一个数"}, "b": {"type": "number", "description": "第二个数"}}, "required": ["a", "b"]}}}
</tools>
For each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:
<tool_call>
{"name": <function-name>, "arguments": <args-json-object>}
</tool_call>
31.415926 的平方根 加上 27.1828 的平方根等于多少?
上面的输入中,描述了目前可用的工具,以及限制了模型输出的格式。其中工具的格式如下:
{
"type": "function",
"function": {
"name": "sqrt",
"description": "计算平方根",
"parameters": {
"type": "object",
"properties": {
"value": {"type": "number", "description": "要计算平方根的数值"}
},
"required": ["value"]
}
}
}
这里使用了 JSON 格式严格地描述了工具的名称和参数,在训练大语言模型的时候也使用了类似的格式来描述工具,因此大语言模型可以很好地理解这些工具的含义和用法。
另外我还限制了模型输出的格式,要求模型输出一个 JSON 格式的工具调用请求,其中包含工具的名称和参数:
<tool_call>
{"name": <function-name>, "arguments": <args-json-object>}
</tool_call>
模型收到我的输入后,返回的输出如下:
<tool_call>
{"name": "sqrt", "arguments": {"value": 31.415926}}
</tool_call>
<tool_call>
{"name": "sqrt", "arguments": {"value": 27.1828}}
</tool_call>
模型返回的是一个 XML 格式的工具调用请求,其中包含工具的名称和参数,这很容易被程序解析和处理。
执行了大语言模型返回的工具调用请求后,需要将结果连同此前的上下文输入给大模型:
<tool_response>
5.604991168592507
</tool_response>
<tool_response>
5.213712688670138
</tool_response>
此时大语言模型认为需要做加法,因此它返回了另外一个工具调用请求:
<tool_call>
{"name": "add", "arguments": {"a": 5.604991168592507, "b": 5.213712688670138}}
</tool_call>
Agent 执行了工具调用后,将结果连同此前的上下文输入给大模型:
<tool_response>
10.818703857262644
</tool_response>
最终模型给出了最终的回答:
31.415926 的平方根 加上 27.1828 的平方根等于:
5.604991168592507 + 5.213712688670138 = 10.818703857262644
所以答案是:10.818703857262644。
实际的数学运算并没有依赖于大语言模型来完成,大语言模型仅仅是发起了工具调用,Agent 执行了实际的计算。所以整个数学运算的结果是完全准确的,并不存在大语言模型凭空创造的情况。
如果你直接让大语言模型回答这个问题,它会给出看似合理的答案,但实际上是错误的。因为大语言模型本质上是一个文本生成模型,它并不具备数学运算能力。它只能根据已有的上下文和训练数据来预测下一个字符,无法进行精确的计算。
31.415926 的平方根 加上 27.1828 的平方根等于多少?
首先,我们计算 的平方根:
然后,计算 的平方根:
最后,将两者的平方根相加:
所以,答案是:
看到这个答案和标准答案很接近,但是并不准确。目前大语言模型通常都在训练阶段使用了大量的数学运算的样本进行了训练,所以大语言模型实际上是能够完成一些数学运算的,但本质上还是基于概率的生成。因此如果你去验证,你会发现大语言模型常常输出和真实的答案会有一些差异。
工具调用格式
需要注意的是,不同的大模型的工具定义、工具调用请求、工具执行结果在格式上可能会有所不同,但本质上都只涉及到三类内容:工具定义、工具调用请求、工具执行结果。
Qwen3 模型使用 XML 标签 <tools> 来包裹所有可用的工具,每一行都是一个工具的 JSON 描述:
<tools>
{"type": "function", "function": {"name": "get_weather", "description": "查询某个城市在指定日期的天气", "parameters": {"type": "object", "properties": {"city": {"type": "string", "description": "需要查询的城市"}, "date": {"type": "string", "description": "需要查询的日期"}}, "required": ["city", "date"]}}}
{"type": "function", "function": {"name": "web_search", "description": "在互联网上搜索信息", "parameters": {"type": "object", "properties": {"query": {"type": "string", "description": "搜索关键词"}}, "required": ["query"]}}}
</tools>
模型在需要调用工具时,会输出一个 <tool_call> 标签,里面包含工具名称和参数:
<tool_call>
{"name":"get_weather","arguments":{"city":"北京","date":"明天"}}
</tool_call>
工具调用完成后,使用如下格式将结果返回给模型:
<tool_response>
{"city": "北京", "date": "2026-07-30", "condition": "阵雨", "temperature": {"min": 25, "max": 32}, "precipitation_probability": 70}
</tool_response>
目前在连接的各个厂商的 LLM 时基本都会使用 OpenAI API 或者 Anthropic API,这些 API 提供了统一的格式。推理引擎会把工具调用请求和执行结果转换成不同模型所需的格式。
实现工具调用
这一节我将使用 OpenAI 的 API 来演示如何实现工具调用。
定义工具
首先我们使用 Python 定义一些工具:
def get_current_weather(location: str, unit: str) -> str:
"""
Get the current temperature at a location.
Args:
location: The location to get the temperature for, in the format "City, Country"
unit: The unit to return the temperature in. (choices: ["celsius", "fahrenheit"])
"""
return {
"temperature": 20,
"unit": unit,
}
def calculate(expression: str) -> float:
"""
Calculate the result of a mathematical expression.
Args:
expression: The mathematical expression to calculate. The expression must be a valid Python expression, e.g. "2 + 2" or "math.sqrt(16)".
"""
return eval(expression, {}, {"math": __import__("math")})
def get_distance_between_cities(city1: str, city2: str) -> float:
"""
Get the distance between two cities.
Args:
city1: The first city.
city2: The second city.
"""
return 100.0
这里我定义了三个工具,分别是获取当前天气、计算数学表达式、计算两个城市之间的距离。
生成工具描述
现在需要把这些工具的描述发送给大语言模型,让它知道有哪些工具可以调用。我们可以使用 OpenAI 提供的 openai.Function 来生成工具描述:
TOOLS = [
{
"type": "function",
"function": {
"name": "get_current_weather",
"description": "Get the current temperature at a location.",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": 'The location to get the temperature for, in the format "City, Country".',
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "The unit to return the temperature in.",
},
},
"required": ["location", "unit"],
},
},
},
{
"type": "function",
"function": {
"name": "calculate",
"description": "Calculate the result of a mathematical expression.",
"parameters": {
"type": "object",
"properties": {
"expression": {
"type": "string",
"description": "The mathematical expression to calculate.",
},
},
"required": ["expression"],
},
},
},
{
"type": "function",
"function": {
"name": "get_distance_between_cities",
"description": "Get the distance between two cities.",
"parameters": {
"type": "object",
"properties": {
"city1": {"type": "string", "description": "The first city."},
"city2": {"type": "string", "description": "The second city."},
},
"required": ["city1", "city2"],
},
},
},
]
调用大语言模型
可以使用 OpenAI 来调用大语言模型,在此之前需要从某大语言模型厂商获取 API Key,这里我使用 DeepSeek 来做演示:
import openai
import os
BASE_URL = os.environ.get("OPENAI_BASE_URL", "https://api.deepseek.com")
MODEL = os.environ.get("OPENAI_MODEL", "deepseek-v4-flash")
API_KEY = os.environ.get("OPENAI_API_KEY", "sk-***********************") # 替换为你自己的 Key
client = openai.OpenAI(base_url=BASE_URL, api_key=API_KEY)
SYSTEM_PROMPT = (
"You are a helpful assistant. Use the provided tools when they help "
"answer the user's question, and always answer based on the tool results."
)
messages = [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": "What is the current temperature in New York City in Celsius?"},
]
response = client.chat.completions.create(
model=MODEL,
messages=messages,
tools=TOOLS,
)
print(response.choices[0].message)
返回的结果经过格式化后大致如下:
ChatCompletionMessage(
content='',
refusal=None,
role='assistant',
annotations=None,
audio=None,
function_call=None,
tool_calls=[
ChatCompletionMessageFunctionToolCall(
id='call_00_fVABEy6x4UDrx4jAM0bF7343',
function=Function(
arguments='{"location": "New York City, USA", "unit": "celsius"}',
name='get_current_weather'
),
type='function',
index=0
)
],
reasoning_content="The user wants the current temperature in New York City in Celsius. I'll use the get_current_weather tool."
)
无论你使用哪一家的模型,使用 OpenAI API 返回的结果都是统一的格式。从上面的结果可以看到,大语言模型选择了 get_current_weather 工具,并且提供了调用参数。
执行工具调用
接收到大语言模型返回的工具调用请求后,可以使用 Python 执行相应的工具调用:
TOOL_MAP = {
"get_current_weather": get_current_weather,
"calculate": calculate,
"get_distance_between_cities": get_distance_between_cities,
}
def append_tool_calls_to_messages(messages, message):
"""
Append the assistant message carrying the tool_calls array to the messages list.
"""
messages.append(
{
"role": "assistant",
"content": message.content,
"tool_calls": [
{
"id": call.id,
"type": "function",
"function": {
"name": call.function.name,
"arguments": call.function.arguments,
},
}
for call in message.tool_calls
],
}
)
if response.choices[0].message.tool_calls:
append_tool_calls_to_messages(messages, response.choices[0].message)
for tool_call in response.choices[0].message.tool_calls:
tool_name = tool_call.function.name
tool_args = json.loads(tool_call.function.arguments)
if tool_name in TOOL_MAP:
func = TOOL_MAP[tool_name]
result = func(**tool_args)
else:
result = {"error": f"Tool {tool_name} not found."}
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps(result)
})
先判断大语言模型是否返回了工具调用请求,如果有,首先需要把工具调用请求添加到历史消息列表中,然后根据工具名称找到对应的函数,并使用提供的参数执行工具调用。执行结果会被添加到消息列表中,作为下一轮输入发送给大语言模型。
完整的工具调用循环
实际实现中,会把用户和模型的交互放在一个循环里,用户输入问题后,如果模型没有返回工具调用请求,就直接返回模型的回答;如果模型返回了工具调用请求,就执行工具调用,并将结果返回给模型,直到模型生成最终回答。
def loop(client: OpenAI) -> None:
"""Run a chat agent that can call tools, until the model gives a final answer."""
messages = [
{"role": "system", "content": SYSTEM_PROMPT},
]
while True:
user_input = input(">>> ")
if not user_input.strip():
continue
if user_input.lower() in ["exit", "quit"]:
break
messages.append({"role": "user", "content": user_input})
resp = client.chat.completions.create(
model=MODEL,
messages=messages,
tools=TOOLS,
)
while resp.choices[0].message.tool_calls:
msg = resp.choices[0].message
if msg.reasoning_content:
print(f"\nAssistant (reasoning): {msg.reasoning_content}\n")
# Add the assistant message carrying the tool_calls array to the messages list
append_tool_calls_to_messages(messages, msg)
for call in msg.tool_calls:
fn_name = call.function.name
try:
fn_args = json.loads(call.function.arguments or "{}")
except json.JSONDecodeError:
fn_args = {}
print("\ntool_call:", fn_name, fn_args)
try:
result = TOOL_MAP[fn_name](**fn_args)
except Exception as exc:
result = {"error": f"{type(exc).__name__}: {exc}"}
print("tool_result:", result)
messages.append(
{
"role": "tool",
"tool_call_id": call.id,
"content": json.dumps(result, ensure_ascii=False),
}
)
resp = client.chat.completions.create(
model=MODEL,
messages=messages,
tools=TOOLS,
)
# No tool calls -> this is the final answer
msg = resp.choices[0].message
messages.append({"role": "assistant", "content": msg.content}) # add the final answer to the messages as chat history
print(f"\nAssistant: {msg.content}\n")
至此就完成了一个完整的工具调用循环。用户输入问题后,模型会根据问题选择合适的工具,并生成调用请求。Agent 执行工具调用后,将结果返回给模型,模型再根据结果生成最终回答。直到模型不再返回工具调用请求后,打印最终回答。
完整的代码可以在这里找到。下面是实际运行的结果:
>>> What are the square roots of 3.1415926 and 2.71828?
Assistant (reasoning): The user wants square roots of 3.1415926 and 2.71828. I can use the calculate tool. These are independent, so I can call both at once.
tool_call: calculate {'expression': '3.1415926 ** 0.5'}
tool_result: 1.7724538357881143
tool_call: calculate {'expression': '2.71828 ** 0.5'}
tool_result: 1.6487207161917994
Assistant: Here are the results:
- **√3.1415926** ≈ **1.7724538**
- **√2.71828** ≈ **1.6487207**
Fun fact: 3.1415926 approximates π, and its square root is close to √π ≈ 1.77245. Similarly, 2.71828 approximates e, and its square root is close to √e ≈ 1.64872.
>>> What is the current temperature in New York City in Celsius?
tool_call: get_current_weather {'location': 'New York City, USA', 'unit': 'celsius'}
tool_result: {'temperature': 20, 'unit': 'celsius'}
Assistant: The current temperature in New York City is **20°C**.
>>> What is the distance between New York City and Los Angeles?
Assistant (reasoning): The user wants the distance between New York City and Los Angeles. I'll use the get_distance_between_cities tool.
tool_call: get_distance_between_cities {'city1': 'New York City', 'city2': 'Los Angeles'}
tool_result: 100.0
Assistant: The distance between New York City and Los Angeles is **100.0** (per the distance tool, with the unit unspecified).
>>>
总结
大语言模型工具调用,原理并不复杂。我认为这里重要的一点是约定好协议,大语言模型在训练过程中,就需要明确它如何接收工具的声明、工具调用请求的格式、以及工具调用响应的格式。大语言模型在训练阶段见过了大量的工具调用的样本,学会了如何在面对新的用户请求时,选择合适的工具,并生成符合指定结构的调用参数。
Agent 接收到大语言模型返回的工具调用请求后,执行相应的操作,并将结果返回给模型,模型再根据结果生成最终回答。因此,工具调用可以视为大语言模型和外部世界之间的桥梁,可以让大语言模型真正触及现实世界,可通过工具调用来查询信息、编辑文本、进行数学运算等操作。