MetaGPT架构拆解:如何用一个Agent模拟整个软件公司的协作流程

关键词

MetaGPT、多代理系统、软件开发生命周期、AI协作、大语言模型应用、智能体架构、软件工程自动化

摘要

本文深入剖析MetaGPT架构,探讨其如何通过单一AI代理模拟完整软件公司的协作流程。我们将从概念基础、理论框架、架构设计、实现机制、实际应用等多个维度进行系统化分析。通过第一性原理思考和结构化推理,本文揭示了MetaGPT如何突破传统AI应用边界,实现从需求分析到代码生成、测试部署的全流程自动化。文章不仅包含理论深度的探讨,还提供了实际代码实现、算法分析和最佳实践指导,旨在为AI驱动的软件工程变革提供全面的技术洞见。


1. 概念基础

核心概念

MetaGPT代表了人工智能在软件工程领域的突破性应用,它的核心理念是将一个大型语言模型(LLM)训练和配置为能够模拟整个软件公司不同角色协作的智能体系统。这个概念打破了传统AI应用在单一任务上的局限性,实现了从需求分析、系统设计、代码实现到测试部署的全流程智能化。

在深入理解MetaGPT之前,我们需要明确几个关键概念:

  1. 智能体(Agent):在人工智能领域,智能体是指能够感知环境、做出决策并采取行动的实体。MetaGPT中的智能体被赋予了多种角色能力,能够模拟软件公司中不同职位的工作。

  2. 多代理系统(Multi-Agent System):虽然MetaGPT被描述为"一个Agent",但它内部实际上实现了多代理的协作逻辑。每个代理代表软件公司中的一个角色,如产品经理、架构师、工程师、测试人员等。

  3. 软件开发生命周期(SDLC):这是软件从概念到退役的整个过程,包括需求收集、设计、实现、测试、部署和维护等阶段。MetaGPT的目标就是自动化这一全过程。

  4. 大语言模型(LLM):MetaGPT的核心技术基础,它通过大量文本数据训练,能够理解和生成人类语言,进而执行各种复杂任务。

  5. 提示工程(Prompt Engineering):这是设计和优化输入提示以引导LLM产生期望输出的技术。MetaGPT巧妙地利用提示工程来模拟不同角色的思维模式和工作流程。

问题背景

传统的软件开发过程面临着多重挑战,这些挑战推动了MetaGPT这样的创新解决方案的出现:

  1. 人力资源瓶颈:高质量的软件开发需要多种专业技能的人才,包括产品经理、UI/UX设计师、系统架构师、前端/后端开发人员、测试工程师等。组建这样一支团队不仅成本高昂,而且人才获取困难。

  2. 协作效率问题:即使拥有完整的团队,不同角色之间的沟通和协作也常常面临挑战。信息传递的失真、决策过程的延迟、不同角色理解的差异等问题都会影响开发效率和产品质量。

  3. 开发周期冗长:传统软件开发从需求到上线通常需要数周甚至数月时间,难以快速响应市场变化和用户需求。

  4. 知识传承困难:软件开发中的经验和最佳实践往往分散在不同团队成员的头脑中,难以系统化地传承和复用。

  5. 小规模团队和独立开发者的限制:对于资源有限的小型团队或独立开发者来说,很难覆盖软件开发的所有方面,导致产品在某些方面存在短板。

这些问题构成了MetaGPT诞生的背景,也为其提供了明确的价值定位方向。

问题描述

MetaGPT旨在解决以下核心问题:

  1. 如何在单一AI系统中整合多种专业角色的能力:传统AI应用通常专注于单一任务或角色,而MetaGPT需要在一个系统中实现产品经理、架构师、开发人员、测试人员等多种角色的功能。

  2. 如何模拟真实软件公司的协作流程:软件公司的运作不仅仅是各个角色独立工作的简单叠加,而是一个复杂的协作过程。MetaGPT需要模拟这种协作,包括角色间的沟通、决策流程、工作移交等。

  3. 如何保证输出的质量和一致性:当AI系统承担多个角色时,如何确保不同"角色"产生的输出是一致的、高质量的,并且能够无缝集成,这是一个重大挑战。

  4. 如何处理软件开发中的不确定性和迭代过程:真实的软件开发过程充满不确定性,需求可能变更,设计可能需要调整,代码可能需要重构。MetaGPT需要能够处理这种动态性和迭代性。

  5. 如何与现有开发工具和流程集成:为了实用化,MetaGPT需要能够与现有的开发工具、版本控制系统、CI/CD流程等集成,而不是创造一个完全孤立的系统。

问题解决

