MetaGPT
一句话定位
MetaGPT 是一个以”软件公司 SOP”为原始隐喻的多智能体框架,核心是 Environment 消息总线 + 角色(Role)订阅/发布,早期版本靠固定流程角色(ProductManager→Architect→Engineer→QaEngineer)模拟瀑布式协作;当前默认路径(software_company.py)已切换为以 RoleZero(ReAct+工具调用基类)为基础的 TeamLeader/DataAnalyst/Engineer2 等角色,由 TeamLeader(“Mike”)做 hub-and-spoke 式编排。商业化产品是 mgx.dev。
核心架构总览
分析基于 commit 11cdf466d042aece04fc6cfd13b28e1a70341b1f(2026-01-21,clone/读取于 2026-07-07),MIT 协议。关键路径(相对 metagpt/ 包根):
- 角色与循环:
roles/role.py(legacy SOP 基类)、roles/di/role_zero.py(现代 ReAct+工具角色)、roles/di/team_leader.py、roles/di/data_interpreter.py - 编排:
team.py(顶层 run loop)、environment/base_env.py(消息总线)、environment/mgx/mgx_env.py(hub-and-spoke 路由)、software_company.py(CLI 入口/默认团队组成) - 工具:
tools/tool_registry.py、tools/tool_recommend.py、tools/libs/terminal.py、actions/di/execute_nb_code.py - Prompt:
prompts/di/role_zero.py - 记忆:
memory/memory.py、memory/longterm_memory.py、memory/role_zero_memory.py - 自进化:
exp_pool/decorator.py、exp_pool/manager.py - 可观测性:
utils/report.py - 配置/安全:
config2.py
官方架构文档不在仓库内,而是外部 docs 站点的 RFC 系列:docs.deepwisdom.ai 的 RFC-116(消息路由从”共享公共内存”改为”私有 per-Role 缓冲 + Environment 路由”的设计变更)被 role.py/memory.py 代码注释直接引用,是目前能找到的最接近正式架构文档的一手材料。
Agent Loop(主循环 / 何时继续何时停)
两套并存的循环实现:
- Legacy SOP 循环(
role.py:454-496):RoleReactMode枚举react | by_order | plan_and_act。react模式做_think(LLM 挑下一个”state”/action 序号)→_act(执行Action.run),循环上限max_react_loop(默认 1);plan_and_act模式先由Planner生成计划,再逐 task 调_act_on_task。 - 现代 ReAct+工具循环(
role_zero.py:303-336):每次run()先走_quick_think——LLM 把意图分类为 QUICK/SEARCH/TASK/AMBIGUOUS,简单请求走快速路径、不进入完整循环;否则进入_observe→_think→_act循环,上限max_react_loop(RoleZero默认 50,TeamLeader默认 3,见role_zero.py:71/team_leader.py:29)。达到上限时若max_react_loop>=10(role_zero.py:328)会触发”询问人类是否继续”的检查点——一个内建的失控循环安全阀。 - 终止条件:角色发出
{"command_name":"end"}(_run_special_command,role_zero.py:431附近);或Plan.is_plan_finished;或预算超支(Team._check_balance抛NoMoneyException,team.py:98-100)。 - 顶层
Team.run(n_round)(team.py:122-138)本身是有界循环:每轮调用Environment.run(),用asyncio.gather并发触发所有非 idle 角色,env.is_idle时提前退出。
记忆与上下文管理(压缩、长期记忆、会话持久化)
三套彼此独立、未统一的机制:
- 基础
Memory(memory/memory.py):纯进程内 list + 按cause_byaction 索引的 dict,无压缩、无容量上限、默认不持久化。 RoleZeroLongTermMemory(memory/role_zero_memory.py,现代路径):短期存储超过memory_k(默认 200 条)时,把溢出的旧消息灌入 Chroma 后端的 RAG 引擎(SimpleEngine);读取时若最后一条是用户需求,会检索相关长期记忆前置到近期窗口。是否启用由config.role_zero.enable_longterm_memory控制,默认关闭。LongTermMemory(memory/longterm_memory.py,legacy 路径):供旧 SOP 角色使用,find_news上做基于 embedding 相似度的去重。- 会话持久化:
Team.serialize()/deserialize()(team.py:59-81)把整个 team+env+role 状态 dump 到storage/team/team.json,用于崩溃恢复(官方文档称”breakpoint recovery”),这是一个独立于记忆压缩的机制。 - 没有发现显式的上下文窗口截断/摘要步骤;
memory_k起到硬截断作用,RAG 是”召回被截断部分”的补偿机制。
工具体系(定义/调用协议/注册/权限)
@register_tool装饰器(tool_registry.py:94-118)通过 AST/inspect(tool_convert.py,未逐行读)从 docstring+签名自动生成 JSON schema,存入全局单例TOOL_REGISTRY,可带tags。- 支持运行时从任意文件路径动态注册工具(
register_tools_from_path,tool_registry.py:185-194)——用户提供的本地.py文件可以直接变成可调用工具。 - 选择:
ToolRecommender(tool_recommend.py)做两段式 召回→排序:BM25ToolRecommender用 BM25 对工具 docstring 召回候选,再用 LLM 调用(TOOL_RECOMMENDATION_PROMPT)排序取 top-k;force=True跳过排序、直接全量返回(TeamLeader用这个模式,因为它固定需要 Plan/RoleZero/TeamLeader 系列工具)。 - 调用协议:
RoleZero._run_commands(role_zero.py:385-415)执行一个 JSON 数组{"command_name": "Class.method", "args": {...}},逐条对照一个普通 Python dicttool_execution_map(set_tool_execution构建,role_zero.py:118-171)分发,例如"Editor.read"→self.editor.read;批次中某条失败即break中断后续命令。不是 OpenAI function-calling 或 MCP 那种正式协议,而是烘焙进系统提示词里的自定义 JSON 命令数组约定。 - 权限:没有正式的按工具权限/白名单系统;唯一的可执行动作限制是 terminal 的 2 条
forbidden_commands子串黑名单(见下)。
Prompt 设计(系统提示结构、动态组装)
SYSTEM_PROMPT(prompts/di/role_zero.py:25-50)逐轮动态组装,拼接内容包括:角色信息(name/goal/constraints + 含同伴角色名的环境描述)、硬编码的 Task pydantic schema(以字面 Python 源码形式展示给模型)、task_type_desc、available_commands(工具推荐器给出的 JSON schema)、从经验池检索到的 example,以及静态 instruction 块。CMD_PROMPT 在此之上叠加当前 plan/task 状态、工具”state”、以及严格的输出格式要求(必须恰好输出一个 JSON 命令数组,允许 JSON 前有思考文字),其中有非常字面化的抗幻觉格式约束,例如明确要求输出必须以 ```json [ 开头(prompts_role_zero.py)。另有一个更轻量的 QUICK_THINK_SYSTEM_PROMPT 专供意图分类快速路径使用。Legacy SOP 角色则用 role.py:51-79 的 PREFIX_TEMPLATE/STATE_TEMPLATE/ROLE_TEMPLATE,走”从状态列表选序号”的更简单模式。
Router / 编排(任务分解、多 agent、子 agent)
多智能体编排是核心而非附加功能:
Environment(environment/base_env.py)是发布订阅式消息总线:publish_message把消息广播给所有订阅地址匹配message.send_to的角色;Role._watch()加默认的”自身类名/自身 name”订阅决定角色对什么消息有反应(对应 RFC-116 的设计)。MGXEnv(environment/mgx/mgx_env.py)重写publish_message,强制所有消息经过TeamLeader(角色名 “Mike”),除非属于已建立的人类↔角色直接会话(direct_chat_roles集合)——默认拓扑是通过一个指定编排角色的 hub-and-spoke,而非扁平广播。这一”强制走 TeamLeader”的设计已用官方 RFC-116 文档交叉核实。TeamLeader(roles/di/team_leader.py)是RoleZero子类,额外工具只有publish_team_message(content, send_to)——它通过发出”给队友 X 发消息”的命令来做任务分解/委派;真正的任务分解逻辑在共享的Planner/plan-task 机制(metagpt/strategy/planner.py,本轮未完整读)里,而非一个独立的层级化 planner 类。- 默认团队组成(
software_company.py:44-54):TeamLeader, ProductManager, Architect, Engineer2, DataAnalyst;更早的ProjectManager/Engineer/QaEngineerSOP 角色已被注释掉——即 RoleZero 系角色已取代原始”软件公司 SOP”角色成为默认路径。
Skill / 插件体系
没有与工具系统分离的独立”skill”抽象——工具注册表本身就是插件系统:任何 Python 类/函数都可以通过 @register_tool 变成”技能”,放进 metagpt/tools/libs/,或从运行时任意外部文件路径注册(register_tools_from_path)。仓库里还有一个看起来是遗留、未被 RoleZero/TeamLeader/DataInterpreter 引用的 metagpt/skills/ 包(早于 tool_registry 设计,本轮未深读)以及未读的 metagpt/learn/。结论:工具系统即技能系统,二者统一,无独立分层。
自进化能力(自我改进 / 学习型记忆 / eval 驱动纠错)
两个容易混淆、但需分开看的机制:
- 经验池 /
exp_cache(exp_pool/decorator.py、exp_pool/manager.py):包裹RoleZero.llm_cached_aask的请求-响应缓存回放循环。每次 LLM 调用前先向向量/BM25 后端的经验存储查询相似的过去 (req, resp) 对,若被SimplePerfectJudge判定为”完美”匹配,直接跳过 LLM 调用复用旧响应;否则正常执行,用SimpleScorer给新响应打分并写回经验池。这是 MetaGPT 里最接近”从自身轨迹学习”的机制,但本质是显式的基于案例推理/缓存,不是梯度学习,且默认关闭(config.exp_pool.enabled=False)。挂载点:RoleZero.llm_cached_aask(role_zero.py:267附近)经@exp_cache(context_builder=RoleZeroContextBuilder(), serializer=RoleZeroSerializer())。 - 长期记忆检索(
role_zero_memory.py)有时被误认为自进化,实际只是 RAG 式上下文管理(见”记忆”节),不做存储内容的打分/筛选。 - 核心框架内未发现 eval 驱动的自我纠错循环或自动 prompt/权重调优。
metagpt/ext/aflow/(AFlow,ICLR 2025 oral,工作流自动生成优化)、metagpt/ext/spo/(SPO,Self-Supervised Prompt Optimization,arXiv 2502.06855)、metagpt/ext/sela/(SELA,数据科学 agent 搜索)是独立的研究扩展模块,README 链接的论文显示它们确实把轨迹/rollout 当优化信号用,但属于 opt-in 的示例/研究模块,不在默认 agent 运行时里,本轮未深读,作为后续 dossier 加强”自进化”证据的线索标注。
可观测性(日志 / trace 格式)
ResourceReporter 及其类型化子类(utils/report.py)把结构化 JSON 块(按 BlockType 枚举打标:TERMINAL/TASK/BROWSER/BROWSER_RT/EDITOR/GALLERY/NOTEBOOK/DOCS/THOUGHT)POST 到可配置的 callback_url(常量 METAGPT_REPORTER_DEFAULT_URL),支持 HTTP(S) 和 Unix-socket(requests_unixsocket)——这是给 mgx.dev 前端做实时 UI 流式展示(思考过程、终端输出、notebook cell、浏览器动作)的管道,在 role_zero.py/execute_nb_code.py 里能看到 ThoughtReporter/NotebookReporter/TerminalReporter 的调用点。普通日志走 metagpt/logs.py(loguru,未完整读)。没有发现 OTel 式的标准 trace/span 格式——reporter 协议是面向自家 UI 的专用协议,不是通用可观测性标准。
安全与权限(审批门、密钥管理)
设计上非常薄:
- 密钥管理:
Config(config2.py)从~/.metagpt/config2.yaml(明文 YAML)与os.environ合并加载(优先级:env < 默认配置路径 < kwargs),API key 等敏感字段是普通 pydantic 字符串字段,无 keyring/vault/加密存储。 - 审批门:RoleZero/TeamLeader 路径下未发现工具执行前的审批机制——LLM 输出的 JSON 命令数组直接经
tool_execution_map执行。唯一例外是达到max_react_loop后的人类确认检查点(见 Agent Loop 节),这是失控循环安全阀,不是逐动作权限门。 - 唯一发现的访问限制是
Terminal.forbidden_commands(terminal.py:41-44):2 条子串黑名单("run dev"、"serve "),命中后静默替换为无操作命令——明确是为了防止 agent 错误地把 dev server 挂到后台,不是安全控制。
沙箱与执行隔离
框架层面没有沙箱:
Terminal(tools/libs/terminal.py)用asyncio.create_subprocess_exec起真实本地bash/cmd.exe子进程,env=os.environ.copy(),工作目录默认就是真实工作区——完整宿主机访问权限,无容器/chroot/VM/seccomp。ExecuteNbCode(actions/di/execute_nb_code.py)通过nbclient/nbformat(NotebookClient)跑本地 Jupyter kernel,同样是真实本地 Python 进程,未沙箱化;仅有的缓解措施是每个 cell 的超时(默认 600s,CellTimeoutError触发 kernel interrupt)和死内核恢复(reset())。- 全仓库未发现 Docker/gVisor/Firecracker/E2B 一类隔离层。
与模型的协同设计
未发现模型级协同设计产物(无定制模型 checkpoint、无 MetaGPT 专属微调模型、无专用模型 server)。MetaGPT 是纯粹的多 provider LLM harness(metagpt/provider/,未深读,但 config2.py 里 LLMConfig/LLMType 的导入暗示广泛支持 openai/azure/ollama/groq 等,与 README 一致)。框架确实针对特定 LLM 行为做了绕行式适配:JSON_REPAIR_PROMPT(prompts/di/role_zero.py:131-146)是专门用一次额外 LLM 调用修复畸形 JSON 工具调用输出的 prompt;repair_llm_raw_output/extract_state_value_from_output(在 role.py/role_zero.py 中引用,未完整读)暗示一整套”LLM 输出不可靠、加修复通道”的模式——即 MetaGPT(至少在 RoleZero 路径上)是靠自由文本 prompting + 正则/JSON 解析约定与模型交互,而非原生 tool-calling/structured-output API。
轨迹利用(session/trajectory 是否反哺训练/评测)
- 经验池(见”自进化”节)是唯一确认的”过去轨迹反哺未来行为”机制,但是推理时的缓存回放,不是训练数据导出。
Team.serialize()把完整会话状态 dump 成 JSON 用于恢复,技术上是可导出的轨迹产物,但核心框架内未发现把它转成训练数据或 benchmark 格式的代码路径。metagpt/ext/aflow/、metagpt/ext/spo/、metagpt/ext/sela/是独立研究项目(工作流优化/prompt 优化/数据科学 agent 搜索),按 README 论文链接(AFlow arXiv/ICLR 2025 oral,SPO arXiv 2502.06855)大概率确实把轨迹/rollout 当优化信号用,但内部实现本轮未读,标记为待跟进的线索而非已确认发现。
与同类 harness 的关键差异
- 与偏”单 agent + 工具”的 harness(如 Claude Code / Warp)相比,MetaGPT 的默认心智模型始终是多角色团队:即使 RoleZero 取代了 SOP 角色,编排层(
MGXEnv强制走TeamLeader)依然是框架级、非可选的一等公民,而不是上层拼出来的功能。 - “quick think 快速路径”(
role_zero.py的意图分类 QUICK/SEARCH/TASK/AMBIGUOUS)是一个相对少见的显式设计:在进入完整 ReAct 循环前先做一次分类判断是否需要走重量级路径。 - 自进化机制是显式的”完美经验缓存回放”(
exp_cache),而非隐式的长期记忆学习或权重更新——且默认关闭,说明团队认为这更像一个性能优化开关而非核心能力。 - 跨 harness 系统对比(与 Claude Code、Warp、OpenHands 等的详细比较矩阵)留待 synthesis 阶段统一做。
原始源码定位
- repo: https://github.com/FoundationAgents/MetaGPT
- commit/version analyzed:
11cdf466d042aece04fc6cfd13b28e1a70341b1f(2026-01-21 18:12:32 +0800 提交,2026-07-07 clone/分析) - 关键文件列表(相对
metagpt/包根):roles/role.pyroles/di/role_zero.pyroles/di/team_leader.pyroles/di/data_interpreter.pyteam.pyenvironment/base_env.pyenvironment/mgx/mgx_env.pysoftware_company.pyprompts/di/role_zero.pytools/tool_registry.pytools/tool_recommend.pytools/libs/terminal.pyactions/di/execute_nb_code.pymemory/memory.pymemory/longterm_memory.pymemory/role_zero_memory.pyexp_pool/decorator.pyexp_pool/manager.pyutils/report.pyconfig2.py
一手源存档(sources/)
/Users/zhao/projects/self-wiki/ai-research/sources/harness/metagpt/:
NOTES.md— stage-1 完整调研笔记(逐维度发现、行号引用、未找到项说明)keyfiles/— 19 份原样保存的源文件:role.py、role_zero.py、team_leader.py、data_interpreter.py、prompts_role_zero.py、team.py、environment_base_env.py、mgx_env.py、execute_nb_code.py、terminal.py、tool_registry.py、tool_recommend.py、memory.py、longterm_memory.py、role_zero_memory.py、exp_pool_decorator.py、exp_pool_manager.py、report.py、software_company.py、config2.py