当前位置:   article > 正文

(18-2)Composition(组合):Tools(工具)_structuredtool

structuredtool

6.2  Tools(工具)

在LangChain框架中,"Tools"(工具)是实现与外部系统交互的一种方式,它们为语言模型(如LLMs)提供了一个接口,使得模型能够执行特定的功能或查询外部数据源。

6.2.1  Tools介绍

LangChain工具的主要特点如下所示:

  1. 名称:每个工具都有一个名称,用于标识和引用。
  2. 描述:提供工具用途的简要说明,帮助理解工具的功能。
  3. 输入的JSON模式:定义了工具所需的输入参数及其结构,确保输入数据的规范性。
  4. 调用函数:工具中定义的一个或多个函数,用于执行具体的操作或计算。
  5. 返回结果:决定工具的输出是否应该直接返回给调用者。

请看下面的代码,演示了实用LangChain中内置工具WikipediaQueryRun的过程,这个工具的作用是查询百科信息并返回结果

  1. from langchain_community.tools import WikipediaQueryRun
  2. from langchain_community.utilities import WikipediaAPIWrapper
  3. # 初始化工具,这里可以按需进行配置
  4. api_wrapper = WikipediaAPIWrapper(top_k_results=1, doc_content_chars_max=100)
  5. tool = WikipediaQueryRun(api_wrapper=api_wrapper)
  6. # 这是工具的默认名称
  7. print(tool.name)
  8. # 这是工具的默认描述
  9. print(tool.description)
  10. # 这是工具输入的默认JSON模式
  11. print(tool.args)
  12. # 我们可以看到工具是否应该直接将结果返回给用户
  13. print(tool.return_direct)

执行后会输出:

  1. 执行后会输出:
  2. wikipedia
  3. A wrapper around Wikipedia. Useful for when you need to answer general questions about people, places, companies, facts, historical events, or other subjects. Input should be a search query.
  4. {'query': {'title': 'Query', 'type': 'string'}}
  5. False

6.2.2  创建自定义Tools

在LangChain中,创建自定义工具(Custom Tools)是创建代理(Agents)的重要环节之一,因为这些工具是执行代理任务的基础。在实现自定义工具时需要考虑如下几个关键组件。

  1. 名称(name):必须提供,且在提供给代理的工具集中必须是唯一的。
  2. 描述(description):可选但推荐,代理会用它来决定工具的使用。
  3. 参数模式(args_schema):可选但推荐,使用Pydantic的BaseModel来提供预期参数的更多信息(例如,少量示例)或验证。

在现实中有多种创建自定义工具的方法,例如下面的代码实现了一个虚构的搜索函数,总是返回字符串“LangChain”。

  1. def search(query: str) -> str:
  2.     return "LangChain"
  3. print(search.name)
  4. print(search.description)
  5. print(search.args)

执行后会输出:

  1. search
  2. search(query: str) -> str - Look up things online.
  3. {'query': {'title': 'Query', 'type': 'string'}}

在上述代码中定义了一个名为 search 的简单函数,该函数接受一个字符串类型的参数 query,并返回字符串 "LangChain"。这个函数本身并没有与LangChain框架中的Tool组件直接关联,因此直接使用print(search.name)、print(search.description)和print(search.args)将不会按期望的方式工作,因为这些属性是在LangChain的Tool类中定义的,而不是在普通的Python函数中定义。

在LangChain中,要想使一个普通的函数变成一个Tool,需要使用LangChain提供的工具装饰器(@tool)或者继承类BaseTool等方法实现。在下面的内容中,将详细讲解将普通函数转换为LangChain工具的方法。

1. 使用 @tool 装饰器

在LangChain中,@tool装饰器是定义自定义工具最简单的方法。装饰器@tool默认使用函数名称作为工具名称,但可以通过传递一个字符串作为第一个参数来覆盖。此外,装饰器会使用函数的文档字符串作为工具的描述,因此必须提供文档字符串。请看下面的例子,演示了使用@tool装饰器创建工具的过程。

