TL;DR:我们正在引入一种新的抽象,以允许使用更复杂的工具。虽然以前的工具只接受单个字符串输入,但新工具可以接受任意数量的任意类型的输入。我们还引入了一个新的代理类,它可以很好地与这些新类型的工具配合使用。
重要链接:
- 工具列表
- 新代理人
早在2022年11月,当我们首次推出LangChain时,代理和工具利用率在我们的设计中发挥了核心作用。我们建立了基于ReAct的首批链之一,这是一篇开创性的论文,将工具的使用带到了提示框架的前沿。
在早期,工具的使用过于简单。一个模型将生成两个字符串:
- 工具名称
- 所选工具的输入字符串
这种方法将代理限制为每转一个工具,并且该工具的输入限制为单个字符串。这些限制主要是由于模型的限制;模型甚至很难熟练地完成这些基本任务。可靠地执行更复杂的操作,例如选择多个工具或填充复杂的模式,将是一件愚蠢的事。
然而,更先进的语言模型(如text-davinci-003、gpt-3.5-turbo和gpt-4)的快速发展为现有模型能够可靠实现的目标奠定了基础。这促使我们重新评估LangChain代理框架中对工具使用的限制。
今年早些时候,我们引入了一个“多动作”代理框架,在该框架中,代理可以计划对代理执行器的每一步执行多个动作。在这一成功的基础上,我们现在正在摆脱单一字符串输入的限制,并自豪地提供结构化工具支持!
结构化工具使语言模型和工具之间能够进行更复杂、多方面的交互,从而更容易构建创新、适应性强、功能强大的应用程序。
什么是“结构化工具”?
结构化工具表示代理可以采取的操作。它包装了您提供的任何功能,使代理可以轻松地与它接口。结构化工具对象由其定义:
- name:一个标签,告诉代理要选择哪个工具。例如,一个名为“GetCurrentWeather”的工具告诉代理它是用来查找当前天气的。
- 描述:一份简短的说明手册,解释了代理何时以及为什么应该使用该工具。
- args_schema:为代理通信工具的接口。它通常从封装的函数的签名中提取,并允许对工具输入进行额外的验证逻辑。
- _run和_arun函数:这些函数定义了工具的内部工作。它可以是简单的,比如返回当前时间,也可以是更复杂的,比如发送消息或控制机器人。
工具名称是其唯一标识符。一个好的名字明确地传达了它的功能,所以一个名为“GetCurrentWeather”的工具比“GCTW”更有用。如果您不清楚工具的名称,那么代理可能也不清楚。如果您允许代理访问多个工具,则该名称还可以提供有关它们之间关系的信息。例如,如果您有“AmazonSearch”、“AmazonCurrentBalance”和“NikeShoppingCart”工具,即使不阅读描述,代理也可以推断出前两者是相关的。
该说明提供了有关如何使用该工具的更详细说明。一个好的描述是简洁的,但能有效地传达工具的功能。如果需要的话,这也可以提供提供简短示例(或反例)的空间。
args_schema是一个Pydantic BaseModel,它定义了要提供给工具的参数(以及它们的类型信息)。它有两个主要的工作:第一,传达代理需要哪些信息。第二项工作是在执行工具的内部功能之前验证这些输入。
最后,_run和伴随的async_arun方法定义了工具的逻辑。您可以在这里放置任何内容,从算术到API请求,再到对其他LLM链的调用。
新的结构化工具
除了这个新的基类之外,我们还发布了以下新工具,这两个工具都继承了这个结构化的工具类。
- 文件管理—一个工具包,用于您可能需要的所有文件系统操作,包括写入、grep、移动、复制、list_dir、查找
- Web浏览器-虽然我们以前有用于文档加载程序的浏览器,但现在我们正在发布一个官方的有状态的PlayWright浏览器工具包,让我们的代理访问网站、点击、提交表单和查询数据
有关所有工具(旧的和新的)的列表,请参阅此处的文档。
实施您自己的结构化工具
最快的入门方法是调用StructuredTool.from_function(your_callable)构造函数。
举个例子,假设您想要一个工具来通过请求库与拥抱面部模型进行交互。
import requests from langchain.tools.base import StructuredTool API_KEY = "<MY-API-KEY>" def get_huggingface_models( path: Optional[str] = None, query_params: Optional[dict] = None ) -> dict: """Tool that calls GET on <https://huggingface.co/models*> apis. Valid params include "search":"search", "author":"author", "filter":"filter" and "sort":"sort".""" base_url = "<https://huggingface.co/api/models>" headers = {"authorization": f"Bearer {API_KEY}"} result = requests.get(base_url + (path or ""), params=query_params, headers=headers) return result.json() get_huggingface_models_tool = StructuredTool.from_function(get_huggingface_models) models = get_huggingface_models_tool.run({"query_params": {"search": "gpt-j"}}) print(models)
在幕后,这将从函数的签名中推断出args_schema。这是用来告诉代理它可以提供查询参数来搜索,也可以提供路径参数来调用其他子端点。
如果您想要对工具定义进行更多控制,可以直接对BaseTool进行子类化。例如,您可能希望从环境变量中自动加载api密钥。
from typing import Optional, Type import aiohttp import requests from langchain.callbacks.manager import ( AsyncCallbackManagerForToolRun, CallbackManagerForToolRun, ) from langchain.tools import BaseTool from pydantic import BaseModel, BaseSettings, Field class GetHuggingFaceModelsToolSchema(BaseModel): path: str = Field(default="", description="the api path") query_params: Optional[dict] = Field( default=None, description="Optional search parameters" ) class GetHuggingFaceModelsTool(BaseTool, BaseSettings): """My custom tool.""" name: str = "get_huggingface_models" description: str = """Tool that calls GET on <https://huggingface.co/models*> apis. Valid params include "search":"search", "author":"author", "filter":"filter" and "sort":"sort".""" args_schema: Type[GetHuggingFaceModelsToolSchema] = GetHuggingFaceModelsToolSchema base_url: str = "<https://huggingface.co/api/models>" api_key: str = Field(..., env="HUGGINGFACE_API_KEY") @property def _headers(self) -> dict: return {"authorization": f"Bearer {self.api_key}"} def _run( self, path: str = "", query_params: Optional[dict] = None, run_manager: Optional[CallbackManagerForToolRun] = None, ) -> dict: """Run the tool""" result = requests.get( self.base_url + path, params=query_params, headers=self._headers ) return result.json() async def _arun( self, path: str = "", query_params: Optional[dict] = None, run_manager: Optional[AsyncCallbackManagerForToolRun] = None, ) -> dict: """Run the tool asynchronously.""" async with aiohttp.ClientSession() as session: async with session.get( self.base_url + path, params=query_params, headers=self._headers ) as response: return await response.json() get_models_tool = GetHuggingFaceModelsTool() models = get_models_tool.run({"query_params": {"search": "gpt-j"}}) print(models)
如何使用结构化工具?
我们添加了一个新的StructuredChatAgent,它可以与这些结构化工具一起本地工作。请参阅此笔记本进行演练。
由于先前代理的默认提示和输出解析器的限制,如果没有额外的自定义,它们就无法有效地使用结构化工具。
要开始,您可以使用以下代码片段实例化结构化聊天代理执行器:
from langchain.agents import initialize_agent, AgentType from langchain.chat_models import ChatAnthropic tools = [] # Add any tools here llm = ChatAnthropic(temperature=0) # or any other LLM agent_chain = initialize_agent(tools, llm, agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION)
这些工具还与langchain.experimental中的AutoGPT代理兼容。
常见问题
Q: 我可以将结构化工具与现有代理一起使用吗?
A: 如果您的结构化工具接受一个字符串参数:YES,那么它仍然可以使用现有的代理。然而,如果没有进一步的自定义,具有多个参数的结构化工具与以下代理不直接兼容:
- 零热反应描述
- 反应文档存储
- 带搜索的自我询问
- 会话反应描述
- 聊天零热点反应描述
- 聊天会话反应描述
Q: 我仍然可以创建字符串工具吗?
A: 您仍然可以使用Tool构造函数和@Tool装饰器来定义简单的字符串工具。从BaseTool类继承并接受单个字符串参数的工具仍将被视为字符串工具。
Q: 我可以将以前定义的字符串BaseTool与为StructuredTool构建的新代理一起使用吗
A: 是的!结构化工具不需要新的代理执行器,而旧的工具是向前兼容的。原始的Tool类与StructuredTool共享相同的基类,这是另一种表示工具应该开箱即用的方式。
需要json序列化字符串输入的工具可能需要进行一些修改,以便与较新代理的输出解析器进行互操作,或者可以将它们更新为新格式,这将为更复杂的接口提供更好的支持。
- 登录 发表评论