MetaGPT通过以下创新思路解决上述问题:

  1. 角色提示工程(Role Prompting):MetaGPT为每个软件公司角色设计专门的提示模板,这些模板包含该角色的职责、工作方式、思维模式等信息,使LLM能够"扮演"不同角色。

  2. 结构化工作流程:MetaGPT将软件开发生命周期分解为一系列结构化的步骤和任务,每个任务由特定的"角色"负责,并有明确的输入和输出。

  3. 上下文管理机制:为了确保不同角色之间的一致性,MetaGPT实现了复杂的上下文管理机制,确保所有角色都基于相同的项目信息和决策进行工作。

  4. 迭代反馈循环:MetaGPT内置了反馈和迭代机制,允许后期阶段的输出(如测试结果)影响前期阶段(如代码修改或设计调整)。

  5. 工具集成能力:通过API和插件机制,MetaGPT能够与外部工具和系统集成,如代码仓库、项目管理工具、测试框架等。

边界与外延

在探讨MetaGPT时,明确其边界和外延是非常重要的:

边界

  • MetaGPT目前主要适用于中小型软件项目,对于超大规模、高度复杂的系统,其能力仍有限制。
  • 它依赖于高质量的输入需求,模糊或矛盾的需求会导致输出质量下降。
  • MetaGPT生成的代码和设计仍需人类专家审查,特别是在安全性和性能关键的应用场景中。
  • 它在处理全新领域或需要深厚领域专业知识的项目时可能面临挑战。

外延

  • MetaGPT的理念可以扩展到软件开发以外的领域,如内容创作、市场营销、教育等任何需要多角色协作的领域。
  • 随着LLM技术的进步,MetaGPT的能力边界将不断扩展,能够处理更复杂的项目和任务。
  • 可以设想MetaGPT与人类开发者混合协作的模式,AI处理常规任务,人类专注于创新和关键决策。
  • MetaGPT可能改变软件教育的方式,通过模拟真实的开发环境,为学习者提供实践机会。

2. 理论框架

第一性原理推导

要理解MetaGPT的理论基础,我们可以从第一性原理出发进行推导:

  1. 基本公理1:软件开发生命周期(SDLC)可以分解为一系列明确的角色和任务。

    软件公司的运作不是混沌的,而是有组织的结构。无论公司规模大小,我们都可以识别出一些基本角色(如产品经理、设计师、开发人员、测试人员)和基本流程(需求分析→设计→实现→测试→部署)。

  2. 基本公理2:每个角色的工作可以通过其输入、处理过程和输出进行形式化描述。

    例如,产品经理的输入是用户需求和市场信息,处理过程是需求分析和优先级排序,输出是产品需求文档(PRD)。这种形式化描述为自动化提供了可能性。

  3. 基本公理3:大语言模型(LLM)具有理解上下文、遵循指令和生成结构化输出的能力。

    现代LLM(如GPT-4、Claude等)已经展示了在各种任务上的出色能力,包括写作、编程、分析等,这为模拟不同角色提供了技术基础。

  4. 推论1:如果我们能为每个SDLC角色创建合适的指令和上下文,LLM就可以模拟该角色的工作。

    这就是"角色提示工程"的理论基础。通过精心设计的提示,我们可以引导LLM采取特定角色的思维模式和工作方式。

  5. 推论2:如果我们能够组织多个角色模拟之间的交互和信息流,就可以模拟整个软件公司的协作流程。

    这需要设计一种机制,确保一个角色的输出能够成为另一个角色的输入,并且整体流程符合SDLC的逻辑。

  6. 推论3:通过反馈循环和迭代机制,这个模拟系统可以不断优化其输出,接近甚至达到人类团队的工作质量。

    真实的软件开发是一个迭代过程,测试结果会导致代码修改,用户反馈会导致需求调整。将这种迭代机制纳入系统是提高输出质量的关键。

通过这些第一性原理的推导,我们可以看到MetaGPT的设计并不是凭空想象,而是建立在对软件工程和AI能力的深刻理解之上。

数学形式化

为了更精确地描述MetaGPT的工作原理,我们可以引入一些数学形式化。

首先,我们将软件开发生命周期定义为一个有限状态机:

SDLC=(S,s0,F,δ,O)SDLC = (S, s_0, F, \delta, O)SDLC=(S,s0,F,δ,O)

其中:

  • SSS 是所有可能的状态集合(如需求收集、系统设计、编码、测试等)
  • s0s_0s0 是初始状态(通常是项目启动或需求收集)
  • F⊆SF \subseteq SFS 是最终状态集合(通常是部署或发布)
  • δ:S×A→S\delta: S \times A \rightarrow Sδ:S×AS 是状态转移函数,其中 AAA 是动作集合
  • O:S→OO: S \rightarrow \mathcal{O}O:SO 是输出函数,为每个状态关联一个输出,O\mathcal{O}O 是可能的输出集合

接下来,我们定义角色集合:

R={r1,r2,...,rn}R = \{r_1, r_2, ..., r_n\}R={r1,r2,...,rn}

其中每个角色 rir_iri 可以表示为:

