Hugging Face:Blog(RSS)
精选
57AI 编辑部评分,满分 100

如何在 Chrome 扩展中使用 Transformers.js

2026-04-23 08:00· 104天前
AI 导读

本文介绍在 Chrome 扩展中集成 Transformers.js 库的具体方法,涵盖从环境配置、模型加载到前后端通信的关键步骤。通过示例代码演示了如何利用该库在扩展中实现本地机器学习推理,同时处理扩展权限限制与安全策略。文中还对比了 Web Worker 与 Service Worker 两种部署方案,并提供了性能优化建议,帮助开发者在浏览器扩展环境中高效运行 Transformer 模型。

推荐理由

Hugging Face 官方出的 Transformers.js 浏览器插件教程,想在 Chrome 里跑端侧推理的前端开发者可以直接抄,省掉自己踩坑的时间。

正文 · AI 翻译

我们最近发布了一款基于 Gemma 4 E2B 的 Transformers.js 演示版浏览器扩展,旨在帮助用户浏览网页。在构建过程中,我们针对 Manifest V3 运行时、模型加载和消息传递等方面积累了一些实用的观察经验,值得分享。

本文面向的读者

本指南面向那些希望在 Manifest V3 约束下,使用 Transformers.js 在 Chrome 扩展中运行本地 AI 功能的开发者。

阅读完本文后,你将掌握本项目所使用的相同架构:一个承载模型的后台服务工作线程、一个侧边栏聊天界面,以及一个用于页面级操作的内容脚本。

我们将构建的内容

在本指南中,我们将以已发布的扩展为参考,并以开源代码库作为实现蓝图,重现 Transformers.js Gemma 4 浏览器助手(Transformers.js Gemma 4 Browser Assistant)的核心架构。

  • 在线扩展:Chrome 网上应用店
  • 源代码:github.com/nico-martin/gemma4-browser-extension
  • 最终成果:一个由后台托管的 Transformers.js 引擎、一个侧边栏聊天界面,以及一个用于页面内容提取和高亮显示的内容脚本。

1) Chrome 扩展架构(MV3)

在深入探讨之前,先快速说明一下范围:我不会深入讲解 React UI 层或 Vite 构建配置。本文的重点是高层架构决策:每个 Chrome 运行时中运行什么,以及这些部分如何协同工作。

如果你对 Manifest V3 还不熟悉,请先阅读这篇简短概述:什么是 Manifest V3?

1.1 运行时上下文与入口点

在 MV3 中,你的架构始于 public/manifest.json 文件。本项目定义了三个入口点:

  • background.service_worker = background.js,由 src/background/background.ts 构建而来。
  • side_panel.default_path = sidebar.html,由 src/sidebar/index.html 构建而来。
  • content_scripts[].js = content.js,其 matches 规则为 http(s)://*/*,run_at 设置为 document_idle,由 src/content/content.ts 构建而来。

后台服务工作线程还会处理 chrome.action.onClicked 事件,以打开当前活动标签页的侧边栏。另一个需要了解的入口点是:可以使用 action.default_popup 定义一个弹出窗口,它非常适合快速操作。本项目使用侧边栏来实现持久聊天,但编排模式是相同的。

各部分运行位置

