前言
在AI与ML的浪潮中,Agents作为Langchain框架的基石,成为智能交互的关键。本博客深入剖析Agents组件,从基础概念到多样类型,逐一揭秘。从ZERO_SHOT_REACT_DESCRIPTION到高级CHAT与CONVERSATIONAL_REACT_DESCRIPTION,各类Agents针对不同需求优化,展现智能潜力。同时,SerpAPI、Dall-E、Text2Speech等工具与Python、GraphQL等技术为Agents构建提供强大支持。最后,文章介绍了如何自定义Agents,扩展功能,优化性能。
一、什么是Agents?
Agents
是一个具有智能功能的智能体,它使用 LLM
和工具来执行任务。
Agents
核心思想是使用LLM
来选择要采取的一系列动作。在链式结构中,一系列动作是硬编码的(在代码中)。 在 Agents
中,使用语言模型作为推理引擎来确定要采取的动作及其顺序。
Agents
包括几个关键组件:
- Agent: 用于生成指令和执行动作的代理。
- Tool: 用于执行动作的函数。
- Memory: 用于存储历史对话和生成的指令。
- LLM: 用于生成指令和执行动作的 LLM。
举个栗子🌰
- 会做数学题
- 不知道答案的时候可以搜索
搭建工具
serpai
是一个聚合搜索引擎,需要安装谷歌搜索包以及申请账号 https://serpapi.com/manage-api-keyllm-math
是一个封装好的数学计算链
pip install google-search-results
pip install numexpr
import os
os.environ["SERPAPI_API_KEY"] = '在serpai中获得的API_KEY'
from langchain_community.llms.tongyi import Tongyi
from langchain.agents import load_tools
from langchain.agents import initialize_agent
from langchain.agents import AgentType
llm = Tongyi()
tools = load_tools(["serpapi","llm-math"], llm=llm)
agent = initialize_agent(
tools,
llm,
agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,#这里有不同的类型
verbose=True,#是否打印日志
)
print(llm.invoke("请问现任的美国总统是谁?他的年龄的乘以100再加上320是多少?"))
print(agent.run("请问现任的美国总统是谁?他的年龄的除以2是多少?"))
二、Agents 的类型
ZERO_SHOT_REACT_DESCRIPTION
零样本反应描述CHAT_ZERO_SHOT_REACT_DESCRIPTION
聊天零样本反应描述CONVERSATIONAL_REACT_DESCRIPTION
会话反应描述CHAT_CONVERSATIONAL_REACT_DESCRIPTION
聊天会话反应描述STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION
聊天结构化零样本反应描述STRUCTURED_ZERO_SHOT_REACT_DESCRIPTION
结构化零样本反应描述
1.ZERO_SHOT_REACT_DESCRIPTION
即在没有示例的情况下可以自主的进行对话的类型。
# 即在没有示例的情况下可以自主的进行对话的类型。
import os
os.environ["SERPAPI_API_KEY"] = '这里输入Serapi API_KEY'
from langchain_community.llms.tongyi import Tongyi
from langchain.agents import load_tools
from langchain.agents import initialize_agent
from langchain.agents import AgentType
llm = Tongyi()
# 定义tools
tools = load_tools(["serpapi","llm-math"],llm=llm)
# 定义agent--(tools、agent、llm、memory)
agent = initialize_agent(
tools,
llm,
agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
verbose=True,
)
print(agent)
print("------------------------")
print(agent.agent.llm_chain.prompt.template)
#agent.invoke("现在美国总统是谁?他的年龄除以2是多少?")
2.CHAT_ZERO_SHOT_REACT_DESCRIPTION
零样本增强式生成,即在没有示例的情况下可以自主的进行对话的类型。
# 零样本增强式生成,即在没有示例的情况下可以自主的进行对话的类型。
import os
os.environ["SERPAPI_API_KEY"] = '这里输入Serapi API_KEY'
from langchain_community.llms.tongyi import Tongyi
from langchain.agents import load_tools
from langchain.agents import initialize_agent
from langchain.agents import AgentType
llm = Tongyi()
tools = load_tools(["serpapi","llm-math"],llm=llm)
agent = initialize_agent(
tools,
llm,
agent=AgentType.CHAT_ZERO_SHOT_REACT_DESCRIPTION,
verbose=True,
)
print(agent)
print("------------------------")
print(agent.agent.llm_chain.prompt.messages[0].prompt.template)
print("------------------------")
# agent.invoke("现在美国总统是谁?他的年龄除以2是多少?")
3.CONVERSATIONAL_REACT_DESCRIPTION
一个对话型的agent,这个agent要求与memory一起使用
# 一个对话型的agent,这个agent要求与memory一起使用
import os
os.environ["SERPAPI_API_KEY"] = '输入 Serpapi API_KEY'
from langchain_community.llms.tongyi import Tongyi
from langchain.agents import load_tools
from langchain.agents import initialize_agent
from langchain.agents import AgentType
llm = Tongyi()
from langchain.memory import ConversationBufferMemory
#记忆组件
memory = ConversationBufferMemory(
memory_key="chat_history",
)
# 定义tool
tools = load_tools(["serpapi","llm-math"],llm=llm)
# 定义agent
agent = initialize_agent(
tools,
llm,
agent=AgentType.CONVERSATIONAL_REACT_DESCRIPTION,
memory=memory,#记忆组件
verbose=True,
)
print(agent)
print(agent.agent.llm_chain.prompt.template)
agent.run("我是张三,今年18岁,性别女,现在在深圳工作,工作年限1年,月薪5000元")
agent.run("我的名字是什么?")
agent.run("有什么好吃的泰国菜可以推荐给我吗?")
agent.run("这些我都没吃过!我名字的最后一个字母是什么?1998年的世界杯谁夺冠了?")
agent.run("中国陕西西安现在的气温多少?截止目前我们聊了什么?")
4.CHAT_CONVERSATIONAL_REACT_DESCRIPTION
# CHAT_CONVERSATIONAL_REACT_DESCRIPTION 使用了chatmodel
import os
os.environ["SERPAPI_API_KEY"] = '输入 serpapi API_KEY'
from langchain_community.llms.tongyi import Tongyi
from langchain.agents import load_tools
from langchain.agents import initialize_agent
from langchain.agents import AgentType
llm = Tongyi()
from langchain.memory import ConversationBufferMemory
#记忆组件
memory = ConversationBufferMemory(
memory_key="chat_history",
return_messages=True,
)
tools = load_tools(["serpapi","llm-math"],llm=llm)
agent = initialize_agent(
tools,
llm,
agent=AgentType.CHAT_CONVERSATIONAL_REACT_DESCRIPTION,
memory=memory,#记忆组件
verbose=True,
)
print(agent)
print("1 ------------------------")
print(len(agent.agent.llm_chain.prompt.messages))
print("2 ------------------------")
print(agent.agent.llm_chain.prompt.messages[0].prompt.template)
print("3 ------------------------")
print(agent.agent.llm_chain.prompt.messages[1])
print("4 ------------------------")
print(agent.agent.llm_chain.prompt.messages[2].prompt.template)
print("5 ------------------------")
print(agent.agent.llm_chain.prompt.messages[3])
agent.run("有什么好吃的泰国菜可以推荐给我吗?用中文回答")
5. STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION
对输出做了结构化处理
# 对输出做了结构化处理
import os
os.environ["SERPAPI_API_KEY"] = '输入 serpapi API_KEY'
from langchain_community.llms.tongyi import Tongyi
from langchain.agents import load_tools
from langchain.agents import initialize_agent
from langchain.agents import AgentType
llm = Tongyi()
from langchain.memory import ConversationBufferMemory
# 记忆组件
memory = ConversationBufferMemory(
memory_key="chat_history",
return_messages=True,
)
## 定义tool
tools = load_tools(["serpapi", "llm-math"], llm=llm)
# 定义agent
agent = initialize_agent(
tools,
llm,
agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION, # agent类型
memory=memory, # 记忆组件
handle_parsing_errors=True,
verbose=True,
)
print(agent)
print(agent.agent.llm_chain.prompt.messages[0].prompt.template)
print(agent.agent.llm_chain.prompt.messages[1].prompt.template)
agent.run("有什么好吃的泰国菜可以推荐给我吗?用中文回答")
三、Tools
1.介绍
langchain预制了大量的tools,基本这些工具能满足大部分需求。 https://python.langchain.com.cn/docs/modules/agents/tools/
- 加载预制tool的方法
- 几种tool的使用方式
#添加预制工具的方法很简单
from langchain.agents import load_tools
tool_names = [...]
tools = load_tools(tool_names) #使用load方法
#有些tool需要单独设置llm
from langchain.agents import load_tools
tool_names = [...]
llm = ...
tools = load_tools(tool_names, llm=llm) #在load的时候指定llm
2.SerpAPI
最常见的聚合搜索引擎 https://serper.dev/dashboard,支持google\bing
from langchain.utilities.serpapi import SerpAPIWrapper
# 支持自定义参数,比如将引擎切换到bing,设置搜索语言等
params = {
"engine": "bing",
"gl": "us",
"hl": "en",
}
# 实例化
search = SerpAPIWrapper(params=params)
search.run("美国现在的总统是谁?")
# tools = load_tools(["serpapi"],llm=llm)
3.Dall-E
Dall-E是openai出品的文生图AI大模型
pip install opencv-python scikit-image
from langchain.agents import initialize_agent, load_tools
tools = load_tools(["dalle-image-generator"])
agent = initialize_agent(
tools,
llm,
agent="zero-shot-react-description",
verbose=True
)
agent.run("Create an image of a halloween night at a haunted museum")
4.Eleven Labs Text2Speech
ElevenLabs 是非常优秀的TTS合成API
pip install elevenlabs
pip install --upgrade pydantic
import os
os.environ["ELEVEN_API_KEY"] = "输入 API_KEY"
from langchain.tools.eleven_labs import ElevenLabsText2SpeechTool
text_to_speak = "Hello! 你好! Hola! नमस्ते! Bonjour! こんにちは! مرحبا! 안녕하세요! Ciao! Cześć! Привіт! வணக்கம்!"
tts = ElevenLabsText2SpeechTool(
voice="Bella",
text_to_speak=text_to_speak,
verbose=True
)
print(tts.name)
speech_file = tts.run(text_to_speak)
tts.stream_speech(text_to_speak)
from langchain.agents import initialize_agent, load_tools
tools = load_tools(["eleven_labs_text2speech"])
agent = initialize_agent(
tools,
llm,
agent="zero-shot-react-description",
verbose=True
)
agent.run("Create an image of a halloween night at a haunted museum")
5.GraphQL
一种api查询语言,类似sql,我们用它来查询奈飞的数据库,查找一下和星球大战相关的电影,API地址:https://swapi-graphql.netlify.app/.netlify/functions/index
pip install httpx gql > /dev/null
pip install gql
pip install requests_toolbelt
### from langchain.chat_models import ChatOpenAI
from langchain.agents import load_tools, initialize_agent, AgentType
from langchain.utilities import GraphQLAPIWrapper
tools = load_tools(
["graphql"],
graphql_endpoint="https://swapi-graphql.netlify.app/.netlify/functions/index",
)
agent = initialize_agent(
tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True
)
四、Tookit
tookit是langchain已经封装好的一系列工具,一个工具包是一组工具来组合完成特定的任务
1. python
一个python代码机器人
pip install langchain_experimental==0.0.4
from langchain_experimental.agents.agent_toolkits import create_python_agent
from langchain_experimental.tools import PythonREPLTool
from langchain_experimental.utilities import PythonREPL
from langchain.agents.agent_types import AgentType
agent_executor = create_python_agent(
llm=llm,
tool=PythonREPLTool(),
verbose=True,
agent_type=AgentType.OPENAI_FUNCTIONS,
agent_executor_kwargs={"handle_parsing_errors": True},
)
agent_executor.run("生成10个斐波那契数列?")
def generate_fibonacci_sequence(n):
sequence = []
a, b = 0, 1
for _ in range(n):
sequence.append(a)
a, b = b, a + b
return sequence
fibonacci_sequence = generate_fibonacci_sequence(10)
print(fibonacci_sequence)
# 输出:[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
2. SQL Database
使用SQLDatabaseChain构建的agent,用来根据数据库回答一般问题
from langchain.agents import create_sql_agent
from langchain.agents.agent_toolkits import SQLDatabaseToolkit
from langchain.sql_database import SQLDatabase
from langchain.agents import AgentExecutor
from langchain.agents.agent_types import AgentType
from langchain_community.chat_models import ChatTongyi
db = SQLDatabase.from_uri("sqlite:///db/Chinook.db")
toolkit = SQLDatabaseToolkit(db=db, llm=llm)
agent_executor = create_sql_agent(
llm=llm,
toolkit=toolkit,
verbose=True,
agent_type=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
)
agent_executor.run("创建用户表,包括用户名和密码两个列")
…
五、自定义Agent
- 定义一个class
- 工具:默认搜索
- 提示词:定义agent要做什么任务
- outparse:约束LLM的行为和输出
- 不同的LLM不同的质量
1.自定义 Tool
from langchain.agents import Tool, AgentExecutor, LLMSingleActionAgent, AgentOutputParser
from langchain.prompts import StringPromptTemplate
from langchain import SerpAPIWrapper, LLMChain
from typing import List, Union
from langchain.schema import AgentAction, AgentFinish, OutputParserException
from langchain_community.llms.tongyi import Tongyi
import re
import os
class MyAgentTool:
def __init__(self) -> None:
os.environ["SERPAPI_API_KEY"] = '输入 Serpapi API_KEY'
self.serpapi = SerpAPIWrapper()
def tools(self):
return [
Tool(
name="search",
description="适用于当你需要回答关于当前事件的问题时",
func=self.serpapi.run,
)
]
s = MyAgentTool()
s.serpapi.run("python")
2.自定义 Agents
from typing import Any
class MyAgent:
llm = None
tools = None
def __init__(self) -> None:
#agent的提示词,用来描述agent的功能
self.template = """尽你最大可能回答下面问题,你将始终用中文回答. 你在必要时可以使用下面这些工具:
{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}
{agent_scratchpad}"""
#定义一个openai的llm
self.llm = Tongyi()
#工具列表
self.tools = self.MyAgentTool().tools()
#agent的prompt
self.prompt = self.MyTemplate(
template=self.template,
tools=self.tools,
#输入变量和中间变量
input_variables=["input", "intermediate_steps"],
)
#定义一个LLMChain
self.llm_chain = LLMChain(
llm=self.llm,
prompt = self.prompt
)
#工具名称列表
self.toolnames = [tool.name for tool in self.tools]
#定义一个agent
self.agent = LLMSingleActionAgent(
llm_chain=self.llm_chain,
allowed_tools=self.toolnames,
output_parser=self.MyOutputParser(),
stop=["\nObservation:"],
)
#运行agent
def run(self, input: str) -> str:
#创建一个agent执行器
agent_executor = AgentExecutor.from_agent_and_tools(
agent=self.agent,
tools=self.tools,
handle_parsing_errors=True,
verbose=True
)
agent_executor.run(input=input)
#自定义工具类
class MyAgentTool:
def __init__(self) -> None:
os.environ["SERPAPI_API_KEY"] = 'db166b810c6b85674b6ceab3bd4e10d5048e1ba837db1c0d962ad91b34558805'
self.serpapi = SerpAPIWrapper()
def tools(self):
return [
Tool(
name="search",
description="适用于当你需要回答关于当前事件的问题时",
func=self.serpapi.run,
)
]
#自定义模版渲染类
class MyTemplate(StringPromptTemplate):
#渲染模版
template: str
#需要用到的工具
tools:List[Tool]
#格式化函数
def format(self, **kwargs: Any) -> str:
#获取中间步骤
intermediate_steps = kwargs.pop("intermediate_steps")
thoughts = ""
for action, observation in intermediate_steps:
thoughts += action.log
thoughts += f"\nObservation: {observation}\nThought: "
#将agent_scratchpad设置为该值
kwargs["agent_scratchpad"] = thoughts
# 从提供的工具列表中创建一个名为tools的变量
kwargs["tools"] = "\n".join([f"{tool.name}: {tool.description}" for tool in self.tools])
#创建一个提供的工具名称列表
kwargs["tool_names"] = ", ".join([tool.name for tool in self.tools])
return self.template.format(**kwargs)
#自定义输出解析类
class MyOutputParser(AgentOutputParser):
#解析函数
def parse(self, output: str) -> Union[AgentAction, AgentFinish]:
#检查agent是否应该完成
if "Final Answer:" in output:
return AgentFinish(
# 返回值通常始终是一个具有单个 `output` 键的字典。
# It is not recommended to try anything else at the moment :)
return_values={"output": output.split("Final Answer:")[-1].strip()},
log=output,
)
#用正则解析出动作和动作输入
regex = r"Action\s*\d*\s*:(.*?)\nAction\s*\d*\s*Input\s*\d*\s*:[\s]*(.*)"
match = re.search(regex, output, re.DOTALL)
#如果没有匹配到则抛出异常
if not match:
raise OutputParserException(f"Could not parse LLM output: `{output}`")
action = match.group(1).strip()
action_input = match.group(2)
# 返回操作和操作输入
return AgentAction(tool=action, tool_input=action_input.strip(" ").strip('"'), log=output)
myagent = MyAgent()
myagent.run("请问现任的美国总统是谁?他的年龄的除以2是多少?")
3.案例
from langchain.agents import Tool, tool, AgentExecutor, LLMSingleActionAgent, AgentOutputParser
from langchain.prompts import StringPromptTemplate
from langchain import SerpAPIWrapper, LLMChain
from typing import List, Union
from langchain.schema import AgentAction, AgentFinish, OutputParserException
from langchain_community.llms.tongyi import Tongyi
from langchain_community.embeddings.dashscope import DashScopeEmbeddings
# 引入向量化的类
from langchain_community.vectorstores import Chroma
import re
import os
from typing import Any
#自定义工具类
class MyAgentTool:
def __init__(self) -> None:
self.db = Chroma(embedding_function=DashScopeEmbeddings(),
persist_directory="./chroma")
def tools(self):
return [
Tool(
name="kg_search",
description="当你需要回答2024年NBA冠军球队的问题时使用",
func=self.search
)
]
def search(self, query: str) -> str:
return self.db.similarity_search(query,k=2)
def getdb(self):
return self.db.__len__()
#可用工具
@tool
def kgg_search(query: str):
"""当你需要回答2024年NBA冠军球队的问题时才会使用这个工具。"""
db = Chroma(embedding_function=DashScopeEmbeddings(),persist_directory="./chroma")
return db.similarity_search(query,k=2)
tool_list = [kgg_search]
class MyAgent:
llm = None
tools = None
def __init__(self,llm,tools) -> None:
#agent的提示词,用来描述agent的功能
self.template = """尽你最大可能回答下面问题,你将始终用中文回答. 你在必要时可以使用下面这些工具:
{tools}
使用下面的格式回答问题:
问题: 输入的问题
分析: 你对问题的分析,决定是否使用工具
动作: 使用工具,工具名称 [{tool_names}]
输入: {input}
观察: 动作名称
...
分析: 我现在知道答案了
答案: 这是最终的答案
记住使用中文回答,如果你使用英文回答将回遭到惩罚.
问题: {input}
{agent_scratchpad}"""
#定义一个openai的llm
self.llm = llm
#工具列表
self.tools = tools
#agent的prompt
self.prompt = self.MyTemplate(
template=self.template,
tools=self.tools,
#输入变量和中间变量
input_variables=["input", "intermediate_steps"],
)
#定义一个LLMChain
self.llm_chain = LLMChain(
llm=self.llm,
prompt = self.prompt
)
#工具名称列表
self.toolnames = [tool.name for tool in self.tools]
#定义一个agent
self.agent = LLMSingleActionAgent(
llm_chain=self.llm_chain,
allowed_tools=self.toolnames,
output_parser=self.MyOutputParser(),
stop=["\n观察:"],
)
#运行agent
def run(self, input: str) -> str:
#创建一个agent执行器
agent_executor = AgentExecutor.from_agent_and_tools(
agent=self.agent,
tools=self.tools,
handle_parsing_errors=True,
verbose=True
)
# agent_executor = AgentExecutor(agent=self.agent, tools=self.tools, verbose=True,handle_parsing_errors=True)
agent_executor.run(input=input)
#自定义模版渲染类
class MyTemplate(StringPromptTemplate):
#渲染模版
template: str
#需要用到的工具
tools:List[Tool]
#格式化函数
def format(self, **kwargs: Any) -> str:
#获取中间步骤
intermediate_steps = kwargs.pop("intermediate_steps")
thoughts = ""
for action, observation in intermediate_steps:
thoughts += action.log
thoughts += f"\n观察: {observation}\n想法: "
#将agent_scratchpad设置为该值
kwargs["agent_scratchpad"] = thoughts
# 从提供的工具列表中创建一个名为tools的变量
kwargs["tools"] = "\n".join([f"{tool.name}: {tool.description}" for tool in self.tools])
#创建一个提供的工具名称列表
kwargs["tool_names"] = ", ".join([tool.name for tool in self.tools])
prompt_ret = self.template.format(**kwargs)
return prompt_ret
#自定义输出解析类
class MyOutputParser(AgentOutputParser):
#解析函数
def parse(self, output: str) -> Union[AgentAction, AgentFinish]:
#检查agent是否应该完成
if "答案:" in output:
return AgentFinish(
# 返回值通常始终是一个具有单个 `output` 键的字典。
# It is not recommended to try anything else at the moment :)
return_values={"output": output.split("答案:")[-1].strip()},
log=output,
)
#用正则解析出动作和动作输入
regex = r"动作\s*\d*\s*:(.*?)\n输入\s*\d*\s*:[\s]*(.*)"
match = re.search(regex, output, re.DOTALL)
#如果没有匹配到则抛出异常
if not match:
raise OutputParserException(f"Could not parse LLM output: `{output}`")
action = match.group(1).strip()
action_input = match.group(2)
# 返回操作和操作输入
return AgentAction(tool=action, tool_input=action_input.strip(" ").strip('"'), log=output)
myagent = MyAgent(Tongyi(),tool_list)
myagent.run("2024年NBA冠军球队是哪只?")
# tool = MyAgentTool()
# tool.search("2024年NBA冠军球队是哪只")
from langchain.agents import initialize_agent, AgentType
agent = initialize_agent(
MyAgentTool().tools(),
Tongyi(),
agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
verbose=True,
)
# agent.run("2024年NBA冠军球队是哪只?")
agent.invoke("2024年NBA冠军球队是哪只?")