数据截至 (上游 commit 911b51b0d148)
Notte 总览 — 这是什么 · 全景图 · 阅读地图
30 秒导读: Notte 是一个全栈 web 自动化 agent 框架——你用一句自然语言("去 Hacker News 抓前 5 条帖子")交给它,它就驱动一个真实浏览器,边看网页边推理,一步步把任务做完。它的设计目标是三个词:快(speed)、省(cost)、稳(reliability);秘诀是"能脚本化的部分就脚本、只在真正需要判断的地方才调 AI"的混合模式,官方称能省下 50%+ 的 LLM 成本。
本章是这套文档的顶层入口:读完你应该能讲清"Notte 是什么、大概怎么转、8 个包各管什么",并知道接下来该翻哪一章。任何单一机制的细节都留给后面各章,本章只给全局认知与路由。
1. 这是什么(零基础也能懂)
一句话定义
Notte 让 AI 像人一样操作真实网页:打开页面、看内容、点按钮、填表单、翻页、抓数据——你只需要用自然语言描述目标,它负责把目标翻译成一连串真实的浏览器操作。
解决谁的什么问题
假设你要做一件"网页上的重复活"——比如批量在某网站登录并导出报表、把一堆商品页的价格抓成表格、自动 RSVP 一个 Google 表单。传统做法有两条路,各有痛点:
- 纯脚本(Playwright/Selenium):快且便宜,但脆——页面一改版、选择器一变,脚本就崩,而且写起来累。
- 纯 AI agent(每步都问大模型):灵活、抗变化,但慢又贵——每一步点击都要烧一次 LLM 调用。
Notte 的立场是"两者混着来":确定的步骤用脚本(session.execute(type="goto", ...)),只有需要临场判断的地方才交给 agent 推理(agent.run(task=...))。这就是它主打 speed / cost / reliability 的根。
它能做什么(功能一览)
| 能力 | 白话 |
|---|---|
| 跑 web agent | 给一句自然语言任务,AI 自己在网站上完成 |
| 结构化输出 | 传一个 Pydantic 模型,AI 按你的字段格式返回数据 |
| 站点交互原语 | observe(看当前页)/ execute(点、填、跳)/ scrape(抓数据),与 Playwright 兼容 |
| 混合工作流 | 脚本 + agent 混排,deterministic 部分脚本、reasoning 部分交给 AI |
| 凭据保险库 | 登录密码/MFA 存进 Vault,注入表单时不泄露给 LLM |
| 数字身份 Persona | 生成带独立邮箱/手机号、能自动过 2FA 的身份,用于注册流程 |
| 隐身会话 | 自动解 CAPTCHA、配代理、反检测(托管服务提供) |
用起来什么样
最小的本地例子(开源特性,自带 LLM key 即可跑)——摘自 README.md:48-56:
import notte
from dotenv import load_dotenv
load_dotenv()
# 开一个真实浏览器会话(headless=False 会弹出可见窗口)
with notte.Session(headless=False) as session:
# 挂一个 agent:选推理模型、限定最多 30 步
agent = notte.Agent(session=session, reasoning_model='gemini/gemini-2.5-flash', max_steps=30)
# 交一句自然语言任务,剩下它自己干
response = agent.run(task="doom scroll cat memes on google images")
想切到托管模式(浏览器跑在 Notte 云上,还带隐身/代理/保险库等高级特性)?几乎只需换个 import——README.md:62-71:
from notte_sdk import NotteClient
import os
client = NotteClient(api_key=os.getenv("NOTTE_API_KEY"))
with client.Session(open_viewer=True) as session:
agent = client.Agent(session=session, reasoning_model='gemini/gemini-2.5-flash', max_steps=30)
response = agent.run(task="doom scroll cat memes on google images")
README.md:73 点破了这个设计意图:"先在本地实验,然后 drop-in 替换 import 就切到 SDK、拿到托管浏览器 + 高级特性"。本地的 notte.Session/notte.Agent 与托管的 client.Session/client.Agent 保持同构接口——这条主线在 05-sdk-deployment.md 细讲。
一句话直觉
把 Notte 想成"给 AI 装了一双能看网页、能动鼠标的手眼,再配一个只在迷路时才叫醒的向导"——大部分路它自己走脚本,遇到岔路口才点亮大模型。
2. 顶层全景(它大概怎么转)
一张顶层图
怎么读这张图:中间竖轴是一次 agent step 的四拍(观察→推理→执行→验证),左边是驱动它的 Agent 循环,右边是承载它的 Session 与真实网页;箭头是数据/控制流向,循环每步都会重置对话再喂给 LLM。
┌──────────────────────────────────────────────┐
一句自然语言 │ Agent 主循环 (_run) │
task="…" ───────▶│ while 步数 < max_steps: step() │
└───────────────────────┬──────────────────────┘
│ 每一步 step()
▼
┌───────────────── 一次 STEP 的四拍 ─────────────────┐
│ │
│ ① 观察 session.aobserve() │ ┌──────────────┐
│ └─ 快照 DOM → 列出"带 ID 的动作空间" ◀────────┼──────│ NotteSession │
│ │ │ ├ Browser │
│ ② 推理 llm.structured_completion(messages) │ │ │ (Playwright)│
│ └─ LLM 读页面文本,只出【一个】动作 │ │ ├ 感知管线 │
│ (含 memory / state / 下一步 action) │ │ │ perception │
│ │ │ └ 控制器 │
│ ③ 执行 session.aexecute(action) ──────────────┼─────▶│ execute │──▶ 真实网页
│ └─ 把 action ID 落到 Playwright 点击/填表 │ └──────────────┘ │
│ │ │
│ ④ 验证 仅当 LLM 说"完成"时: │ 页面变化 ◀──────────┘
│ validator.validate() = LLM 当裁判 │
│ ├ 通过 → 结束,返回 answer │
│ └ 不通过 → 回灌"验证失败",继续下一步 │
└────────────────────────────────────────────────────┘
执行结果 / 观察结果 → 回灌进对话 → 进入下一 step
6 个 workspace 包各管什么
Notte 是 uv workspace 多包仓库,pyproject.toml:32-33 声明成员为 packages/* + 根。六个包按"从底层浏览器到对外形态"分层:
| 包 | 干什么 |
|---|---|
notte-core | 地基:数据模型(动作 BaseAction、观察 Observation、动作空间 ActionSpace)、配置 NotteConfig、凭据抽象 BaseVault、轨迹 Trajectory |
notte-browser | 浏览器与会话:NotteSession——快照 DOM、感知成动作空间(observe)、把动作落到 Playwright(execute)、抓数据(scrape) |
notte-agent | 大脑:NotteAgent 主循环、FalcoPrompt/FalcoPerception、CompletionValidator(LLM 裁判);唯一 agent 风格 falco(文本;早期视觉向 gufo 已移除) |
notte-llm | 模型接入:LLMEngine、结构化补全、用量追踪 LlmUsageDictTracer(底层走 litellm,兼容各家模型) |
notte-sdk | 对外 SDK:NotteClient——把 Session/Agent/Vault/scrape 映射到托管 REST API,接口与本地同构 |
notte-integrations | 第三方集成:外部浏览器 provider、通知器、存储等可插拔组件 |
早期版本还有
notte-eval(benchmark 评测)与notte-mcp(本地 FastMCP 服务器)两个包,当前 commit 已移除:MCP 能力改由官方托管端点https://api.notte.cc/mcp/提供(见 05 章)。
顶层入口在
src/notte/__init__.py:1-18:它把NotteSession重命名为notte.Session、把notte_agent.Agent暴露成notte.Agent、把NotteClient暴露成notte.Client——所以你import notte就能拿到本地/托管两套入口。
主线走一遍(高层,不进代码)
一次 agent.run(task=...) 的生命周期,端到端是这样(各环节的真源码见下方"代码地图",机制细节见对应章节):
- 进入循环 —
agent.arun()校验请求后交给_run(),while trajectory.num_steps < max_steps反复调step()(packages/notte-agent/src/notte_agent/agent.py:390_run,:401循环)。 - 观察 —
step()先observe_and_completion()调session.aobserve(),把当前 DOM 快照成一 份带 ID 的页面文本 + 动作空间(agent.py:123observe_and_completion;感知细节 → 01-perception.md)。 - 组消息 + 推理 —
get_messages()把系统提示、任务、历史轨迹、当前页面拼成对话,交给llm.structured_completion(),LLM 只输出一个动作(含 memory/state/next action)(agent.py:273get_messages,:131调用 LLM)。 - 执行 — 按动作类型分派:普通动作走
session.aexecute(action)落到 Playwright(agent.py:248-262;动作与执行 → 03-actions-execution.md)。 - 验证 — 只有当 LLM 声称"任务完成"(
CompletionAction(success=True))时,才叫validator.validate()让另一个 LLM 当裁判核对;通过才真结束,否则把"验证失败"写回轨迹继续跑(agent.py:207-247)。 - 回灌 — 每步的观察/执行/验证结果都 append 进
Trajectory,下一步get_messages()会把它们读回对话——这样 agent 才有"记忆"。整体主循环 → 02-agent-loop.md。
3. 阅读地图(按 01→05 顺序)
建议从上到下顺读,每章由浅入深、彼此用相对链接互链、不重复内容:
| 章节 | 讲什么 | 建议谁读 |
|---|---|---|
| 01-perception.md | 感知层:DOM 怎么被快照、过滤、编号,变成 LLM 能读的"带 ID 的动作空间";fast 与 deep 两种感知的取舍 | 想懂"AI 到底看到了什么"的人;所有人(这是理解全局的前提) |
| 02-agent-loop.md | Agent 主循环的四拍、每步为什么重置对话、轨迹如何充当记忆、连续失败如何熔断 | 想懂控制流/为什么这样省 token 的人 |
| 03-actions-execution.md | 动作体系(BaseAction 家族)与执行:从一个 action ID 怎么定位并落到 Playwright 的点击/填表 | 要扩展动作、或调试"点错元素"的人 |
| 04-enterprise.md | 企业级增强:凭据保险库(不泄露给 LLM)、结构化抓取、表单填充、混合脚本与 AgentFallback 兜底 | 要上生产、处理登录/敏感数据/成本的人 |
| 05-sdk-deployment.md | 对外两种形态:本地引擎 vs 托管 SDK(换个 import 即切换)、以及 MCP 服务把能力暴露给别的 LLM | 要部署、集成到别的 agent/IDE 的人 |
4. 巧妙之处速览
这是读者要带走的"精华",每条一句话点出妙在哪,并指向后续章节深挖:
- ID 化的动作空间 — 感知层把可交互元素统一编号(
* \click(id=B3)`: 登录按钮),LLM 只需吐一个 ID,不必生成脆弱的 CSS 选择器或坐标。真源码packages/notte-core/src/notte_core/space.py:122(id={action.id}`)。→ 01-perception.md - fast / deep 双感知 —
PerceptionType = Literal["fast", "deep"](packages/notte-core/src/notte_core/common/config.py:266),便宜任务用 fast 文本感知、难任务才上 deep(含视觉),是省钱的第一道闸。→ 01-perception.md - 每步重置对话 —
get_messages()每个推理步都从头重建对话而非无限追加(agent.py:277注释 "For every re[a]sonning model step, the conversation is reset"),用轨迹回放代替长上下文,控制 token 膨胀。→ 02-agent-loop.md - LLM 当裁判 — agent 说"我做完了"不算数,
CompletionValidator用另一次 LLM 调用核对任务是否真完成,不过则驳回继续跑(packages/notte-agent/src/notte_agent/common/validator.py:39CompletionValidator,agent.py:207-247)。→ 02-agent-loop.md - Vault 不泄露凭据 — 密码/MFA 存 Vault,注入表单时才替换真值,并给 LLM 输入与截图都打码(
agent.py:81-88vault setup,:90action_with_credentials)。→ 04-enterprise.md - 混合脚本 + 兜底 — 确定步骤
session.execute(...)脚本 、临场判断才agent.run(...);脚本失败还能用AgentFallback让 agent 优雅补救(README.md:280-305)。→ 04-enterprise.md - 本地/托管同构 — 本地引擎与托管 SDK 共享同一套 Session/Agent 接口,
README.md:73明说 "drop-in replace the import"。→ 05-sdk-deployment.md
5. 顶层代码地图(导航索引)
给要跳进源码的人/agent 的入口表(相对克隆根;符号名比行号更抗漂移,可直接 grep):
| 主题 | 文件 | 符号 |
|---|---|---|
| 顶层入口(本地/托管别名) | src/notte/__init__.py | Session、Agent、Client、NotteClient |
| 包 workspace 声明 | pyproject.toml | [tool.uv.workspace] members(:33,通配 packages/*) |
| Agent 门面(选 falco、挂 vault/persona) | packages/notte-agent/src/notte_agent/main.py | Agent、AgentType(默认 FALCO,:20) |
| 主循环 | packages/notte-agent/src/notte_agent/agent.py | NotteAgent._run(:390)、step(:144) |
| 一步:观察+推理 | packages/notte-agent/src/notte_agent/agent.py | observe_and_completion(:123)、get_messages(:273) |
| falco agent(文本风格) | packages/notte-agent/src/notte_agent/falco/agent.py | FalcoAgent(:15) |
| 会话:观察 | packages/notte-browser/src/notte_browser/session.py | aobserve(:362)、_aobserve_impl(:375) |
| 会话:执行 | packages/notte-browser/src/notte_browser/session.py | aexecute(:555+ 一组重载) |
| 感知渲染(带 ID 的动作空间) | packages/notte-core/src/notte_core/space.py | ActionSpace.render_actions(:105-129) |
| 感知类型 fast/deep | packages/notte-core/src/notte_core/common/config.py | PerceptionType(:266) |
| LLM 裁判 | packages/notte-agent/src/notte_agent/common/validator.py | CompletionValidator.validate(:96) |
| SDK 客户端(托管) | packages/notte-sdk/src/notte_sdk/client.py | NotteClient(:28)、Session/Agent/Vault/scrape(:84+) |
| MCP 服务 | packages/notte-mcp/src/notte_mcp/server.py | FastMCP(:94) |
引用锚定的 commit:
accc0bb38f7d35a499c408032cea2342ef50d73d(见 frontmattersourceCommit)。各机制的深入走读,请进对应章节。