关键设计决策是将繁重的编排工作放在后台,并保持 UI/页面逻辑精简。

  • 后台(src/background/background.ts)是控制平面:负责智能体生命周期、模型初始化、工具执行以及特征提取等共享服务。
  • 侧边面板(src/sidebar/*)是交互层:负责聊天输入/输出、流式更新和设置控制。
  • 内容脚本(src/content/content.ts)是页面桥梁:负责 DOM 提取和高亮操作。

这种划分的一个实际结果是,对话历史也保存在后台(Agent.chatMessages)中:UI 发送诸如 AGENT_GENERATE_TEXT 之类的事件,后台追加消息、运行推理,然后向侧边面板发送回 MESSAGES_UPDATE。

这种分离避免了重复加载模型,保持了 UI 的响应性,并遵守了 Chrome 关于 DOM 访问的安全边界。

1.3 消息传递契约

一旦运行时被分离,消息传递就成为核心支柱。在这个项目中,所有消息都通过 src/shared/types.ts 中的枚举进行类型化。

  • 侧边面板 -> 后台(BackgroundTasks):

    • CHECK_MODELS, INITIALIZE_MODELS
    • AGENT_INITIALIZE, AGENT_GENERATE_TEXT, AGENT_GET_MESSAGES, AGENT_CLEAR
    • EXTRACT_FEATURES
  • 后台 -> 侧边面板(BackgroundMessages):

    • DOWNLOAD_PROGRESS, MESSAGES_UPDATE
  • 后台 -> 内容脚本(ContentTasks):

    • EXTRACT_PAGE_DATA, HIGHLIGHT_ELEMENTS, CLEAR_HIGHLIGHTS

编排规则很简单:后台是唯一的协调者;侧边面板和内容脚本是专门的执行者,负责请求操作并渲染结果。

典型的请求流程:

  1. 侧边面板发送 AGENT_GENERATE_TEXT。
  2. 后台将其追加到 Agent.chatMessages 并运行模型/工具步骤。
  3. 后台发出 MESSAGES_UPDATE。
  4. 侧边面板根据更新后的消息列表重新渲染。

2) Transformers.js 集成细节

2.1 模型与职责

在 src/shared/constants.ts 中,此扩展使用了两个模型角色:

  • 文本生成 / 大语言模型:onnx-community/gemma-4-E2B-it-ONNX(文本生成,q4f16)
  • 向量嵌入:onnx-community/all-MiniLM-L6-v2-ONNX(特征提取,fp32)

这种拆分是有意为之:Gemma 4 负责推理和工具决策,而 MiniLM 则生成嵌入向量,用于 `ask_website` 和 `find_history` 中的语义相似度搜索。

2.2 推理在哪里运行

所有推理均在后台运行(`src/background/background.ts`):

  • 通过 `pipeline("text-generation", ...)` 进行文本生成,并启用由我们新的 `DynamicCache` 类实现的一致性 KV 缓存
  • 通过 `pipeline("feature-extraction", ...)` 进行嵌入向量生成,并辅以向量归一化

这为所有标签页/会话提供了一个统一的模型宿主,避免了重复的内存占用,并保持了侧面板 UI 的响应性。由于模型是从后台 Service Worker 加载的,工件会缓存在扩展源(`chrome-extension://<extension-id>`)下,而非每个网站的源下,从而为整个扩展安装提供了一个共享缓存。

MV3 生命周期说明:Service Worker 可能被挂起并重新启动,因此模型运行时状态应被视为可恢复的,并在需要时重新初始化。

2.3 下载与缓存生命周期

模型生命周期是明确的:

  • `CHECK_MODELS` 检查已缓存的内容,并估算剩余下载大小。
  • `INITIALIZE_MODELS` 下载/初始化模型,并向 UI 发送 `DOWNLOAD_PROGRESS` 事件。
  • Long-lived instances are reused after setup:
    • 生成管道位于 `src/background/agent/Agent.ts`
    • 嵌入向量管道位于 `src/background/utils/FeatureExtractor.ts`

权限和隐私是架构的一部分,而非事后的复选框。在此项目中,`public/manifest.json` 请求了 `sidePanel`、`storage`、`scripting` 和 `tabs` 权限,以及 `http(s)://*/*` 的主机权限:

  • `sidePanel`:用于打开和控制侧面板用户体验。
  • `storage`:用于跨会话持久化工具/设置状态。
  • `tabs` + `scripting`:用于需要感知标签页的工具和页面级操作。
  • `http(s)://*/*` 的主机权限:因为内容提取/高亮功能被设计为可在任意网站上工作。

为何要保持权限范围狭窄:权限定义了用户的信任度和 Chrome 网上应用店的审核风险。只请求你的功能实际需要的权限,并明确说明推理在扩展运行时内本地执行,以便用户了解其数据在何处被处理。

3) 智能体与工具执行循环

3.1 工具调用基础(为何需要这一层)

在执行循环之前,理解模型工具调用的工作原理(任何智能体工作流的基础)会有所帮助。你传入消息加上工具架构(名称、描述和参数),然后 Transformers.js 使用模型的聊天模板将这些输入格式化为实际的提示词。由于聊天模板是模型特定的,具体的工具调用格式取决于你使用的模型。使用 Gemma-4 风格的模板时,当模型决定调用某个工具时,它会发出一个特殊的工具调用 token 块。

import { pipeline } from "@huggingface/transformers";

const generator = await pipeline(
  "text-generation",
  "onnx-community/gemma-4-E2B-it-ONNX",
  {
    dtype: "q4f16",
    device: "webgpu",
  },
);

