SuperAPI AI 工具集

gemma-gem-browser-ai

智能体与模型

查看项目

更新于 10-15 18:09

技能介绍

用途

Gemma Gem 是一个 Chrome 扩展,通过 WebGPU 完全在设备本地运行 Google 的 Gemma 4 模型。它向每个页面注入聊天浮层,并暴露一个可调用工具的智能体循环,能够读取页面、点击元素、填写表单、执行 JavaScript 以及截图,全程不向任何服务器发送数据。本技能用于构建和扩展 Gemma Gem。

架构概览

系统由三部分组成:

  • Offscreen Document(offscreen/):通过 @huggingface/transformers 加载 ONNX 模型,运行智能体循环,流式输出 token。
  • Service Worker(background/):路由消息,处理 take_screenshot 和 run_javascript。
  • Content Script(content/):注入 Shadow DOM 聊天界面,执行 DOM 工具。

此外,agent/ 是一个零依赖模块,定义 ModelBackend 和 ToolExecutor 接口,可作为独立库提取。

安装与构建

前置条件:Node.js 18+、pnpm。

pnpm install

# 开发构建(启用日志、source maps)
pnpm build

# 生产构建(仅错误、压缩)
pnpm build:prod

加载扩展:

  1. 打开 chrome://extensions
  2. 启用开发者模式
  3. 点击“加载已解压的扩展程序”→ 选择 .output/chrome-mv3-dev/

模型在首次打开聊天时自动下载:

  • onnx-community/gemma-4-E2B-it-ONNX — 约 500 MB(默认)
  • onnx-community/gemma-4-E4B-it-ONNX — 约 1.5 GB

首次运行后模型会缓存在浏览器的 cache storage 中。

核心接口(agent/)

ModelBackend 接口定义 generate 方法,接收消息、工具定义和生成选项,返回 StreamChunk 异步生成器。ToolDefinition 包含 name、description 和 parameters(JSONSchema)。GenerateOptions 包含 maxNewTokens 和 thinking。

ToolExecutor 接口定义 execute 方法,接收工具名和参数,返回 Promise。

Agent Loop 的 runAgentLoop 函数接收用户消息、历史、模型、工具执行器、工具定义和最大迭代次数,返回 AgentEvent 异步生成器。循环中调用 model.generate,遇到 token 类型则产出 token 事件,遇到 tool_call 类型则执行工具并产出 tool_start 和 tool_result 事件,将结果追加到消息历史,遇到 done 则返回。

内置工具

工具 位置 说明
read_page_content Content script 读取页面文本/HTML 或 CSS 选择器
take_screenshot Service worker 将可见标签页截取为 PNG
click_element Content script 通过 CSS 选择器点击
type_text Content script 通过 CSS 选择器向输入框输入文本
scroll_page Content script 按像素滚动
run_javascript Service worker 在页面上下文中执行 JS

新增工具

工具定义分布在两处:定义(在 offscreen 智能体中)和执行器(在 content script 或 service worker 中)。

步骤 1 — 定义工具 schema:在 offscreen/tools/definitions.ts 中导出 ToolDefinition,包含 name、description 和 parameters。

步骤 2 — 注册到工具列表:在 offscreen/tools/index.ts 中导入定义并加入 ALL_TOOLS 数组。

步骤 3 — 在 content script 中实现执行:在 content/tools/executor.ts 的 executeContentTool 函数中通过 switch 处理工具名,例如 get_page_title 返回 document.title,read_page_content 根据 selector 返回元素文本或 body 文本,click_element 查找元素并点击,type_text 聚焦输入框、设置值并派发 input 和 change 事件。

步骤 4 — 处理 service worker 侧工具:在 background/tools.ts 的 executeSwTool 中处理 take_screenshot(调用 chrome.tabs.captureVisibleTab)和 run_javascript(调用 chrome.scripting.executeScript),其他工具返回 null 以转发到 content script。

消息路由模式

Service worker 充当消息总线,所有通信使用 chrome.runtime.sendMessage。消息类型包括 TOOL_CALL、TOOL_RESULT、TOKEN、AGENT_DONE、AGENT_ERROR。Offscreen 向 SW 发送 TOOL_CALL,SW 再通过 chrome.tabs.sendMessage 转发给 content script。

模型配置

在 offscreen/model.ts 中使用 @huggingface/transformers 的 pipeline 加载模型。MODEL_IDS 映射 E2B 和 E4B 到对应的 ONNX 模型 ID。loadModel 函数接收模型大小和进度回调,返回 TextGenerationPipeline,配置 dtype 为 q4f16,device 为 webgpu,并通过 progress_callback 报告进度。

设置与持久化

设置通过 chrome.storage.sync 存储。GemmaGemSettings 包含 modelSize(E2B 或 E4B)、thinking、maxIterations 和 disabledHosts。DEFAULT_SETTINGS 默认 modelSize 为 E2B,thinking 为 false,maxIterations 为 10,disabledHosts 为空数组。getSettings 合并默认值和已存储值,saveSettings 合并补丁后保存。disableOnCurrentSite 函数将当前主机名加入 disabledHosts。

Shadow DOM 聊天界面模式

Content script 注入 Shadow DOM 以隔离样式。injectChatOverlay 函数创建宿主 div,附加 closed 模式的 shadow root,注入样式和聊天容器,最后添加到 document.body。

调试

所有日志使用 [Gemma Gem] 前缀。开发构建记录 info/debug/warn,生产构建仅记录错误。

  • Service worker 日志:chrome://extensions → Gemma Gem → “Inspect views: service worker”
  • Offscreen document 日志(最有用:模型加载、提示词、工具调用):chrome://extensions → Gemma Gem → “Inspect views: offscreen.html”
  • Content script 日志:任意页面 DevTools → Console(过滤 [Gemma Gem])
  • 所有扩展上下文:chrome://inspect#other

在 offscreen document 日志中重点检查:模型下载进度、完整提示词构建、每轮 token 数、原始模型输出(工具调用解析前)、工具执行结果。

常见模式与注意事项

WebGPU 可用性检查:若 navigator.gpu 不存在则抛出错误,提示使用 Chrome 113+ 并启用硬件加速;请求适配器失败也抛出错误。

Offscreen document 生命周期:Chrome 可能挂起 offscreen document,发送消息前需先 ping。ensureOffscreen 函数检查 chrome.offscreen.hasDocument(),不存在则通过 chrome.offscreen.createDocument 创建,理由为 WORKERS,说明为“Run Gemma 4 inference via WebGPU”。

上下文窗口管理:Gemma 4 支持 128K token,但长上下文会拖慢推理。可通过 clear_context 按页清除历史,或限制存储轮数。trimHistory 函数在消息超过 MAX_HISTORY_TURNS * 2 时截取最后部分。

工具调用解析:Gemma 4 以结构化格式输出工具调用。自定义解析时需防范部分/流式 JSON,safeParseToolCall 函数在 JSON.parse 失败时返回 null。

DOM 工具的 CSS 选择器安全:safeQuerySelector 函数在 document.querySelector 抛出异常时返回 null,以应对模型给出的无效选择器。