君子性非异也,善假于物也。
普通人使用大语言模型,很容易就把大语言模型当作百科全书。的确,使用ChatGPT的时候,很容易让我们有错觉,感觉它已经记忆并且学会了大量从互联网上获取的信息:无论你问任何问题,几乎总能得到一个看似合理的答案 😅 (偶尔会装死说不懂某个问题,其实是程序拦截,由于某些原因不方便回答而已。)
其实就目前大语言模型的水平而言,把大语言模型作为推理引擎更有用:给它提供一些文本或者其他信息来源,大语言模型可能会使用从互联网学习的背景知识,同时也会利用我们提供的新信息来帮助回答问题、推理内容,甚至决定接下来要做什么。LangChain的代理框架会协助我们做这件事。
代理就是一种用大语言模型做出决策、调用工具来执行具体操作的系统。通过设定代理的性格、背景以及工具的描述,你可以定制代理的行为,使其能够根据输入的文本做出理解和推理,从而实现自动化的任务处理。
很多年前,我用过一个流行的工作流开源软件:shark,shark支持WFMC 的 "ToolAgents"接口。ToolAgent和今天要说的代理(Agent)使用Tool的方式很类似,阳光底下无新鲜事,以前是工作流系统要和外部世界打交道,现在是大语言模型和外部世界交流,采用类似的机制很正常。当然,科技是在发展的,以前只会固定调用某个工具,现在会由大语言模型自主选择调用哪个工具。
工具代理(Tool Agent)为 Enhydra Shark 提供了面向不同软件的接口。例如,MailToolAgents、SOAPToolAgent 和 RuntimeApplicationToolAgent 实现了发送和接收邮件、调用网络服务以及启动本地可用应用程序所需的连接性。
如果说链是LangChain的基础功能,代理就是LangChain里最强大的功能。我们今天会深入了解什么是代理,如何创建和使用代理,如何使用不同类型的LangChain内置工具:数学工具、维基百科工具、搜索引擎等,当内置的工具不能满足时,如何创建自己的工具,这样就可以让代理与任何数据存储、接口或者功能进行互动。
代理就像一个多功能的调度总管,它能够接触并使用一套工具。根据用户的输入,代理会决定调用哪些工具。它不仅可以同时使用多种工具,而且可以将一个工具的输出结果作为另一个工具的输入。
准备
同样,先通过.env文件初始化环境变量,记住我们用的是微软Azure的GPT,具体内容参考本专栏的第一篇。
from dotenv import load_dotenv, find_dotenv
_ = load_dotenv(find_dotenv()) # read local .env file
deployment = "gpt-4"
# deployment = "gpt-35-turbo"
model = "gpt-4"
需要安装wikipedia库。
!pip install -U wikipedia
我们会用到AzureChatOpenAI
类,注意temperature会设置为0以消除任何可能出现的随机性。因为我们会使用LLM作为代理的推理引擎,它会连接到其他数据和计算资源,所以,我们希望这个推理引擎尽可能好、尽可能精确。
AzureChatOpenAI(temperature=0, deployment_name=deployment)
使用Agent
使用工具计算数学题
from langchain.agents import load_tools, initialize_agent
from langchain.agents import AgentType
from langchain.chat_models import AzureChatOpenAI
llm = AzureChatOpenAI(temperature=0, deployment_name=deployment)
tools = load_tools(["llm-math","wikipedia"], llm=llm) #加载工具
agent= initialize_agent(
tools,
llm,
agent=AgentType.CHAT_ZERO_SHOT_REACT_DESCRIPTION,
handle_parsing_errors=True,
verbose = True)
agent("What is the 25% of 300?")
llm-math工具实际上是一个链,它结合LLM和计算器来解决数学问题。
wikipedia工具是一个连接到维基百科API的程序,允许你查询维基百科上的内容,并返回查询结果。
我们会使用CHAT_ZERO_SHOT_REACT_DESCRIPTION的代理类型,这种类型用了ReAct来激发更好的推理能力。
这里简单说一下大语言模型的ReAct框架。(注意不要和前端的React混淆)
ReAct 是一种组织Prompt的技术,能最大化语言模型的推理能力。通过 ReAct 框架,大模型将被引导生成一个任务解决轨迹,即观察环境 - 进行思考 - 采取行动。观察和思考阶段被统称为推理(Reasoning),而实施下一步行动的阶段被称为行动(Acting)。每一步推理过程中,都会详细记录,这也改善了大模型解决问题时的可解释性和可信度。
- 在推理阶段,模型对当前环境和状态进行观察,并生成推理轨迹,从而使模型能够诱导、跟踪和更新操作计划,甚至处理异常情况。
- 在行动阶段,模型会采取下一步的行动,如与外部源(如知识库或环境)进行交互并收集信息,或给出最终答案。
ReAct 框架的这些优点,使得它在未来的发展中具有巨大的潜力。我们可以期待 ReAct 框架将能够处理更多、更复杂的任务。特别是随着具身智能的发展,ReAct 框架将能够使智能代理在虚拟或实际环境中进行更复杂的交互。例如,智能代理可能会在虚拟环境中进行导航,或者在实际环境中操作物理对象。这将大大扩展 AI 的应用范围,使得它们能够更好地服务于我们的生活和工作。
上面的代码很简单,就是调用load_tools来加载工具函数,然后通过initialize_agent初始化代理,最后运行代理计算数学问题:What is the 25% of 300?
这个例子很简单,但是麻雀虽小,五脏俱全,我们刚好可以用来理解ReAct的基本框架。
进入AgentExecutor链,开始思考(Thought)需要做什么,然后开始行动(Action),这里会传入一个json对象:操作(action)和操作(action_input)的输入,action对应的是要使用的工具,action_input就是要使用工具的输入。接下来,就到了观察结果(Observation):观察工具返回的计算结果Answer: 75.0,大模型思考(Thought)之后,认为得到了结果就返回最终答案(Final Answer)。如果思考之后认为还没有得到最终的结果,可能会继续调用其他工具,继续Action、Action Input、Observation、Thought……(Thought/Action/Action Input/Observation can repeat N times)
调用维基百科接口
接下来,我们来看一个调用维基百科接口的示例。我们会问一个问题:Tom M. Mitchell 写了什么书?为了预防重名的情况,我们加入背景信息:
Tom M. Mitchell is an American computer scientist and the Founders University Professor at Carnegie Mellon University (CMU).
question = "Tom M. Mitchell is an American computer scientist \
and the Founders University Professor at Carnegie Mellon University (CMU).\
what book did he write?"
result = agent(question)
可以看到,代理会调用维基百科工具,并且找到了两个维基百科的页面,LLM结合我们给出的背景信息,找到了正确的页面,并且从页面的摘要返回了我们想要的结果。
执行Python代码
如果你用过GitHub Copilot/Cursor(如果没用过,赶紧去体验),或者ChatGPT的代码解释器插件,就知道它们最常用的功能就是用LLM写代码,执行生成的代码。我们这里用代理做同样的事情。
from langchain_experimental.agents.agent_toolkits.python.base import create_python_agent
from langchain_experimental.tools.python.tool import PythonREPLTool
agent = create_python_agent(
llm,
tool=PythonREPLTool(),
verbose=True
)
customer_list = [["Harrison", "Chase"],
["Lang", "Chain"],
["Dolly", "Too"],
["Elle", "Elem"],
["Geoff","Fusion"],
["Trance","Former"],
["Jen","Ayai"]
]
agent.run(f"""Sort these customers by last name and then first name
and print the output: {customer_list}""")
首先,要创建一个Python的代理,传入一个工具:PythonREPLTool(REPL是一种与代码互动的方式,类似Jupyter Notebook),代理可以用REPL执行代码,得到代码运行结果,结果回传给代理,代理决定接下来做什么。
我们需要代理解决的问题是让它给一组客户名单customer_list排序。这里很重要的一点是让代理打印输出结果,打印出的内容后面会反馈给LLM,让LLM对代码输出的结果进行推理。
如果想了解具体的执行过程,可以开启Debug模式:
import langchain
langchain.debug=True
agent.run(f"""Sort these customers by \
last name and then first name \
and print the output: {customer_list}""")
langchain.debug=False
再次推荐LangChain公司开发的平台:https://smith.langchain.com/ (邀请码去上一篇文章找),有了它,看调用过程就如掌上观纹。
使用自定义工具
上面都是使用LangChain内置的工具,代理的一大优势是可以连接你自己的信息来源、接口、数据,这时候就需要创建自己的工具。
简单起见,我们定义一个工具来获取当前日期。
需要先安装库:!pip install DateTime
from langchain.agents import tool
from datetime import date
@tool
def time(text: str) -> str:
"""Returns todays date, use this for any
questions related to knowing todays date.
The input should always be an empty string,
and this function will always return todays
date - any date mathmatics should occur \
outside this function."""
return str(date.today())
agent= initialize_agent(
tools + [time],
llm,
agent=AgentType.CHAT_ZERO_SHOT_REACT_DESCRIPTION,
handle_parsing_errors=True,
verbose = True)
try:
result = agent("whats the date today?")
except:
print("exception on external access")
定义tool很简单:导入tool装饰器,将这个装饰器用于任何函数就可以将函数转换为LangChain可以使用的tool。关键是要写好工具函数的注释,那样LLM才清楚什么情况下要调用、如何调用这个tool。
展望
在2023-11-6日的OpenAI开发者大会上,OpenAl放出了一系列Assistants API,方便人们构建类AIAgent的应用(能学习新知识,然后调用模型和工具来执行任务)那么是否没必要使用LangChain的代理机制了?