README_CN.md

September 20, 2026 · View on GitHub

Jev for Chrome

Jev for Chrome

check release license

一个直接驱动你当前标签页的 Chrome 扩展,决策模型是 TypeSafe Jev:它不生成文本,几百毫秒内直接选出下一步该点哪、该在哪输入、该选哪个下拉项。本项目是 browser-use/jev-ultrafast 的 Manifest V3 移植:同样的观察格式、同样的问题、同样的执行规则。社区项目,与 TypeSafe 和 Browser Use 均无隶属关系。

English | 简体中文

Google Flights 单程苏黎世到伦敦 2026-09-20,由扩展在 headless Chromium 里以真实速度完成

在弹窗里输入一个目标。十次决策,约十三秒,测试环境按真实速度录制(webm);编号徽章是模型能看到的元素。

为什么做成扩展

  • 你的浏览器,你的登录态。 它就在你已经打开的标签页里跑,用你的 Cookie、登录状态和日常 Chrome 配置。除了扩展什么都不用装,没有 Python、没有 Playwright、没有第二个浏览器。
  • 拒绝自动化浏览器的网站不再是问题。 在数据中心的 headless 浏览器里,测试套件会被好几个站点的 Cloudflare "verify you are human" 页面拦住;同样的页面在真实 Chrome 配置下正常打开,模型看到的就是页面本身。
  • 看得见,也能单步走。 徽章标出模型看到的元素,状态栏显示决策和延迟,Step 一次只执行一个决策,Copy trace 把整次运行导出来报 bug。
  • 和参考实现同一套策略,外加几件它还没做的事。 跟进点击打开的新标签页、按渲染位置点击换行的链接、不提供被其他块盖住的控件、两个独立的概率检查否决过早的 DONE。
Jev for Chromejev-ultrafastjev-browser (jkudish)browser-use
运行在你自己的 Chrome 标签页和配置Browser Harness 持有的 Chrome 标签页(CDP)Playwright 浏览器Playwright 或 Browser Use 云端
需要的运行时ChromePython、uv、Browser HarnessNodePython
每步谁做决定Jev:一次请求选操作和元素Jev:一次请求选操作和元素Jev 选动作;目标/卡住检查一个 LLM
谁写输入的文字小文本模型小文本模型文本模型或启发式那个 LLM

其他项目的描述取自它们 2026 年 9 月的 README。

工作方式

工作方式

  1. 内容脚本读取当前可见页面:每个可交互元素得到一个由代码分配的编号、角色、可访问名称和当前值;可见文本最多取 6,000 字符。不截图。
  2. 后台 worker 把目标、元素表和最近动作一次性发给 Jev。Jev 同时回答两个问题:做什么操作(CLICKTYPE_TEXTSELECTSCROLL_*WAITDONEBLOCKED),以及每种操作对应哪个元素。只消费被选中操作的那个目标答案。
  3. 如果操作是 TYPE_TEXT,由一个小型对话模型(DeepSeek、Gemini 或任何 OpenAI 兼容接口)根据目标和字段上下文给出要输入的字符串。扩展本身从不猜测字段值。
  4. 页面先把目标准备好(还没过期、滚进视口、位置稳定、没被遮住),然后通过 Chrome 的 DevTools 协议派发输入,页面收到的是和鼠标键盘一样的真实点击和按键。nanobrowser、Taxy、browser-use 都是这么做的;这需要 debugger 权限(Chrome 不允许把它设为可选),可在选项页关闭,关掉就退回合成 DOM 事件。点击只触发一次。不会偷偷替模型按 Enter:当聚焦的文本框里有内容时,会单独提供一个 PRESS_ENTER 控件,由模型自己选择(这是相对参考项目动作空间唯一的新增;arXiv、Wolfram Alpha 这类站点没有提交按钮)。