ri=(Ki,Pi,Ti)r_i = (K_i, P_i, T_i)ri=(Ki,Pi,Ti)

这里:

  • KiK_iKi 是角色 rir_iri 的知识集合(通过提示提供)
  • PiP_iPi 是角色 rir_iri 的处理过程(通过提示中的指令定义)
  • TiT_iTi 是角色 rir_iri 负责的任务集合

每个任务 t∈Tit \in T_itTi 可以表示为一个函数:

t:I×C→Ot: I \times C \rightarrow Ot:I×CO

其中:

  • III 是任务输入集合
  • CCC 是上下文集合
  • OOO 是任务输出集合

现在,我们可以将MetaGPT的核心工作流程定义为:

  1. 初始化项目上下文 C0C_0C0
  2. 对于每个状态 sk∈Ss_k \in SskS(从 s0s_0s0 开始):
    a. 选择合适的角色 rir_iri 和任务 t∈Tit \in T_itTi
    b. 获取输入 IkI_kIk(可能是前序任务的输出或外部输入)
    c. 应用任务函数 t(Ik,Ck)=Okt(I_k, C_k) = O_kt(Ik,Ck)=Ok
    d. 更新上下文 Ck+1=update(Ck,Ok)C_{k+1} = update(C_k, O_k)Ck+1=update(Ck,Ok)
    e. 通过状态转移函数 δ(sk,ak)=sk+1\delta(s_k, a_k) = s_{k+1}δ(sk,ak)=sk+1 转移到下一状态,其中 aka_kak 是基于 OkO_kOk 选择的动作
  3. 当到达 sf∈Fs_f \in FsfF 时,终止并输出最终结果

这个形式化描述为我们理解和实现MetaGPT提供了精确的框架。接下来,我们可以进一步定义上下文更新函数:

Ck+1=Ck∪{Ok}∪D(Ok,Ck)C_{k+1} = C_k \cup \{O_k\} \cup D(O_k, C_k)Ck+1=Ck{Ok}D(Ok,Ck)

其中 D(Ok,Ck)D(O_k, C_k)D(Ok,Ck) 是从输出 OkO_kOk 和现有上下文 CkC_kCk 中推导出的新信息。

为了处理迭代和反馈,我们引入一个评估函数 EEE,用于评估输出质量:

E(Ok,G)=[0,1]E(O_k, G) = [0, 1]E(Ok,G)=[0,1]

其中 GGG 是目标或需求集合。如果 E(Ok,G)<θE(O_k, G) < \thetaE(Ok,G)<θθ\thetaθ 是质量阈值),则系统返回到适当的前序状态进行修正。

这些数学形式化不仅帮助我们精确理解MetaGPT的工作原理,也为实现和优化这个系统提供了指导。

理论局限性

尽管MetaGPT展示了巨大的潜力,但我们也需要认识到其理论局限性:

  1. 上下文窗口限制:当前的LLM有固定的上下文窗口大小,这限制了MetaGPT能够处理的项目复杂度。随着项目规模增长,上下文管理变得越来越困难。

  2. 推理深度限制:虽然LLM能够进行一定程度的逻辑推理,但它们的推理能力在深度和复杂度上仍有限制,特别是在需要深入数学分析或复杂系统设计的场景中。

  3. 知识截止日期:LLM的训练数据有截止日期,这意味着它们可能不了解最新的技术趋势、框架或安全问题。

  4. 缺乏真正的理解:LLM生成的内容是基于统计模式而非真正的理解,这可能导致在处理全新或高度特异性问题时出现错误或不切实际的方案。

  5. 创新能力限制:虽然MetaGPT可以组合现有概念,但在真正的创新性设计或突破性解决方案方面,其能力仍不及人类专家团队。

  6. 验证和确认挑战:目前还没有完善的方法来自动验证MetaGPT生成的所有输出的正确性和完整性,特别是在复杂系统中。

理解这些局限性对于合理设定期望值、设计有效的人类-AI协作模式以及指导未来研究方向至关重要。

竞争范式分析

在AI驱动的软件开发领域,MetaGPT并不是唯一的范式。让我们分析几种主要的竞争范式:

  1. 单任务AI工具

    • 例如:GitHub Copilot、CodeLlama等
    • 特点:专注于软件开发中的特定任务,如代码补全
    • 优势:简单易用,集成方便,针对特定任务优化
    • 劣势:功能有限,无法处理完整开发流程
  2. 专门化多代理系统

    • 例如:AutoGPT、BabyAGI等
    • 特点:多个代理协作,但不针对软件开发流程优化
    • 优势:灵活性高,可适用于多种任务
    • 劣势:缺乏软件开发领域的专业知识,效率和质量不稳定
  3. 低代码/无代码平台

    • 例如:OutSystems、Mendix等
    • 特点:通过可视化界面和预构建组件简化开发
    • 优势:快速开发,减少编码需求
    • 劣势:灵活性有限,难以处理复杂或高度定制化的需求
  4. 传统开发辅助工具

    • 例如:IDE插件、静态分析工具等
    • 特点:辅助开发者工作,但不主动承担任务
    • 优势:提高开发者效率,集成成熟
    • 劣势:功能有限,需要人工驱动
  5. MetaGPT范式

    • 特点:模拟完整软件公司协作流程,多角色集成
    • 优势:端到端解决方案,模拟真实开发流程,产出完整
    • 劣势:复杂度高,需要仔细管理,仍在发展阶段

