0%

Agent构建心得及避坑(三)

继续Agent的话题,开一个(三)来讨论讨论工具调用吧

工具调用能干什么

首先通用的定义:工具调用(Tool Calling)是指大模型通过调用外部工具或API来获取信息、执行任务或进行计算的能力。工具调用可以极大地扩展模型的功能,使其能够处理更复杂的任务。

自然很多开发者都认为工具调用就是让模型获取外部数据,比如天气、股票等信息。但其实工具调用的作用远不止于此,工具调用的本质是给模型注入工具的定义(名称、描述、参数等),让模型生成一个结构化的数据。

我觉得上面这个定义才是工具调用的核心。因为结构化数据可以表示很多东西,不仅仅是获取外部数据。下面我来介绍几个特别工具调用的作用。

工具调用能解决幻觉

幻觉是怎么产生的?主要原因在于模型的训练数据有限,模型只能基于已有的数据进行推理和生成。当模型遇到未知的问题或需要精确的信息时,往往会凭空编造答案,从而产生幻觉。

那怎么解决幻觉呢?最有效的方法就是让模型调用外部的工具或API,获取准确的信息。

比如对于企业内部的知识库查询,模型本身并不具备企业的内部知识,但通过调用知识库查询工具,模型可以获取到准确的答案,从而避免幻觉的产生。再比如时间这个概念,模型本身并不具备实时的时间信息,在训练数据中模型的时间是早于当前时间的,对于时间敏感的任务,模型很容易产生幻觉,给出错误的时间信息,导致Agent执行错误的操作。但通过调用时间查询工具,模型可以获取到当前的时间,从而避免幻觉。

工具调用能控制工作流

典型的TODO工具就是一个工作流控制工具。模型通过调用TODO工具,可以将任务拆解为多个子任务,并按照一定的顺序执行这些子任务,从而实现复杂的工作流。

再比如你希望Agent最终输出一个可以解析的报告,而不用使用正则从模型输出的文本中去提取相应内容。这时候你就可以定义一个生成报告的工具,工具的参数就是报告的结构化内容。模型通过调用这个工具,生成结构化的报告内容。因为工具的参数本来就是结构化的。在这之前甚至现在还是有许多开发者是在Prompt里定义xml或json的格式,让模型输出;其实Agent模型都有工具调用能力,原生的就支持结构化输出,直接定义工具调用就行了。

工具能让模型听话

如果大家写过提示词,做过Prompt Engineering的时候就会发现,模型有时候会不听话,明明提示词里说了不要做某事,模型还是会去做。这个问题其实是很难解决的,尤其是当提示词比较复杂的时候,模型更容易忽略一些细节要求。

上面的问题是一个非常常见的问题,在社交媒体(X等)上经常能看到类似的Prompt Engineering的讨论。比如:提示词应该写让模型干什么,而不是写让模型不干什么。但是有时候在设计Agent的时候,确实需要让模型不去做某事。
我在设计Agent的时候就经常遇到这种问题,比如我写提示词

1
2
3
4
5
<note>
1. DO NOT xxx
2. NEVER use xxx
3. 你不能做 xxx
</note>

结果发现无论怎么调整提示词,模型还是会去做xxx。这个问题其实是很难通过提示词来解决的。

再举个例子,有一些企业内部的名词,在使用websearch工具的时候,往往是搜索不到的甚至是错误的。但同时你无论怎么在提示词里强调不要使用websearch工具去检索企业内部相关名词,模型还是会去使用,导致错误的结果。这时你就可以通过定义工具调用的方式,来让模型听话。比如定义一个叫internal_search的工具,专门用来检索企业内部名词。然后在提示词里告诉模型,所有企业内部名词都必须使用internal_search工具去检索,不能使用websearch工具。这样模型就会遵守这个规则,避免使用错误的工具。

上面只是一个例子,实际上你可以构造一个输出内容为没有内容的工具(理解为假的工具),然后告诉模型在某些情况下必须调用这个工具。这样就把DO NOT改成了 MUST CALL,从而让模型听话。

工具调用可以让非思考模型变成思考模型

思考模型(Reasoning Model)是指具备多步推理和复杂问题解决能力的模型。相比之下,非思考模型(Non-Reasoning Model)则缺乏这种能力,通常只能处理简单的任务。

确实,思考模型的能力通常更强大,但是延迟更高。我们理想的模型是能够根据任务的复杂度,动态调整思考能力。对于简单任务,比如hello,应该就不需要思考,直接给出答案即可;而对于复杂任务,比如多步推理、复杂计算等,则需要启用思考能力,进行多步推理和计算。这种我们成为混合思考模型

而现在没有比较好的混合思考模型(除了glm4.5/4.6),比如o3/GPT-5是无法关闭思考能力的,而且openai给出的方案是使用一个路由模型来决定后端使用什么模型来回答用户问题,这样看起来是一个混合思考模型的方案。再比如Claude Deepseek Qwen只能通过静态设置来决定是否启用思考能力和思考token数,也无法实现正真的混合思考。

实际上我个人认为Openai开创的思考模型o1的思路是错误的;思考其实是模型的推理能力,这个应该是模型本身内生的能力,对于非思考模型来说,虽然是直接输出非思考token,但是这也是模型推理的一部分,thinking过程只是模型推理的一种方式而已。也许就不应该把思考和非思考模型区分开来。

比如说Agent任务,模型在接收到工具的结果后输出:好的,我看到已经返回了结果,这不是一个xxx, 接下来我需要去xxx;那么这个过程其实也是模型在思考,只不过这种思考不是通过thinking token来实现的,而是传统的非思考模型的输出的答案token。

那怎么让非思考模型具备像o1这种长的思维链呢?其实很简单,就是通过一个Thought工具来实现,这也是Claude曾经提出过的一个工具,Thought工具。这个工具没有实际的功能,只是让模型调用这个工具,生成一个长的思维链,从而实现类似思考模型的能力。

比如这个工具的定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"name": "Thought",
"description": "Use this tool to think through complex problems step by step. Generate detailed reasoning and analysis to arrive at a solution.",
"parameters": {
"type": "object",
"properties": {
"thought_process": {
"type": "string",
"description": "A detailed explanation of the reasoning process."
}
},
"required": ["thought_process"]
}
}

我在使用Claude Sonnet 4.5模型的时候,没有开启思考,而是通过定义Thought工具,让模型在需要思考的时候调用这个工具,并观察到这个思维链中出现等等,wait这种deepseek称之为aha moment的词汇,说明模型确实在进行思考。

Thought工具还有一个优势是真正的实现了混合思考,因为模型只有在需要思考的时候才会调用这个工具,而不是每次都进行思考,这是工具调用的优势。对于hello这种简单任务,模型根本不会调用Thought工具,会直接给出答案。

而且我们可以利用并行工具调用的能力,甚至让模型开启并行思考能力,这对于模型自带的思考token是无法实现的。