1.agent概念
1.1什么是agent?
agent(智能体)是能够自主行动、感知环境、做出决策并与其他agent或人类进行交互的计算机程序或实体。
1.2LLM:agent的全新大脑
agent本身是一个大的概念,它并不是大模型的一个应用(比如在强化学习中,我们也可以训练一个能与环境交互,学习复杂策略的agent,这和大模型没关系)。但现在一提到agent大家就会自然联想到大模型,或者直接认为agent就是大模型的一个应用,是因为大模型让agent的能力,通用性和易用性实现了质变。
本职工作是自回归预测下一个token的大模型,在训练阶段接收了大量的现实场景语料,被迫拥有了强大的现实问题的思考能力,所以就可以充当agent的大脑,直接指导agent的思考和行动。
LLM powered agent使用大模型充当大脑逐步思考该怎么解决问题,在合适的时机调用正确的工具(接口),再把调用工具后生成的结果返回,重复这个流程,不断思考不断调用各种工具并发挥大模型自身的能力,最终解决问题。大模型和我们配置的这些接口工具就构成了agent。
图1 llm powered agent示意图
让大模型当大脑,给他调用各种工具的能力,来构成智能体。这个听起来很自然的想法天生就有很多优势:
在强化学习中,我们想让agent学到好的strategy。由于不同的问题之间难以建立通用的策略,所以我们要为各种任务设置不同的奖励函数,训练多个agent。对比强化学习的agent,大模型不需要我们训练就具备这种思考和计划的能力,此外泛化性非常强,什么任务都能做。(意味着不管什么任务,只需要一股脑抛给大模型,让大模型思考,做任务的拆解和执行就好) 大模型可以理解自然语言,比如我们想构建一个用自然语言查询数据库的agent,我们输入一条指令“找出总销售收入最高的前5个产品”,无论这句话是用中文,英文,法语,增加个“你好”或者“谢谢”或者写错两个字,大模型都能理解这句话的目的。(信达雅地用语言表达自己的想法不是所有人都具备的能力,但大模型能容忍这些)
2.LLM-powered-agent实践
2.1LangChain是什么,为什么需要LangChain
LangChain是一个热门的模块化构建大模型应用的开源框架。其提供了一系列工具比如SQL查询,谷歌搜索等。预定义并实现了一些通用的大模型思考行动策略比如ReAct(Reasoning and Acting 推理+行动)。LangChain这个庞大的框架还有很多其他功能,但总的来说LangChain把智能体的实现模块化了,并且提供了一些现成好用的模块。
2.2一个简单的LangChain案例
我们从一个简单的案例来讲怎么使用LangChain,这个案例中我们要查询北京和上海今日的气温,并计算它们的差值。
import os
os.environ["AZURE_OPENAI_API_KEY"] = ""
os.environ["AZURE_OPENAI_ENDPOINT"] = ""
os.environ["SERPAPI_API_KEY"] = ""
from langchain_openai import AzureChatOpenAI
from langchain.agents import initialize_agent
from langchain.agents import load_tools
from langchain.agents import AgentType
#接入chatgpt
llm = AzureChatOpenAI(
azure_deployment="gpt-35-turbo", # or your deployment
api_version="2023-03-15-preview",
temperature=0,
max_tokens=512,
timeout=None,
max_retries=2,
# other params...
)
#预先给agent配置好谷歌搜索和数学工具
tools = load_tools(['serpapi', 'llm-math'], llm=llm)
#定义一个agent,agent的工作方式为ZERO_SHOT_REACT_DESCRIPTION,它使用
agent = initialize_agent(tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True, handle_parsing_errors=True)
agent.run("用中文回答我,北京和上海的温度现在是多少,以及这两个地方的温度差")
首先我们要提供一个大模型接口,这里我们选择的是gpt-35-turbo(国产大模型和LangChain的默认配合不是很理想,可能因为LangChain是用英文写的预设prompt模板,国产大模型的英文能力不如gpt)。其次是传入工具,大模型本身的信息截止在其训练完成时间之前,是不可能知道现在的天气信息的,所以我们要通过load_tools让它可以调用谷歌搜索(serpapi)的接口,让它有查询当前天气的能力。明面上我们做的工作就这些,接着实例化一个agent,然后把大模型和工具传入就好了。那么agent做了什么解决这个问题呢?
图2 案例的输出结果
agent为了解决这个问题,它遵循了ReAct范式,表现为thought→action→observation。首先让大模型思考(thought),大模型思考的结果是“我要查询当前两个城市的温度,然后计算它们的差”,根据思考的结果,它采取行动(action),调用查询工具和计算工具,调用的过程就是action1和action2。每次调用后都要有一个观察(observation),观察调用工具后的结果。比如调用查询工具后,agent观察到北京上海的天气分别是23°和28°,这两个温度就作为已知信息(observation1)参与后续的计算(action2)。
2.3LangChain源码解析
为什么大模型能遵守这么复杂的范式,大模型要怎么调用工具呢,LangChain中用提示工程和输出解析器来实现这些。
LangChain使用提示工程,要求大模型给出自己的思考,将要采取的行动以及观察。下面是LangChain自带的提示模板。这个提示模板核心在于
告诉大模型可以调用工具,以及它能调用的工具和工具描述 要求大模型格式化输出,包括大模型的思考,将要调用的工具以及行动获得的结果(观察)”
#langchain/libs/langchain/langchain/agents/chat/prompt.py
#langchain自带的提示模板
SYSTEM_MESSAGE_PREFIX = """Answer the following questions as best you can. You have access to the following tools:"""
FORMAT_INSTRUCTIONS = """The way you use the tools is by specifying a json blob.
Specifically, this json should have a `action` key (with the name of the tool to use) and a `action_input` key (with the input to the tool going here).
The only values that should be in the "action" field are: {tool_names}
The $JSON_BLOB should only contain a SINGLE action, do NOT return a list of multiple actions. Here is an example of a valid $JSON_BLOB:
```
{{{{
"action": $TOOL_NAME,
"action_input": $INPUT
}}}}
```
ALWAYS use the following format:
Question: the input question you must answer
Thought: you should always think about what to do
Action:
```
$JSON_BLOB
```
Observation: the result of the action
... (this Thought/Action/Observation can repeat N times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question"""
SYSTEM_MESSAGE_SUFFIX = """Begin! Reminder to always use the exact characters `Final Answer` when responding."""
HUMAN_MESSAGE = "{input}\n\n{agent_scratchpad}"
对于大模型的输出,LangChain使用一个输出解析器解析。核心在于判断大模型的输出中是否包含action,如果有action,就要去调用对应的工具并返回新的observation。由于提示工程要求大模型以json格式输出action,对应的解析器只需用正则表达式匹配,就能知道要调用哪些工具。
#langchain/libs/langchain/langchain/agents/chat/prompt.py
import json
import re
from typing import Pattern, Union
from langchain_core.agents import AgentAction, AgentFinish
from langchain_core.exceptions import OutputParserException
from langchain.agents.agent import AgentOutputParser
from langchain.agents.chat.prompt import FORMAT_INSTRUCTIONS
FINAL_ANSWER_ACTION = "Final Answer:"
class ChatOutputParser(AgentOutputParser):
"""Output parser for the chat agent."""
format_instructions: str = FORMAT_INSTRUCTIONS
"""Default formatting instructions"""
pattern: Pattern = re.compile(r"^.*?`{3}(?:json)?\n(.*?)`{3}.*?$", re.DOTALL)
"""Regex pattern to parse the output."""
def get_format_instructions(self) -> str:
"""Returns formatting instructions for the given output parser."""
return self.format_instructions
def parse(self, text: str) -> Union[AgentAction, AgentFinish]:
"""Parse the output from the agent into
an AgentAction or AgentFinish object.
Args:
text: The text to parse.
Returns:
An AgentAction or AgentFinish object.
Raises:
OutputParserException: If the output could not be parsed.
ValueError: If the action could not be found.
"""
includes_answer = FINAL_ANSWER_ACTION in text
try:
found = self.pattern.search(text)
if not found:
# Fast fail to parse Final Answer.
raise ValueError("action not found")
action = found.group(1)
response = json.loads(action.strip())
includes_action = "action" in response
if includes_answer and includes_action:
raise OutputParserException(
"Parsing LLM output produced a final answer "
f"and a parse-able action: {text}"
)
return AgentAction(
response["action"], response.get("action_input", {}), text
)
except Exception as exc:
if not includes_answer:
raise OutputParserException(
f"Could not parse LLM output: {text}"
) from exc
output = text.split(FINAL_ANSWER_ACTION)[-1].strip()
return AgentFinish({"output": output}, text)
@property
def _type(self) -> str:
return "chat"
3.总结
llm-powered-agent作为当下最热门的agent形式,也存在一些争议。比如有人觉得提示工程并不是最好的发挥llm规划能力的方式,也有人觉得llm的本职工作只是预测token因此它根本没有真正理解世界。有一个形象的比喻是:
假设存在一个全知全能的神(llm),那么人类需要做的就是建立礼拜机制,虔诚的向神祈祷,让神能够感知到人的疾苦,从而帮人解决实际问题。但全流程中神是一个黑盒,如果他不响应你的祈祷,可能是因为你不够虔诚,也可能是因为你拜的神压根就不是全知全能的。
agent是一个宏大的话题。LangChain也是一个复杂的框架。本文简单介绍了agent,如何使用LangChain构建agent以及LangChain是怎么让LLM拥有思考和调用工具的能力的。希望能起到抛砖引玉的效果,引发大家的兴趣和思考。
实验室算法库简介
两算法库均获得OpenI启智社区“社区优秀孵化项目奖”
GammaGL:
GammaGL是一个基于TensorLayerX实现的开源的多框架的图神经网络(GNN)算法平台,该框架抽象出图数据、消息传递、采样等组件,已经实现了二十余种图数据集和四十余种术界经典GNN算法。 GammaGL发布0.5版本 https://github.com/BUPT-GAMMA/GammaGL
OpenHGNN:
OpenHGNN是一个基于深度学习框架PyTorch和图神经网络框架DGL作为底层框架的异质图神经网络(HGNN)工具包。该工具包专注于HGNN的算法模型,抽象出了数据集管理、算法流程、任务评测等模块,提供了超参数调优组件和最优的超参数设置,发布了HGNN设计空间和Benchmark等。OpenHGNN发布0.7版本OpenHGNN:https://github.com/BUPT-GAMMA/OpenHGNN