跳到主要内容

MiroThinker — 深度研究 Agent 的架构与原理(总览与阅读地图)

30 秒导读: MiroThinker 是一个开源的深度研究(deep research)Agent——你给它一个需要"查很多资料、推很多步"才能答的难问题,它会自己反复搜索、抓网页、跑代码、调用子 Agent,来回几百轮,最后把答案塞进一个 \boxed{...} 里交出来。这一页带你先看清"它是什么、大盘怎么转",再给你一张读完整套文档的地图。


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

一句话定义: MiroThinker 是一个为"深度研究 / 预测"优化、在 BrowseComp 这类难基准上刷分的开源 research agent。它的价值不在"聊天",而在把一个需要几十上百步、几百次工具调用才能查清的问题真的查到底

README 里的自我定位是"a deep research agent optimized for research and prediction",并声称在 BrowseComp 基准上拿到 88.2 分(README.md:26)。

给谁用、解决什么问题。 想象你要回答这样一个问题:

"某位科学家 1990 年代发表在某冷门期刊上的那篇论文,第二作者后来创办的公司,如今的 CEO 是谁?"

这类问题的难点不是"回答",而是长链条:得先搜到论文、定位第二作者、查到他创办的公司、再查现任 CEO——中间任何一步搜错、抓不到网页、或者把上下文撑爆,整条链就断了。MiroThinker 就是冲着这种多步、易断、需要几百次工具调用的任务设计的。

它能做什么(功能):

  • 反复做网页搜索(Google / Sogou)和抓取(Jina / MarkItDown 兜底);
  • 沙箱里跑 Python(E2B);
  • 调用视觉 / 音频 / 推理等专用工具(见 05-tools-mcp.md);
  • 把一个子 Agent(浏览 Agent)当成一个工具去调,做分工;
  • 撑到几百轮不崩:靠回滚自纠 + 上下文压缩 + 失败重试。

用起来什么样(概念级,不进代码)。 最小用法就是一行命令,指定用哪个 LLM、哪套 agent 配置:

# 概念示意,取自 README quick start 的形态
uv run python main.py llm=qwen-3 agent=single_agent_keep5

main.py 里就硬编码了一个示例任务("What is the title of today's arxiv paper in computer science?"),把配置交给流水线跑一遍(apps/miroflow-agent/main.py,amain)。刷基准则走另一条外层入口,对整个数据集批量跑(见下文"主线"与 01-config-and-assembly.md)。

一句话直觉/类比。 把它想成一个不知疲倦的研究助理:你给它一个课题,它自己列检索、翻网页、记笔记、发现走错了就撕掉重来,笔记太厚了就先总结一版再继续,最后把结论工工整整写进一个方框里交给你。


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

怎么读这张图: 从上到下是"装配 → 运行 → 收尾"。上半截(Hydra → 组件)是一次性搭好舞台;中间的 Orchestrator 是反复转的主循环;最外圈的 benchmark 重试是整场跑砸了再来一遍

┌─────────────────────────────────────────┐
benchmark 外层重试 │ common_benchmark:对数据集批量跑 │
(格式错→带失败经验) │ 失败→用失败经验增强任务描述→再跑一遍 │
└───────────────────┬─────────────────────┘
│ 每题一次

┌────────────┐ create_pipeline_ ┌──────────────────────────┐
│ Hydra 配置 │──_components──────────▶ │ 装配出三大件: │
│ (YAML) │ │ ① ToolManager(工具) │
└────────────┘ │ ② LLM Client(会话) │
│ ③ OutputFormatter(格式)│
└───────────┬──────────────┘

execute_task_pipeline 建 Orchestrator


┌───────────────────────────── Orchestrator ──────────────────────────┐
│ │
│ run_main_agent() 主 Agent while 循环 │
│ 每轮: 调 LLM → 拿到工具调用 → 执行 → 结果塞回历史 → 查上下文 │
│ │ │
│ ├─ 普通工具 ──▶ ToolManager ─▶ MCP 子进程 │
│ │ (搜索/抓取/沙箱…) │
│ │ │
│ └─ "agent-*" ──▶ run_sub_agent() 子 Agent 循环 │
│ (自己也是一圈 while,当工具用) │
│ │
│ 循环结束 ──▶ AnswerGenerator.generate_and_finalize_answer() │
│ 收尾:生成/重试最终答案,抽 \boxed{},或写失败总结 │
└──────────────────────────────────────────────────────────────────────┘