通过比较,我们可以看到MetaGPT的独特价值在于它提供了一种端到端的解决方案,不仅处理编码任务,还覆盖了需求分析、系统设计、测试等完整流程。同时,它通过模拟真实软件公司的角色和协作方式,使得产出更符合实际工程实践。


3. 架构设计

系统分解

MetaGPT的架构设计体现了"分而治之"的思想,将复杂的软件开发流程分解为可管理的组件和模块。系统可以从以下几个维度进行分解:

按功能模块分解
  1. 核心协调引擎:负责整个系统的调度、状态管理和流程控制。这是MetaGPT的"大脑",决定哪个角色在什么时候执行什么任务。

  2. 角色库:包含软件公司各种角色的定义,每个角色都有专门的提示模板、工作流程和输出格式。

  3. 上下文管理模块:负责项目信息的存储、检索和更新,确保所有角色都基于一致的信息工作。

  4. 工具集成层:提供与外部工具(如Git、Docker、测试框架等)的接口,使MetaGPT能够与现实开发环境交互。

  5. 输出验证模块:负责检查和验证各阶段的输出质量,确保符合标准和要求。

  6. 迭代优化模块:根据反馈和评估结果,决定是否需要回到前序阶段进行修改和优化。

按SDLC阶段分解
  1. 需求分析阶段

    • 市场调研组件
    • 用户需求收集组件
    • 需求分析和文档生成组件
  2. 系统设计阶段

    • 架构设计组件
    • 数据库设计组件
    • API设计组件
    • UI/UX设计组件
  3. 实现阶段

    • 代码生成组件
    • 代码审查组件
    • 重构优化组件
  4. 测试阶段

    • 测试计划生成组件
    • 测试用例设计组件
    • 自动化测试执行组件
    • 测试报告生成组件
  5. 部署阶段

    • 部署计划生成组件
    • 环境配置组件
    • CI/CD流程配置组件

组件交互模型

MetaGPT的组件交互模型模拟了真实软件公司的工作流程和信息流向。我们可以从几个层面来理解这个交互模型:

顺序交互流

这是最基本的交互模式,模拟了传统瀑布式开发流程:

部署工程师 测试人员 开发人员 架构师 产品经理 用户 部署工程师 测试人员 开发人员 架构师 产品经理 用户 初始需求 需求分析和PRD撰写 PRD和需求文档 系统设计和架构文档 设计文档和技术规范 代码实现 代码和开发文档 测试计划和执行 测试通过的产品 部署配置和执行 最终产品
迭代交互流

在实际开发中,流程往往不是线性的,而是有迭代和反馈:

通过

需求调整

设计修改

代码修复

测试问题

需求收集

系统设计

代码实现

测试

评估

部署

并行协作流

在复杂项目中,多个角色可能同时工作,并行处理不同任务:

质量保证

实现阶段

设计阶段

项目启动

产品经理
需求分析

系统架构师
架构设计

UI设计师
界面设计

数据库设计师
数据模型

前端开发
界面实现

后端开发
API开发

数据库开发
表结构和脚本

测试工程师
测试计划

测试工程师
测试执行

可视化表示

为了更直观地理解MetaGPT的架构,我们可以通过多种可视化方式进行展示。

整体架构图

工具与服务层

角色层

核心引擎层

用户界面层

Web/CLI界面

协调引擎

状态管理

工作流引擎

产品经理代理

架构师代理

开发者代理

测试工程师代理

部署工程师代理

大语言模型

上下文管理

版本控制系统

测试服务

部署服务

角色内部结构图

每个角色代理都有相似的内部结构,但配置不同:

角色代理

验证通过

验证失败

输入接口

角色提示模板

上下文检索

LLM交互

输出解析

验证

输出接口

上下文管理架构

上下文管理是MetaGPT的关键组件,确保所有角色共享一致的项目信息:

上下文类型

上下文管理层

上下文存储

信息检索

信息更新

元数据管理

项目上下文

需求上下文

设计上下文

代码上下文

测试上下文

产品经理

架构师

开发者

测试工程师

设计模式应用

