文章目录
自定义工具
在构建代理时,需要为其提供一个Tool
列表,以便代理可以使用这些工具。除了实际调用的函数之外,Tool
由几个组件组成:
属性 | 类型 | 描述 |
---|---|---|
name | str | 在提供给LLM或代理的工具集必须是唯一的 |
description | str | 描述工具的功能。LLM或代理将使用此描述作为上下文。 |
args_schema | Pydantic BaseModel | 可选但建议, 可用于提供更多信息(例如,few_shot示例)或验证预期参数 |
return_direct | boolean | 仅对代理相关。当为True时,在调用给定工具后,代理将停止并将结果返回给用户 |
LangChain提供了三种创建工具的方式:
- 使用@tool装饰器 ——定义自定义工具的最简单方式
- 使用StructuredTool.from_function 类方法 —— 这类似于
@tool
装饰器,但允许更多配置和同步和异步实现的规范。 - 通过子类BaseTool —— 这是最灵活的方法,它提供了最大程度的控制,但需要更多的工作量和代码。
@tool
或structuredTool.from_function
类方法对于大多数用例已经足够了。 如果工具具有精心选择的名称、描述和JSON模式,模型的性能会更好。
@tool装饰器
这个@tool
装饰器是自定自定义工具的最简单方式。该装饰器默认使用函数名称作为工具名称,但可以通过字符串作为第一个参数来覆盖。此外,装饰器使用函数的文档字符串作为工具的描述——因此必须提供文档字符串。
普通工具
# 导入工具装饰器库
from langchain_core.tools import tool
@tool
def multiply(a: int, b: int) -> int:
"""Multiply two numbers."""
return a * b
# 打印工具的属性
print(multiply.name)
print(multiply.description)
print(multiply.args)
输出:
multiply
Multiply two numbers.
{'a': {'title': 'A', 'type': 'integer'}, 'b': {'title': 'B', 'type': 'integer'}}
异步实现
# 导入工具装饰器库
from langchain_core.tools import tool
@tool
async def multiply(a: int, b: int) -> int:
"""Multiply two numbers."""
return a * b
# 打印工具的属性
print(multiply.name)
print(multiply.description)
print(multiply.args)
BaseModel
自定义名称和参数
还可以通过将它们传递给工具装饰器来自定义工具名称和JSON参数
# 导入工具装饰器库
from langchain_core.tools import tool
from pydantic import BaseModel, Field
class CalculatorInput(BaseModel):
a: int = Field(description="first number")
b: int = Field(description="second number")
@tool("multiplication-tool", args_schema=CalculatorInput, return_direct=True)
def multiply(a: int, b: int) -> int:
"""Multiply two numbers."""
return a * b
# 打印工具的属性
print(multiply.name)
print(multiply.description)
print(multiply.args)
print(multiply.return_direct)
print(multiply.invoke({"a": 2, "b": 3}))
输出
multiplication-tool
Multiply two numbers.
{'a': {'description': 'first number', 'title': 'A', 'type': 'integer'}, 'b': {'description': 'second number', 'title': 'B', 'type': 'integer'}}
True
6
这个写法多了一些描述
StructuredTool
structuredTool.from_function
类方法提供了比@tool
装饰器更多的可配置性,而无需太多额外的代码
支持在一个方法里面动态调用同步和异步方法
from langchain_core.tools import StructuredTool
import asyncio
def multiply(a: int, b: int) -> int:
"""Multiply two numbers"""
return a * b
async def amultiply(a: int, b: int) -> int:
"""Multiply two numbers."""
return a * b
async def main():
# func 参数:指定一个同步函数。当你在同步上下文中调用工具时,它会使用这个同步函数来执行操作。
# oroutine 参数:指定一个异步函数。当你在异步上下文中调用工具时,它会使用这个异步函数来执行操作。
calculator = StructuredTool.from_function(func=multiply, coroutine=amultiply)
# invoke是同步调用
print(calculator.invoke({"a": 2, "b": 3}))
# ainvoke是异步调用
print(await calculator.ainvoke({"a": 2, "b": 5}))
asyncio.run(main())
输出
6
10
配置自定义参数
from langchain_core.tools import StructuredTool
import asyncio
from pydantic import BaseModel, Field
class CalculatorInput(BaseModel):
a: int = Field(description="first number")
b: int = Field(description="second number")
def multiply(a: int, b: int) -> int:
"""Multiply two numbers"""
return a * b
async def amultiply(a: int, b: int) -> int:
"""Multiply two numbers."""
return a * b
async def main():
calculator = StructuredTool.from_function(
func=multiply,
name="calculator",
description="multiply numbers",
args_schema=CalculatorInput,
return_direct=True,
)
print(calculator.invoke({"a": 2, "b": 3}))
print(calculator.name)
print(calculator.description)
print(calculator.args)
asyncio.run(main())
处理工具错误
如果您正在使用带有代理的工具,可能需要一个错误处理策略,以便代理可以从错误中回复并继续执行。一个简单的策略是在工具内部抛出ToolException
,并使用handle_tool_error
指定一个错误处理程序。当指定了错误处理程序时,异常将被补货,错误处理程序将决定从工具返回哪个输出。可以将handle_tool_error
设置为True
、字符串值或函数。如果是函数,该函数应该以ToolException
作为参数,并返回一个值。请注意,仅仅抛出ToolException
是不会生效的。您需要首先设置工具的handle_tool_error
,因为其默认值是False
。
handle_tool_error=True/False
from langchain_core.tools import StructuredTool
# 导入工具出现异常的时候,处理的库
from langchain_core.tools import ToolException
def get_weather(city: str) -> int:
"""获取给定城市的天气"""
raise ToolException(f"错误,没有名为{city}的城市。")
get_weather_tool = StructuredTool.from_function(
func=get_weather,
# 默认情况下,如果函数抛出ToolException,则将ToolException的message作为响应。
# 如果设置为True,则将返回ToolException异常文本,False将会抛出ToolException
handle_tool_error=True,
)
response = get_weather_tool.invoke({"city": "测试"})
print(response)
下面是一个使用默认的handle_tool_error=True
行为的示例。
handle_tool_error=True
时输出示例
错误,没有名为测试的城市。
handle_tool_error=False
时输出示例
langchain_core.tools.base.ToolException: 错误,没有名为测试的城市。
handle_tool_error=异常信息
我们可以将handle_tool_error
设置为一个始终返回的字符串
from langchain_core.tools import StructuredTool
# 导入工具出现异常的时候,处理的库
from langchain_core.tools import ToolException
def get_weather(city: str) -> int:
"""获取给定城市的天气"""
raise ToolException(f"错误:没有名为{city}的城市。")
get_weather_tool = StructuredTool.from_function(
func=get_weather,
handle_tool_error="没找到这个城市",
)
response = get_weather_tool.invoke({"city": "测试"})
print(response)
handle_tool_error=函数
from langchain_core.tools import StructuredTool
from langchain_core.tools import ToolException
def get_weather(city: str) -> int:
"""获取给定城市的天气"""
raise ToolException(f"错误:没有名为{city}的城市。")
def _handle_error(error: ToolException) -> str:
return f"工具执行期间发生错误:`{error.args[0]}`"
get_weather_tool = StructuredTool.from_function(
func=get_weather,
handle_tool_error=_handle_error,
)
response = get_weather_tool.invoke({"city": "测试"})
print(response)
输出示例:
工具执行期间发生错误:`错误:没有名为测试的城市。`
调用内置工具包和拓展工具
工具
工具是代理、链或聊天模型/LLM用来与世界交互的接口。一个工具由以下组件组成:
- 工具的名称
- 工具的功能描述
- 工具输入的JSON模式
- 要调用的函数
- 工具的结果是否应直接返回给用户(仅对代理相关)名称、描述和JSON模式作为上下文提供给LLM,允许LLM适当地确定如何使用工具。给定一组可用工具和提示,LLM可以请求调用一个或多个工具,并提供恰当的参数。通常,在设计供聊天模型或LLM使用的工具时,要牢记以下几点:
- 经过微调以进行工具调用的聊天模型将比未经微调的模型更擅长进行工具调用。
- 未经微调的模型可能根本无法使用工具,特别是如果工具复杂或需要多次调用工具。
- 如果工具具有精心选择的名称、描述和JSON模式,则模型的性能将更好。
- 简单的工具通常比更复杂的工具更容易让模型使用。
LangChain拥有大量第三方工具。
https://python.langchain.com/docs/integrations/tools/
在使用第三方工具时,请确保了解工具的工作原理、权限情况。请阅读其文档,并检查是否需要从安全角度考虑任何事项。请查看安全只能获取更多信息。
让我们尝试一下维基百科集成(需要魔法)。
pip install wikipedia
代码:
from langchain_community.tools import WikipediaQueryRun
from langchain_community.utilities import WikipediaAPIWrapper
api_wrapper = WikipediaAPIWrapper(top_k_result=1, doc_content_chars_max=100)
tool = WikipediaQueryRun(api_wrapper=api_wrapper)
print(tool.invoke({"query": "langchain"}))
print(f"Name: {tool.name}")
print(f"Description: {tool.description}")
print(f"args schema: {tool.args}")
print(f"returns directly?: {tool.return_direct}")
输出示例:
Page: LangChain
Summary: LangChain is a software framework that helps facilitate the integration of
Name: wikipedia
Description: 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.
args schema: {'query': {'description': 'query to look up on wikipedia', 'title': 'Query', 'type': 'string'}}
returns directly?: False
自定义默认工具
我们还可以修改内置工具的名称、描述和参数的JSON模式
在自定义参数的JSON模式时,重要的是输入保持与函数相同,因此您不应更改它。但您可以轻松为每个输入定义自定义描述。
from langchain_community.tools import WikipediaQueryRun
from langchain_community.utilities import WikipediaAPIWrapper
from pydantic import BaseModel, Field
api_wrapper = WikipediaAPIWrapper(top_k_result=1, doc_content_chars_max=100)
class WikiInputs(BaseModel):
"""维基百科工具的输入"""
query: str = Field(
description="query to look up in Wikipedia, should be 3 or less words"
)
tool = WikipediaQueryRun(
name="wiki-tool",
description="look ip things in wikipedia",
args_schema=WikiInputs,
api_wrapper=api_wrapper,
return_direct=True,
)
print(tool.run("langchain"))
输出示例:
Page: LangChain
Summary: LangChain is a software framework that helps facilitate the integration of
如果使用内置工具包
工具包是一组旨在一起使用以执行特定任务的工具。它们具有便捷的加载方法。
要获取可用的现成工具包完整列表,请访问集成。
所有工具包都公开了一个get_tools
方法,该方法返回一个工具列表。
通常您应该这样使用它们:
# 初始化一个工具包
toolkit = ExampleTookit()
# 获取工具列表
tools = toolkit.get_tools()
例如:使用SQLDatabaseToolkit
读取langchian.db
数据库表结构
from langchain_community.agent_toolkits.sql.toolkit import SQLDatabaseToolkit
from langchain_community.utilities import SQLDatabase
from langchain_openai import ChatOpenAI
from langchain_community.agent_toolkits.sql.base import create_sql_agent
from langchain.agents.agent_types import AgentType
import os
llm = ChatOpenAI(
api_key=os.getenv("DASHSCOPE_API_KEY"),
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
model="qwen-turbo",
)
db = SQLDatabase.from_uri("sqlite:///langchain.db")
toolkit = SQLDatabaseToolkit(db=db, llm=llm)
print(toolkit.get_tools())
agent_executor = create_sql_agent(
llm=llm,
toolkit=toolkit,
verbose=False,
agent_type=AgentType.OPENAI_FUNCTIONS,
)
result = agent_executor.invoke("描述 langchain_test 表结构")
print(result)
建表语句
create table langchain_test
(
name integer,
code integer,
id integer
constraint id
primary key
);
输出示例:
[QuerySQLDatabaseTool(description="Input to this tool is a detailed and correct SQL query, output is a result from the database. If the query is not correct, an error message will be returned. If an error is returned, rewrite the query, check the query, and try again. If you encounter an issue with Unknown column 'xxxx' in 'field list', use sql_db_schema to query the correct table fields.", db=<langchain_community.utilities.sql_database.SQLDatabase object at 0x00000223BE228E60>), InfoSQLDatabaseTool(description='Input to this tool is a comma-separated list of tables, output is the schema and sample rows for those tables. Be sure that the tables actually exist by calling sql_db_list_tables first! Example Input: table1, table2, table3', db=<langchain_community.utilities.sql_database.SQLDatabase object at 0x00000223BE228E60>), ListSQLDatabaseTool(db=<langchain_community.utilities.sql_database.SQLDatabase object at 0x00000223BE228E60>), QuerySQLCheckerTool(description='Use this tool to double check if your query is correct before executing it. Always use this tool before executing a query with sql_db_query!', db=<langchain_community.utilities.sql_database.SQLDatabase object at 0x00000223BE228E60>, llm=ChatOpenAI(client=<openai.resources.chat.completions.completions.Completions object at 0x00000223BE156030>, async_client=<openai.resources.chat.completions.completions.AsyncCompletions object at 0x00000223BE228D10>, root_client=<openai.OpenAI object at 0x00000223BBCC6A20>, root_async_client=<openai.AsyncOpenAI object at 0x00000223BE206B40>, model_name='qwen-turbo', model_kwargs={}, openai_api_key=SecretStr('**********'), openai_api_base='https://dashscope.aliyuncs.com/compatible-mode/v1'), llm_chain=LLMChain(verbose=False, prompt=PromptTemplate(input_variables=['dialect', 'query'], input_types={}, partial_variables={}, template='\n{query}\nDouble check the {dialect} query above for common mistakes, including:\n- Using NOT IN with NULL values\n- Using UNION when UNION ALL should have been used\n- Using BETWEEN for exclusive ranges\n- Data type mismatch in predicates\n- Properly quoting identifiers\n- Using the correct number of arguments for functions\n- Casting to the correct data type\n- Using the proper columns for joins\n\nIf there are any of the above mistakes, rewrite the query. If there are no mistakes, just reproduce the original query.\n\nOutput the final SQL query only.\n\nSQL Query: '), llm=ChatOpenAI(client=<openai.resources.chat.completions.completions.Completions object at 0x00000223BE156030>, async_client=<openai.resources.chat.completions.completions.AsyncCompletions object at 0x00000223BE228D10>, root_client=<openai.OpenAI object at 0x00000223BBCC6A20>, root_async_client=<openai.AsyncOpenAI object at 0x00000223BE206B40>, model_name='qwen-turbo', model_kwargs={}, openai_api_key=SecretStr('**********'), openai_api_base='https://dashscope.aliyuncs.com/compatible-mode/v1'), output_parser=StrOutputParser(), llm_kwargs={}))]
{'input': '描述 langchain_test 表结构', 'output': "The table 'langchain_test' has the following structure:\n\n- It contains four columns: 'name', 'code', 'id'.\n- The column 'id' is the primary key.\n\nThere are three rows in this table."}
源码地址
https://github.com/lys1313013/langchain-example/tree/main/09-tools