跳到主要内容

MGX 与 RoleZero:TeamLeader 调度的动态命令智能体

30 秒导读: 前四章讲的是「第一代」MetaGPT——角色沿着写死的 SOP 流水线(需求→PRD→设计→代码)一棒接一棒跑。本章讲第二代:Team 默认 use_mgx=True,换上 MGXEnv 环境和一个叫 MikeTeamLeader。此后每个角色不再走固定剧本,而是每一轮都让 LLM 现场决定「这一步调哪个工具、传什么参数」。这些角色全都继承同一个基类 RoleZero——一个"能动态思考和行动"的智能体骨架。

本章是第 4 章「经典 SOP 流水线」的对照面。读之前建议先读过第 1 章 Role 循环第 3 章 Environment 消息总线,本章会大量复用它们的概念(_observe/_think/_actpublish_messageMessage 路由)。


1. 这是什么(零基础也能懂)

一句话定义: MGX(MetaGPT 的第二代多智能体模式)是"一个 LLM 队长 + 一群通用工具型智能体"的团队;每个成员靠逐轮生成工具命令干活,而不是照着固定流程走。

它解决的问题。 第一代 SOP 流水线的强项是"确定性"——步骤写死,产物齐整。但它也:每个角色只会做剧本里那一步,遇到剧本没覆盖的需求(改个 bug、爬个网页、临时查资料)就抓瞎。MGX 想要的是通用性:同一套角色,既能写 2048 小游戏,也能解 GitHub issue、做数据分析,靠的是"给角色一批工具,让它自己看着办"。

两代对比(先建立心智模型):

维度第一代 SOP(第 4 章)第二代 MGX(本章)
每一步怎么定写死的 Action 序列每轮 LLM 现场选工具命令
角色专职(ProductManager 只写 PRD)通用(RoleZero 子类,配一批工具)
谁来调度Environment 按订阅广播TeamLeader「Mike」居中收发、派活
停不停跑完 n_round角色自己发 end 命令收工
适合结构化、可预测的软件生成开放任务、需要临场应变

用起来什么样。 用户几乎无感——入口还是那句 generate_repo("write a 2048 game")。差别在内部:software_company.py:45 雇的第一个人就是 TeamLeader(),后面跟着 ProductManager / Architect / Engineer2 / DataAnalyst(metagpt/software_company.py:45-53)。这些成员大多是 RoleZero 的子类。

一句话直觉/类比。 把它想成一个真实的项目组:第一代像工厂流水线(每个工位固定动作);第二代像一个有项目经理(Mike)的敏捷小组——你把需求丢给经理,经理拆活、点名派给合适的人,每个人拿到活后自己决定用什么工具、怎么下手,干完汇报,经理再决定下一步。


2. 顶层全景(它大概怎么转)

MGX 有两个主角:消息怎么流(MGXEnv + TeamLeader),和单个角色怎么想-做(RoleZero)。先看全景,再逐个拆。

怎么读下面这张图: 左边是"消息中转"——所有消息都先经过队长 Mike;右边是"一个角色收到活之后的内循环"。数字是一次任务的大致时序。

用户需求 "写个 2048"


┌─────────────────────────────────────────────┐
│ MGXEnv.publish_message (mgx_env.py:24) │
│ 规则:除队长自己发的,一切消息都追加 send_to=Mike │
└───────────────┬─────────────────────────────┘
│ ① 需求先到 Mike

┌──────────────────────┐
│ TeamLeader「Mike」 │ ② _think:LLM 生成命令
│ (RoleZero 子类) │ Plan.append_task(...) 拆活
│ │ publish_team_message(→Alex)
└──────────┬───────────┘
│ ③ 派活:发一条 UserMessage 给 Alex,并把自己挂起

┌──────────────────────────────────────┐
│ Engineer2「Alex」= RoleZero 子类 │
│ ┌── _react 每轮重新 observe ────────┐ │
│ │ _think: 拼 system_prompt │ │ ④ 角色内循环
│ │ = 角色说明 + 工具schema + 计划状态 │ │
│ │ → LLM 吐一段 JSON 命令 │ │
│ │ _act: parse_commands → 逐个执行 │ │
│ │ Editor.write / Terminal.run / ... │ │
│ └── 直到发出 end 命令 ───────────────┘ │
└───────────────┬──────────────────────┘
│ ⑤ 干完汇报,消息又经 MGXEnv 回到 Mike

