目录
工具自定义
langchain 中提供了内置工具的,但是基本不能用,除了一个计算器和一个执行 python 代码的,其他的都要 api
Tool 模块相当于是使用外部工具,或者自定义工具
官方文档:Defining Custom Tools | 🦜️🔗 LangChain
接受单个输入
这种定义方式,只能接受一个参数,且不能封装成类,要不然会报少参数
定义一个搜索的工具
from langchain.pydantic_v1 import BaseModel, Field
from langchain.tools import BaseTool, StructuredTool, tool
class SearchInput(BaseModel):
query: str = Field(description="search query")
@tool("search-tool", args_schema=SearchInput, return_direct=True)
def search(query: str) -> str:
"""Look up things online."""
return "我是一个搜索的工具"
接受多个输入
这种既可以接受多个,也可以接受单个
定义一个计算 a * b 的计算器
from langchain.pydantic_v1 import BaseModel, Field
from langchain.tools import BaseTool, StructuredTool, tool
class CalculatorInput(BaseModel):
s: str = Field(description="输入字符串")
def multiply(a: int, b: int) -> int:
"""Multiply two numbers."""
return a * b
calculator = StructuredTool.from_function(
func=multiply, # 工具具体逻辑
name="Calculator", # 工具名
description="multiply numbers", # 工具信息
args_schema=CalculatorInput, # 工具接受参数信息
return_direct=True, # 直接作为工具的输出返回给调用者
handle_tool_error=True, # 报错了继续执行,不会吧那些报错行抛出,也可以自定义函数处理,handle_tool_error=函数名
)
定义一个排序列表的工具
class SortList(BaseModel):
num: str = Field(description="待排序列表")
def dort_fun(num):
"""Multiply two numbers."""
return sorted(eval(num))
sorter = StructuredTool.from_function(
func=dort_fun, # 工具具体逻辑
name="sort_num", # 工具名
description="排序列表中的数字", # 工具信息
args_schema=SortList, # 工具接受参数信息
return_direct=True, # 直接作为工具的输出返回给调用者
handle_tool_error=True, # 报错了继续执行,不会吧那些报错行抛出,也可以自定义函数处理,handle_tool_error=函数名
)
不管是单个输入,还是多个输入,传进去的参数一定是 str 类型,如果要使用其他类型需要转换
agent 调用工具
直接调用
定义工具,直接调用
from langchain_core.tools import tool
@tool
def multiply(first: int, second: int) -> int:
"""实现两个整数的乘法运算。"""
return first * second
result = multiply.invoke({"first": 4, "second": 5})
print(result) # 输出结果:20
initialize_agent 调用
这是中文文档给的代码:入门指南 | LangChain中文网:500页中文文档教程,助力大模型LLM应用开发从入门到精通
这种调用方式,自定义工具只能接受单个输入
看到一种说法,initialize_agent 废弃了滑动验证页面
from langchain.pydantic_v1 import BaseModel, Field
from langchain.tools import BaseTool, StructuredTool, tool
from langchain.agents import initialize_agent
from langchain.agents import AgentType
from langchain.agents import load_tools
import os
from dotenv import load_dotenv
from langchain_community.llms import Tongyi
load_dotenv('../key.env') # 指定加载 env 文件
key = os.getenv('DASHSCOPE_API_KEY') # 获得指定环境变量
DASHSCOPE_API_KEY = os.environ["DASHSCOPE_API_KEY"] # 获得指定环境变量
model = Tongyi(temperature=1)
"""这种定义方式,只能接受一个参数,且不能封装成类,要不然会报少参数"""
class SearchInput(BaseModel):
query: str = Field(description="search query")
@tool("search-tool", args_schema=SearchInput, return_direct=True)
def search(query: str) -> str:
"""Look up things online."""
return "我是一个搜索的工具"
# print(search.name)
# print(search.description)
# print(search.args)
# print(search.return_direct)
"""这种定义方式,可以接受多个参数"""
class CalculatorInput(BaseModel):
a: str = Field(description="第一个数字")
b: str = Field(description="第二个数字")
class SortList(BaseModel):
num: str = Field(description="待排序列表")
def dort_fun(num):
"""Multiply two numbers."""
return sorted(eval(num))
sorter = StructuredTool.from_function(
func=dort_fun, # 工具具体逻辑
name="sort_num", # 工具名
description="排序列表中的数字", # 工具信息
args_schema=SortList, # 工具接受参数信息
return_direct=True, # 直接作为工具的输出返回给调用者
handle_tool_error=True, # 报错了继续执行,不会吧那些报错行抛出,也可以自定义函数处理,handle_tool_error=函数名
)
"""
zero-shot-react-description
此代理使用ReAct框架,仅基于工具的描述来确定要使用的工具。
可以提供任意数量的工具。
此代理需要为每个工具提供描述
工具只能接受一个参数,不支持多个参数
"""
tools = load_tools(["llm-math"], llm=model)
tools = [search, sorter] + tools
agent = initialize_agent(tools, model, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True)
agent.run("计算 2 ** 3 + 4,`[10,4,7]`排一下序,并且")
可以看到能识别要调用两个工具,但只调用了一个,换了换位置,工具没问题
在 github 上看到说是 agent 有点问题
ReAct 调用单个输入的工具
这是官网找到的,ReAct | 🦜️🔗 LangChain
定义一个搜索工具,与计算字符串长度的工具
from langchain import hub
from langchain.agents import AgentExecutor, create_react_agent
import os
from dotenv import load_dotenv
from langchain_community.llms import Tongyi
load_dotenv('key.env') # 指定加载 env 文件
key = os.getenv('DASHSCOPE_API_KEY') # 获得指定环境变量
DASHSCOPE_API_KEY = os.environ["DASHSCOPE_API_KEY"] # 获得指定环境变量
model = Tongyi(temperature=1)
from langchain.pydantic_v1 import BaseModel, Field
from langchain.tools import BaseTool, StructuredTool, tool
class CalculatorInput(BaseModel):
s: str = Field(description="输入字符串")
def multiply(s: str) -> int:
"""Multiply two numbers."""
return len(s)
calculator = StructuredTool.from_function(
func=multiply, # 工具具体逻辑
name="Calculator", # 工具名
description="计算字符长度", # 工具信息
args_schema=CalculatorInput, # 工具接受参数信息
return_direct=True, # 直接作为工具的输出返回给调用者
handle_tool_error=True, # 报错了继续执行,不会吧那些报错行抛出,也可以自定义函数处理,handle_tool_error=函数名
)
class SearchInput(BaseModel):
query: str = Field(description="should be a search query")
@tool("search-tool", args_schema=SearchInput, return_direct=True)
def search(query: str) -> str:
"""Look up things online."""
return "你好啊"
tools = [search, calculator]
prompt = hub.pull("hwchase17/react")
agent = create_react_agent(model, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
agent_executor.invoke({"input": "langchian是什么东西?`阿萨德防守打法`有多少个字符?"})
可以看到能识别要两个工具,但是值调用第一个问题就没了,,,
structured_chat 调用多输入工具
Structured chat | 🦜️🔗 LangChain
定义一个搜索工具,一个接受两个字符串,返回它们长度的乘积工具
from langchain import hub
from langchain.agents import AgentExecutor, create_react_agent
import os
from dotenv import load_dotenv
from langchain_community.llms import Tongyi
load_dotenv('../key.env') # 指定加载 env 文件
key = os.getenv('DASHSCOPE_API_KEY') # 获得指定环境变量
DASHSCOPE_API_KEY = os.environ["DASHSCOPE_API_KEY"] # 获得指定环境变量
model = Tongyi(temperature=1)
from langchain.pydantic_v1 import BaseModel, Field
from langchain.tools import BaseTool, StructuredTool, tool
from langchain.agents import AgentExecutor, create_structured_chat_agent
class CalculatorInput(BaseModel):
a: str = Field(description="第一个字符串")
b: str = Field(description="第二个字符串")
def multiply(a: str, b: str) -> int:
"""Multiply two numbers."""
return len(a) * len(b)
calculator = StructuredTool.from_function(
func=multiply, # 工具具体逻辑
name="Calculator", # 工具名
description="计算字符长度的乘积", # 工具信息
args_schema=CalculatorInput, # 工具接受参数信息
return_direct=True, # 直接作为工具的输出返回给调用者
handle_tool_error=True, # 报错了继续执行,不会吧那些报错行抛出,也可以自定义函数处理,handle_tool_error=函数名
)
class SearchInput(BaseModel):
query: str = Field(description="should be a search query")
@tool("search-tool", args_schema=SearchInput, return_direct=True)
def search(query: str) -> str:
"""Look up things online."""
return "你好啊"
tools = [search, calculator]
prompt = hub.pull("hwchase17/structured-chat-agent")
agent = create_structured_chat_agent(model, tools, prompt)
agent_executor = AgentExecutor(
agent=agent, tools=tools, verbose=True, handle_parsing_errors=True
)
agent_executor.invoke({"input": "`asd`的字符串长度乘以`as`的字符串长度是多少?langchiani是什么?"})
还是一样弄了个识别要调用两个工具,但是值针对第一个问题响应
链式调用参考
LangChain 中如何使用不支持 Function Calling 的模型实现工具 - 知乎
关于只调用了第一个工具解决
前面看到都有一个问题就是都能识别要调用多个工具,但是,运行后只调用了第一个问题对应的工具,就不继续了,参考改了改,是要异步的才能继续调用:https://juejin.cn/post/7329719726892482595
initialize_agent 异步顺序调用
initialize_agent 这个应该用的 ReAct 框架,看输出结构是的
from langchain.pydantic_v1 import BaseModel, Field
from langchain.tools import BaseTool, StructuredTool, tool
from langchain.agents import initialize_agent
from langchain.agents import AgentType
from langchain.agents import load_tools
import os
from dotenv import load_dotenv
from langchain_community.llms import Tongyi
load_dotenv('../key.env') # 指定加载 env 文件
key = os.getenv('DASHSCOPE_API_KEY') # 获得指定环境变量
DASHSCOPE_API_KEY = os.environ["DASHSCOPE_API_KEY"] # 获得指定环境变量
model = Tongyi(temperature=1)
import asyncio
import nest_asyncio
nest_asyncio.apply()
"""这种定义方式,只能接受一个参数,且不能封装成类,要不然会报少参数"""
class SearchInput(BaseModel):
query: str = Field(description="search query")
@tool("search-tool", args_schema=SearchInput, return_direct=True)
def search(query: str) -> str:
"""Look up things online."""
return "我是一个搜索的工具"
# print(search.name)
# print(search.description)
# print(search.args)
# print(search.return_direct)
"""这种定义方式,可以接受多个参数"""
class CalculatorInput(BaseModel):
a: str = Field(description="第一个数字")
b: str = Field(description="第二个数字")
class SortList(BaseModel):
num: str = Field(description="待排序列表")
def dort_fun(num):
"""Multiply two numbers."""
return sorted(eval(num))
sorter = StructuredTool.from_function(
func=dort_fun, # 工具具体逻辑
name="sort_num", # 工具名
description="排序列表中的数字", # 工具信息
args_schema=SortList, # 工具接受参数信息
return_direct=True, # 直接作为工具的输出返回给调用者
handle_tool_error=True, # 报错了继续执行,不会吧那些报错行抛出,也可以自定义函数处理,handle_tool_error=函数名
)
tools = load_tools(["llm-math"], llm=model)
tools = [search, sorter] + tools
agent = initialize_agent(tools, model, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True)
# agent.run("计算 2 ** 3 + 4,`[10,4,7]`排一下序,")
async def run():
response = await agent.arun(input="算 2 ** 3 + 4,`[10,4,7]`排一下序,")
print(response)
if __name__ == '__main__':
asyncio.run(run())
看了官网,没找到说 initialize_agent 可以把中间步骤单独保存;这里是只打印最后一次工具调用的结果,可以理解为按照需要调用的工具顺序,最终调用玩最后一个后,把最终结果进行输出的。
如果想看到每一步工具的输出,或者将中间输出拿出来再使用,参考
https://github.com/langchain-ai/langchain/issues/6956
最后要使用 acall,return_intermediate_steps=True
tools = load_tools(["llm-math"], llm=model)
tools = [search, sorter] + tools
agent = initialize_agent(tools, model, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True,
return_intermediate_steps=True)
# agent.run("计算 2 ** 3 + 4,`[10,4,7]`排一下序,")
async def run():
response = await agent.acall(inputs="算 2 ** 3 + 4,第一步的结果转为字符串长度是多少?")
print(response)
if __name__ == '__main__':
asyncio.run(run())
ReAct
观察-思考-行动
ReAct 指定 agent ,试过了改prompt,换异步,都不行,以下是试过代码,还是只能调用第一个问题的工具,就没了,应该是 langchian 内部实现哪里有问题
from langchain import hub
from langchain.agents import AgentExecutor, create_react_agent
import os
from dotenv import load_dotenv
from langchain_community.llms import Tongyi
load_dotenv('key.env') # 指定加载 env 文件
key = os.getenv('DASHSCOPE_API_KEY') # 获得指定环境变量
DASHSCOPE_API_KEY = os.environ["DASHSCOPE_API_KEY"] # 获得指定环境变量
model = Tongyi(temperature=1)
from langchain.pydantic_v1 import BaseModel, Field
from langchain.tools import BaseTool, StructuredTool, tool
from langchain.agents import AgentExecutor, create_structured_chat_agent
class CalculatorInput(BaseModel):
s: str = Field(description="输入字符串")
def multiply(s: str) -> int:
"""Multiply two numbers."""
return len(s)
calculator = StructuredTool.from_function(
func=multiply, # 工具具体逻辑
name="Calculator", # 工具名
description="计算字符长度", # 工具信息
args_schema=CalculatorInput, # 工具接受参数信息
return_direct=True, # 直接作为工具的输出返回给调用者
handle_tool_error=True, # 报错了继续执行,不会吧那些报错行抛出,也可以自定义函数处理,handle_tool_error=函数名
)
class SearchInput(BaseModel):
query: str = Field(description="should be a search query")
@tool("search-tool", args_schema=SearchInput, return_direct=True)
def search(query: str) -> str:
"""Look up things online."""
return "你好啊"
tools = [search, calculator]
# https://smith.langchain.com/hub/hwchase17/react?organizationId=c4887cc4-1275-5361-82f2-b22aee75bad1
prompt = hub.pull("hwchase17/react")
# 修改提示词
from langchain.prompts import PromptTemplate
prompt_template = """
Answer the following questions as best you can. You have access to the following tools:
{tools}
Use the following format:
Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take, should be one of [{tool_names}]
Action Input: the input to the action
Observation: the result of the action
... (this Thought/Action/Action Input/Observation can repeat N times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question
Begin!
Question: {input}
Thought:{agent_scratchpad}
"""
prompt = PromptTemplate.from_template(prompt_template)
agent = create_react_agent(model, tools, prompt)
# 查看提示词
prompt_template = agent.get_prompts()[0]
# print(prompt_template.format(input="what's 4.1*7.9=?", agent_scratchpad=""))
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True, return_intermediate_steps=True)
# agent_executor.invoke({"input": "langchian是什么东西?`阿萨德防守打法`有多少个字符?"})
import asyncio
async def run():
response = await agent_executor.ainvoke({"input": "`阿萨德防守打法`有多少个字符?langchian是什么东西?"})
print(response)
if __name__ == '__main__':
asyncio.run(run())
Plan-and-Execute
也是一个 agent 框架,这个主要是分解任务,即先计划,再依次去执行,这种方法可以用
from langchain import hub
from langchain.agents import AgentExecutor, create_react_agent
import os
from dotenv import load_dotenv
from langchain_community.llms import Tongyi
load_dotenv('key.env') # 指定加载 env 文件
key = os.getenv('DASHSCOPE_API_KEY') # 获得指定环境变量
DASHSCOPE_API_KEY = os.environ["DASHSCOPE_API_KEY"] # 获得指定环境变量
model = Tongyi(temperature=1)
from langchain.pydantic_v1 import BaseModel, Field
from langchain.tools import BaseTool, StructuredTool, tool
from langchain.agents import AgentExecutor, create_structured_chat_agent
class CalculatorInput(BaseModel):
s: str = Field(description="输入字符串")
def multiply(s: str) -> int:
"""Multiply two numbers."""
return len(s)
calculator = StructuredTool.from_function(
func=multiply, # 工具具体逻辑
name="Calculator", # 工具名
description="计算字符长度", # 工具信息
args_schema=CalculatorInput, # 工具接受参数信息
return_direct=True, # 直接作为工具的输出返回给调用者
handle_tool_error=True, # 报错了继续执行,不会吧那些报错行抛出,也可以自定义函数处理,handle_tool_error=函数名
)
class SearchInput(BaseModel):
query: str = Field(description="should be a search query")
@tool("search-tool", args_schema=SearchInput, return_direct=True)
def search(query: str) -> str:
"""Look up things online."""
return "你好啊"
tools = [search, calculator]
# https://smith.langchain.com/hub/hwchase17/react?organizationId=c4887cc4-1275-5361-82f2-b22aee75bad1
prompt = hub.pull("hwchase17/react")
# 修改提示词
from langchain.prompts import PromptTemplate
prompt_template = """
Answer the following questions as best you can. You have access to the following tools:
{tools}
Use the following format:
Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take, should be one of [{tool_names}]
Action Input: the input to the action
Observation: the result of the action
... (this Thought/Action/Action Input/Observation can repeat N times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question
Begin!
Question: {input}
Thought:{agent_scratchpad}
"""
prompt = PromptTemplate.from_template(prompt_template)
agent = create_react_agent(model, tools, prompt)
# 查看提示词
prompt_template = agent.get_prompts()[0]
# 设置计划者和执行者
from langchain_experimental.plan_and_execute import PlanAndExecute, load_agent_executor, load_chat_planner
planner = load_chat_planner(model)
executor = load_agent_executor(model, tools, verbose=True)
# 初始化Plan-and-Execute Agent
agent = PlanAndExecute(planner=planner, executor=executor, verbose=True)
# 运行Agent解决问题
agent.run("`阿萨德防守打法`有多少个字符?langchian是什么东西?前面的问题都使用中文回答,并汇总一起给我答案")
虽然肯有时候还是有点问题,但弄了个调用多个自定义工具了。