【AI Agent系列】【MetaGPT】【深入源码】智能体的运行周期以及多智能体间如何协作

文章目录

    • 1. 单智能体运行周期
      • 1.1 运行入口:Role.run
      • 1.2 _observe函数
      • 1.3 react函数
      • 1.4 publish_message函数
    • 2. 智能体运行周期总结
    • 3. 多智能体间如何协作
    • 推荐阅读 - 实战系列

书接上篇文章 【AI Agent系列】【MetaGPT】7. 一句话订阅专属信息 - 订阅智能体进阶,实现一个更通用的订阅智能体,我们跑通了一个多智能体的例子。但是相信很多同学跟我一样懵懵懂懂,对多智能体间如何协作,消息如何传递还是不太了解。今天这篇文章,我们就来学习下MetaGPT中多智能体如何协作,深入源码,一层层剖析

单智能体的例子可看这篇文章:【AI的未来 - AI Agent系列】【MetaGPT】2. 实现自己的第一个Agent

1. 单智能体运行周期

【AI Agent系列】【MetaGPT】【深入源码】智能体的运行周期以及多智能体间如何协作_第1张图片

    @role_raise_decorator
    async def run(self, with_message=None) -> Message | None:
        """Observe, and think and act based on the results of the observation"""
        if with_message:
            msg = None
            if isinstance(with_message, str):
                msg = Message(content=with_message)
            elif isinstance(with_message, Message):
                msg = with_message
            elif isinstance(with_message, list):
                msg = Message(content="\n".join(with_message))
            if not msg.cause_by:
                msg.cause_by = UserRequirement
            self.put_message(msg)

        if not await self._observe():
            # If there is no new information, suspend and wait
            logger.info(f"{self._setting}: no news. waiting.")
            return

        rsp = await self.react()

        # Reset the next action to be taken.
        self.rc.todo = None
        # Send the response message to the Environment object to have it relay the message to the subscribers.
        self.publish_message(rsp)
        return rsp

对照着上面这张图和这段代码,我们来详细看下一个智能体的运行周期。

1.1 运行入口:Role.run

一个智能体的运行周期都在这个Role类的run函数中,它接收一个with_message参数。
(1) 对 with_message参数的处理,如果with_message参数不是None,会执行下面三句指令:

if not msg.cause_by:
    msg.cause_by = UserRequirement
self.put_message(msg)
  • cause_by是什么?

    • 这里简单理解是记录了这个消息来自哪个类,如果不属于哪个类,则标记为来自用户需求UserRequirement。
    • UserRequirement是用来标记用户需求的类,没有其它用处
      【AI Agent系列】【MetaGPT】【深入源码】智能体的运行周期以及多智能体间如何协作_第2张图片
  • self.put_message(msg)干了什么?

    • 将msg放到自身的msg_buffer中
    def put_message(self, message):
        """Place the message into the Role object's private message buffer."""
        if not message:
            return
        self.rc.msg_buffer.push(message)

(2)执行 self._observe()函数,这个函数的作用是观察环境信息,当环境中出现了想要观察的信息,会返回信息的长度,否则长度为0,也就是会进入if not await self._observe()的条件内,直接return,不触发后续动作的执行。

(3)触发相应动作的执行:rsp = await self.react()

(4) self.rc.todo = None,这里如果不设置为None会怎样?

(5) 添加执行结果到环境中,self.publish_message(rsp)

1.2 _observe函数

源码如下:

	async def _observe(self, ignore_memory=False) -> int:
        """Prepare new messages for processing from the message buffer and other sources."""
        # Read unprocessed messages from the msg buffer.
        news = []
        if self.recovered: 
            news = [self.latest_observed_msg] if self.latest_observed_msg else []
        if not news:
            news = self.rc.msg_buffer.pop_all()
        # Store the read messages in your own memory to prevent duplicate processing.
        old_messages = [] if ignore_memory else self.rc.memory.get()
        self.rc.memory.add_batch(news)
        # Filter out messages of interest.
        self.rc.news = [
            n for n in news if (n.cause_by in self.rc.watch or self.name in n.send_to) and n not in old_messages
        ]
        self.latest_observed_msg = self.rc.news[-1] if self.rc.news else None  # record the latest observed msg

        # Design Rules:
        # If you need to further categorize Message objects, you can do so using the Message.set_meta function.
        # msg_buffer is a receiving buffer, avoid adding message data and operations to msg_buffer.
        news_text = [f"{i.role}: {i.content[:20]}..." for i in self.rc.news]
        if news_text:
            logger.debug(f"{self._setting} observed: {news_text}")
        return len(self.rc.news)

这个函数主要是观察环境中有没有需要本role处理的消息,有处理的消息返回消息长度,没有处理的消息,返回0,该role将结束动作周期,不执行实际动作。

(1)recovered状态还没用到,不知道具体作用是啥,看代码是使用上一次的消息内容。以后用到再仔细说。

(2)news = self.rc.msg_buffer.pop_all()取出msg_buffer中的所有消息。
(3)重点在这一句代码,从news中筛选出本role关注的消息

self.rc.news = [
            n for n in news if (n.cause_by in self.rc.watch or self.name in n.send_to) and n not in old_messages
        ]
  • 关注的消息:self.rc.watch 或者 n.send_to
    • watch的内容,可以通过_watch函数设置
    def _watch(self, actions: Iterable[Type[Action]] | Iterable[Action]):
        """Watch Actions of interest. Role will select Messages caused by these Actions from its personal message
        buffer during _observe.
        """
        self.rc.watch = {any_to_str(t) for t in actions}