final_boxed_answer ( \boxed{...} )

部件一句话职责:

部件干什么文件
Pipeline组件工厂 + 单题执行入口;create_pipeline_components 造三大件,execute_task_pipeline 建 Orchestrator 跑一题apps/miroflow-agent/src/core/pipeline.py
Orchestrator核心大脑:跑主 Agent while 循环、把子 Agent 当工具调、驱动收尾apps/miroflow-agent/src/core/orchestrator.py
ToolManager工具总管:按配置连每个 MCP 子进程、列工具定义、执行工具调用libs/miroflow-tools/src/miroflow_tools/manager.py
BaseClientLLM 会话:发请求、组织消息历史、按 keep_tool_result 裁剪、ensure_summary_context 判上下文超限apps/miroflow-agent/src/llm/base_client.py
AnswerGenerator收尾:带重试地生成最终答案、必要时写失败经验总结apps/miroflow-agent/src/core/answer_generator.py
ToolExecutor工具调用的辅助:修参数、去重判定、结果后处理、判是否该回滚apps/miroflow-agent/src/core/tool_executor.py
settings把配置翻译成运行时:MCP 子进程参数、把子 Agent 暴露成工具apps/miroflow-agent/src/config/settings.py
prompt_utils / parsing_utils系统提示模板 / 各种解析(工具调用、\boxed{}、失败总结、容错)apps/miroflow-agent/src/utils/

3. 主线走一遍(高层,不进代码)

这节把"一个任务从进到出"顺一遍,只讲流向,细节留给各章。

第 0 步:装配舞台。 create_pipeline_components(cfg) 读 Hydra 配置,造出 main_agent_tool_managersub_agent_tool_managersoutput_formatter 三样东西(pipeline.py:180 create_pipeline_components)。这一步只搭骨架,不干活。详见 01-config-and-assembly.md

第 1 步:建 Orchestrator,进主循环。 execute_task_pipeline 造 LLM Client 和 Orchestrator,然后调 run_main_agent(pipeline.py:35 execute_task_pipeline)。主 Agent 的核心是一个 while turn_count < max_turns 循环(orchestrator.py:812),每一轮:

  1. 调 LLM 拿回复(可能含工具调用);
  2. 若模型给了工具调用,逐个执行;
  3. 把工具结果塞回消息历史;
  4. 检查上下文是否要压缩。

第 2 步:工具调用分两路。 循环里对每个工具调用判断(orchestrator.py:923):

  • 名字以 agent- 开头 → 走 run_sub_agent,把子 Agent 当工具跑一圈(它内部也是一个 while 循环,orchestrator.py:390);
  • 否则 → 走 main_agent_tool_manager.execute_tool_call,连到对应 MCP 子进程执行(manager.py:198)。

第 3 步:收尾抽答案。 循环因"到达最大轮数 / 上下文超限 / 模型不再要工具"而结束后,调 generate_and_finalize_answer(orchestrator.py:1170),它带重试地让模型给最终答案,再用 _extract_boxed_content 抠出 \boxed{...} 里的内容(answer_generator.py:462;抽取见 output_formatter.py:18)。这个 final_boxed_answer 就是对外的答案。详见 04-context-and-answer.md

第 4 步(仅刷基准时):外层重试。 跑基准的外层循环发现"没抽到合规 \boxed{}"时,会把模型自己写的失败经验总结拼进任务描述,再整题重跑一遍(benchmarks/common_benchmark.py,run_single_taskwhile format_retry_count <= max_format_retries)。


4. 巧妙之处清单(指向各章)

这份代码里真正值得带走的设计,分散在各章。先给你一张"精华索引":

