MetaGPT
MetaGPT: 多智能体框架
使 GPT 以软件公司的形式工作,协作处理更复杂的任务
MetaGPT一行要求作为输入,输出用户故事 / 竞品分析 / 需求 / 数据结构 / APIs / 文件等
MetaGPT内部包括产品经理 / 架构师 / 项目经理 / 工程师,它提供了一个软件公司的全过程与精心调配的SOP
Code = SOP(Team)
是核心哲学。将SOP具象化,并且用于LLM构成的团队
快速开始
安装稳定版本
1 | pip install metagpt |
以开发模式安装
根据自己的独特需求定制框架、尝试新的想法或者利用框架创建复杂功能(如新颖的记忆机制)的开发者和研究者。
1 | git clone https://github.com/geekan/MetaGPT.git |
安装子模块
- RAG,
pip install 'metagpt[rag]'
. 用途:用于基于 RAG(Retrieval-Augmented Generation,检索增强生成)的系统,结合多个 LLM(大语言模型)和向量存储技术。 - OCR,
pip install 'metagpt[ocr]'
. 用途:用于光学字符识别(OCR)任务,识别和提取图像中的文本。 - search-ddg,
pip install 'metagpt[search-ddg]'
. 用途:用于 DuckDuckGo 搜索功能。 - search-google,
pip install 'metagpt[search-google]'
. 用途:用于与 Google API(如 Google 搜索 API)进行交互。 - selenium,
pip install 'metagpt[selenium]'
. 用途:用于自动化浏览器操作和网页抓取。
配置大模型API
OpenAI API
其他大模型的API配置过程是相同的。可以通过设置 config2.yaml 完成配置
使用config2.yaml
- 在当前工作目录中创建一个名为
config
的文件夹,并在其中添加一个名为config2.yaml
的新文件。 - 将示例config2.yaml文件的内容复制到新文件中。
- 将自己的值填入文件中:
1 | llm: |
注意: MetaGPT将按照以下优先顺序读取:
~/.metagpt/config2.yaml > config/config2.yaml
一句话需求的软件开发
首先,导入已实现的角色
1 | import asyncio |
然后,初始化公司团队,配置对应的智能体,设置对应的预算以及提供一个写一个小游戏的需求。
1 | async def startup(idea: str): |
最后,运行并得到生成的游戏代码!
1 | await startup(idea="write a cli blackjack game") # blackjack: 二十一点 |
具有单一动作的智能体
假设我们想用自然语言编写代码,并想让一个智能体为我们做这件事。让我们称这个智能体为 SimpleCoder,我们需要两个步骤来让它工作:
- 定义一个编写代码的动作
- 为智能体配备这个动作
定义动作
在 MetaGPT 中,类 Action
是动作的逻辑抽象。用户可以通过简单地调用 self._aask 函数令 LLM 赋予这个动作能力,即这个函数将在底层调用 LLM api。
定义了一个 SimpleWriteCode
子类 Action
。虽然它主要是一个围绕提示和 LLM 调用的包装器,但这个 Action
抽象更直观。在下游和高级任务中,使用它作为一个整体感觉更自然,而不是分别制作提示和调用 LLM,尤其是在智能体的框架内。
1 | from metagpt.actions import Action # 导入 Action 基类 |
定义角色
在 MetaGPT 中,Role
类是智能体的逻辑抽象。一个 Role
能执行特定的 Action
,拥有记忆、思考并采用各种策略行动。基本上,它充当一个将所有这些组件联系在一起的凝聚实体。
在这个示例中,r嗯创建了一个 SimpleCoder
,它能够根据人类的自然语言描述编写代码。步骤如下:
- 我们为其指定一个名称和配置文件。
- 我们使用
self._init_action
函数为其配备期望的动作SimpleWriteCode
。 - 我们覆盖
_act
函数,其中包含智能体具体行动逻辑。我们写入,我们的智能体将从最新的记忆中获取人类指令,运行配备的动作,MetaGPT将其作为待办事项 (self.rc.todo
) 在幕后处理,最后返回一个完整的消息。
1 | from metagpt.roles import Role |
运行你的角色
现在我们可以让我们的智能体开始工作,只需初始化它并使用一个起始消息运行它。
1 | import asyncio |
具有多个动作的智能体
智能体的力量,或者说Role
抽象的惊人之处,在于动作的组合(以及其他组件,比如记忆,但我们将把它们留到后面的部分)。通过连接动作,我们可以构建一个工作流程,使智能体能够完成更复杂的任务。
假设现在我们不仅希望用自然语言编写代码,而且还希望生成的代码立即执行。一个拥有多个动作的智能体可以满足我们的需求。让我们称之为RunnableCoder
,一个既写代码又立即运行的Role
。我们需要两个Action
:SimpleWriteCode
和 SimpleRunCode
定义动作
首先,定义 SimpleWriteCode
。我们将重用上面创建的那个。
接下来,定义 SimpleRunCode
。如前所述,从概念上讲,一个动作可以利用LLM,也可以在没有LLM的情况下运行。在SimpleRunCode
的情况下,LLM不涉及其中。我们只需启动一个子进程来运行代码并获取结果。我们希望展示的是,对于动作逻辑的结构,我们没有设定任何限制,用户可以根据需要完全灵活地设计逻辑。
1 | class SimpleRunCode(Action): |
定义角色
与定义单一动作的智能体没有太大不同!让我们来映射一下:
用
self.set_actions
初始化所有Action
指定每次
Role
会选择哪个Action
。我们将react_mode
设置为 “by_order”,这意味着Role
将按照self.set_actions
中指定的顺序执行其能够执行的Action
(有关更多讨论,请参见 思考和行动)。在这种情况下,当Role
执行_act
时,self.rc.todo
将首先是SimpleWriteCode
,然后是SimpleRunCode
。标准ReAct模式(默认)
先思考,再行动,直到角色认为是时候停止。这是ReAct论文中的标准思考-行动循环,即交替进行任务解决中的思考和行动,即
_think
->_act
->_think
->_act
-> …每次在
_think
期间,Role
将选择一个Action
来响应当前的观察,并在_act
阶段运行所选的Action
。然后,操作输出将成为下一步在_think
中再次使用的新观察。我们在_think
中动态地使用LLM来选择动作,这使得该模式具有良好的通用性。按顺序执行
每次按照\set_actions中定义的顺序执行可行的操作,即
_act
(Action1) ->_act
(Action2) ->_act
(Action3) -> …这种模式适合于确定性的标准操作程序(SOP),在这种情况下我们确切地知道
Role
应该采取什么行动以及它们的顺序。使用这种模式,你只需要定义Action
,框架将接管管道构建。覆盖
_act
函数。Role
从上一轮的人类输入或动作输出中检索消息,用适当的Message
内容提供当前的Action
(self.rc.todo
),最后返回由当前Action
输出组成的Message
。
1 | class RunnableCoder(Role): |
运行你的角色
现在可以让你的智能体开始工作,只需初始化它并使用一个起始消息运行它。
1 | import asyncio |
使用记忆
记忆是智能体的核心组件之一。智能体需要记忆来获取做出决策或执行动作所需的基本上下文,还需要记忆来学习技能或积累经验。
1.理解MetaGPT中记忆的概念
2.如何添加或检索记忆
什么是记忆
在MetaGPT中,Memory
类是智能体的记忆的抽象。当初始化时,Role
初始化一个Memory
对象作为self.rc.memory
属性,它将在之后的_observe
中存储每个Message
,以便后续的检索。简而言之,Role
的记忆是一个含有Message
的列表。
检索记忆
当需要获取记忆时(获取LLM输入的上下文),你可以使用self.get_memories
。函数定义如下:
1 | def get_memories(self, k=0) -> list[Message]: |
1 | async def _act(self) -> Message: |
添加记忆
可以使用self.rc.memory.add(msg)
添加记忆,,其中msg
必须是Message
的实例。请查看上述的代码片段以获取示例用法。
建议在定义_act
逻辑时将Message
的动作输出添加到Role
的记忆中。通常,Role
需要记住它先前说过或做过什么,以便采取下一步的行动。
创建和使用工具
在 MetaGPT 中创建工具是一个直接的过程,涉及创建自己的函数或类,并将它们放在 metagpt/tools/libs
目录下。
创建工具的步骤
创建预提供的函数或类:
编写专门用于与外部环境进行特定交互的函数或类,并将它们放置在
metagpt/tools/libs
目录中。使用谷歌风格的文档字符串(Docstring):
为每个函数或类配备谷歌风格的文档字符串。这作为一个简洁而全面的参考资料,详细说明其用途、输入参数和预期输出。
应用@register_tool装饰器:
使用
@register_tool
装饰器以确保在工具注册表中准确注册。这个装饰器简化了函数或类与DataInterpreter
的集成。
自定义计算阶乘的工具
在
metagpt/tools/libs
中创建一个你自己的函数,假设它是calculate_factorial.py
,并添加装饰器@register_tool
以将其注册为工具1
2
3
4
5
6
7
8
9
10
11
12
13# metagpt/tools/libs/calculate_factorial.py
import math
from metagpt.tools.tool_registry import register_tool
# 使用装饰器注册工具
def calculate_factorial(n):
"""
计算非负整数的阶乘
"""
if n < 0:
raise ValueError("输入必须是非负整数")
return math.factorial(n)在数据解释器
DataInterpreter
中使用工具1
2
3
4
5
6
7
8
9
10
11
12# main.py
import asyncio
from metagpt.roles.di.data_interpreter import DataInterpreter
from metagpt.tools.libs import calculate_factorial
async def main(requirement: str):
role = DataInterpreter(tools=["calculate_factorial"]) # 集成工具
await role.run(requirement)
if __name__ == "__main__":
requirement = "请计算 5 的阶乘"
asyncio.run(main(requirement))
注意:
- 别忘了为你的函数编写文档字符串(docstring),这将有助于
DataInterpreter
选择合适的工具并理解其工作方式。 - 在注册工具时,工具的名称就是函数的名称。
- 在运行 DataInterpreter 之前,记得从
metagpt.tools.libs
导入你的calculate_factorial
模块,以确保该工具已被注册。
人类介入
在一些实际情境中,我们确实希望人类介入,无论是为了项目的质量保证,在关键决策中提供指导,还是在游戏中扮演角色。
在LLM和人类之间切换
最初,LLM扮演 SimpleReviewer
的角色。假设我们想对更好地控制审阅过程,我们可以亲自担任这个Role
。这只需要一个开关:在初始化时设置 is_human=True
。代码变为:
1 | team.hire( |
我们作为人类充当 SimpleReviewer
,现在与两个基于LLM的智能体 SimpleCoder
和 SimpleTester
进行交互。我们可以对SimpleTester
写的单元测试进行评论,比如要求测试更多边界情况,让SimpleTester
进行改写。这个切换对于原始的SOP和 Role
定义是完全不可见的(无影响),这意味着可以应用于任何场景。
每次轮到我们回应时,运行过程将暂停并等待我们的输入。只需输入我们想要输入的内容,然后就将消息发送给其他智能体了!
集成开源LLM
由于上述部署为API接口,因此通过修改配置文件config/config2.yaml
进行生效。
openai兼容接口
如 LLaMA-Factory、FastChat、vllm部署的openai兼容接口
config/config2.yaml
1 | llm: |
ollama api接口
如通过ollama部署的模型服务
config/config2.yaml
1 | llm: |
ollama chat接口的完整路由http://127.0.0.1:11434/api/chat
,base_url
只需要配置到http://127.0.0.1:11434/api
,剩余部分由OllamaLLM
补齐。model
为请求接口参数model
的实际值。
为角色或动作配置不同LLM
MetaGPT允许为团队中的不同Role和Action使用不同的LLM,这极大地增强了团队互动的灵活性和现实性,使得每个Role可以根据其特定的需求和背景,以及每个Action的特点,选择最合适的LLM。通过这种方式,可以更精细地控制对话的质量和方向,从而创造出更加丰富和真实的交互体验。
以下是设置步骤:
- 定义配置:使用默认配置,或者从
~/.metagpt
目录中加载自定义配置。 - 分配配置:将特定的LLM配置分配给Role和Action。配置的优先级:Action config > Role config > Global config(config in config2.yaml)。
- 团队交互:创建一个带有环境的团队,开始交互。
示例
考虑一个美国大选的现场直播环境,创建三个Role:A、B和C。A和B是两个候选人,C是一个选民。
定义配置
可以使用默认配置,为不同的Role和Action配置LLM,也可以在~/.metagpt
目录中加载自定义配置。
1 | from metagpt.config2 import Config |
分配配置
创建Role和Action,并为其分配配置。
1 | from metagpt.roles import Role |
请注意,对于关注的Action而言,配置的优先级为:Action config > Role config > Global config 。不同Role和Action的配置情况如下:
Action和角色的撕裂
Action of interest | Global config | Role config | Action config | Effective config for the Action |
---|---|---|---|---|
a1 | gpt4 | gpt4 | gpt4t | gpt4t |
a2 | gpt4 | gpt35 | unspecified | gpt35 |
a3 | gpt4 | unspecified | unspecified | gpt4 |
团队交互
创建一个带有环境的团队,并使其进行交互。
1 | import asyncio |
完整代码和对应配置示例
默认配置: ~/.metagpt/config2.yaml
1 | llm: |
自定义配置: ~/.metagpt/gpt-4.yaml
1 | llm: |
python
1 | from metagpt.config2 import Config |