Mike 标记任务完成 → 派下一个 → …

部件一句话职责:

部件干什么在哪
MGXEnv消息总线的 MGX 版:强制一切消息经队长中转metagpt/environment/mgx/mgx_env.py:11
TeamLeader(Mike)拆需求成计划、点名派活、跟踪进度metagpt/roles/di/team_leader.py:23
RoleZero动态智能体基类:think→act→react 的通用引擎metagpt/roles/di/role_zero.py:55
Engineer2/DataAnalyst/SWEAgentRoleZero 的具体职业子类,各配一批工具metagpt/roles/di/engineer2.py:32
Planner/Plan/Task计划数据模型 + 更新/评审逻辑metagpt/strategy/planner.py:58metagpt/schema.py:496
ToolRegistry + BM25ToolRecommender工具注册表 + 每轮召回"该给 LLM 看哪些工具"metagpt/tools/tool_registry.py:91tool_recommend.py:195
exp_cache 经验池缓存/复用历史决策,省 LLM 调用metagpt/exp_pool/decorator.py:29

主线走一遍(高层): 需求 → MGXEnv 转给 Mike → Mike Plan.append_task 拆活 + publish_team_message 点名 → 被点到的 RoleZero 子类进入 _react 内循环:每轮 recommend_tools 挑工具、_think 让 LLM 出命令、_act 执行 → 角色发 end 收工汇报 → 消息回 Mike → Mike 推进下一任务。


3. 核心原理(逐个机制,由浅入深)

3.1 消息都要经过队长:MGXEnv 的中转规则

要解决的小问题: 一群通用角色若各自乱发消息,很快就退化成"谁都听不懂谁"。MGX 的答案简单粗暴——设一个中枢(队长 Mike),几乎所有消息都先过他一遍

思路。 MGXEnv.publish_message 接管了发消息这件事(mgx_env.py:24)。它按发信人身份分几种情况处理,最关键的是最后那条兜底规则:任何"普通消息"都会被追加收件人 Mike,于是队长永远知情。

# 示意,非源码。重点看:普通消息一律加上收件人 Mike
def publish_message(self, message, user_defined_recipient="", publicer=""):
tl = self.get_role("Mike") # 队长
if 用户直接 @某角色:
直接投递 # 私聊,绕过队长
elif publicer == 队长身份:
直接投递 # 队长处理过的消息,放行
else:
message.send_to.add(tl.name) # ← 兜底:普通消息都抄送队长
self._publish_message(message)

真实实现见 mgx_env.py:24-62;兜底那句在 mgx_env.py:56-58(message.send_to.add(tl.name))。

两个巧妙细节:

  • 公开群聊模式。 is_public_chat = True(mgx_env.py:16)时,_publish_message 会给消息加 MESSAGE_ROUTE_TO_ALL(mgx_env.py:18-22),等于"发到群里所有人可见"。
  • 把路由信息塞进正文。 LLM API 只认 content 字段,不认 send_to。所以 move_message_info_to_content(mgx_env.py:73)把发件人/收件人写进正文,变成 "[Message] from Alice to Mike: ...",这样 LLM 读 content 就知道"谁对谁说的"(mgx_env.py:88)。

3.2 队长怎么派活:TeamLeader 也是个 RoleZero

要解决的小问题: 谁把"写个 2048"拆成一串带负责人的任务,并在合适的时候把某个任务"喊"给某个角色?

关键认知:队长自己也是 RoleZero 子类(team_leader.py:23 class TeamLeader(RoleZero))。也就是说,他"拆活"和"派活"用的也是"让 LLM 生成工具命令"这一套,只不过他的工具是 ["Plan", "RoleZero", "TeamLeader"](team_leader.py:31)——即操作计划、以及一个特有命令 publish_team_message

派活 = 一次工具调用。 LLM 决定"该让 Alex 干了",就生成命令 TeamLeader.publish_team_message(content=..., send_to="Alex")。它的实现有两个要点(team_leader.py:75-86):

  1. 派活即把自己挂起:self._set_state(-1)——发完就停,等被点名的人回话(team_leader.py:80)。这保证同一时刻只有一个角色在动。
  2. 消息用 UserMessage 发出:对收件人而言,队长转来的活"像用户请求"(team_leader.py:84-85)。