Jev 在两次调用之间没有任何记忆,所以每次请求的 state 里要把它需要的全部给到:任务本身;当前 URL、标题和可见文本;每个元素的角色、当前值、链接目标(href)和它所在的标题(section);最近的动作以及每个动作实际造成了什么("跳转到 …""页面内容变了""没有变化");以及到目前为止访问过的 URL。规则文本按 TypeSafe 文档的建议用字段名引用这些内容。

同一请求还会附带两个独立的是非题:当前页面上任务是否已经完成、最近的动作是否在原地打转。它们看不到动作选择的答案,所以是诚实的交叉检查:目标检查不支持(低于 50%)的 DONE 会被撤回一次,并告诉模型还缺什么;卡住检查不支持的 BLOCKED 同样处理。弹窗每一步都显示这两个概率,Copy trace 按钮把整次运行以 JSON 复制到剪贴板,方便报 bug。

Jev 返回的是候选项上的概率分布,弹窗里每一步都能看到模型考虑了什么、有多确定。答案会被严格校验:出现未提供的候选或分布不自洽(超出渠道两位小数的舍入)时先重问一次,再不行才终止,不做"修补"。同一个控件在六步内被选中三次,即使每次点击都改变了页面(比如反复开关一个菜单),在警告过模型之后也会以 BLOCKED 结束。

实测结果

下面每条任务都是把构建好的扩展装进 headless Chromium(真实的 service worker、内容脚本和 popup)、走 OpenRouter 跑出来的。任务来源:本扩展弹窗里的示例、参考项目、X 上别人发的演示(Steve Krouse 的 jev + kernel 试玩页、jkudish/jev-browser、Vlad Terin 的 Codex 适配器),以及 WebVoyager 风格的站点。"结果"是对最终 URL 或页面文本的独立校验,不看模型自己报的 DONE。这次运行的完整轨迹见 docs/e2e-suite-2026-09-19.md

来源任务结果步数耗时
弹窗示例Google Flights 单程苏黎世→伦敦,2026-09-201421 s
弹窗示例Wikipedia 搜 Taylor Swift 并打开 Early life39.6 s
弹窗示例把评分最高的商品加入购物车(OpenCart 演示站)56.8 s
参考项目Wikipedia 打开哥德尔不完备定理词条25.0 s
X / KrouseWikiracing:只靠链接从 Rubber duck 到 Eiffel Tower26.3 s
X / KrouseHacker News:打开头条的评论12.0 s
X / KrouseVal Town:找 Airtable API 示例37.3 s
X / jev-browserWikipedia:Coffee → Espresso13.2 s
X / jev-browserGitHub:打开 browser-use 最新 release25.2 s
X / jev-browserWikipedia:搜索 Ristretto 并停在词条05.5 s
X / TerinPython 文档:打开教程的 Data Structures 章节15.6 s
WebVoyager 风格Wiktionary:查 serendipity312.5 s
WebVoyagerarXiv:搜 "Attention Is All You Need" 并打开摘要页622.4 s
WebVoyagerHugging Face:打开 openai/whisper-large-v3310.7 s
WebVoyagerWolfram Alpha:求 x³ sin x 的导数48.3 s
WebVoyager 风格Wikibooks 食谱:打开香蕉面包配方37.4 s
WebVoyagerBBC:打开科技版块12.7 s

这轮 17 过 13。七轮下来同一套任务分别得到 9、10、11、14、13、13、13,期间在修执行器和循环的 bug,同一条任务在不同运行间会翻转。这轮的失败:

  • RistrettoPython 文档:OpenRouter 后面的文本模型在第一次 TYPE_TEXT 时返回 HTTP 429(限流),这两条在之前几轮都通过。瞬时错误现在重试三次、退避更长。
  • Val Town:跳到 docs.val.town 时在这个 headless 环境里落到了浏览器错误页,内容脚本无法运行。
  • arXiv:模型拿到了每条结果的 href 和"按相关性排序"的下拉框,仍然在按日期排序的结果里翻页,最后打开了一篇 2026 年的同名论文。这是模型的决策,不是缺上下文。