const messages = [{ role: "user", content: "What's the weather in Bern?" }];

const output = await generator(messages, {
  max_new_tokens: 128,
  do_sample: false,
  tools: [
    {
      type: "function",
      function: {
        name: "getWeather",
        description: "Get the weather in a location",
        parameters: {
          type: "object",
          properties: {
            location: {
              type: "string",
              description: "The location to get the weather for",
            },
          },
          required: ["location"],
        },
      },
    },
  ],
});

在生成阶段,模型可以输出类似这样的内容:

<|tool_call>call:getWeather{location:<|"|>Bern<|"|>}<tool_call|>

这正是本项目拥有一个归一化层(webMcp)和一个解析器(extractToolCalls)的原因:模型输出必须被转换为确定性的工具执行。

3.2 本项目中的工具接口

src/background/agent/webMcp.tsx 将扩展工具归一化为模型友好的形状:

  • 名称、描述、输入架构、执行

示例工具包括 get_open_tabs、go_to_tab、open_url、close_tab、find_history、ask_website 和 highlight_website_element。

3.3 循环设计(Agent.runAgent)

这里的核心设计选择是将内部模型消息与面向 UI 的聊天消息分离开来:

  • 内部模型记录(messages):用于 generator(...) 中消息的 system/user/tool/assistant 轮次。
  • UI 记录(chatMessages):用户看到的内容,包括流式传输的助手文本以及工具执行元数据(tools)和性能指标。

执行流程:

  1. 将用户输入添加到 chatMessages,创建一个占位符助手消息,并流式传输 token。
  2. 使用 extractToolCalls.ts 将流式传输的/最终的模型输出解析为 { message, toolCalls }。
  3. 将用户可见的助手消息保留为纯文本,同时工具调用在后台执行。
  4. 将工具结果附加到助手工具元数据中,并将结果作为下一轮提示词输入反馈回去。
  5. 重复此过程,直到没有剩余的工具调用,然后最终确定助手内容 + 指标。

这保持了用户通信的简洁性,同时在后台保留了一个确定性的工具循环。

4) 数据边界与持久化

状态放置是另一个在 MV3 中非常重要的架构决策。在此实现中,状态根据生命周期和访问模式进行拆分:

  • 对话状态:后台内存(Agent.chatMessages),用于快速的逐轮编排。
  • 工具偏好设置:chrome.storage.local,确保设置跨会话持久化。
  • 语义历史向量:IndexedDB(VectorHistoryDB),用于存储更大的本地检索数据。
  • 提取的页面内容:后台缓存(WebsiteContentManager),以当前活动 URL 为键值。

如第 1.2 节所述,将对话历史保存在后台,可在 UI 更新时提供一个规范的全局状态。这样,短生命周期状态保存在内存中,持久化设置保存在扩展存储中,而大量检索数据则保存在本地数据库中。

5) 构建与打包说明

你不需要复杂的构建配置,但 MV3 确实要求每个运行时都有可预测的输出。

  • 在 vite.config.ts 中进行多入口构建:

    • src/sidebar/index.html
    • src/background/background.ts
    • src/content/content.ts
  • 确保输出名称/路径与 manifest 清单对齐(sidebar.html、background.js、content.js)。

  • 将内容脚本保持为独立的输出,以避免运行时加载 chunk 的问题。

目标很简单:每个 Chrome 入口点对应一个产物,且恰好放在 public/manifest.json 所期望的位置。

最终总结

实现整个项目的架构选择在于清晰的关注点分离:后台负责编排和模型执行,UI 界面保持轻量,内容脚本处理页面访问。

本项目使用了侧边面板,但同样的方法也适用于其他设置:

  • 以弹窗为主的助手:使用 action.default_popup 进行快速交互,由后台负责对话状态和模型执行。
  • 侧边面板协同助手:在持久化面板中保持长时间运行的对话,同时由后台处理工具循环和缓存。
  • 按标签页划分的智能体:当每个标签页需要拥有自己的上下文时,在后台为每个 tabId 维护一个智能体状态。
  • 混合 UI(弹窗 + 侧边面板 + 选项页面):所有 UI 入口点都与同一个后台协调器通信,并复用相同的消息契约。

实际规则很简单:决定状态存放的位置(全局、tabId 或站点范围),将该状态和模型推理保留在后台(本质上作为后台服务),并让 UI/内容运行时充当专注的客户端。

