技术分享 | LLM Powered Agents概念与实践

文摘   2024-09-19 09:16   上海  

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自带的提示模板。这个提示模板核心在于

  1. 告诉大模型可以调用工具,以及它能调用的工具和工具描述
  2. 要求大模型格式化输出,包括大模型的思考,将要调用的工具以及行动获得的结果(观察)”
#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


北邮 GAMMA Lab
北邮图数据挖掘与机器学习实验室
 最新文章