购物车这条在此前六轮都失败,原因值得记一笔:那个商店排序后把旧的商品卡片留在 DOM 里、压在新列表下面,它们能通过所有可见性检查,于是模型一直被提供一个谁都点不到的链接。观察阶段现在对每个控件做命中测试,被别的块盖住的一律不提供。Google Flights 在中文界面(hl=zh-CN、locale zh-CN)下同样 10 步完成,结果尚未加载时的一次过早 DONE 被目标检查否决。

被 Cloudflare "verify you are human" 拦住的站点(Cambridge Dictionary、Allrecipes、demo.nopcommerce.com、demo.opencart.com)在数据中心的 headless 浏览器里会停在验证页,模型会正确地报 BLOCKED。E2E_TASKS=scripts/e2e-tasks.json npm run e2e:ext 可以复现这张表。

安装

暂未上架 Chrome 应用商店。

用发布包:到 Releases 页面下载 jev-for-chrome-<版本>.zip,解压,打开 chrome://extensions,开启开发者模式,点 加载已解压的扩展程序,选择解压出来的目录。

从源码

git clone https://github.com/chy4pro/jev-for-chrome.git
cd jev-for-chrome
npm install
npm run build

然后同样方式加载 dist/ 目录。

配置

打开扩展的选项页。

Jev 渠道(三选一):

渠道端点模型
OpenRouterhttps://openrouter.ai/api/alpha/decisionstypesafe/jev-1.13
TypeSafe.aihttps://api.typesafe.ai/v1/systemonejev-latest
Cloudflare Workers AIhttps://api.cloudflare.com/client/v4/accounts/{id}/ai/runtypesafe/jev

文本助手(只在 TYPE_TEXT 时用到):OpenRouter、DeepSeek 直连,或任意 OpenAI 兼容 Base URL。切换渠道会自动填入该渠道的默认 Base URL 和模型。如果助手走 OpenRouter 且已经填了 OpenRouter key,助手的 key 可以留空。

运行参数:每次运行的最大步数、步间延迟、是否在页面上画编号徽章。

每个渠道旁边的 Test 按钮会发一个很小的真实请求并显示返回,可以在开跑前确认 key 是否可用。

使用

点工具栏图标,输入目标,按 RunStep 只执行一步,方便逐步观察决策;Stop 中止。页面上会给模型能看到的元素画编号徽章,底部有一条状态栏显示当前动作和延迟。

运行会在以下情况停止:模型给出 DONEBLOCKED、连续三个动作都没有改变页面、步数预算用完、任何渠道报错。置信度低于 50% 的 DONE/BLOCKED 会等页面稳定后再问一次,重复才算数。目标被遮挡、或文本模型无法从目标里推出该填什么,会反馈给模型,两次之后该目标不再提供。DONE 是模型的判断,不是证明,请自己看一眼页面。

哪些数据会离开你的浏览器

完整声明见 docs/PRIVACY.md(英文)。

每一步向你选择的 Jev 渠道(OpenRouter、TypeSafe 或 Cloudflare)发一个请求,内容是:你的目标、当前标签页的 URL、标题和可见文本(最多 6,000 字符)、可交互元素表(标签、当前值、链接目标)、最近十个动作。模型决定打字时,会向文本模型发一个请求,内容是目标、字段和同样的页面文本。不会发往任何别的地方,没有遥测。API key 只存在你本机的 chrome.storage.local。密码框永远不读不填,chrome:// 页面会被拒绝。申请 <all_urls> 权限是因为扩展必须读取你指定的那个标签页;没有开始运行的标签页上它什么都不做。debugger 权限只用来通过 DevTools 协议向运行中的标签页发送点击和按键;运行期间 Chrome 会在该标签页顶部显示"已开始调试"横幅,不会通过它读取任何内容。

能处理和不能处理的