MetaGPT的架构设计巧妙地应用了多种软件设计模式,这些模式帮助系统实现了高内聚、低耦合的目标:

  1. 策略模式(Strategy Pattern)

    • 应用:不同的角色代理可以看作是不同的策略,协调引擎根据当前阶段选择合适的策略执行。
    • 优势:使得角色可以独立变化和扩展,不影响系统其他部分。
  2. 模板方法模式(Template Method)

    • 应用:每个角色代理内部的工作流程是固定的(接收输入、检索上下文、调用LLM、解析输出、验证结果),但具体实现可以有所不同。
    • 优势:提供了统一的工作流框架,同时允许各角色有特定的实现细节。
  3. 命令模式(Command Pattern)

    • 应用:系统中的每个任务(如写PRD、设计架构、编写代码等)都可以被封装为命令对象。
    • 优势:支持任务队列、撤销操作和日志记录,这对于迭代开发非常重要。
  4. 观察者模式(Observer Pattern)

    • 应用:上下文管理模块是被观察者,各个角色代理是观察者。当上下文更新时,相关角色会收到通知。
    • 优势:确保各角色及时了解项目状态变化,保持信息一致性。
  5. 中介者模式(Mediator Pattern)

    • 应用:协调引擎作为中介者,管理各角色代理之间的交互,而不是让它们直接通信。
    • 优势:减少了角色间的直接依赖,简化了通信逻辑。
  6. 状态模式(State Pattern)

    • 应用:系统的行为取决于其所处的SDLC阶段(状态),每个阶段都有相应的行为和转换规则。
    • 优势:使得状态转换逻辑清晰,易于管理和扩展。
  7. 工厂模式(Factory Pattern)

    • 应用:角色工厂负责创建各种角色代理实例,根据需要配置不同的参数和提示模板。
    • 优势:将对象创建逻辑集中管理,提高了系统的灵活性。

通过这些设计模式的组合应用,MetaGPT实现了一个灵活、可扩展、易于维护的架构,这也是它能够模拟复杂软件公司协作流程的关键所在。


4. 实现机制

算法复杂度分析

在深入了解MetaGPT的实现之前,我们需要分析其核心算法的复杂度,这有助于理解系统的性能特性和瓶颈。

上下文管理算法

上下文管理是MetaGPT的核心功能之一,它需要高效地存储、检索和更新项目信息。上下文管理的主要操作包括:

  1. 信息添加

    • 时间复杂度:O(1)O(1)O(1) 使用哈希表或O(log⁡n)O(\log n)O(logn) 使用有序数据结构
    • 空间复杂度:O(n)O(n)O(n),其中n是信息片段数量
  2. 语义检索

    • 这是最关键的操作,需要找到与当前查询最相关的上下文片段
    • 如果使用向量相似度搜索,时间复杂度为O(n⋅d)O(n \cdot d)O(nd),其中d是向量维度
    • 如果使用索引结构(如HNSW),可以优化到近似O(log⁡n)O(\log n)O(logn)
  3. 上下文窗口构建

    • 需要将检索到的信息片段组合成适合LLM输入的上下文
    • 时间复杂度:O(k)O(k)O(k),其中k是选中的片段数量
工作流调度算法

工作流调度算法决定了系统在什么时候调用哪个角色执行什么任务:

  1. 状态转换

    • 时间复杂度:O(1)O(1)O(1) 使用查找表或状态机
  2. 任务队列管理

    • 如果使用优先级队列,插入和提取操作的时间复杂度为O(log⁡n)O(\log n)O(logn)
    • 如果是简单的FIFO队列,则为O(1)O(1)O(1)
  3. 依赖关系解析

    • 在有向无环图(DAG)中解析任务依赖关系
    • 时间复杂度:O(V+E)O(V + E)O(V+E),其中V是顶点(任务)数,E是边(依赖)数
角色执行算法

每个角色代理执行时的算法复杂度:

  1. 提示构建

    • 时间复杂度:O(l+c)O(l + c)O(l+c),其中l是角色提示模板长度,c是上下文字符数
  2. LLM调用

    • 这通常是系统的瓶颈,时间复杂度取决于LLM的具体实现
    • 一般可以认为是O(p+o)O(p + o)O(p+o),其中p是输入token数,o是输出token数
  3. 输出解析

    • 时间复杂度:O(o)O(o)O(o),其中o是输出长度
  4. 验证检查

    • 取决于验证规则的复杂度,一般为O(o)O(o)O(o)或更高
整体复杂度分析

MetaGPT的整体复杂度可以从以下几个维度考虑:

  1. 项目规模敏感性

    • 随着项目规模增长,上下文检索和管理的复杂度会增加
    • 代码生成和测试的复杂度也会随着代码库大小增长
  2. 迭代次数影响

    • 如果系统需要多次迭代来优化输出,整体复杂度会乘以迭代次数
    • 最坏情况下可能是O(k⋅f(n))O(k \cdot f(n))O(kf(n)),其中k是迭代次数,f(n)f(n)f(n)是单次迭代复杂度
  3. LLM瓶颈

    • 在实际应用中,LLM调用通常是主要的性能瓶颈
    • 优化策略包括提示工程减少token使用、并行调用、缓存常见查询等