来源:Hugging Face:Blog(RSS) · huggingface.co

如何在 Chrome 扩展中使用 Transformers.js

Hugging Face:Blog(RSS)·2026-04-23 08:00·104天前
AI 导读

本文介绍在 Chrome 扩展中集成 Transformers.js 库的具体方法,涵盖从环境配置、模型加载到前后端通信的关键步骤。通过示例代码演示了如何利用该库在扩展中实现本地机器学习推理,同时处理扩展权限限制与安全策略。文中还对比了 Web Worker 与 Service Worker 两种部署方案,并提供了性能优化建议,帮助开发者在浏览器扩展环境中高效运行 Transformer 模型。

正文 · AI 翻译

我们最近发布了一款基于 Gemma 4 E2B 的 Transformers.js 演示版浏览器扩展,旨在帮助用户浏览网页。在构建过程中,我们针对 Manifest V3 运行时、模型加载和消息传递等方面积累了一些实用的观察经验,值得分享。

本文面向的读者

本指南面向那些希望在 Manifest V3 约束下,使用 Transformers.js 在 Chrome 扩展中运行本地 AI 功能的开发者。

阅读完本文后,你将掌握本项目所使用的相同架构:一个承载模型的后台服务工作线程、一个侧边栏聊天界面,以及一个用于页面级操作的内容脚本。

我们将构建的内容

在本指南中,我们将以已发布的扩展为参考,并以开源代码库作为实现蓝图,重现 Transformers.js Gemma 4 浏览器助手(Transformers.js Gemma 4 Browser Assistant)的核心架构。

  • 在线扩展:Chrome 网上应用店
  • 源代码:github.com/nico-martin/gemma4-browser-extension
  • 最终成果:一个由后台托管的 Transformers.js 引擎、一个侧边栏聊天界面,以及一个用于页面内容提取和高亮显示的内容脚本。

1) Chrome 扩展架构(MV3)

在深入探讨之前,先快速说明一下范围:我不会深入讲解 React UI 层或 Vite 构建配置。本文的重点是高层架构决策:每个 Chrome 运行时中运行什么,以及这些部分如何协同工作。

如果你对 Manifest V3 还不熟悉,请先阅读这篇简短概述:什么是 Manifest V3?

1.1 运行时上下文与入口点

在 MV3 中,你的架构始于 public/manifest.json 文件。本项目定义了三个入口点:

  • background.service_worker = background.js,由 src/background/background.ts 构建而来。
  • side_panel.default_path = sidebar.html,由 src/sidebar/index.html 构建而来。
  • content_scripts[].js = content.js,其 matches 规则为 http(s)://*/*,run_at 设置为 document_idle,由 src/content/content.ts 构建而来。

后台服务工作线程还会处理 chrome.action.onClicked 事件,以打开当前活动标签页的侧边栏。另一个需要了解的入口点是:可以使用 action.default_popup 定义一个弹出窗口,它非常适合快速操作。本项目使用侧边栏来实现持久聊天,但编排模式是相同的。

各部分运行位置

