概念
拥有适当的工具抽象是构建数据代理的核心。定义一组 Tools 类似于定义任何 API 接口,不同之处在于这些工具供代理使用,而不是供人工使用。我们允许用户定义 Tool
和 ToolSpec
(工具规格说明),其中包含一系列后台功能。
将代理或 LLM
与函数调用一起使用时,所选工具(以及为该工具编写的参数)在很大程度上依赖于工具名称和工具用途和参数的描述。花时间调整这些参数可能会导致 LLM
调用这些工具的方式发生巨大变化。
一个工具实现了一个非常通用的接口——简单地定义__call__并返回一些基本的元数据(名称,描述,函数模式)。
我们提供几种不同类型的工具:
FunctionTool
:函数工具允许用户轻松地将任何用户定义的函数转换为工具。它还可以自动推断函数架构。QueryEngineTool
:包装现有查询引擎的工具。注意:由于我们的 agent 抽象继承自 ,这些工具也可以包装其他 agent。BaseQueryEngine
- 社区贡献的工具,围绕单个服务(如 Gmail)定义一个或多个工具
ToolSpecs
(工具规格说明) - 用于包装其他工具以处理从工具返回大量数据的实用工具
FunctionTool
函数工具是对任何现有函数的简单包装(同步和异步都支持!)
from llama_index.core.tools import FunctionTooldef 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.from_tools(tools, llm=llm, verbose=True)
为了更好地定义函数,您还可以将 pydantic
用于函数参数。
from pydantic import Fielddef get_weather(location: str = Field(description="A city name and state, formatted like '<name>, <state>'"),
) -> str:"""Usfeful for getting the weather for a given location."""...tool = FunctionTool.from_defaults(get_weather)
默认情况下,工具名称将是函数名称,文档字符串将是工具描述。但您也可以覆盖此权限。
tool = FunctionTool.from_defaults(get_weather, name="...", description="...")
QueryEngineTool
任何查询引擎都可以转换为工具,使用:QueryEngineTool
from llama_index.core.tools import QueryEngineTooltool = QueryEngineTool.from_defaults(query_engine, name="...", description="..."
)
Tool Specs
我们还通过 LlamaHub
🦙 提供丰富的工具和工具规格。
您可以将工具规格视为旨在一起使用的工具包。通常,这些涵盖单个界面/服务中的有用工具,例如 Gmail
。
要与代理一起使用,您可以安装特定的工具规范集成:
pip install llama-index-tools-google
然后使用它:
from llama_index.agent.openai import OpenAIAgent
from llama_index.tools.google import GmailToolSpectool_spec = GmailToolSpec()
agent = OpenAIAgent.from_tools(tool_spec.to_tool_list(), verbose=True)
有关社区贡献的工具规格的完整列表,请参阅 LlamaHub
。
Utility Tools
通常,直接查询 API 可能会返回大量数据,这些数据本身可能会使 LLM
的上下文窗口溢出(或者至少不必要地增加您正在使用的令牌数量)。
为了解决这个问题,我们在 LlamaHub
工具中提供了一组初始的“Utility Tools”——实用工具在概念上并不与给定的服务(例如 Gmail、Notion)相关联,而是可以增强现有工具的功能。在这种特殊情况下,实用工具有助于抽象出需要缓存/索引和查询从任何 API 请求返回的数据的常见模式。
让我们来看看下面的两个主要实用工具。
OnDemandLoaderTool
此工具将任何现有的 LlamaIndex
数据加载器(类)转换为代理可以使用的工具。可以使用从数据加载程序触发所需的所有参数以及自然语言查询字符串来调用该工具。在执行过程中,我们首先从数据加载器加载数据,为其编制索引(例如使用向量存储),然后“按需”查询它。所有这三个步骤都发生在单个工具调用中。BaseReaderload_data
通常,这比自己弄清楚如何加载和索引 API 数据更可取。虽然这可能允许数据可重用性,但通常用户只需要一个临时索引来抽象出任何 API 调用的提示窗口限制。
下面给出了一个使用示例:
from llama_index.readers.wikipedia import WikipediaReader
from llama_index.core.tools.ondemand_loader_tool import OnDemandLoaderTooltool = OnDemandLoaderTool.from_defaults(reader,name="Wikipedia Tool",description="A tool for loading data and querying articles from Wikipedia",
)
LoadAndSearchToolSpec
LoadAndSearchToolSpec
将任何现有工具作为输入。作为工具规范,它实现了 ,当调用该函数时,将返回两个工具:一个 tool,然后是一个 tool。to_tool_listloadsearch
Tool 执行将调用底层 Tool,索引将调用输出(默认情况下使用向量索引)。Tool 执行将采用查询字符串作为输入并调用底层索引。loadsearch
这对于任何默认返回大量数据的 API 端点都很有用 - 例如,我们的 WikipediaToolSpec 将默认返回整个 Wikipedia 页面,这很容易使大多数 LLM 上下文窗口溢出。
示例用法如下所示:
from llama_index.tools.wikipedia import WikipediaToolSpec
from llama_index.core.tools.tool_spec.load_and_search import (LoadAndSearchToolSpec,
)wiki_spec = WikipediaToolSpec()# Get the search wikipedia tool
tool = wiki_spec.to_tool_list()[1]# Create the Agent with load/search tools
agent = OpenAIAgent.from_tools(LoadAndSearchToolSpec.from_defaults(tool).to_tool_list(), verbose=True
)
Return Direct
您会注意到 tool 类构造函数中的选项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使用的另一个例子
工具设置
from typing import Optionalfrom llama_index.core.tools import FunctionTool
from llama_index.core.bridge.pydantic import BaseModel# we will store booking under random IDs
bookings = {}# we will represent and track the state of a booking as a Pydantic model
class Booking(BaseModel):name: Optional[str] = Noneemail: Optional[str] = Nonephone: Optional[str] = Nonedate: Optional[str] = Nonetime: Optional[str] = Nonedef get_booking_state(user_id: str) -> str:"""Get the current state of a booking for a given booking ID."""try:return str(bookings[user_id].dict())except:return f"Booking ID {user_id} not found"def update_booking(user_id: str, property: str, value: str) -> str:"""Update a property of a booking for a given booking ID. Only enter details that are explicitly provided."""booking = bookings[user_id]setattr(booking, property, value)return f"Booking ID {user_id} updated with {property} = {value}"def create_booking(user_id: str) -> str:"""Create a new booking and return the booking ID."""bookings[user_id] = Booking()return "Booking created, but not yet confirmed. Please provide your name, email, phone, date, and time."def confirm_booking(user_id: str) -> str:"""Confirm a booking for a given booking ID."""booking = bookings[user_id]if booking.name is None:raise ValueError("Please provide your name.")if booking.email is None:raise ValueError("Please provide your email.")if booking.phone is None:raise ValueError("Please provide your phone number.")if booking.date is None:raise ValueError("Please provide the date of your booking.")if booking.time is None:raise ValueError("Please provide the time of your booking.")return f"Booking ID {user_id} confirmed!"# create tools for each function
get_booking_state_tool = FunctionTool.from_defaults(fn=get_booking_state)
update_booking_tool = FunctionTool.from_defaults(fn=update_booking)
create_booking_tool = FunctionTool.from_defaults(fn=create_booking, return_direct=True
)
confirm_booking_tool = FunctionTool.from_defaults(fn=confirm_booking, return_direct=True
)
开始使用工具
from llama_index.llms.anthropic import Anthropic
from llama_index.core.llms import ChatMessage
from llama_index.core.agent import FunctionCallingAgentllm = Anthropic(model="claude-3-sonnet-20240229", temperature=0.1)user = "user123"
prefix_messages = [ChatMessage(role="system",content=(f"You are now connected to the booking system and helping {user} with making a booking. ""Only enter details that the user has explicitly provided. ""Do not make up any details."),)
]agent = FunctionCallingAgent.from_tools(tools=[get_booking_state_tool,update_booking_tool,create_booking_tool,confirm_booking_tool,],llm=llm,prefix_messages=prefix_messages,max_function_calls=10,allow_parallel_tool_calls=False,verbose=True,
)
调试Tools工具
通常,调试发送到 API 的工具定义到底是什么可能很有用。
您可以通过使用底层函数来获取当前工具架构,该架构在 OpenAI
和 Anthropic
等 API 中得到利用。
schema = tool.metadata.get_parameters_dict()
print(schema)