MetaGPT-Agent相关代码分析

Agent

参考资料:智能体入门 | MetaGPT

组成

Agent = LLM + Observation + Thought + Action + Memory

-大语言模型(LLM):LLM作为智能体的“大脑”部分,使其能够处理信息,从交互中学习,做出决策并执行行动。

-观察:这是智能体的感知机制,使其能够感知其环境。智能体可能会接收来自另一个智能体的文本消息、来自监视摄像头的视觉数据或来自客户服务录音的音频等一系列信号。这些观察构成了所有后续行动的基础。

-思考:思考过程涉及分析观察结果和记忆内容并考虑可能的行动。这是智能体内部的决策过程,其可能由LLM进行驱动。

-行动:这些是智能体对其思考和观察的显式响应。行动可以是利用 LLM 生成代码,或是手动预定义的操作,如阅读本地文件。此外,智能体还可以执行使用工具的操作,智能体还可以执行使用工具的操作,包括在互联网上搜索天气,使用计算器进行数学计算等。

-记忆:智能体的记忆存储过去的经验。这对学习至关重要,因为它允许智能体参考先前的结果并据此调整未来的行动。

多Agent组成

多智能体 = Agents + Environment + SOP + Communication + Economy

这些组件各自发挥着重要的作用:

Agents:在上面单独定义的基础上,在多智能体系统中的智能体协同工作,每个智能体都具备独特有的LLM、观察、思考、行动和记忆。

Environment:环境是智能体生存和互动的公共场所。智能体从环境中观察到重要信息,并发布行动的输出结果以供其他智能体使用。

SOP:这些是管理智能体行动和交互的既定程序,确保系统内部的有序和高效运作。例如,在汽车制造的SOP中,一个智能体焊接汽车零件,而另一个安装电缆,保持装配线的有序运作。

Communication:通信是智能体之间信息交流的过程。它对于系统内的协作、谈判和竞争至关重要。

Economy:这指的是多智能体环境中的价值交换系统,决定资源分配和任务优先级。

MetaGPT-Agent相关代码分析_第1张图片

案例:

-在环境中,存在三个智能体Alice、Bob和Charlie,它们相互作用。

-他们可以将消息或行动的输出结果发布到环境中,同时也会被其他智能体观察到。

-下面将揭示智能体Charlie的内部过程,该过程同样适用于Alice和Bob。

-在内部,智能体Charlie具备我们上述所介绍的部分组件,如LLM、观察、思考、行动。

-Charlie思考和行动的过程可以由LLM驱动,并且还能在行动的过程中使用工具。

-Charlie观察来自Alice的相关文件和来自Bob的需求,获取有帮助的记忆,思考如何编写代码,执行写代码的行动,最终发布结果。

-Charlie通过将结果发布到环境中以通知Bob。Bob在接收后回复了一句赞美的话。

Agent案例

1使用现成agent

预期

让agent写一个产品需求文档

代码

MetaGPT-Agent相关代码分析_第2张图片

代码分析

关键类
Role

Role 类是智能体的逻辑抽象。

一个 Role 能执行特定的 Action,拥有记忆、思考并采用各种策略行动。基本上,它充当一个将所有这些组件联系在一起的凝聚实体。

有以下几个属性(具体作用后面会分析):

MetaGPT-Agent相关代码分析_第3张图片

RoleContext

Role的运行时上下文

拥有environment,memory,state,todo等等

有以下几个属性(具体作用后面会分析):

MetaGPT-Agent相关代码分析_第4张图片

Action

Action 是动作的逻辑抽象,里面有一个run方法,指具体执行的Action的动作(由于Action是抽象类,所以没有定义具体的Action)

有以下几个属性(具体作用后面会分析):

MetaGPT-Agent相关代码分析_第5张图片

Environment

环境,承载一批角色,角色可以向环境发布消息,可以被其他角色观察到

有以下几个属性(具体作用后面会分析):

MetaGPT-Agent相关代码分析_第6张图片