(4)return len(self.rc.news)返回关注的消息的数量

1.3 react函数

    async def react(self) -> Message:
        """Entry to one of three strategies by which Role reacts to the observed Message"""
        if self.rc.react_mode == RoleReactMode.REACT:
            rsp = await self._react()
        elif self.rc.react_mode == RoleReactMode.BY_ORDER:
            rsp = await self._act_by_order()
        elif self.rc.react_mode == RoleReactMode.PLAN_AND_ACT:
            rsp = await self._plan_and_act()
        self._set_state(state=-1)  # current reaction is complete, reset state to -1 and todo back to None
        return rsp

该函数根据RoleReactMode规划动作的执行顺序:

  • RoleReactMode.REACT:_react方式,这种方式是先思考下一个动作是什么(_think函数),然后再执行相应动作(_act函数)。
  • RoleReactMode.BY_ORDER:_act_by_order方式,按顺序执行通过_init_actions设置进来的action列表。
  • RoleReactMode.PLAN_AND_ACT:_plan_and_act 方式,目前没有实现,预留的方式

并在执行完动作后,将状态state置成-1。

    def _set_state(self, state: int):
        """Update the current state."""
        self.rc.state = state
        logger.debug(f"actions={self.actions}, state={state}")
        self.rc.todo = self.actions[self.rc.state] if state >= 0 else None

1.4 publish_message函数

    def publish_message(self, msg):
        """If the role belongs to env, then the role's messages will be broadcast to env"""
        if not msg:
            return
        if not self.rc.env:
            # If env does not exist, do not publish the message
            return
        self.rc.env.publish_message(msg)

如果Role设置了env环境,则会向环境中广播这条msg。目前还没用到。

2. 智能体运行周期总结

我将上述智能体的运行周期总结成下图,以更清晰的看到里面函数和参数的配合:
【AI Agent系列】【MetaGPT】【深入源码】智能体的运行周期以及多智能体间如何协作_第3张图片

3. 多智能体间如何协作

理解了单智能体的运行周期后,很容易理解多智能体间的协作。

看下图(图片来自:https://docs.deepwisdom.ai/main/zh/guide/tutorials/multi_agent_101.html):
【AI Agent系列】【MetaGPT】【深入源码】智能体的运行周期以及多智能体间如何协作_第4张图片
其实就是:
(1)每一个Role都在不断观察环境中的信息(_observe函数)
(2)当观察到自己想要的信息后,就会触发后续相应的动作
(3)如果没有观察到想要的信息,则会一直循环观察
(4)执行完动作后,会将产生的msg放到环境中(publish_message),供其它Role智能体来使用。

拿我们实现过的例子来看(【AI Agent系列】【MetaGPT】7. 一句话订阅专属信息 - 订阅智能体进阶,实现一个更通用的订阅智能体):

class SubscriptionAssistant(Role):
    """Analyze user subscription requirements."""

    name: str = "同学小张的订阅助手"
    profile: str = "Subscription Assistant"
    goal: str = "analyze user subscription requirements to provide personalized subscription services."
    constraints: str = "utilize the same language as the User Requirement"

    def __init__(self, **kwargs) -> None:
        super().__init__(**kwargs)

        self._init_actions([ParseSubRequirement, RunSubscription]) ## 2. 先解析用户需求,然后运行订阅
        self._watch([UserRequirement, WriteCrawlerCode])  ## 触发

我们实现了一个SubscriptionAssistant的智能体,它_watch了UserRequirement用户消息和WriteCrawlerCode消息。

运行后,它就会_observe环境:

  • 当用户输入信息时,环境中存在了UserRequirement消息,它_observe到了,就会触发后续相应动作。从而实现了与用户的协作。
  • 当其它智能体的WriteCrawlerCode执行完毕,会向环境中写入执行结果的msg,来源(cause_by标记为WriteCrawlerCode),然后SubscriptionAssistant的_observe观察到了,就会触发后续相应动作。从而实现了与其它智能体的协作,也就是多智能体间的协作。

本文就先写到这里了,欢迎各位批评指正,可+v jasper_8017 一起学习。最后,点个赞呗!

推荐阅读 - 实战系列

以下文章从最基础的开始,循序渐进的入门MetaGPT智能体开发。

  • 【AI的未来 - AI Agent系列】【MetaGPT】0. 你的第一个MetaGPT程序
  • 【AI的未来 - AI Agent系列】【MetaGPT】1. AI Agent如何重构世界
  • 【AI的未来 - AI Agent系列】【MetaGPT】2. 实现自己的第一个Agent
  • 【AI的未来 - AI Agent系列】【MetaGPT】3. 实现一个订阅智能体,订阅消息并打通微信和邮件
  • 【AI的未来 - AI Agent系列】【MetaGPT】4. ActionNode从理论到实战
  • 【AI的未来 - AI Agent系列】【MetaGPT】5. 更复杂的Agent实战 - 实现技术文档助手
  • 【AI的未来 - AI Agent系列】【MetaGPT】6. 用ActionNode重写技术文档助手
  • 【AI Agent系列】【MetaGPT】7. 实战:只用两个字,让MetaGPT写一篇小说
  • 【AI Agent系列】【MetaGPT】8. 一句话订阅专属信息 - 订阅智能体进阶,实现一个更通用的订阅智能体

你可能感兴趣的:(大模型,python,人工智能,python,开发语言,笔记,经验分享,AIGC,AI编程)