关键设计决策是将繁重的编排工作放在后台,并保持 UI/页面逻辑精简。

  • 后台(src/background/background.ts)是控制平面:负责智能体生命周期、模型初始化、工具执行以及特征提取等共享服务。
  • 侧边面板(src/sidebar/*)是交互层:负责聊天输入/输出、流式更新和设置控制。
  • 内容脚本(src/content/content.ts)是页面桥梁:负责 DOM 提取和高亮操作。

这种划分的一个实际结果是,对话历史也保存在后台(Agent.chatMessages)中:UI 发送诸如 AGENT_GENERATE_TEXT 之类的事件,后台追加消息、运行推理,然后向侧边面板发送回 MESSAGES_UPDATE。

这种分离避免了重复加载模型,保持了 UI 的响应性,并遵守了 Chrome 关于 DOM 访问的安全边界。

1.3 消息传递契约

一旦运行时被分离,消息传递就成为核心支柱。在这个项目中,所有消息都通过 src/shared/types.ts 中的枚举进行类型化。

  • 侧边面板 -> 后台(BackgroundTasks):

    • CHECK_MODELS, INITIALIZE_MODELS
    • AGENT_INITIALIZE, AGENT_GENERATE_TEXT, AGENT_GET_MESSAGES, AGENT_CLEAR
    • EXTRACT_FEATURES
  • 后台 -> 侧边面板(BackgroundMessages):

    • DOWNLOAD_PROGRESS, MESSAGES_UPDATE
  • 后台 -> 内容脚本(ContentTasks):

    • EXTRACT_PAGE_DATA, HIGHLIGHT_ELEMENTS, CLEAR_HIGHLIGHTS

编排规则很简单:后台是唯一的协调者;侧边面板和内容脚本是专门的执行者,负责请求操作并渲染结果。

典型的请求流程:

  1. 侧边面板发送 AGENT_GENERATE_TEXT。
  2. 后台将其追加到 Agent.chatMessages 并运行模型/工具步骤。
  3. 后台发出 MESSAGES_UPDATE。
  4. 侧边面板根据更新后的消息列表重新渲染。

2) Transformers.js 集成细节

2.1 模型与职责

在 src/shared/constants.ts 中,此扩展使用了两个模型角色:

  • 文本生成 / 大语言模型:onnx-community/gemma-4-E2B-it-ONNX(文本生成,q4f16)
  • 向量嵌入:onnx-community/all-MiniLM-L6-v2-ONNX(特征提取,fp32)

这种拆分是有意为之:Gemma 4 负责推理和工具决策,而 MiniLM 则生成嵌入向量,用于 `ask_website` 和 `find_history` 中的语义相似度搜索。

2.2 推理在哪里运行

所有推理均在后台运行(`src/background/background.ts`):

  • 通过 `pipeline("text-generation", ...)` 进行文本生成,并启用由我们新的 `DynamicCache` 类实现的一致性 KV 缓存
  • 通过 `pipeline("feature-extraction", ...)` 进行嵌入向量生成,并辅以向量归一化

这为所有标签页/会话提供了一个统一的模型宿主,避免了重复的内存占用,并保持了侧面板 UI 的响应性。由于模型是从后台 Service Worker 加载的,工件会缓存在扩展源(`chrome-extension://<extension-id>`)下,而非每个网站的源下,从而为整个扩展安装提供了一个共享缓存。

MV3 生命周期说明:Service Worker 可能被挂起并重新启动,因此模型运行时状态应被视为可恢复的,并在需要时重新初始化。

2.3 下载与缓存生命周期

模型生命周期是明确的:

  • `CHECK_MODELS` 检查已缓存的内容,并估算剩余下载大小。
  • `INITIALIZE_MODELS` 下载/初始化模型,并向 UI 发送 `DOWNLOAD_PROGRESS` 事件。
  • Long-lived instances are reused after setup:
    • 生成管道位于 `src/background/agent/Agent.ts`
    • 嵌入向量管道位于 `src/background/utils/FeatureExtractor.ts`

权限和隐私是架构的一部分,而非事后的复选框。在此项目中,`public/manifest.json` 请求了 `sidePanel`、`storage`、`scripting` 和 `tabs` 权限,以及 `http(s)://*/*` 的主机权限:

  • `sidePanel`:用于打开和控制侧面板用户体验。
  • `storage`:用于跨会话持久化工具/设置状态。
  • `tabs` + `scripting`:用于需要感知标签页的工具和页面级操作。
  • `http(s)://*/*` 的主机权限:因为内容提取/高亮功能被设计为可在任意网站上工作。

为何要保持权限范围狭窄:权限定义了用户的信任度和 Chrome 网上应用店的审核风险。只请求你的功能实际需要的权限,并明确说明推理在扩展运行时内本地执行,以便用户了解其数据在何处被处理。

3) 智能体与工具执行循环

3.1 工具调用基础(为何需要这一层)

在执行循环之前,理解模型工具调用的工作原理(任何智能体工作流的基础)会有所帮助。你传入消息加上工具架构(名称、描述和参数),然后 Transformers.js 使用模型的聊天模板将这些输入格式化为实际的提示词。由于聊天模板是模型特定的,具体的工具调用格式取决于你使用的模型。使用 Gemma-4 风格的模板时,当模型决定调用某个工具时,它会发出一个特殊的工具调用 token 块。

import { pipeline } from "@huggingface/transformers";

const generator = await pipeline(
  "text-generation",
  "onnx-community/gemma-4-E2B-it-ONNX",
  {
    dtype: "q4f16",
    device: "webgpu",
  },
);

const messages = [{ role: "user", content: "What's the weather in Bern?" }];