关键代码

MetaGPT-Agent相关代码分析_第7张图片

1)role = ProductManager()

MetaGPT-Agent相关代码分析_第8张图片

ProductManager初始化:

①调用父类Role初始化函数,创建RoleSetting,创建LLM,创建RoleContext

②初始化actions和states(图里是WritePRD构成的单链表)

MetaGPT-Agent相关代码分析_第9张图片

a. 将actions放到自己的属性里

b. 以key-value对的形式,将actions里的index与action放到states里面

③添加watch Actions,监听对应的Action

MetaGPT-Agent相关代码分析_第10张图片

2)result = await role.run(msg)

MetaGPT-Agent相关代码分析_第11张图片

①将msg存到memory中

②执行react,获取response

MetaGPT-Agent相关代码分析_第12张图片MetaGPT-Agent相关代码分析_第13张图片

关键变量:state当前的状态,todo需要完成的任务

a.开启循环,当react循环次数达到上限,或者todo为空时,退出循环

b._think:根据角色对应的prompt前缀,以及memory,让大模型给出下一步的Action,并且更新state和todo

MetaGPT-Agent相关代码分析_第14张图片

构建prompt并发给LLM获取答案:

将RoleContext中的history,在init_actions时构建的states(即Index-Action对)和但当前的状态state等信息发给LLM,让LLM给出下一个state(也即Actions所对应的index)

MetaGPT-Agent相关代码分析_第15张图片

根据LLM的答案,更新state和todo

c._act:根据todo和memory,执行action,得到结果后,更新msg,更新todo,更新memory

MetaGPT-Agent相关代码分析_第16张图片

具体action:

MetaGPT-Agent相关代码分析_第17张图片

其中,prd = await self._aask_v1(prompt, "prd", OUTPUT_MAPPING, format=format)这句,会调用llm_api去询问大模型,得到结果

将结果添加到RoleContext的memory中

d.更新token,循环次数等等

e.进入下一次循环

f.当循环退出时,返回rsp

③将rsp发布到RoleContext的environment中

MetaGPT-Agent相关代码分析_第18张图片

将msg广播到environment

MetaGPT-Agent相关代码分析_第19张图片MetaGPT-Agent相关代码分析_第20张图片

放到消息队列里面,并且添加到environment的memory中

总结完整调用逻辑

1初始化Role

1.1创建RoleContext等

1.2初始化actions,初始化states

1.3添加Action监听

2 role.run()

2.1将msg存到memory中

2.2执行react

2.2.1开启循环,当react循环次数达到上限,或者todo为空时,推出循环

2.2.2_think:根据角色对应的prompt前缀,以及memory,让大模型给出下一步的Action,并且更新state和todo

将RoleContext中的history,在init_actions时构建的states(即Index-Action对)和但当前的状态state等信息发给LLM,让LLM给出下一个state(也即Actions所对应的index)

根据LLM的答案,更新state和todo

2.2.3_act:根据todo和memory,执行action,得到结果后,更新msg,更新todo,更新memory

将结果添加到RoleContext的memory中

2.2.4更新token,循环次数等等

2.2.5进入下一次循环

2.2.6当循环退出时,返回rsp

2.3将rsp发布到RoleContext的environment中

2自定义单一动作的Agent

预期

用自然语言编写代码,并想让一个agent(假设为SimpleCoder)为我们做这件事。

代码

1)定义一个编写代码的动作

2)为智能体配备这个动作

定义Action实现类SimpleWriteCode

MetaGPT-Agent相关代码分析_第21张图片

定义Role的实现类SimpleCoder

在这个示例中,我们创建了一个 SimpleCoder,它能够根据人类的自然语言描述编写代码。

MetaGPT-Agent相关代码分析_第22张图片

步骤如下:

1)我们为其指定一个名称和配置文件。

2)我们使用 self._init_action 函数为其配备期望的动作 SimpleWriteCode。

