工具#
概念#
拥有适当的工具抽象是构建数据代理的核心。定义一组工具类似于定义任何 API 接口,唯一的区别在于这些工具是为代理而不是人类使用的。我们允许用户定义工具和工具规范,其中包含一系列在幕后运行的函数。
在使用代理或 LLM 进行函数调用时,所选的工具(以及为该工具编写的参数)强烈依赖于工具名称和工具用途及参数的描述。花时间调整这些参数可能会导致 LLM 调用这些工具的方式发生很大变化。
工具实现了一个非常通用的接口 - 只需定义 __call__
并返回一些基本元数据(名称、描述、函数模式)。
我们提供了几种不同类型的工具:
FunctionTool
:函数工具允许用户轻松地将任何用户定义的函数转换为工具。它还可以自动推断函数模式。QueryEngineTool
:包装现有查询引擎的工具。注意:由于我们的代理抽象继承自BaseQueryEngine
,这些工具也可以包装其他代理。- 社区贡献的
ToolSpecs
,定义了围绕单个服务(如 Gmail)的一个或多个工具。 - 用于包装其他工具以处理从工具返回大量数据的实用工具
FunctionTool#
函数工具是对任何现有函数(支持同步和异步!)的简单包装。
from llama_index.core.tools import FunctionTool
def get_weather(location: str) -> str:
"""用于获取给定位置的天气。"""
...
tool = FunctionTool.from_defaults(
get_weather,
# async_fn=aget_weather, # 可选!
)
agent = ReActAgent.from_tools(tools, llm=llm, verbose=True)
为了更好地定义函数,您还可以利用 pydantic 来定义函数参数。
from pydantic import Field
def get_weather(
location: str = Field(
description="一个城市名称和州,格式为'<名称>,<州>'"
),
) -> str:
"""用于获取给定位置的天气。"""
...
tool = FunctionTool.from_defaults(get_weather)
默认情况下,工具名称将是函数名称,文档字符串将是工具描述。但您也可以覆盖这一点。
tool = FunctionTool.from_defaults(get_weather, name="...", description="...")
QueryEngineTool#
任何查询引擎都可以使用 QueryEngineTool
转换为工具:
from llama_index.core.tools import QueryEngineTool
tool = QueryEngineTool.from_defaults(
query_engine, name="...", description="..."
)
工具规范#
我们还通过LlamaHub提供了丰富的工具和工具规范。
您可以将工具规范视为一起使用的工具包。通常,这些工具覆盖了单个接口/服务(如 Gmail)上的有用工具。
要与代理一起使用,您可以安装特定的工具规范集成:
pip install llama-index-tools-google
然后使用它:
from llama_index.agent.openai import OpenAIAgent
from llama_index.tools.google import GmailToolSpec
tool_spec = GmailToolSpec()
agent = OpenAIAgent.from_tools(tool_spec.to_tool_list(), verbose=True)
请参阅LlamaHub获取社区贡献的工具规范的完整列表,或查看我们的指南以获取有关 LlamaHub 中的工具/工具规范的完整概述!
实用工具#
通常,直接查询 API 可能会返回大量数据,这些数据本身可能会超出 LLM 的上下文窗口(或者至少会不必要地增加您使用的标记数量)。
为了解决这个问题,我们在 LlamaHub 工具中提供了一组初始的“实用工具” - 实用工具在概念上不与特定服务(例如 Gmail、Notion)绑定,而是可以增强现有工具的功能。在这种情况下,实用工具有助于抽象化来自任何 API 请求返回的数据的常见模式,需要缓存/索引和查询数据。
让我们在下面详细介绍我们的两个主要实用工具。
OnDemandLoaderTool#
这个工具将任何现有的 LlamaIndex 数据加载器(BaseReader
类)转换为代理可以使用的工具。可以使用调用工具时需要的所有参数来调用工具,以及一个自然语言查询字符串。在执行过程中,我们首先从数据加载器加载数据,对其进行索引(例如使用向量存储),然后“按需”查询它。所有这三个步骤都在单个工具调用中发生。
通常,这可能比自己找出如何加载和索引 API 数据更可取。虽然这可能允许数据的可重用性,但通常用户只需要一个临时索引来抽象化任何 API 调用的提示窗口限制。
下面给出了一个使用示例:
from llama_index.readers.wikipedia import WikipediaReader
from llama_index.core.tools.ondemand_loader_tool import OnDemandLoaderTool
tool = OnDemandLoaderTool.from_defaults(
reader,
name="Wikipedia Tool",
description="A tool for loading data and querying articles from Wikipedia",
)
LoadAndSearchToolSpec#
LoadAndSearchToolSpec 接受任何现有的工具作为输入。作为工具规范,它实现了 to_tool_list
方法,当调用该方法时,会返回两个工具:一个 load
工具,然后是一个 search
工具。
load
工具的执行会调用底层工具,并对输出进行索引(默认情况下使用向量索引)。search
工具的执行会接受查询字符串作为输入,并调用底层索引。
这对于默认情况下会返回大量数据的任何 API 端点非常有用 - 例如,我们的 WikipediaToolSpec 默认情况下会返回整个维基百科页面,这将很容易超出大多数 LLM 上下文窗口的容量。
下面显示了一个示例用法:
from llama_index.tools.wikipedia import WikipediaToolSpec
from llama_index.core.tools.tool_spec.load_and_search import (
LoadAndSearchToolSpec,
)
wiki_spec = WikipediaToolSpec()
# 获取搜索维基百科的工具
tool = wiki_spec.to_tool_list()[1]
# 使用 load/search 工具创建 Agent
agent = OpenAIAgent.from_tools(
LoadAndSearchToolSpec.from_defaults(tool).to_tool_list(), verbose=True
)
直接返回#
您会注意到工具类构造函数中的选项 return_direct
。如果将其设置为 True
,则代理的响应将直接返回,而不经过代理解释和重写。这有助于减少运行时间,或设计/指定将结束代理推理循环的工具。
例如,假设您指定了一个工具:
tool = QueryEngineTool.from_defaults(
query_engine,
name="<name>",
description="<description>",
return_direct=True,
)
agent = OpenAIAgent.from_tools([tool])
response = agent.chat("<question that invokes tool>")
在上面的示例中,将调用查询引擎工具,并且该工具的响应将直接作为响应返回,执行循环将结束。
如果使用了 return_direct=False
,那么代理将使用聊天历史记录的上下文重写响应,甚至进行另一个工具调用。
我们还提供了一个示例笔记本来演示如何使用 return_direct
。
调试工具#
通常,调试被发送到 API 的工具定义是非常有用的。
您可以通过使用获取当前工具模式的基础函数来很好地查看这一点,这在 OpenAI 和 Anthropic 等 API 中被利用。
schema = tool.metadata.get_parameters_dict()
print(schema)