数据截至 (上游 commit 24a14b76e14a)
双 agent 主循环:Planner 定策略、Navigator 执行
30 秒导读: Nanobrowser 让浏览器自己"看网页、点按钮"完成任务。真正的"大脑调度中枢"是一个
for循环:每隔几步让 Planner(策略师) 想一想"下一步大方向",其余每一步让 Navigator(执行者) 干具体的活。任务什么时候算完,由 Planner 说了算。本章只讲这个调度循环——谁调度谁、怎么判完成、怎么容错;Navigator 内部怎么"看网页"和"点元素",留给 02 和 03。
还没建立整体心智模型?先读入口页 index — 这是什么 · 全景 · 主线 · 阅读地图 建立全局认知,再回到本章深入循环内部。
1. 这是什么(零基础也能懂)
想象你雇了两个人合作在浏览器里做事:
- 一个策略师:不动手,只负 责看当前局面、判断"任务完成了没"、给出"接下来大方向"(比如"先搜索,再点第一条结果")。
- 一个执行者:埋头干活,把"点这个按钮、往输入框打字"这类具体动作真正落到网页上。
Nanobrowser 就是让这两个"人"(其实是两次不同的大模型调用)交替上场,循环往复,直到策略师宣布"完事了"。这个负责排班、计数、喊停的调度者,就是 Executor 类。
一句话直觉: 把它想成一个**项目经理(Planner)+ 一个员工(Navigator)**在开短会——经理每几步开一次会定方向,员工其余时间照做,经理觉得目标达成了就散会。
用起来什么样: 用户在侧边栏输入一句"帮我在 GitHub 上搜 nanobrowser 并打开第一个结果",Executor 就启动这个循环,几轮之后 Planner 判定 done=true,把最终答复写进 final_answer 返回给用户。
2. 顶层全景(它大概怎么转)
整个调度都在 Executor.execute() 一个方法里,核心是一段 for 循环(agent/executor.ts:145)。
怎么读这张图: 从上往下是一次循环迭代(step),每步先问"要不要停",再决定"这步要不要开个策略会",最后一定让执行者干一次活。
┌─────────────────────────────────┐
│ execute(): for step in maxSteps │ executor.ts:145
└───────────────┬─────────────────┘
│ 每一步:
┌───────────▼───────────┐
│ shouldStop()? │ 停/暂停/失败太多 → break
└───────────┬───────────┘ executor.ts:152 / 316
│ 否
┌──────────────▼───────────────┐
│ 该开策略会吗? │
│ nSteps % planningInterval==0 │ executor.ts:157
│ 或 navigatorDone │
└──────┬────────────────┬──────┘
是 │ │ 否
┌────────▼─────────┐ │
│ runPlanner() │ │ executor.ts:235
│ → Planner 大模型 │ │
│ done? → break │ │ checkTaskCompletion:112
└────────┬─────────┘ │
└───────┬────────┘
┌───────▼────────┐
│ navigate() │ executor.ts:274
│ → Navigator 大模型│
│ 返回 navigatorDone│
└───────┬────────┘
│ 回到循环顶
▼
部件一句话职责:
| 部件 | 干什么 | 在哪 |
|---|---|---|
Executor | 调度中枢:跑循环、计步、判完成、容错、响应暂停/停止 | agent/executor.ts |
PlannerAgent | 策略师:看全局、判 done、给 next_steps;也判断这到底是不是网页任务 | agent/agents/planner.ts |
NavigatorAgent | 执行者:每步产出并执行具体动作,返回"我这步是否声明完成" | agent/agents/navigator.ts |
AgentContext | 共享黑板:nSteps、consecutiveFailures、paused/stopped、finalAnswer 等状态 | agent/types.ts:36 |
主线走一遍(高层): 用户任务 → Executor 构造两个 agent、初始化消息历史(executor.ts:89)→ 进入 for 循环 → 周期性 runPlanner 定策略 / 每步 navigate 执行 → Planner 报 done → 循环 break → 发出 TASK_OK 事件并带上 final_answer。