3)我们覆盖 _act 函数,其中包含智能体具体行动逻辑。我们写入,我们的智能体将从最新的记忆中获取人类指令,运行配备的动作,MetaGPT将其作为待办事项 (self._rc.todo) 在幕后处理,最后返回一个完整的消息。

运行Role

MetaGPT-Agent相关代码分析_第23张图片

代码分析

关键类

和上面1使用现成agent的流程一样

关键代码
1)role = SimpleCoder()(原理和上面1使用现成agent的流程一样)

①初始化基本信息,创建RoleContext等

②初始化Actions和states

MetaGPT-Agent相关代码分析_第24张图片

2)result = await role.run(msg)

逻辑基本和上面的run一样,但是在_react中,调用_act方法时,会调用我们覆盖后的方法,如下图:

MetaGPT-Agent相关代码分析_第25张图片MetaGPT-Agent相关代码分析_第26张图片

通过SimpleWriteCode().run(),可以调用到SimpleWriteCode这个Action的run方法,它会向LLM提出要求,写python代码,并用正则匹配提取出来

MetaGPT-Agent相关代码分析_第27张图片

拿到code_text后,构建msg,并返回给外层

MetaGPT-Agent相关代码分析_第28张图片

总结完整调用逻辑

和上面1使用现成agent的流程序列基本一样,区别仅仅在于Role的_act()和Action的run()

3自定义多动作的Agent

预期

假设现在我们不仅希望用自然语言编写代码,而且还希望生成的代码立即执行。

一个拥有多个动作的智能体可以满足我们的需求。让我们称之为RunnableCoder,一个既写代码又立即运行的Role。

我们需要两个Action:SimpleWriteCode 和 SimpleRunCode

代码

定义Action实现类SimpleWriteCode

直接调用命令行去执行python代码

MetaGPT-Agent相关代码分析_第29张图片

定义Role实现类RunnableCoder

和上面2定义单一动作的智能体差不多

①构造函数

用 self._init_actions 初始化所有 Action

指定每次 Role 会选择哪个 Action。我们将 react_mode 设置为 "by_order",这意味着 Role 将按照 self._init_actions 中指定的顺序执行其能够执行的 Action。在这种情况下,当 Role 执行 _act 时,self._rc.todo 将首先是 SimpleWriteCode,然后是 SimpleRunCode。

②覆盖 _act 函数

Role 从上一轮的人类输入或动作输出中检索消息,用适当的 Message 内容提供当前的 Action (self._rc.todo),最后返回由当前 Action 输出组成的 Message。

MetaGPT-Agent相关代码分析_第30张图片

运行Role

MetaGPT-Agent相关代码分析_第31张图片

代码分析

关键类

和上面1使用现成agent的流程一样

关键代码
1)role = RunnableCoder()

RunnableCoder初始化:

①调用父类Role初始化函数,创建RoleSetting,创建LLM,创建RoleContext

②初始化actions和states

③设置react_mode

React_mode有3中模式,具体的描述如下

这里设置成by_order,也就是让RunnableCoder按照init_actions的顺序执行(也就是先执行SimpleWriteCode,后执行SimpleRunCode)

2)result = await role.run(msg)

框架和上面使用现成agent一样

不同之处在于:

在role执行react的时候,由于react_mode是by_order,这里会执行_act_by_order(),如下图

MetaGPT-Agent相关代码分析_第32张图片

其中order的执行逻辑如下图:

也即按照init_actions的顺序进行_act,注意这里的_set_state,会把rc.todo设置为当前遍历的action

而_act的逻辑如下:

在其中会执行todo.run,也就是action的run方法

MetaGPT-Agent相关代码分析_第33张图片

也就是会依次执行SimpleWriteCode, SimpleRunCode

执行完成后,返回rsp

总结完整调用逻辑

和上面1使用现成agent基本一样,区别仅仅在于react_mode不同,进而act的方式不同

4多agent

预期

创建一名编码人员,一名测试人员,一名审阅人员,来组成一个团队,完成从编码到测试到审阅的过程。