docstring 里有句很实在的叮嘱:派活时别漏掉任何必要信息(路径、链接、语言、框架、约束),"因为你是他们唯一的信息来源"(team_leader.py:76-78)。这暴露了中枢式调度的软肋:下游只看得到队长转述的内容。

队长还有个特点:max_react_loop = 3(team_leader.py:29)——他每次只反应一两下(拆活/派活)就停,把舞台让给干活的人。相比之下 Engineer2 是 40(engineer2.py:55),DataAnalyst 继承默认 50(role_zero.py:71)。

3.3 RoleZero 的心跳:think → act,外裹 react

这是本章的心脏。RoleZero 把第 1 章_think/_act 循环,改造成"每轮让 LLM 自选命令"的动态引擎。

_think 干一件事:把一大堆上下文拼成 prompt,让 LLM 吐一段 JSON 命令。 拼进去的料有(role_zero.py:198-265):

拼进 prompt 的部分来源行号
检测响应语言(首轮)DETECT_LANGUAGE_PROMPTrole_zero.py:210-211
相关经验示例_retrieve_experience()role_zero.py:213
计划状态 + 当前任务get_plan_status(planner)role_zero.py:216
可用工具的 schematool_recommender.recommend_tools()role_zero.py:219-220
角色说明 + 任务类型描述system_prompt.format(...)role_zero.py:224-230
最近观察(浏览器/编辑器/图片)parse_browser_actionsrole_zero.py:241-244

拼好后一次 LLM 调用产出 self.command_rsp——一段 JSON 命令文本(role_zero.py:254)。

_act 干一件事:把那段 JSON 解析成命令列表,逐个执行。role_zero.py:280-301:

# 示意,非源码。_act 的主干
async def _act(self):
commands, ok, rsp = await parse_commands(self.command_rsp, ...) # JSON → [{command_name, args}, ...]
if not ok:
记下错误,交回 LLM 下轮修正
return
outputs = await self._run_commands(commands) # 逐个查 tool_execution_map 并调用
return AIMessage("我干完了,请标记任务完成。Outputs: ...")

_run_commands(role_zero.py:385-415)是真正的执行器:对每条命令,先看是不是"特殊命令",否则去 tool_execution_map(命令名→真实 Python 函数的字典)里查出函数并调用。这张映射表是 RoleZero 的"手"——在 set_tool_execution(role_zero.py:118-171)里建好,把 "Editor.write""Browser.click" 这类字符串命令对应到 self.editor.writeself.browser.click 等真实方法。子类通过 _update_tool_execution 往里加自己的工具(如 Engineer2._update_tool_executionwrite_new_code,engineer2.py:75)。

_react 是外壳:每轮都重新观察。 与第一代最大的行为差异写在注释里(role_zero.py:303-336):

_react 循环(role_zero.py:314-336):
while 动作数 < max_react_loop:
await self._observe() # ← Diff 2:每轮重新观察,能吸收新到的消息
has_todo = await self._think()
if not has_todo: break
await self._act()
# 到达上限时,问人类"要不要继续?"(role_zero.py:328-335)

"每轮 re-observe" 是关键:传统 Role 的 react 是"想一次做一次到底",而 RoleZero 在循环体里再次 _observe,于是干活途中队长/用户发来的新消息能被即时吸收。这让动态智能体能"边干边听"。

3.4 命令的两类特权:special 与 exclusive

LLM 生成的命令不是一律平等,RoleZero 给两类命令开了后门:

  • special tool commands(role_zero.py:77):Plan.finish_current_taskendTerminal.run_commandRoleZero.ask_human 这几个需要特殊处理,不走普通 tool_execution_map,而进 _run_special_command(role_zero.py:420-447)。例如 end 会触发收尾:先确保回复过人类,再让 LLM 生成一段任务总结(role_zero.py:474-491)。这就是"角色自己决定收工"的开关。
  • exclusive tool commands(role_zero.py:80-85):像 Editor.edit_file_by_replace 这种"一轮里出现多次会互相打架"的命令。parse_commands只保留第一个(role_zero_utils.py:137-143),避免 LLM 在一轮里重复改同一文件。

3.5 省 LLM 调用:quick_think 路由

要解决的小问题: 不是每句话都值得走"拼一大坨 prompt + 生成命令"的重流程。用户随口问一句"你好""这段代码啥意思",没必要惊动整个计划机器。

思路:先分类,再决定走轻还是走重。 _quick_think(role_zero.py:342-383)在正式 think-act 前先跑一次轻量分类:

_quick_think 路由(role_zero.py:342-383):
只对"来自用户"的消息触发(role_zero.py:345-347,省掉角色间的自问自答)


LLM 分类 intent ──► QUICK / AMBIGUOUS ─► 直接一次 LLM 作答,不进计划循环
├─► SEARCH ───────────► 走 SearchEnhancedQA 联网搜
└─► TASK ─────────────► 返回空,交给正式 _react 处理

有个自我纠错的小机关:若被判成 QUICK 的回答里却含 command_name(说明其实是个要动手的 TASK),就把它改回 TASK,退给正式流程(role_zero.py:366-369)。

3.6 该给 LLM 看哪些工具:注册表 + BM25 召回

要解决的小问题: 系统里注册了几十上百个工具,但一次 prompt 塞不下所有 schema(贵、还稀释注意力)。得每轮挑最相关的几个给 LLM。

注册: 任何类/函数加 @register_tool(...) 装饰器就进全局 TOOL_REGISTRY(tool_registry.py:91-118)。装饰器会用 inspect 抓源码、把函数签名转成 JSON schema 存好(tool_registry.py:97-115)。RoleZero、TeamLeader、Plan 这些本身也是被 @register_tool 注册的——所以"操作计划"能变成 LLM 可调的命令。

召回 + 排序两段式(tool_recommend.py:77-109):

recommend_tools:
① recall(召回) ── BM25 用「当前任务指令」当查询,从工具库里检索 topk 个候选
② rank(排序) ── 再让 LLM 从候选里选出最终 topk 个

BM25ToolRecommender.recall_tools(tool_recommend.py:216-228)把每个工具的 name + tags + description 当文档,用经典 BM25(一种按词频/逆文档频率打分的关键词检索算法)算相关度,取分数最高的几个。注意一个务实设定:当 force=True 或没有上下文时,直接返回用户指定的全部工具、跳过召回(tool_recommend.py:96-99)——RoleZero 初始化时正是 BM25ToolRecommender(tools=self.tools, force=True)(role_zero.py:109-110)。

3.7 Planner:动态模式下计划怎么被驱动

第 1 章只给了"plan_and_act"这个模式名(RoleReactMode.PLAN_AND_ACT),这里展开它在动态模式下怎么被驱动。计划的数据模型很朴素(metagpt/schema.py):

类型是什么关键字段/方法行号
Task一个任务instructiontask_typeassigneeis_finishedupdate_task_resultschema.py:457
TaskResult一次执行的产物coderesultis_successschema.py:480
Plan有依赖关系的任务序列append_taskfinish_current_task_topological_sortschema.py:496

Plan 会对任务做拓扑排序(按 dependent_task_ids 排先后,schema.py:505-522),并始终指向"第一个未完成任务"作为 current_task(schema.py:640-660)。

两种驱动方式,MGX 用前者:

  • 动态命令驱动(RoleZero/MGX)。 计划的增删改直接由 LLM 生成的命令触发:Plan.append_taskPlan.replace_taskPlan.reset_task 都注册成了工具(schema.py:488-495),映射进 tool_execution_map(role_zero.py:122-124)。所以"改计划"和"写代码"一样,都是一条命令。RoleZero 里的 Planner 是被 auto_run=True 悄悄初始化的(role_zero.py:114)。
  • 经典 plan_and_act 驱动(DataInterpreter)。 Planner.update_plan / process_task_result / ask_review(planner.py:77/102/119)是老一套:先 WritePlan 出计划、ask_review 让人类确认、跑完任务再 process_task_result 决定确认/重做/改计划。DataInterpreter 用的就是这条路(data_interpreter.py:45 react_mode="plan_and_act")。

注入人类先验:TaskType.guidance。 get_plan_status 组装计划状态时,会按当前任务的 task_type 取出对应的 guidance 文本拼进 prompt(planner.py:178-189)。TaskType 枚举(strategy/task_type.py:22)为 EDA、数据预处理、特征工程、模型训练等预设了各自的写码指导——注释说得直白:"识别任务类型,就能注入人类先验(guidance)来帮助解题"(task_type.py:23)。

3.8 经验池:llm_cached_aask + exp_cache

要解决的小问题: 同样的处境反复出现时,能不能不每次都重新问 LLM?