巧妙之处白话它妙在哪去哪一章看
interactive scaling(几百轮)把"能跑多少轮工具交互"当成和模型大小、上下文长度并列的第三个性能维度;循环用 max_turns + EXTRA_ATTEMPTS_BUFFER 撑到几百次调用(orchestrator.py:53)02-orchestrator-loop.md
rollback 自纠模型格式错了、搜索空结果、拒答、重复查询——不是硬吞,而是弹掉最后一条消息、turn 回退一步重来(orchestrator.py:180 _handle_response_format_issues)03-robustness-rollback.md
查询去重记住每个 (工具, 查询串) 的次数,重复查就回滚,逼模型换角度(orchestrator.py:257 _check_duplicate_query)03-robustness-rollback.md
容错修复模型常把参数名写错(code 而非 code_block)、把 MCP 标签写进正文——代码主动改对参数、识别标签(tool_executor.py:68 fix_tool_call_arguments)03-robustness-rollback.md
上下文压缩 + 失败经验重试上下文快撑爆时先"退一步总结";整题失败时把经过压成结构化失败总结,喂给下一次重试(answer_generator.py:170 generate_failure_summary)04-context-and-answer.md
把 sub-agent 当工具暴露子 Agent 不是特殊分支,而是被 expose_sub_agents_as_tools 包装成一个普通工具定义,主 Agent 像调工具一样调它(settings.py:383)01-config-and-assembly.md02-orchestrator-loop.md
把强模型当推理工具"推理"本身也是一个 MCP 工具(tool-reasoning,底层接 Anthropic 模型),让主模型能把难推理外包给更强的模型(settings.py:221)05-tools-mcp.md

5. 代码地图 + 阅读地图

建议阅读顺序(由浅入深):先读本页建立大盘 → 01 看"配置怎么变成运行时" → 02 看"循环怎么转,这是全项目最核心的一章" → 03、04 看两大硬骨头(鲁棒性、上下文) → 05 看工具层。

各章一句话讲什么:

  1. 01-config-and-assembly.md — 配置驱动的装配。 一份 Hydra YAML(llm=? agent=? benchmark=?)如何经 create_pipeline_components / create_mcp_server_parameters 长出 ToolManager、LLM Client、把子 Agent 暴露成工具的整套运行时。
  2. 02-orchestrator-loop.md — 编排主循环。 主 Agent 的 while 循环节奏、子 Agent 循环、一次工具调用从"模型请求"到"结果回填"的完整路径,以及 interactive scaling 的实现。
  3. 03-robustness-rollback.md — 自我纠错。 回滚计数、查询去重、should_rollback_result、参数名修复、拒答/格式标签识别——让几百轮循环不因单点跑偏而崩。
  4. 04-context-and-answer.md — 上下文管理与答案收尾。 keep_tool_result 裁剪、ensure_summary_context 退一步总结、context_compress_limit 的决策表、失败经验重试、\boxed{} 抽取。
  5. 05-tools-mcp.md — 工具即 MCP 服务。 每个工具是一个 stdio 子进程:搜索、抓取、E2B 沙箱、视觉/音频、以及"把强模型当推理工具"各自怎么接入。

代码地图(导航索引,可按符号名 grep 定位):

主题文件路径符号名
组件工厂apps/miroflow-agent/src/core/pipeline.pycreate_pipeline_components
单题执行入口apps/miroflow-agent/src/core/pipeline.pyexecute_task_pipeline
主 Agent 循环apps/miroflow-agent/src/core/orchestrator.pyOrchestrator.run_main_agent
子 Agent 循环apps/miroflow-agent/src/core/orchestrator.pyOrchestrator.run_sub_agent
回滚/格式容错apps/miroflow-agent/src/core/orchestrator.py_handle_response_format_issues
查询去重apps/miroflow-agent/src/core/orchestrator.py_check_duplicate_query
参数名修复apps/miroflow-agent/src/core/tool_executor.pyfix_tool_call_arguments
是否回滚判定apps/miroflow-agent/src/core/tool_executor.pyshould_rollback_result
最终答案 + 失败总结apps/miroflow-agent/src/core/answer_generator.pygenerate_and_finalize_answer / generate_failure_summary
\boxed{} 抽取apps/miroflow-agent/src/io/output_formatter.py_extract_boxed_content
工具管理 / 执行libs/miroflow-tools/src/miroflow_tools/manager.pyToolManager.execute_tool_call
MCP 子进程参数apps/miroflow-agent/src/config/settings.pycreate_mcp_server_parameters
子 Agent 当工具apps/miroflow-agent/src/config/settings.pyexpose_sub_agents_as_tools
上下文超限判定apps/miroflow-agent/src/llm/base_client.py(provider 实现于 src/llm/providers/)ensure_summary_context
工具结果裁剪apps/miroflow-agent/src/llm/base_client.py_remove_tool_result_from_messages
基准外层重试apps/miroflow-agent/benchmarks/common_benchmark.pyrun_single_task

本文是子库总索引(Layer 0 + Layer 1 + 阅读地图)。各机制的代码走读与图示在对应章节文件里展开;所有引用 as-of commit 370f98361553ddf787bedc5745760e04114cb161