具体的角色与其对应的职责如下:

  • SimpleCoder:具有 SimpleWriteCode 动作,接收用户的指令并编写主要代码
  • SimpleTester 具有 SimpleWriteTest 动作,从 SimpleWriteCode 的输出中获取主代码并为其提供测试套件
  • SimpleReviewer 具有 SimpleWriteReview 动作,审查来自 SimpleWriteTest 输出的测试用例,并检查其覆盖范围和质量

代码

定义Action和Role
定义Action

①Action1:写代码

MetaGPT-Agent相关代码分析_第34张图片

②Action2:根据代码写k条测试用例

MetaGPT-Agent相关代码分析_第35张图片

③根据代码和测试用例进行整体评估

MetaGPT-Agent相关代码分析_第36张图片

定义Role

①SimpleCoder:和自定义单一动作的Agent中的Role是一样的

MetaGPT-Agent相关代码分析_第37张图片

②SimpleTester:代码测试人员

MetaGPT-Agent相关代码分析_第38张图片

③SimpleReviewer:代码审阅人员

MetaGPT-Agent相关代码分析_第39张图片

构建并运行Team

现在我们已经定义了三个 Role,接着我们初始化所有角色,设置一个 Team,并hire 它们。构建完成之后,运行team。

MetaGPT-Agent相关代码分析_第40张图片

代码分析

MetaGPT-Agent相关代码分析_第41张图片

关键类
Team

拥有一个或多个Role(也就是agent),SOP和消息平台的类。专门用于执行多agent的活动。

有以下几个属性(具体作用后面会分析):

关键代码
1)team = Team()

创建一个team对象

注意,这里会创建一个默认的Environment对象,并赋值给environment字段

2)team.hire([SimpleCoder(),SimpleTester(),SimpleReviewer()])

Team雇佣员工来合作:

具体完成的工作有两步(具体逻辑如下):

①将role的environment设置成team中创建的environment

②将role以role.profile-role的key-value对的形式,放到team所对应的environment的roles中

MetaGPT-Agent相关代码分析_第42张图片MetaGPT-Agent相关代码分析_第43张图片MetaGPT-Agent相关代码分析_第44张图片

3)team.invest(investment=investment)

投资investment的预算max_budget。

其作用为:当我们跑LLM时的cost超过max_budget时,抛出NoMoneyException

这里的investment为我们在main函数中设置的参数

MetaGPT-Agent相关代码分析_第45张图片

具体的工作:

①把team的investment设置成investment

②把config的最大预算也设置成investment

4)team.start_project(idea)

①设置ieda,这里的idea和investment一样,来自于main函数中的参数

②将idea作为content,把cause_by的Action设置成BossRequirement,构建Message,并发布到environment中

MetaGPT-Agent相关代码分析_第46张图片

注意这里message会被存在两个集合中,一个是list格式的storage,另一个是dict格式的index(用来根据cause_by来检索message)

MetaGPT-Agent相关代码分析_第47张图片

5)await team.run(n_round=n_round)

MetaGPT-Agent相关代码分析_第48张图片

①进入循环

②self._check_balance()检查余额,判断当前总的花费是否超过我们的最大预算,如果超过,就抛出异常

③执行self.environment.run()

MetaGPT-Agent相关代码分析_第49张图片

遍历所有的role,并发执行其中的run方法,并且用futures收集这部分异步任务的结果,等最后所有的role都执行完成之后,返回

由于environment.run()的run()方法

④当environment.run()执行完成后,进入下一次循环退出循环

④返回environment中的history

role.run()和多agent协作的解析

我们有三个角色:

SimpleCoder会执行SimpleWriteCode的Action,且监视着BossRequirement

SimpleTester会执行SimpleWriteTest的Action,且监视着SimpleWriteCode

SimpleReviewer会执行SimpleWriteReview的Action,且监视着SimpleWriteTest

也就是这个调用顺序会是SimpleCoder->SimpleTester->SimpleReviewer