实例6-1使用@tool装饰器创建工具(源码路径:codes\6\tool01.py

实例文件tool01.py的具体实现代码如下所示。

  1. from langchain.tools import tool
  2. @tool
  3. def search(query: str) -> str:
  4. """Look up things online."""
  5. return "LangChain"
  6. print(search.name) # 应输出: search
  7. print(search.description) # 应输出: Look up things online.
  8. print(search.args) # 应输出: {'query': {'title': 'Query', 'type': 'string'}}

在上述代码中,@tool 装饰器会自动使用函数名作为工具的名称,并使用函数的文档字符串作为工具的描述。同时,它会推断出函数参数的JSON模式。执行后会输出:

  1. search
  2. search(query: str) -> str - Look up things online.
  3. {'query': {'title': 'Query', 'type': 'string'}}

2.继承类BaseTool

在LangChain框架中,通过继承类BaseTool来实现自定义工具是一种更为明确和灵活的方法。这种方法允许我们完全控制工具的行为,包括工具的名称、描述、参数模式以及执行逻辑。通过继承BaseTool类创建自定义工具的步如下:

(1)导入必要的类:首先,你需要从LangChain框架中导入BaseTool类以及其他可能需要的类,比如BaseModel用于定义参数模式。

(2)定义输入模型:使用Pydantic的BaseModel定义工具的输入参数模式,这有助于验证输入数据并提供关于预期输入的文档。

(3)创建自定义工具类:创建一个新的类,继承自BaseTool,并实现必要的方法和属性。

(4)实现_run方法:这是工具的核心执行逻辑,在这个方法中实现工具的具体功能。

(5)实现异步执行(可选):如果工具需要支持异步执行,可以额外实现一个_arun方法。

(6)设置工具的属性:包括工具的名称、描述、参数模式以及是否直接返回结果。

请看下面的例子,演示了使用@tool装饰器创建工具的过程。

实例6-2:通过继承类BaseTool实现自定义工具(源码路径:codes\6\tool02.py

实例文件tool02.py的具体实现代码如下所示。

  1. from langchain.tools import BaseTool
  2. from langchain.pydantic_v1 import BaseModel, Field
  3. from typing import Optional
  4. from langchain.callbacks.manager import CallbackManagerForToolRun
  5. # 定义工具的输入参数模式
  6. class SearchInput(BaseModel):
  7. query: str = Field(description="The search query to be processed by the search tool")
  8. # 自定义工具类
  9. class CustomSearchTool(BaseTool):
  10. name = "Custom Search" # 工具的名称
  11. description = "A custom search tool that returns a fixed string." # 工具的描述
  12. args_schema: type = SearchInput # 工具的参数模式
  13. def _run(
  14. self, query: str, run_manager: Optional[CallbackManagerForToolRun] = None
  15. ) -> str:
  16. "工具的核心执行逻辑"
  17. return "LangChain"
  18. # 如果需要支持异步执行,可以实现_arun方法
  19. # def _arun(self, query: str, run_manager: Optional[AsyncCallbackManagerForToolRun] = None) -> str:
  20. # ""“工具的异步执行逻辑”""
  21. # raise NotImplementedError("CustomSearchTool does not support async")
  22. # 实例化工具
  23. search_tool = CustomSearchTool()
  24. # 使用工具
  25. print(search_tool.name) # 输出工具名称
  26. print(search_tool.description) # 输出工具描述
  27. print(search_tool.args) # 输出工具参数模式

在上述代码中,类CustomSearchTool继承自类BaseTool,并定义了一个SearchInput模型来描述输入参数。方法_run实现了工具的执行逻辑,简单地返回字符串"LangChain"。通过实例化CustomSearchTool类,你将得到一个完整的自定义工具,可以被LangChain代理使用。请注意,如果你的工具需要与LangChain的回调管理器集成,可能需要实现额外的逻辑来处理run_manager参数。此外,如果你的工具是异步的,需要实现_arun方法。在本例中没有实现异步执行,并且通过NotImplementedError明确了这一点。执行后会输出:

  1. Custom Search
  2. A custom search tool that returns a fixed string.
  3. {'query': {'title': 'Query', 'type': 'string'}}

3. 使用数据类StructuredTool

StructuredTool是 LangChain中的一个数据类(dataclass),允许以一种更结构化和声明式的方式来定义工具。使用 StructuredTool 可以方便地将一个普通的函数转换成一个 LangChain 工具,同时允许为工具提供名称、描述和参数模式等元数据。

使用数据类StructuredTool实现自定义工具的步骤如下:

(1)定义输入参数的 Pydantic 模型(如果需要):使用 Pydantic 的 BaseModel 来定义工具的输入参数模式。

(2)创建 StructuredTool 实例:使用 StructuredTool.from_function 静态方法来从现有函数创建一个 StructuredTool 实例。

(3)提供工具的元数据:包括工具的名称、描述、参数模式以及是否直接返回结果等。

请看下面的例子,演示了使用数据类StructuredTool创建工具的过程。

实例6-3使用数据类StructuredTool创建工具(源码路径:codes\6\tool03.py

实例文件tool03.py的具体实现代码如下所示。

  1. from langchain.tools import BaseTool, StructuredTool
  2. from langchain.pydantic_v1 import BaseModel, Field
  3. # 定义输入参数的 Pydantic 模型
  4. class SearchInput(BaseModel):
  5. query: str = Field(description="The search query to be processed.")
  6. # 定义一个简单的搜索函数
  7. def search_function(query: str) -> str:
  8. return "LangChain"
  9. # 使用 StructuredTool.from_function 创建 StructuredTool 实例
  10. search_tool = StructuredTool.from_function(
  11. func=search_function,
  12. name="Search",
  13. description="A tool that returns 'LangChain' for any search query.",
  14. args_schema=SearchInput # 提供参数模式
  15. )
  16. # 使用工具
  17. print(search_tool.name) # 输出工具名称
  18. print(search_tool.description) # 输出工具描述
  19. print(search_tool.run("test")) # 运行工具并打印结果

在上述代码中,首先定义了一个 SearchInput 模型来描述 search_function 函数的输入参数。然后,创建函数search_function,用于简单地返回字符串 "LangChain"。接着,使用 StructuredTool.from_function创建一个 StructuredTool 实例,同时提供了工具的名称、描述和参数模式。最后,通过调用 search_tool.run("test")来运行工具,这将调用原始的 search_function 函数,并返回其结果。执行后会输出:

  1. Search
  2. Search(query: str) -> str - A tool that returns 'LangChain' for any search query.
  3. LangChain

注意:使用 StructuredTool 是一种快速定义工具的方式,特别适用于那些不需要额外逻辑或回调处理的简单工具。

6.2.3  将工具转换为OpenAI函数

在实际应用中,通过将LangChain工具转换为OpenAI函数方式,可以在OpenAI的模型中使用这些工具,从而扩展模型的功能。将LangChain工具转换为OpenAI函数的基本步骤如下所示:

(1)导入工具和转换函数:从LangChain社区工具中导入你想要使用的特定工具类,以及用于将LangChain工具转换为OpenAI函数的convert_to_openai_function函数。

  1. from langchain_community.tools import YourToolClass  # 替换为具体的工具类
  2. from langchain_core.utils.function_calling import convert_to_openai_function

(2)创建工具实例:实例化LangChain工具。

tool = YourToolClass()  # 替换为具体的工具实例化

(3)将工具转换为OpenAI函数:使用convert_to_openai_function函数将LangChain工具转换为OpenAI函数。

function = convert_to_openai_function(tool)

(4)使用OpenAI模型:使用OpenAI的模型,比如ChatOpenAI,来调用这个函数。

  1. from langchain_openai import ChatOpenAI
  2. model = ChatOpenAI(model="gpt-3.5-turbo")  # 选择适合的模型

(5)调用函数:通过OpenAI模型的invoke方法调用转换后的函数。

  1. message = model.invoke(
  2.     [HumanMessage(content="Your input here")],
  3.     functions=[function],
  4. )

请看下面的例子,演示了将LangChain工具转换为OpenAI函数并调用的过程。

实例6-4将LangChain工具转换为OpenAI函数并调用(源码路径:codes\6\tool04.py

实例文件tool04.py的具体实现代码如下所示。

  1. # 初始化OpenAI模型
  2. model = ChatOpenAI(model="gpt-3.5-turbo")
  3. # 创建工具实例
  4. api_wrapper = MoveFileTool()
  5. # 将LangChain工具转换为OpenAI函数
  6. tool_as_openai_function = convert_to_openai_function(api_wrapper)
  7. # 打印转换后的OpenAI函数信息
  8. print(tool_as_openai_function)
  9. # 构建要传递给OpenAI模型的函数列表
  10. functions = [tool_as_openai_function]
  11. # 创建一个HumanMessage实例,模拟用户的输入
  12. message = HumanMessage(content="move file foo to bar")
  13. # 使用OpenAI模型调用转换后的函数
  14. invoke_message = model.invoke(
  15. [message],
  16. functions=functions
  17. )
  18. print(invoke_message)#打印调用结果
  19. # 如果需要,可以进一步处理invoke_message以获取所需信息。例如,提取函数调用的详细信息
  20. function_call_info = invoke_message.additional_kwargs.get("function_call", {})
  21. print(function_call_info)

上述代码的实现流程如下所示:

(1)首先,导入,所有必要的依赖项,然后初始化了ChatOpenAI模型和MoveFileTool工具。

(2)接着,将MoveFileTool工具转换为一个OpenAI函数,并打印出了该函数的详细信息。

(3)然后,创建一个HumanMessage实例来模拟用户的输入,然后调用model.invoke方法来执行转换后的函数,并将结果打印出来。

(4)最后,从调用结果中提取函数调用的详细信息,并打印出来。

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/小桥流水78/article/detail/875211
推荐阅读
相关标签
  

闽ICP备14008679号