RoleZero 生成命令的那次 LLM 调用被包了一层缓存:llm_cached_aask 上挂 @exp_cache(...)(role_zero.py:267-274)。exp_cache 装饰器(exp_pool/decorator.py:29-94)的逻辑是:先按 req 查历史经验,若有"完美经验"就直接返回(省掉 LLM 调用),否则真正调用并把结果评分后存回经验池。配套的 RoleZeroContextBuilder/RoleZeroSerializer 负责把经验拼进请求、以及把长请求裁剪成便于存储的精华(role_zero.py:16-17267-273)。开关由 config.exp_pool.enabled/enable_read/enable_write 控制(decorator.py:44-46)。


4. 深入实现:三个职业子类怎么各显神通

RoleZero 是骨架,真正的"职业"靠子类填 tools_update_tool_execution

Engineer2「Alex」——写代码/建站/部署(engineer2.py:32)。工具清单包含 EditorTerminal:run_commandBrowsergit_create_pullCodeReviewDeployer 等(engineer2.py:39-51)。它的特色是每轮 _think 前先 _format_instruction当前终端目录、编辑器打开的文件注入 prompt(engineer2.py:62-73),让 LLM 知道"我现在站在哪"。核心命令 write_new_code(engineer2.py:112-142)是"独占命令",单独跑一次 LLM 专门产出整份代码文件。

DataAnalyst「David」——数据分析/爬虫/文档 QA(data_analyst.py:26)。它把 DataInterpreter(第一代数据解释器,本节末尾会与它对照)的"写码-执行-反思"循环收进一条命令 write_and_exec_code(data_analyst.py:61-125):

write_and_exec_code(data_analyst.py:93-119):
counter=0
while 未成功 and counter<3:
写代码(counter>0 时启用 reflection 反思上次报错)
在 notebook 里执行
成功 → 记 TaskResult、更新任务;失败 → 带着报错重来

这就是"数据解释器"的精髓:代码在真实 notebook 里跑,失败了带着 traceback 让 LLM 自我修正(data_analyst.py:96-119)。DataAnalyst 还有第二个工具推荐器 custom_tool_recommender,专门为"写代码"这步召回细粒度库工具(data_analyst.py:41-52)。

SWEAgent「Swen」——解 GitHub issue(swe_agent.py:17)。工具极简:BashBrowsergit_create_pull(swe_agent.py:22-27)。它每轮把 bashstate 命令输出(当前仓库状态)注入 prompt(swe_agent.py:46-54),并在评测模式下用 git diff --cached 抽出补丁存进 output_diff(swe_agent.py:62-80)——这是它跑 SWE-bench 的接口。

对照 DataInterpreter(第一代)。 同名角色 David,但 DataInterpreter 直接继承 Role 而非 RoleZero(data_interpreter.py:36),走 plan_and_act 固定模式;而 DataAnalyst 走 RoleZero 的动态命令模式。二者共用 WriteAnalysisCode/ExecuteNbCode 这套写码-执行底座,但谁来决定下一步不同:前者是 SOP,后者是 LLM 逐轮选命令。


5. 巧妙之处(可借鉴的技术)

  • 把"控制"也做成工具。 拆活(Plan.append_task)、派活(publish_team_message)、收工(end)、问人(ask_human)全是注册过的命令,和"写文件""跑终端"走同一条 tool_execution_map 通路。于是"调度"这件事被统一成"生成命令",系统没有第二套控制流。见 role_zero.py:118-171team_leader.py:37-43

  • 单主循环 + 挂起换手。 队长派活时 _set_state(-1) 把自己停掉(team_leader.py:80),被点名者干完消息回流才唤醒下一步。用"谁在动"这一个隐式令牌,避免了多智能体并发的乱序——简单但有效。

  • 每轮 re-observe。 _react 在循环体里再次 _observe(role_zero.py:316),让长任务能中途吸收新消息。这是相对传统 Role"想一次做到底"的关键升级(源码注释直接标了 Diff 2)。

  • exclusive 命令去重。 LLM 爱在一轮里重复调同一个编辑命令,parse_commands 直接只留第一个(role_zero_utils.py:137-143),把"模型手抖"挡在执行之前。

  • 把 send_to 写进 content。 LLM API 不认路由字段,move_message_info_to_content(mgx_env.py:73-89)把"谁对谁说"塞进正文,让队长/角色单看 content 就能理解会话结构——一个绕开 API 限制的朴素技巧。

  • 两段式工具召回 + force 短路。 BM25 先粗召回、LLM 再精排(tool_recommend.py:77-109);但用户明确指定工具时 force=True 直接跳过检索(tool_recommend.py:96-99),省一次 LLM。