接下来我们先看role的run()方法,接着,从上面第五步await team.run(n_round=n_round)中的遍历所有的role,并发执行其中的run方法这个部分,来看metaGPT是如何一步一步执行的

role.run()

由于上面第五步await team.run(n_round=n_round)中的遍历所有的role,并发执行其中的run方法这个部分,其中的run是不带任何参数的,而我们看role的run方法(如下图),如果message为None,是会走到elif not wait self._observe()这里的,这里会执行self._observe(),然后根据其结果来判断是否要执行这一个elif里面的代码块

MetaGPT-Agent相关代码分析_第50张图片

_observe()

作用:从environment中观察,并获取重要的信息,将其添加到memory中

MetaGPT-Agent相关代码分析_第51张图片

①env_msgs = self._rc.env.memory.get()

从environment的memory中获取所有的Message,具体参考下面代码:

MetaGPT-Agent相关代码分析_第52张图片

②observed = self._rc.env.memory.get_by_actions(self._rc.watch)

在environment的memory的index: dict[Type[Action], list[Message]]中,获取由watch的Action所触发的Message,获取完之后,拼接在一起,形成一个list返回

MetaGPT-Agent相关代码分析_第53张图片

③self._rc.news = self._rc.memory.find_news(observed)

找到在observered中存在,且在rc自己的memory的storage中(注意这里是rc的memory,和上面②中的不一样,不是rc的environment的memory)不存在的Message,并形成list返回,也就是找到之前没有监测到的message,也就是找到新产生的message

并且设置到当前role的context中的news变量中

④for i in env_msgs: self.recv(i)

将environment中所有的message都存到该role的memory中,也就是把environment中的memory的Message信息,添加到rc的memory中

这一步相当于是告诉role,这些message我都已经看过了,下次有类似的message直接跳过,避免产生重复的news

⑤将news的简略信息拼起来,并打印到控制台

⑥返回news的长度

elif not判断

如果没有新的news(也就是news的长度为0),那么就直接返回

如果有新的news就执行react(),并publish到environment中

MetaGPT-Agent相关代码分析_第54张图片

rsp = await self.react()

和1使用现成agent解释的差不多

self._publish_message(rsp)

和1使用现成agent解释的差不多

多agent协作

我们按照上面分析的role.run()的代码来每个agent执行的动作

SimpleCoder

①_observe()

MetaGPT-Agent相关代码分析_第55张图片

注意observed = self._rc.env.memory.get_by_actions(self._rc.watch)这一句

因为SimpleCoder添加watch的Action是BossRequirement,所以这一步会过滤出BossRequirement相关的message,如下图:

MetaGPT-Agent相关代码分析_第56张图片

接着,会把这条消息Human: write a function that calculates the product of a list设置到rc的news中

然后,当运行到最后时,各个变量的状态如下(参考下面红框中的部分):

MetaGPT-Agent相关代码分析_第57张图片MetaGPT-Agent相关代码分析_第58张图片

②elif not判断

由于news有1条message,所以len(self._rc.news)为1,不会进到对应的代码块里面,相应的会执行后面的react()和publish_message()

③self.react()

MetaGPT-Agent相关代码分析_第59张图片

a._think()

MetaGPT-Agent相关代码分析_第60张图片MetaGPT-Agent相关代码分析_第61张图片

由于当前role里面只配备了一个Action,所以这里直接把当前的todo设置成此Action,也就是SimpleWriteCode,设置完成之后,直接返回

b._act()

MetaGPT-Agent相关代码分析_第62张图片

会走到我们自定义的_act方法里面去

MetaGPT-Agent相关代码分析_第63张图片

正常情况下应该是todo.run(),但是官网代码写的是code_text = await SimpleWriteCode().run(msg.content),但是也不影响,因为在这个role里,todo只能是SimpleWriteCode或者None

c. SimpleWriteCode().run()

MetaGPT-Agent相关代码分析_第64张图片