理解这些复杂度分析有助于我们在实现MetaGPT时做出合理的设计决策,例如选择合适的数据结构、优化关键路径、设置合理的迭代次数限制等。

优化代码实现

接下来,我们将提供MetaGPT核心组件的优化Python代码实现。这些实现展示了如何构建一个简化但功能完整的MetaGPT系统。

上下文管理模块

首先,我们实现一个高效的上下文管理模块:

import hashlib
import json
from typing import List, Dict, Any, Tuple
from dataclasses import dataclass, field
from datetime import datetime
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity

@dataclass
class ContextItem:
    """表示一个上下文项的数据类"""
    id: str
    content: str
    metadata: Dict[str, Any] = field(default_factory=dict)
    embedding: List[float] = field(default_factory=list)
    timestamp: datetime = field(default_factory=datetime.now)
    relevance_score: float = 0.0

class ContextManager:
    """上下文管理器,负责存储、检索和管理项目上下文"""
    
    def __init__(self, embedding_model=None):
        self.items: Dict[str, ContextItem] = {}
        self.embedding_model = embedding_model
        self.index = {}  # 简单索引结构
        
    def _generate_id(self, content: str) -> str:
        """生成内容的唯一ID"""
        return hashlib.md5(content.encode()).hexdigest()
    
    def _get_embedding(self, text: str) -> List[float]:
        """获取文本的向量表示"""
        if self.embedding_model:
            return self.embedding_model.encode(text)
        # 简化的模拟embedding,实际应用中应使用真实模型
        return list(np.random.rand(128))
    
    def add_item(self, content: str, metadata: Dict[str, Any] = None) -> str:
        """添加上下文项"""
        item_id = self._generate_id(content)
        if item_id in self.items:
            return item_id  # 已存在,返回现有ID
            
        embedding = self._get_embedding(content)
        
        item = ContextItem(
            id=item_id,
            content=content,
            metadata=metadata or {},
            embedding=embedding
        )
        
        self.items[item_id] = item
        
        # 更新索引
        for key, value in (metadata or {}).items():
            if key not in self.index:
                self.index[key] = {}
            if value not in self.index[key]:
                self.index[key][value] = set()
            self.index[key][value].add(item_id)
            
        return item_id
    
    def get_item(self, item_id: str) -> ContextItem:
        """根据ID获取上下文项"""
        return self.items.get(item_id)
    
    def search_by_metadata(self, **kwargs) -> List[ContextItem]:
        """通过元数据搜索上下文项"""
        result_ids = None
        
        for key, value in kwargs.items():
            if key in self.index and value in self.index[key]:
                ids = self.index[key][value]
                if result_ids is None:
                    result_ids = set(ids)
                else:
                    result_ids &= ids
        
        if result_ids is None:
            return []
            
        return [self.items[id] for id in result_ids]
    
    def semantic_search(self, query: str, top_k: int = 5, filter_metadata: Dict[str, Any] = None) -> List[ContextItem]:
        """语义搜索相关的上下文项"""
        query_embedding = self._get_embedding(query)
        
        # 先应用元数据过滤
        if filter_metadata:
            candidate_items = self.search_by_metadata(**filter_metadata)
        else:
            candidate_items = list(self.items.values())
        
        if not candidate_items:
            return []
            
        # 计算相似度
        candidate_embeddings = np.array([item.embedding for item in candidate_items])
        query_embedding = np.array(query_embedding).reshape(1, -1)
        
        similarities = cosine_similarity(query_embedding, candidate_embeddings)[0]
        
        # 排序并返回top_k
        sorted_indices = np.argsort(similarities)[::-1][:top_k]
        
        results = []
        for idx in sorted_indices:
            item = candidate_items[idx]
            item.relevance_score = similarities[idx]
            results.append(item)
            
        return results
    
    def build_context_window(self, query: str, max_tokens: int = 3000, filter_metadata: Dict[str, Any] = None) -> str:
        """构建适合LLM输入的上下文窗口"""
        # 首先获取相关上下文
        relevant_items = self.semantic_search(query, top_k=10, filter_metadata=filter_metadata)
        
        # 构建上下文文本
        context_parts = []
        total_tokens = 0
        
        for item in relevant_items:
            # 简化的token计数,实际应用应使用具体模型的tokenizer
            item_tokens = len(item.content.split()) * 1.3  # 近似转换为token
            
            if total_tokens + item_tokens <= max_tokens:
                # 添加元数据信息
                meta_str = ", ".join([f"{k}: {v}" for k, v in item.metadata.items()])
                context_parts.append(f"[{meta_str}]\n{item.content}")
                total_tokens += item_tokens
            else:
                # 空间不足,停止添加
                break
                
        return "\n\n---\n\n".join(context_parts)
    
    def save(self, filepath: str) -> None:
        """保存上下文到文件"""
        data = {
            "items": {
                id: {
                    "content": item.content,
                    "metadata": item.metadata,
                    "embedding": item.embedding,
                    "timestamp": item.timestamp.isoformat()
                }
                for id, item in self.items.items()
            },
            "index": {
                k: {v: list(ids) for v, ids in v_dict.items()}
                for k, v_dict in self.index.items()
            }
        }
        
        with open(filepath, 'w', encoding='utf-8') as f:
            json.dump(data, f, ensure_ascii=False, indent=2)
    
    @classmethod
    def load(cls, filepath: str, embedding_model=None) -> 'ContextManager':
        """从文件加载上下文"""
        with open(filepath, 'r', encoding='utf-8') as f:
            data = json.load(f)
        
        manager = cls(embedding_model)
        
        for id, item_data in data["items"].items():
            item = ContextItem(
                id=id,
                content=item_data["content"],
                metadata=item_data["metadata"],
                embedding=item_data["embedding"],
                timestamp=datetime.fromisoformat(item_data["timestamp"])
            )
            manager.items[id] = item
        
        manager.index = {
            k: {v: set(ids) for v, ids in v_dict.items()}
            for k, v_dict in data["index"].items()
        }
        
        return manager