能处理:链接、按钮、文本输入框、textarea、contenteditable、原生 <select>、真正渲染出来的复选框和单选框、ARIA 角色(buttonlinkcomboboxoptiontabmenuitem 等)、自动补全列表、页内和跨页导航、滚动,以及会打开新标签页的链接:agent 会跟进自己点开的新标签页继续执行,该标签页被关闭时退回原来的标签页。

不能处理:shadow root 和 iframe 里的元素、canvas 界面、文件上传、拖拽、纯键盘控件,以及通过 label 做样式、本身被隐藏的原生复选框和单选框(模型看不到它们)。密码框永远不观察也不填写。chrome:// 内部页面会被拒绝。商品卡片内部悬停才出现的浮层会被穿透点击;对话框或整页遮罩下面的元素不会。悬停才展开的菜单在可信输入下能打开,因为指针真的移过去了。

策略和边界与参考实现一致;在两个网站上跑通不代表普遍可靠。

基于 jev-dev-kit

严格的答案校验、请求和回答的类型、文本助手的返回格式约定来自 jev-dev-kit,一个从本扩展抽出来的小框架:Jev 只做选择,候选和文本通过明确的 provider 提供,循环机制共用。扩展自己保留浏览器相关的部分:元素表、规则、可信输入和标签页处理。

路线图

接下来的计划(包括做一个 MCP server,让别的 agent 把 Jev 当执行器用)在 ROADMAP.md

开发

npm run check      # 类型检查 + 单元测试 + 生产构建
npm test           # vitest
npm run typecheck

测试覆盖动作空间与答案校验、渠道适配器与重试策略、文本助手解析、DOM 快照分类、内容脚本启动守卫、页内执行器(jsdom)和 agent 循环(mock chrome),不会调用任何付费 API。设置 OPENROUTER_API_KEYscripts/test_live_e2e.ts 会发两个真实请求。

在真实浏览器里跑构建好的扩展

npx playwright install chromium          # 一次即可
npm run e2e:ext                          # 冒烟:全部启动,期望得到清晰的缺 key 错误
OPENROUTER_API_KEY=... npm run e2e:ext   # 完整运行,真实决策

脚本把 dist/ 加载进 Playwright 的 Chromium(新版 headless,不需要显示器),把设置写入 chrome.storage,打开选项页、popup 和一个真实网页,通过真实的 service worker 和内容脚本跑完目标,并把 run.log 和每步截图写到 .e2e-out/。可用变量:E2E_URLE2E_GOALE2E_MAX_STEPSE2E_OUTCHROMIUM_PATH。popup 支持 ?tabId=,测试(或分离窗口)可以指定目标标签页。

没有 root 的机器上,可用 apt-get download 拿到 Chromium 缺的共享库、dpkg -x 解包,然后把 LD_LIBRARY_PATHFONTCONFIG_FILE 指过去;本项目开发所在的容器就是这么跑的。

src/
  background/agent.ts        观察 → 决策 → 执行 循环,预算,死锁检测
  background/index.ts        消息路由,设置存储
  content/snapshot.ts        DOM 观察,节点身份缓存,新鲜度守卫
  content/executor.ts        防过期的 click / fill / select / scroll
  content/overlay.ts         徽章和状态栏
  shared/action-space.ts     元素表,Jev 问题,严格答案校验
  shared/text-helper.ts      TYPE_TEXT 取值
  shared/providers/          TypeSafe、OpenRouter、Cloudflare 适配器
  popup/, options/           React 界面
public/manifest.json         MV3 manifest(构建时复制到 dist/)

关键词

浏览器智能体, 网页自动化, 浏览器自动化, Chrome 扩展, Manifest V3, TypeSafe Jev, jev-1.13, 系统一模型, 非自回归, 决策模型, OpenRouter Decisions API, Cloudflare Workers AI, browser-use, jev-ultrafast, DOM 自动化, AI agent, browser agent, web agent, TypeScript, React, Vite

许可

MIT