【AI Agent系列】【MetaGPT多智能体学习】4. 基于MetaGPT的Team组件开发你的第一个智能体团队

news2024/11/18 3:41:37

本系列文章跟随《MetaGPT多智能体课程》(https://github.com/datawhalechina/hugging-multi-agent),深入理解并实践多智能体系统的开发。

本文为该课程的第四章(多智能体开发)的第二篇笔记。主要是对MetaGPT中Team组件的学习和实践。

文章目录

  • 0. Team组件介绍
    • 0.1 基本参数
    • 0.2 重要函数
      • 0.2.1 hire - 雇佣员工,往Team中添加Role
      • 0.2.2 invest - 投资,设置程序总预算
      • 0.2.3 run_project
      • 0.2.4 run - Team开始运行的入口
    • 0.3 总结
  • 1. 基于Team开发你的第一个智能体团队
    • 1.1 demo需求描述
    • 1.2 写代码
      • 1.2.1 SimpleCoder
      • 1.2.2 SimpleTester
      • 1.2.3 SimpleReviewer
      • 1.2.4 组成Team并运行
      • 1.2.5 完整代码
      • 1.2.6 运行过程及结果展示
  • 2. 总结

0. Team组件介绍

我们在刚开始搭建环境的时候,跑的第一个例子就使用了Team组件。当时只是复制粘贴,用它将程序跑起来了,但其背后的机制和原理是什么还没有学习过。下面从部分源码中,看下Team组件的运行机制。

0.1 基本参数

class Team(BaseModel):
    """
    Team: Possesses one or more roles (agents), SOP (Standard Operating Procedures), and a env for instant messaging,
    dedicated to env any multi-agent activity, such as collaboratively writing executable code.
    团队:拥有一个或多个角色(代理人)、标准操作流程(SOP)和一个用于即时通讯的环境,致力于开展任何多代理活动,如协作编写可执行代码。
    """

    model_config = ConfigDict(arbitrary_types_allowed=True)

    env: Optional[Environment] = None
    investment: float = Field(default=10.0)
    idea: str = Field(default="")

其中主要三个参数:

  • env:多智能体运行的环境
  • investment:投资,用来设置整个程序运行的预算,控制token消耗,当程序运行超过这个预设值后,会强制停止
  • idea:用户的输入、需求

0.2 重要函数

0.2.1 hire - 雇佣员工,往Team中添加Role

这个函数实现的功能其实就是往自身的环境中添加Role。

def hire(self, roles: list[Role]):
    """Hire roles to cooperate"""
    self.env.add_roles(roles)

0.2.2 invest - 投资,设置程序总预算

用来设置整个程序运行的预算,控制token消耗,当程序运行超过这个预设值后,会强制停止。

def invest(self, investment: float):
    """Invest company. raise NoMoneyException when exceed max_budget."""
    self.investment = investment
    self.cost_manager.max_budget = investment
    logger.info(f"Investment: ${investment}.")

0.2.3 run_project

这个函数的名有点欺骗性,你可能以为这是开始运行整个Team的入口,其实不是。它只是往Team的环境中放入第一条用户消息而已。

idea 为用户的输入或需求。这个函数的主要功能是调用了 Environment 的 publish_message 往环境中送入了一个用户消息。

def run_project(self, idea, send_to: str = ""):
    """Run a project from publishing user requirement."""
    self.idea = idea

    # Human requirement.
    self.env.publish_message(
        Message(role="Human", content=idea, cause_by=UserRequirement, send_to=send_to or MESSAGE_ROUTE_TO_ALL),
        peekable=False,
    )

0.2.4 run - Team开始运行的入口

这个才是Team运行的入口函数,当输入了idea时,会转到 run_project 去往自身的环境中放置用户消息。然后在 while循环中,循环运行各个Role。

n_round指定循环的次数,这里默认为3,执行三次 self.env.run()env.run我们上篇文章已经知道了,就是顺序执行环境中所有Role的run函数。

_check_balance函数的功能是检查当前程序消耗的token或钱数是否超过了预算。如果超过了预算,直接弹窗警告 raise NoMoneyException

 @serialize_decorator
async def run(self, n_round=3, idea="", send_to="", auto_archive=True):
    """Run company until target round or no money"""
    if idea:
        self.run_project(idea=idea, send_to=send_to)

    while n_round > 0:
        # self._save()
        n_round -= 1
        logger.debug(f"max {n_round=} left.")
        self._check_balance()

        await self.env.run()
    self.env.archive(auto_archive)
    return self.env.history
def _check_balance(self):
    if self.cost_manager.total_cost >= self.cost_manager.max_budget:
        raise NoMoneyException(self.cost_manager.total_cost, f"Insufficient funds: {self.cost_manager.max_budget}")

0.3 总结

看了上面的几个重要函数,是否觉得有点眼熟?这不就是将上篇文章中我们在运行多智能体系统时的main函数拆分成了 hire / run_project / run 函数嘛。

async def main(topic: str, n_round=3):
	## 类比 Team 的 hire 函数添加 Roles
    classroom.add_roles([Student(), Teacher()])

	## 类比 Team 的 run_project 函数往环境中写入用户消息
    classroom.publish_message(
        Message(role="Human", content=topic, cause_by=UserRequirement,
                send_to='' or MESSAGE_ROUTE_TO_ALL),
        peekable=False,
    )

	## 类比 Team 的 run 函数控制循环次数
    while n_round > 0:
        # self._save()
        n_round -= 1
        logger.debug(f"max {n_round=} left.")
        await classroom.run()
    return classroom.history

所以,Team 组件的本质,就是对 Environment 接口的封装,同时在此基础上增加了 invest 的预算控制而已。

1. 基于Team开发你的第一个智能体团队

1.1 demo需求描述

总的需求,简单的软件开发流程:一个写代码,一个测试代码,一个review代码。

所以需要三个智能体Role:

  • SimpleCoder,Action是 SimpleWriteCode,写代码
  • SimpleTester,Action是 SimpleWriteTest,接收 SimpleCoder 的代码进行测试。也接收 SimpleReviewer 的修改意见进行测试用例改写。
  • SimpleReviewer,Action是 SimpleWriteReview,接收 SimpleTester 的测试用例,检查其覆盖范围和质量,给出测试用例的修改意见。

1.2 写代码

1.2.1 SimpleCoder

SimpleCoder主要用来写代码。

  • 它的Action是SimpleWriteCode,通过 self.set_actions([SimpleWriteCode]) 将该Action设置给SimpleCoder
  • 它的行动指令来源是 UserRequirement,当环境中出现 UserRequirement 来源的消息时,它开始执行Action。通过 self._watch([UserRequirement]) 设置其关注的消息来源。
def parse_code(rsp):
    pattern = r"```python(.*)```"
    match = re.search(pattern, rsp, re.DOTALL)
    code_text = match.group(1) if match else rsp
    return code_text

class SimpleWriteCode(Action):
    PROMPT_TEMPLATE: str = """
    Write a python function that can {instruction}.
    Return ```python your_code_here ```with NO other texts,
    your code:
    """
    name: str = "SimpleWriteCode"
    async def run(self, instruction: str):
        prompt = self.PROMPT_TEMPLATE.format(instruction=instruction)
        rsp = await self._aask(prompt)
        code_text = parse_code(rsp)
        return code_text

class SimpleCoder(Role):
    name: str = "Alice"
    profile: str = "SimpleCoder"
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self._watch([UserRequirement])
        self.set_actions([SimpleWriteCode])

1.2.2 SimpleTester

SimpleTester 用来写测试用例代码。

  • 其Action为SimpleWriteTest,通过 self.set_actions([SimpleWriteTest]) 指定。
  • 其行动指令来源,一个是 SimpleWriteCode,接收主代码,根据主代码写单测的测试用例。第二个来源是 SimpleWriteReview,接收测试用例修改意见,根据修改意见完善测试用例。通过 self._watch([SimpleWriteCode, SimpleWriteReview]) 来指定关注的消息来源。
class SimpleWriteTest(Action):
    PROMPT_TEMPLATE: str = """
    Context: {context}
    Write {k} unit tests using pytest for the given function, assuming you have imported it.
    Return ```python your_code_here ```with NO other texts,
    your code:
    """

    name: str = "SimpleWriteTest"
    async def run(self, context: str, k: int = 3):
        prompt = self.PROMPT_TEMPLATE.format(context=context, k=k)
        rsp = await self._aask(prompt)
        code_text = parse_code(rsp)
        return code_text

class SimpleTester(Role):
    name: str = "Bob"
    profile: str = "SimpleTester"

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.set_actions([SimpleWriteTest])
        # self._watch([SimpleWriteCode])
        self._watch([SimpleWriteCode, SimpleWriteReview])  # feel free to try this too
        
    async def _act(self) -> Message:
        logger.info(f"{self._setting}: to do {self.rc.todo}({self.rc.todo.name})")
        todo = self.rc.todo

        # context = self.get_memories(k=1)[0].content # use the most recent memory as context
        context = self.get_memories()  # use all memories as context
        code_text = await todo.run(context, k=5)  # specify arguments
        msg = Message(content=code_text, role=self.profile, cause_by=type(todo))
        return msg

1.2.3 SimpleReviewer

SimpleReviewer 用来对测试用例代码进行Review,给出修改意见。

  • 其Action为SimpleWriteReview,通过 self.set_actions([SimpleWriteReview]) 指定。
  • 其行动指令来源为 SimpleWriteTest,接收测试用例代码,根据测试用例代码给出修改意见。通过 self._watch([SimpleWriteTest]) 来指定关注的消息来源。
class SimpleWriteReview(Action):
    PROMPT_TEMPLATE: str = """
    Context: {context}
    Review the test cases and provide one critical comments:
    """

    name: str = "SimpleWriteReview"

    async def run(self, context: str):
        prompt = self.PROMPT_TEMPLATE.format(context=context)
        rsp = await self._aask(prompt)
        return rsp

class SimpleReviewer(Role):
    name: str = "Charlie"
    profile: str = "SimpleReviewer"

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.set_actions([SimpleWriteReview])
        self._watch([SimpleWriteTest])

1.2.4 组成Team并运行

下面就是将上面的三个 Role 放到一个 Team 中。

  • hire 函数添加上面的三个 Role 到 Team 中
  • invest 函数设置总预算
  • run_project 函数将 idea 任务放到环境中
  • run 函数让整个 Team 运行起来
async def main(
    idea: str = "write a function that calculates the product of a list",
    investment: float = 3.0,
    n_round: int = 5,
    add_human: bool = False,
):
    logger.info(idea)

    team = Team()
    team.hire(
        [
            SimpleCoder(),
            SimpleTester(),
            SimpleReviewer(is_human=add_human),
        ]
    )

    team.invest(investment=investment)
    team.run_project(idea)
    await team.run(n_round=n_round)


if __name__ == "__main__":
    fire.Fire(main)

1.2.5 完整代码

"""
Filename: MetaGPT/examples/build_customized_multi_agents.py
Created Date: Wednesday, November 15th 2023, 7:12:39 pm
Author: garylin2099
"""
import re

import fire

from metagpt.actions import Action, UserRequirement
from metagpt.logs import logger
from metagpt.roles import Role
from metagpt.schema import Message
from metagpt.team import Team


def parse_code(rsp):
    pattern = r"```python(.*)```"
    match = re.search(pattern, rsp, re.DOTALL)
    code_text = match.group(1) if match else rsp
    return code_text


class SimpleWriteCode(Action):
    PROMPT_TEMPLATE: str = """
    Write a python function that can {instruction}.
    Return ```python your_code_here ```with NO other texts,
    your code:
    """
    name: str = "SimpleWriteCode"

    async def run(self, instruction: str):
        prompt = self.PROMPT_TEMPLATE.format(instruction=instruction)

        rsp = await self._aask(prompt)

        code_text = parse_code(rsp)

        return code_text


class SimpleCoder(Role):
    name: str = "Alice"
    profile: str = "SimpleCoder"

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self._watch([UserRequirement])
        self.set_actions([SimpleWriteCode])


class SimpleWriteTest(Action):
    PROMPT_TEMPLATE: str = """
    Context: {context}
    Write {k} unit tests using pytest for the given function, assuming you have imported it.
    Return ```python your_code_here ```with NO other texts,
    your code:
    """

    name: str = "SimpleWriteTest"

    async def run(self, context: str, k: int = 3):
        prompt = self.PROMPT_TEMPLATE.format(context=context, k=k)

        rsp = await self._aask(prompt)

        code_text = parse_code(rsp)

        return code_text


class SimpleTester(Role):
    name: str = "Bob"
    profile: str = "SimpleTester"

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.set_actions([SimpleWriteTest])
        # self._watch([SimpleWriteCode])
        self._watch([SimpleWriteCode, SimpleWriteReview])  # feel free to try this too

    async def _act(self) -> Message:
        logger.info(f"{self._setting}: to do {self.rc.todo}({self.rc.todo.name})")
        todo = self.rc.todo

        # context = self.get_memories(k=1)[0].content # use the most recent memory as context
        context = self.get_memories()  # use all memories as context

        code_text = await todo.run(context, k=5)  # specify arguments
        msg = Message(content=code_text, role=self.profile, cause_by=type(todo))

        return msg


class SimpleWriteReview(Action):
    PROMPT_TEMPLATE: str = """
    Context: {context}
    Review the test cases and provide one critical comments:
    """

    name: str = "SimpleWriteReview"

    async def run(self, context: str):
        prompt = self.PROMPT_TEMPLATE.format(context=context)

        rsp = await self._aask(prompt)

        return rsp


class SimpleReviewer(Role):
    name: str = "Charlie"
    profile: str = "SimpleReviewer"

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.set_actions([SimpleWriteReview])
        self._watch([SimpleWriteTest])


async def main(
    idea: str = "write a function that calculates the product of a list",
    investment: float = 3.0,
    n_round: int = 5,
    add_human: bool = False,
):
    logger.info(idea)

    team = Team()
    team.hire(
        [
            SimpleCoder(),
            SimpleTester(),
            SimpleReviewer(is_human=add_human),
        ]
    )

    team.invest(investment=investment)
    team.run_project(idea)
    await team.run(n_round=n_round)
    # 最后这两句可以合成一句:await team.run(n_round=n_round, idea=idea)


if __name__ == "__main__":
    fire.Fire(main)

1.2.6 运行过程及结果展示

(1)用户消息输入,SimpleCoder开始动作,写出代码

在这里插入图片描述

(2)SimpleTester 接收到 SimpleCoder 写完的代码,开始写测试用例。

在这里插入图片描述

(3)SimpleReviewer 接收到 SimpleTester 写的测试用例,开始审核并给出修改意见

在这里插入图片描述

(4)SimpleTester 接收到 SimpleReviewer 的修改意见,开始优化测试用例。
在这里插入图片描述
(5)SimpleReviewer 接收到 SimpleTester 优化后的测试用例,进行审核并再次给出修改意见
在这里插入图片描述
(6)SimpleTester 和 SimpleReviewer 之间循环交互 n 次

2. 总结

通过本节内容,学习了MetaGPT中Team组件的原理与使用方法。

Team 组件就是在原来 Environment 组件的基础上进行封装,增加了一个invest来控制整体成本。其主要函数为 hireinvestrun

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1479190.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

每日一练:LeeCode-701、二叉搜索树中的插入操作【二叉搜索树+DFS+全搜】

本文是力扣 每日一练:LeeCode-701、二叉搜索树中的插入操作【二叉搜索树DFS全搜】学习与理解过程,本文仅做学习之用,对本题感兴趣的小伙伴可以出门左拐LeeCode。 给定二叉搜索树(BST)的根节点 root 和要插入树中的值 …

机器学习图像识别如何处理标签以外的图像?

机器学习图像识别技术是一种基于人工智能的图像处理方法,它通过训练大量的图像数据集来让计算机学习如何识别和分类图像。在图像识别任务中,我们通常需要对图像进行标注和分类,以便让计算机能够从中学习。但是,有时候我们可能会遇…

减少页面加载时间:提升用户体验的关键

✨✨ 祝屏幕前的您天天开心,每天都有好运相伴。我们一起加油!✨✨ 🎈🎈作者主页: 喔的嘛呀🎈🎈 目录 引言 一、为什么页面加载时间重要? 二、如何减少页面加载时间? …

集中和离散

数据分类&#xff1a;定性(分类&#xff0c;顺序)&#xff0c;定量(数值) 分类&#xff1a;男&#xff0c;女 顺序&#xff1a;高&#xff0c;中&#xff0c;低 数值&#xff1a;可计算的数字 数据等级&#xff1a;分类<顺序<数值。高级数据可以用低级数据&#xff0c…

day09_面向对象_构造方法_封装

今日内容 零、 复习昨日 一、构造方法 二、重载 三、封装 零、 复习昨日 1 类和对象是什么关系? 类是模板(原材料)对象是具体实例(成品)类创建出对象 2 类中有什么?(类的成员) 成员属性(成员变量), 成员方法 3 创建对象的语法? 类名 对象名 new 类名(); 4 调用对象属性,方法…

音频筑基:CD还是HiRes?高清音频分类一文说透

音频筑基&#xff1a;CD还是HiRes&#xff1f;高清音频分类一文说透 前言音乐品质分类相关资料 前言 音频信号中&#xff0c;经常遇到高清音乐、无损音质、CD、HiRes等说法&#xff0c;本文主要在纯数字信号级别&#xff0c;从音源分类和编码质量两个维度&#xff0c;做一个分析…

[AutoSar]BSW_Com06 CAN报文应用层到Can总线的函数调用

目录 关键词平台说明一、背景二、PDU转换三、函数调用 关键词 嵌入式、C语言、autosar、OS、BSW 平台说明 项目ValueOSautosar OSautosar厂商vector &#xff0c;芯片厂商TI 英飞凌编程语言C&#xff0c;C编译器HighTec (GCC)autosar版本4.3.X >>>>>回到总目…

《商用密码应用安全性评估管理办法》解读

根据《中华人民共和国密码法》&#xff08;以下简称《密码法》&#xff09;、《商用密码管理条例》&#xff08;以下简称《条例》&#xff09;等法律法规&#xff0c;国家密码管理局研究制定了《商用密码应用安全性评估管理办法》&#xff08;国家密码管理局令第3号&#xff09…

Vue页面更新后刷新页面不会渲染解决

小编今天犯了个很低级的错误&#xff0c;导致VUE页面刷新样式不会更新的问题&#xff01; 解决方法&#xff1a;查看你的路由路径大小写是否正确&#xff01;小编是犯了这种错误&#xff0c;特此分享下&#xff01;

Vite 构建的 Vue3 项目如何整合 Monaco Editor 代码编辑器

目录 &#x1f981; 一. 前言&#x1f981; 二. 探索过程2.1 安装2.2 配置 Monaco Editor2.3 编写 Monaco Editor 代码编辑器2.3.1 创建 Coding Editor 组件2.3.2 父组件使用 CodingEditor 组件 2.4 效果展示 三. 总结 &#x1f981; 一. 前言 各位好&#xff01;我是&#x1…

Unity 预制体与变体

预制体作用&#xff1a; 更改预制体&#xff0c;则更改全部的以预制体复制出的模型。 生成预制体&#xff1a; 当你建立好了一个模型&#xff0c;从层级拖动到项目中即可生成预制体。 预制体复制模型&#xff1a; 将项目中的预制体拖动到层级中即可复制。或者选择物体复制粘贴。…

2024年2月总结及随笔之平平安安过大年

1. 回头看 日更坚持了425天。 读《千脑智能》开更并更新完成 读《十堂极简人工智能课》开更并更新完成 读《人工不智能&#xff1a;计算机如何误解世界》开更并持续更新中 2023年至2024年2月底累计码字898882字&#xff0c;累计日均码字2115字。 2024年2月码字84475字&am…

【机器学习:Recommendation System】推荐系统

推荐系统&#xff08;或推荐系统&#xff09;是一类机器学习&#xff0c;它使用数据来帮助预测、缩小范围并在呈指数级增长的选项中找到人们正在寻找的内容。 【机器学习&#xff1a;Recommendation System】推荐系统 什么是推荐系统&#xff1f;用例和应用电子商务与零售&…

基于springboot+vue的常规应急物资管理系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

动态规划|【路径问题】礼物的最大价值(LCR 166.珠宝的最高价值)

目录 题目 题目解析 思路 1.状态表示 2.状态转移方程 3.初始化 4.填表顺序 5.返回值 代码 题目 LCR 166. 珠宝的最高价值 &#xff08;现在leetcode上面是这个题&#xff09;这个题跟下面这个题叙述方式一样&#xff0c;就拿下面这个 题来讲解&#xff09; 题目描述&…

基于语义解析的KBQA——代码和论文详细分析

根据论文&#xff1a;Semantic Parsing on Freebase from Question-Answer Pairs&#xff0c;分析其代码和步骤&#xff0c;以加强对这一流程的深入理解&#xff0c;重点关注模型的输入、输出和具体方法。 前言 提供阅读本文的前提知识&#xff0c;引用自Semantic Parsing on…

Python利用pandas对数据进行特定排序

更多Python学习内容&#xff1a;ipengtao.com 在数据分析和处理过程中&#xff0c;排序是一项常见而重要的操作。Python中的pandas库提供了丰富的功能&#xff0c;可以方便地对数据进行各种排序操作。本文将详细介绍如何利用pandas对数据进行特定排序&#xff0c;包括基本排序、…

ODOO12设置收发邮件服务器教程

一、设置-技术 二、设置–技术–发件服务器 信息填写完整后&#xff0c;点击‘测试连接’&#xff0c;若提示成功&#xff0c;则发件服务器设置成功。 三、设置–技术–收件服务器 四、设置–参数–系统参数 修改之前的email系统参数&#xff1a; mail.catchall.alias: 收件服…

vivo 在离线混部探索与实践

作者&#xff1a;来自 vivo 互联网服务器团队 本文根据甘青、黄荣杰老师在“2023 vivo开发者大会"现场演讲内容整理而成。 伴随 vivo 互联网业务的高速发展&#xff0c;数据中心的规模不断扩大&#xff0c;成本问题日益突出。在离线混部技术可以在保证服务质量的同时&…

在 Linux 环境下安装 Kibana

目录 一、Kibana 是什么 二、在 Linux 环境下安装 Kibana 1、下载安装包 2、解压 3、修改 Kibana的配置文件 config/kibana.yml 4、启动 5、浏览器登录 Kibana 6、测试查询 一、Kibana 是什么 Kibana 是通向 Elastic 产品集的窗口。 它可以在 Elasticsearch 中对数据进…