const output = await generator(messages, {
  max_new_tokens: 128,
  do_sample: false,
  tools: [
    {
      type: "function",
      function: {
        name: "getWeather",
        description: "Get the weather in a location",
        parameters: {
          type: "object",
          properties: {
            location: {
              type: "string",
              description: "The location to get the weather for",
            },
          },
          required: ["location"],
        },
      },
    },
  ],
});

在生成阶段,模型可以输出类似这样的内容:

<|tool_call>call:getWeather{location:<|"|>Bern<|"|>}<tool_call|>

这正是本项目拥有一个归一化层(webMcp)和一个解析器(extractToolCalls)的原因:模型输出必须被转换为确定性的工具执行。

3.2 本项目中的工具接口

src/background/agent/webMcp.tsx 将扩展工具归一化为模型友好的形状:

  • 名称、描述、输入架构、执行

示例工具包括 get_open_tabs、go_to_tab、open_url、close_tab、find_history、ask_website 和 highlight_website_element。

3.3 循环设计(Agent.runAgent)

这里的核心设计选择是将内部模型消息与面向 UI 的聊天消息分离开来:

  • 内部模型记录(messages):用于 generator(...) 中消息的 system/user/tool/assistant 轮次。
  • UI 记录(chatMessages):用户看到的内容,包括流式传输的助手文本以及工具执行元数据(tools)和性能指标。

执行流程:

  1. 将用户输入添加到 chatMessages,创建一个占位符助手消息,并流式传输 token。
  2. 使用 extractToolCalls.ts 将流式传输的/最终的模型输出解析为 { message, toolCalls }。
  3. 将用户可见的助手消息保留为纯文本,同时工具调用在后台执行。
  4. 将工具结果附加到助手工具元数据中,并将结果作为下一轮提示词输入反馈回去。
  5. 重复此过程,直到没有剩余的工具调用,然后最终确定助手内容 + 指标。

这保持了用户通信的简洁性,同时在后台保留了一个确定性的工具循环。

4) 数据边界与持久化

状态放置是另一个在 MV3 中非常重要的架构决策。在此实现中,状态根据生命周期和访问模式进行拆分:

  • 对话状态:后台内存(Agent.chatMessages),用于快速的逐轮编排。
  • 工具偏好设置:chrome.storage.local,确保设置跨会话持久化。
  • 语义历史向量:IndexedDB(VectorHistoryDB),用于存储更大的本地检索数据。
  • 提取的页面内容:后台缓存(WebsiteContentManager),以当前活动 URL 为键值。

如第 1.2 节所述,将对话历史保存在后台,可在 UI 更新时提供一个规范的全局状态。这样,短生命周期状态保存在内存中,持久化设置保存在扩展存储中,而大量检索数据则保存在本地数据库中。

5) 构建与打包说明

你不需要复杂的构建配置,但 MV3 确实要求每个运行时都有可预测的输出。

  • 在 vite.config.ts 中进行多入口构建:

    • src/sidebar/index.html
    • src/background/background.ts
    • src/content/content.ts
  • 确保输出名称/路径与 manifest 清单对齐(sidebar.html、background.js、content.js)。

  • 将内容脚本保持为独立的输出,以避免运行时加载 chunk 的问题。

目标很简单:每个 Chrome 入口点对应一个产物,且恰好放在 public/manifest.json 所期望的位置。

最终总结

实现整个项目的架构选择在于清晰的关注点分离:后台负责编排和模型执行,UI 界面保持轻量,内容脚本处理页面访问。

本项目使用了侧边面板,但同样的方法也适用于其他设置:

  • 以弹窗为主的助手:使用 action.default_popup 进行快速交互,由后台负责对话状态和模型执行。
  • 侧边面板协同助手:在持久化面板中保持长时间运行的对话,同时由后台处理工具循环和缓存。
  • 按标签页划分的智能体:当每个标签页需要拥有自己的上下文时,在后台为每个 tabId 维护一个智能体状态。
  • 混合 UI(弹窗 + 侧边面板 + 选项页面):所有 UI 入口点都与同一个后台协调器通信,并复用相同的消息契约。

实际规则很简单:决定状态存放的位置(全局、tabId 或站点范围),将该状态和模型推理保留在后台(本质上作为后台服务),并让 UI/内容运行时充当专注的客户端。

来源:Hugging Face:Blog(RSS)· huggingface.co