正常情况下,应该是执行上面这几步,去询问LLM,但我这里连不上open ai的接口,就直接写死了一段代码来测试,如下图

MetaGPT-Agent相关代码分析_第65张图片

d.回到_act()方法中,构建message

注意content是code_text,cause_by是type(todo)

MetaGPT-Agent相关代码分析_第66张图片

④self.publish_message(rsp)

向environment中添加消息

MetaGPT-Agent相关代码分析_第67张图片MetaGPT-Agent相关代码分析_第68张图片MetaGPT-Agent相关代码分析_第69张图片

SimpleTester

MetaGPT-Agent相关代码分析_第70张图片

①_observe()

MetaGPT-Agent相关代码分析_第71张图片

因为SimpleTester添加watch的Action是SimpleWriteCode,所以这里会过滤出SimpleWriteCode相关的且以前没有处理过的新的message,并设置到rc的news中

然后,当运行到最后时,各个变量的状态如下(参考下面红框中的部分):

MetaGPT-Agent相关代码分析_第72张图片MetaGPT-Agent相关代码分析_第73张图片

②elif not判断

由于news有1条message,所以len(self._rc.news)为1,不会进到对应的代码块里面,相应的会执行后面的react()和publish_message()

③self.react()

MetaGPT-Agent相关代码分析_第74张图片

a._think()

MetaGPT-Agent相关代码分析_第75张图片MetaGPT-Agent相关代码分析_第76张图片MetaGPT-Agent相关代码分析_第77张图片

由于当前role里面只配备了一个Action,所以这里直接把当前的todo设置成此Action,也就是SimpleWriteTest,设置完成之后,直接返回

b._act()

会走到我们自定义的_act方法里面去

MetaGPT-Agent相关代码分析_第78张图片

c. SimpleWriteTest ().run()

MetaGPT-Agent相关代码分析_第79张图片

正常情况下,应该是执行上面这几步,去询问LLM,但我这里连不上open ai的接口,就直接写死了一段代码来测试,如下图

MetaGPT-Agent相关代码分析_第80张图片

d.回到_act()方法中,构建message

④self.publish_message(rsp)

向environment中添加消息

SimpleReviewer

MetaGPT-Agent相关代码分析_第81张图片

①_observe()

MetaGPT-Agent相关代码分析_第82张图片

因为SimpleReviewer添加watch的Action是SimpleWriteTest,所以这里会过滤出SimpleWriteTest相关的且以前没有处理过的新的message,并设置到rc的news中

然后,当运行到最后时,各个变量的状态如下(参考下面红框中的部分):

MetaGPT-Agent相关代码分析_第83张图片MetaGPT-Agent相关代码分析_第84张图片

②elif not判断

由于news有1条message,所以len(self._rc.news)为1,不会进到对应的代码块里面,相应的会执行后面的react()和publish_message()

③self.react()

MetaGPT-Agent相关代码分析_第85张图片

a._think()

MetaGPT-Agent相关代码分析_第86张图片MetaGPT-Agent相关代码分析_第87张图片MetaGPT-Agent相关代码分析_第88张图片

由于当前role里面只配备了一个Action,所以这里直接把当前的todo设置成此Action,也就是SimpleWriteReview,设置完成之后,直接返回

b._act()

由于我们没有在SimpleReviewer中自定义_act(),因此这里会调用其父类Role的_act方法()

MetaGPT-Agent相关代码分析_第89张图片

先获取important_memory

MetaGPT-Agent相关代码分析_第90张图片

然后执行todo.run(),也就是其所持有的SimpleWriteReview的Action对象的run方法

c. SimpleWriteReview.run()

MetaGPT-Agent相关代码分析_第91张图片

调用open ai的接口去询问LLM

MetaGPT-Agent相关代码分析_第92张图片

但由于我这里连不上open ai的接口,这里出现了异常……

你可能感兴趣的:(6000的机器学习笔记,AIGC,人工智能,语言模型)