这个上下文管理器提供了高效的存储、检索和上下文构建功能,是MetaGPT系统的基础组件。

角色和代理模块

接下来,我们实现角色和代理的核心模块:

from abc import ABC, abstractmethod
from typing import Any, Dict, List, Optional
from dataclasses import dataclass, field
import json
import re

@dataclass
class RoleDefinition:
    """角色定义"""
    name: str
    description: str
    responsibilities: List[str]
    skills: List[str]
    prompt_template: str
    output_format: Dict[str, Any]
    tools: List[str] = field(default_factory=list)

@dataclass
class Task:
    """任务定义"""
    id: str
    description: str
    role: str
    input_data: Dict[str, Any] = field(default_factory=dict)
    expected_output: Dict[str, Any] = field(default_factory=dict)
    dependencies: List[str] = field(default_factory=list)
    priority: int = 1
    status: str = "pending"  # pending, in_progress, completed, failed
    result: Any = None

class BaseAgent(ABC):
    """代理基类"""
    
    def __init__(self, role_definition: RoleDefinition, context_manager, llm_client):
        self.role = role_definition
        self.context_manager = context_manager
        self.llm_client = llm_client
    
    def _build_prompt(self, task: Task) -> str:
        """构建提示"""
        # 获取相关上下文
        context = self.context_manager.build_context_window(
            query=task.description,
            filter_metadata={"role": self.role.name}
        )
        
        # 填充提示模板
        prompt = self.role.prompt_template.format(
            role_name=self.role.name,
            role_description=self.role.description,
            responsibilities="\n".join([f"- {r}" for r in self.role.responsibilities]),
            skills="\n".join([f"- {s}" for s in self.role.skills]),
            task_description=task.description,
            input_data=json.dumps(task.input_data, indent=2, ensure_ascii=False),
            context=context,
            output_format=json.dumps(self.role.output_format, indent=2, ensure_ascii=False)
        )
        
        return prompt
    
    def _parse_output(self, output: str) -> Dict[str, Any]:
        """解析LLM输出"""
        # 尝试提取JSON部分
        json_match = re.search(r'```json\s*([\s\S]*?)\s*```', output)
        if json_match:
            json_str = json_match.group(1)
        else:
            # 尝试直接解析整个输出
            json_str = output
        
        try:
            return json.loads(json_str)
        except json.JSONDecodeError:
            # 如果解析失败,返回原始输出
            return {"raw_output": output}
    
    def _validate_output(self, parsed_output: Dict[str, Any]) -> bool:
        """验证输出是否符合预期格式"""
        if "raw_output" in parsed_output:
            return False  # 解析失败
            
        # 检查必要字段
        for field in self.role.output_format.get("required", []):
            if field not in parsed_output:
                return False
                
        return True
    
    @abstractmethod
    def _process_specific(self, parsed_output: Dict[str, Any], task: Task) -> Dict[str, Any]:
        """特定角色的处理逻辑"""
        pass
    
    def execute(self, task: Task) -> Dict[str, Any]:
        """执行任务"""
        # 构建提示
        prompt = self._build_prompt(task)
        
        # 调用LLM
        output = self.llm_client.generate(prompt)
        
        # 解析输出
        parsed_output = self._parse_output(output)
        
        # 验证输出
        if not self._validate_output(parsed_output):
            # 可以尝试重新生成或采取其他策略
            pass
        
        # 特定角色处理
        processed_output = self._process_specific(parsed_output, task)
        
        # 更新上下文
        self.context_manager.add_item(
            content=json.dumps(processed_output, ensure_ascii=False, indent=2),
            metadata={
                "role": self.role.name,
                "task_id": task.id,
                "type": "task_output"
            }
        )
        
        return processed_output

