跳到内容

工具#

概念#

拥有合适的工具抽象是构建 LlamaIndex 智能体系统的核心。定义一组工具类似于定义任何 API 接口,不同之处在于这些工具是为智能体而非人类使用而设计的。我们允许用户定义 工具 (Tool) 和包含一系列底层功能的 工具规范 (ToolSpec)

当使用支持函数调用的智能体或大型语言模型时,所选择的工具(以及为该工具编写的参数)强烈依赖于工具目的和参数的工具名称 (tool name)描述 (description)。花时间调整这些参数可以显著改变大型语言模型调用这些工具的方式。

工具实现了一个非常通用的接口——只需定义 __call__ 方法并返回一些基本元数据(名称、描述、函数 schema)。

我们提供了几种不同类型的工具:

  • FunctionTool: 函数工具允许用户轻松地将任何用户定义的函数转换为工具。它还可以自动推断函数 schema,或者允许您自定义各个方面。
  • QueryEngineTool: 一种封装现有查询引擎的工具。注意:由于我们的智能体抽象继承自 BaseQueryEngine,这些工具也可以封装其他智能体。
  • 社区贡献的 ToolSpecs,它们围绕单一服务(如 Gmail)定义了一个或多个工具
  • 用于封装其他工具以处理从工具返回大量数据的实用工具

FunctionTool#

函数工具是对任何现有函数(支持同步和异步!)的简单封装。

from llama_index.core.agent.workflow import ReActAgent
from llama_index.core.tools import FunctionTool


def get_weather(location: str) -> str:
    """Usfeful for getting the weather for a given location."""
    ...


tool = FunctionTool.from_defaults(
    get_weather,
    # async_fn=aget_weather,  # optional!
)

agent = ReActAgent(llm=llm, tools=tools)

为了更好的函数定义,您还可以利用 Annotated 类型来指定参数描述。

from typing import Annotated


def get_weather(
    location: Annotated[
        str, "A city name and state, formatted like '<name>, <state>'"
    ],
) -> str:
    """Useful for getting the weather for a given location."""
    ...


tool = FunctionTool.from_defaults(get_weather)

默认情况下,工具名称将是函数名称,docstring 将是工具描述。但您也可以覆盖它。

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.core.agent.workflow import FunctionAgent
from llama_index.tools.google import GmailToolSpec

tool_spec = GmailToolSpec()
agent = FunctionAgent(llm=llm, tools=tool_spec.to_tool_list())

请参阅 LlamaHub,查看社区贡献的完整工具规范列表。

实用工具#

通常,直接查询 API 可能会返回大量数据,这些数据本身可能会超出大型语言模型的上下文窗口(或者至少会不必要地增加您使用的 token 数量)。

为了解决这个问题,我们在 LlamaHub Tools 中提供了一套初步的“实用工具”——实用工具在概念上不绑定到特定服务(例如 Gmail、Notion),而是可以增强现有工具的功能。在这种特定情况下,实用工具有助于抽象出需要缓存/索引和查询从任何 API 请求返回的数据的常见模式。

下面让我们详细介绍我们的两个主要实用工具。

OnDemandLoaderTool#

此工具可以将任何现有的 LlamaIndex 数据加载器(BaseReader 类)转换为智能体可以使用的工具。调用此工具时可以提供触发数据加载器 load_data 所需的所有参数,以及一个自然语言查询字符串。执行期间,我们首先从数据加载器加载数据,对其进行索引(例如使用向量存储),然后“按需”查询它。这三个步骤都在一次工具调用中完成。

通常,这比您自己弄清楚如何加载和索引 API 数据更可取。虽然这可能允许数据重用,但用户通常只需要一个即时索引来抽象掉任何 API 调用的提示窗口限制。

下面给出了一个使用示例

pip install llama-index-readers-wikipedia
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 默认会返回整个维基百科页面,这很容易超出大多数大型语言模型的上下文窗口。

下面显示了使用示例

pip install llama-index-tools-wikipedia
from llama_index.core.agent.workflow import FunctionAgent
from llama_index.core.tools.tool_spec.load_and_search import (
    LoadAndSearchToolSpec,
)
from llama_index.tools.wikipedia import WikipediaToolSpec


wiki_spec = WikipediaToolSpec()
# Get the search wikipedia tool
tool = wiki_spec.to_tool_list()[1]

# Create the Agent with load/search tools
agent = FunctionAgent(
    llm=llm, tools=LoadAndSearchToolSpec.from_defaults(tool).to_tool_list()
)

直接返回#

您会注意到工具类构造函数中的 return_direct 选项。如果将其设置为 True,智能体的响应将直接返回,而不会被智能体解释和重写。这有助于减少运行时长,或设计/指定会结束智能体推理循环的工具。

例如,假设您指定一个工具

tool = QueryEngineTool.from_defaults(
    query_engine,
    name="<name>",
    description="<description>",
    return_direct=True,
)

agent = FunctionAgent(llm=llm, tools=[tool])

response = await agent.run("<question that invokes tool>")

在上面的示例中,查询引擎工具将被调用,并且该工具的响应将直接作为最终响应返回,执行循环将结束。

如果使用了 return_direct=False,则智能体将利用聊天历史的上下文重写响应,甚至可能进行另一次工具调用。

我们还提供了一个使用 return_direct示例 Notebook

调试工具#

通常,调试发送到 API 的工具定义具体是什么内容会很有用。

您可以通过使用底层函数获取当前工具 schema 来很好地查看这一点,像 OpenAI 和 Anthropic 这样的 API 就利用了这一点。

schema = tool.metadata.get_parameters_dict()
print(schema)