6. 边界与局限(诚实)

  • 中枢是瓶颈也是单点。 几乎一切消息经 Mike 中转(mgx_env.py:56-58),下游只看得到队长转述的内容——队长漏传信息,下游就抓瞎(源码 docstring 自己承认这点,team_leader.py:76-78)。
  • 基本串行。 派活即挂起(team_leader.py:80),同一时刻通常只有一个角色在动;这不是为高并发设计的架构。
  • 靠 LLM 出合法 JSON。 parse_commands 堆了多层容错(repair、re-ask、转义修复,role_zero_utils.py:109-144),侧面说明"让 LLM 稳定生成结构化命令"本身就不稳。
  • 循环上限即"求人"。max_react_loop 不是报错而是 ask_human"要不要继续"(role_zero.py:328-335)——无人值守场景会卡住。
  • 经验池默认可能未开。 exp_cacheconfig.exp_pool.* 控制(decorator.py:44-46),未启用时 llm_cached_aask 就是普通调用,不省钱。
  • 成本。 每个角色每轮至少一次(quick_think 命中则更多次)LLM 调用来生成命令;开放任务轮数一多,token 消耗显著高于固定 SOP。

7. 横向对比

库内两代之争(同 shelf、同项目): 第一代 SOP(见第 4 章)用确定性换来齐整产物,适合"一句话生成一个规整仓库";第二代 MGX 用动态命令换来通用性与应变,适合开放任务(改 bug、爬数据、临场查资料)。二者共享同一套 Role/Action/Environment/Message 底座(第 1–3 章),差别只在"下一步谁定"。

与其它多智能体框架的取舍: MGX 是中枢式(队长居中)+ 单主循环;这与"去中心化群聊 / 事件驱动并发"的编排是另一条路——中枢式易于追踪和对齐,代价是并发度和队长单点。RoleZero 的"每轮 LLM 选工具命令"则与业界通行的 ReAct/工具调用 agent 同源,MetaGPT 的独到处在于把计划、调度、收工也一并做成了可被 LLM 调用的命令。


8. 代码地图(导航索引)

主题文件路径符号名
MGX 消息中转 / 群聊metagpt/environment/mgx/mgx_env.pyMGXEnv.publish_messagemove_message_info_to_contentis_public_chat
队长:拆活/派活/挂起metagpt/roles/di/team_leader.pyTeamLeaderpublish_team_messagefinish_current_task
动态智能体基类metagpt/roles/di/role_zero.pyRoleZero._think_act_react_run_commandstool_execution_map
quick_think 路由metagpt/roles/di/role_zero.pyRoleZero._quick_think
special/exclusive 命令metagpt/roles/di/role_zero.pyspecial_tool_commandsexclusive_tool_commands_run_special_command
命令解析/计划状态metagpt/utils/role_zero_utils.pyparse_commandsget_plan_status
工程师子类metagpt/roles/di/engineer2.pyEngineer2write_new_code_format_instruction
数据分析子类(写码-执行-反思)metagpt/roles/di/data_analyst.pyDataAnalyst.write_and_exec_code
Issue 求解子类metagpt/roles/di/swe_agent.pySWEAgent_format_instruction
第一代数据解释器(对照)metagpt/roles/di/data_interpreter.pyDataInterpreter._write_and_exec_code
计划模型metagpt/schema.pyTask(:457)、TaskResult(:480)、Plan(:496)、append_taskfinish_current_task
计划驱动/评审metagpt/strategy/planner.pyPlanner.update_planprocess_task_resultask_reviewget_plan_status
任务类型 → 人类先验metagpt/strategy/task_type.pyTaskTypeTaskTypeDef.guidance
工具注册表metagpt/tools/tool_registry.pyregister_toolTOOL_REGISTRYvalidate_tool_names
工具召回(BM25)metagpt/tools/tool_recommend.pyBM25ToolRecommender.recall_toolsToolRecommender.rank_tools
经验池缓存metagpt/exp_pool/decorator.pyexp_cacheExpCacheHandler
MGX 开关/装配metagpt/team.pymetagpt/software_company.pyTeam.use_mgxgenerate_repo