# 具体角色实现
class ProductManagerAgent(BaseAgent):
    """产品经理代理"""
    
    def _process_specific(self, parsed_output: Dict[str, Any], task: Task) -> Dict[str, Any]:
        """产品经理特定处理"""
        # 产品经理特定的处理逻辑
        return parsed_output

class ArchitectAgent(BaseAgent):
    """架构师代理"""
    
    def _process_specific(self, parsed_output: Dict[str, Any], task: Task) -> Dict[str, Any]:
        """架构师特定处理"""
        # 架构师特定的处理逻辑
        return parsed_output

class DeveloperAgent(BaseAgent):
    """开发者代理"""
    
    def _process_specific(self, parsed_output: Dict[str, Any], task: Task) -> Dict[str, Any]:
        """开发者特定处理"""
        # 可以在这里添加代码保存、语法检查等功能
        if "code" in parsed_output:
            # 这里可以添加保存代码到文件的逻辑
            pass
            
        return parsed_output

class TesterAgent(BaseAgent):
    """测试工程师代理"""
    
    def _process_specific(self, parsed_output: Dict[str, Any], task: Task) -> Dict[str, Any]:
        """测试工程师特定处理"""
        # 可以在这里添加测试执行逻辑
        if "test_cases" in parsed_output:
            # 这里可以添加执行测试用例的逻辑
            pass
            
        return parsed_output

这些代码实现了角色和代理的基本框架,提供了通用的任务执行流程,并为特定角色的定制处理留出了接口。

工作流引擎

最后,我们实现一个简化的工作流引擎,用于协调不同角色之间的协作:

from typing import List, Dict, Any, Optional
from dataclasses import dataclass, field
import networkx as nx
from enum import Enum

class WorkflowStatus(Enum):
    """工作流状态"""
    IDLE = "idle"
    RUNNING = "running"
    PAUSED = "paused"
    COMPLETED = "completed"
    FAILED = "failed"

@dataclass
class WorkflowDefinition:
    """工作流定义"""
    id: str
    name: str
    description: str
    tasks: List[Task]
    dependencies: List[Dict[str, str]]  # {"from": task_id, "to": task_id}
    entry_point: str  # 起始任务ID

class WorkflowEngine:
    """工作流引擎"""
    
    def __init__(self, agents: Dict[str, BaseAgent], context_manager):
        self.agents = agents
        self.context_manager = context_manager
        self.current_workflow: Optional[WorkflowDefinition] = None
        self.status: WorkflowStatus = WorkflowStatus.IDLE
        self.task_results: Dict[str, Any] = {}
        
    def _build_dependency_graph(self, workflow: WorkflowDefinition) -> nx.DiGraph:
        """构建依赖图"""
        G = nx.DiGraph()
        
        # 添加节点
        for task in workflow.tasks:
            G.add_node(task.id, task=task)
            
        # 添加边
        for dep in workflow.dependencies:
            G.add_edge(dep["from"], dep["to"])
            
        return G
    
    def _get_executable_tasks(self, graph: nx.DiGraph) -> List[Task]:
        """获取可执行的任务"""
        executable_tasks = []
        
        for node_id in graph.nodes:
            task = graph.nodes[node_id]["task"]
            
            # 检查任务是否已完成或正在执行
            if task.status in ["completed", "in_progress", "failed"]:
                continue
                
            # 检查所有依赖是否已完成
            predecessors = list(graph.predecessors(node_id))
            all_deps_completed = all(
                graph.nodes[pred]["task"].status == "completed"
                for pred in predecessors
            )
            
            if all_deps_completed:
                executable_tasks.append(task)
                
        # 按优先级排序
        executable_tasks.sort(key=lambda t: t.priority, reverse=True)
        
        return executable_tasks
    
    def _all_tasks_completed(self, graph: nx.DiGraph) -> bool:
        """检查是否所有任务都已完成"""
        return all(
            graph.nodes[node_id]["task"].status == "completed"
            for node_id in graph.nodes
        )
    
    def _any_task_failed(self, graph: nx.DiGraph) -> bool:
        """检查是否有任务失败"""
        return any(
            graph.nodes[node_id]["task"].status == "failed"
            for node_id in graph.nodes
        )
    
    def execute_workflow(self, workflow: WorkflowDefinition) -> Dict[str, Any]:
        """执行工作流"""
        self.current_workflow = workflow
        self.status = WorkflowStatus.RUNNING
        self.task_results = {}
        
        # 
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