为了真正让我们的LLM代理多功能,我们需要给它们提供互动的方式,而不仅仅是处理文本。想象一下,请求代理查询当前天气、数学问题结果,或在线查找事实。LLM本身虽然在理解语言和生成回复方面表现出色,但它并非天生就能直接或准确地执行这些操作。这就是“工具”发挥作用的地方。在LLM代理的语境中,工具本质上是一种特定的能力或功能,代理可以借此执行自身无法完成的任务或获取信息。可以把它想象成给人类助手配备计算器、搜索引擎或日历。助手(LLM代理)很聪明,但这些工具赋予它超凡的能力。让我们通过设计和实现一个非常简单的工具——计算器,来具体说明这一点。LLM通常可以执行文本中包含的简单算术,但对于可靠且精确的计算,特别是作为更大任务的一部分时,一个专门的计算器工具要好得多。它们可能会“胡编乱造”或在数学上出错,因为它们的优势在于语言模式识别,而非正式计算。设计我们的计算器工具在我们编写任何代码之前,先思考一下我们的计算器工具需要完成什么以及代理如何使用它。一个好的工具定义应该是清晰明确的。目的: 该工具应执行基本的算术运算:加法、减法、乘法和除法。输入: 为了执行计算,工具需要:第一个数字(我们称之为 operand1)。第二个数字(operand2)。要执行的运算(例如,'add'、'subtract'、'multiply'、'divide')。我们将用字符串表示。输出: 工具应返回:计算的数值结果。或者,如果出现问题(如尝试除以零或提供未知运算),则返回错误消息。这种设计帮助我们为工具定义了一个明确的“契约”。代理将学习到(或被告知),要使用计算器,它需要以特定格式提供这些具体的输入,并且可以预期获得数值结果或错误消息作为回报。在Python中实现一个基本计算器现在,让我们创建一个简单的Python函数,作为我们的计算器工具。对于初学者课程,我们将保持其直接简单。def simple_calculator(operand1, operand2, operation): """ 执行基本算术运算。 支持的运算:'add'(加)、'subtract'(减)、'multiply'(乘)、'divide'(除)。 """ allowed_operations = ['add', 'subtract', 'multiply', 'divide'] if not isinstance(operand1, (int, float)) or not isinstance(operand2, (int, float)): return "错误:两个操作数都必须是数字。" if operation not in allowed_operations: return f"错误:未知运算 '{operation}'。支持的运算有:{', '.join(allowed_operations)}。" if operation == 'add': return operand1 + operand2 elif operation == 'subtract': return operand1 - operand2 elif operation == 'multiply': return operand1 * operand2 elif operation == 'divide': if operand2 == 0: return "错误:不能除以零。" return operand1 / operand2我们来解析一下这段Python代码:我们定义了一个函数 simple_calculator,它接受三个参数:operand1、operand2 和 operation。我们首先检查 operand1 和 operand2 是否确实是数字(整数或浮点数)。如果不是,我们返回一条有用的错误消息。这种输入验证对于使工具可靠很重要。然后,我们检查 operation 字符串是否是我们支持的运算之一('add'、'subtract'、'multiply'、'divide')。如果它是无法识别的运算,我们返回另一条错误消息。接着,我们使用 if/elif 语句,根据 operation 字符串执行正确的计算。对于除法,我们添加了一个特定检查,以防止“除以零”错误,这种错误会导致许多程序崩溃。相反,我们返回一条有用的错误消息。如果所有输入都有效且运算受支持,函数将返回计算结果。你可以在Python中直接测试这个函数:result1 = simple_calculator(10, 5, 'add') print(f"10 + 5 = {result1}") # 输出: 10 + 5 = 15 result2 = simple_calculator(10, 0, 'divide') print(f"10 / 0 = {result2}") # 输出: 10 / 0 = 错误:不能除以零。 result3 = simple_calculator(10, 'hello', 'multiply') print(f"10 * 'hello' = {result3}") # 输出: 10 * 'hello' = 错误:两个操作数都必须是数字。这个简单的函数现在就是一个我们的代理可以使用的“工具”。代理如何“看待”工具理解这一点很重要:LLM代理不会直接读取或执行 simple_calculator 的Python代码。相反,代理通常会获得一份工具的描述。这份描述会告知代理:工具名称: 一个唯一标识符,例如 CalculatorTool。工具描述: 对工具功能的人类可读解释,例如:“对两个数字执行基本的算术运算,如加法、减法、乘法和除法。”输入参数:operand1:计算的第一个数字。(类型:数字)operand2:计算的第二个数字。(类型:数字)operation:要执行的算术运算。(类型:字符串,例如'add'、'subtract'、'multiply'、'divide')输出:计算的数值结果。(类型:数字)或者,如果无法执行计算,则返回错误消息。(类型:字符串)这种结构化的描述是代理底层LLM用来理解如何使用工具以及预期从中获得什么的信息来源。当代理决定需要计算某物时,它会尝试格式化其请求以匹配这些输入参数。我们将在后续章节中更正式地介绍代理如何决定使用工具以及我们如何格式化这些描述。The following diagram illustrates the components involved in defining a tool for an agent:digraph G { rankdir=TB; graph [fontname="Arial", fontsize=10]; node [shape=box, style="rounded,filled", fontname="Arial", fontsize=10]; edge [fontname="Arial", fontsize=9]; subgraph cluster_tool_definition { label = "工具定义的组成部分"; bgcolor = "#f0f4f8"; style = "rounded"; color = "#adb5bd"; tool_meta [label="工具元数据\n\n名称: 简单计算器\n目的: 基本算术", fillcolor="#ffec99", shape=plaintext]; tool_inputs [label="输入规范\n\n- operand1 (数字)\n- operand2 (数字)\n- operation (文本: 'add', 'sub', 等)", fillcolor="#ffd8a8", shape=plaintext]; tool_outputs [label="输出规范\n\n- 结果 (数字) 或\n- 错误消息 (文本)", fillcolor="#ffc9c9", shape=plaintext]; tool_logic [label="工具实现\n(例如,Python函数)\n\ndef simple_calculator(...):\n # ... 代码 ...\n return result_or_error", fillcolor="#b2f2bb", shape=note, peripheries=1, color="#495057"]; tool_meta -> tool_inputs [style=invis]; tool_inputs -> tool_outputs [style=invis]; tool_outputs -> tool_logic [style=invis]; } agent_perception [label="代理的理解\n(来自元数据、输入、输出)", fillcolor="#a5d8ff", shape=ellipse]; agent_perception -> tool_meta [label=" 读取描述以了解:", dir=back, constraint=false, color="#495057"]; }该图显示,工具由其元数据(名称、目的)、输入要求、输出规范以及实际底层实现(如我们的Python函数)定义。代理主要通过描述部分来理解如何使用该工具。为什么计算器是一个好的起点计算器示例很有用,因为:需求明确: 它解决了一个LLM单独处理可能不可靠的常见任务。LLM擅长理解数学问题,但不总能准确地执行计算。接口简单: 输入(两个数字,一个运算)和输出(一个数字或一个错误)易于定义和理解。确定性输出: 对于给定输入,计算器工具提供可预测的正确答案(或可预测的错误)。这种可靠性是代理从工具中所需的重要特性。体现外部能力: 它清楚地说明了代理如何将特定类型的任务交给更专业的组件处理。通过创建这个 simple_calculator 函数,我们迈出了为代理配备外部能力的第一步。在接下来的章节中,我们将学习如何让代理感知此类工具,它如何决定针对特定问题使用哪个工具,以及它如何实际调用工具并使用其输出。这个计算器是构建更复杂、更有能力的代理的组成部分。