1. 核心设计背景与架构目标 (Architecture Motivation & Goals)

在纯前端运行的轻量智能体(InPage Agent)中,传统网页插件或扩展系统常采用刷新页面(Reloading)或重载沙箱的方式生效与卸载。这种做法不仅会打断用户正在进行的对话流与编辑器状态,且在移动端和单页应用中体验极为生硬。

核心架构目标:

  1. 零刷新热启停 (Non-Refreshing Hot-Plugging):插件启用即生效,关闭即注销,绝不依赖 location.reload()。
  2. 可数可控度量 (Countable & Controllable Metrics):所有插件(内置、市场安装、用户自定义导入)状态全量受控,具备精确的运行态与安装态计数统计。
  3. 提示词糖 (Prompt Sugar) 与上下文胶囊 (Context Badge) 双态机制:对于前端用户展现为精简、可关闭的胶囊(Badge),但输入到底层大模型(LLM)时则是具备严格结构化上下文语义的 XML/Markdown 提示词块。
  4. 即插即用的宿主桥接 (Plugin Bridge):插件与宿主 UI(对话栏、工作区、全局通知)通过受控 API 交互,互不侵入。

2. 零刷新生命周期与资源追踪 (Zero-Refresh Disposable Pattern)

2.1 传统前端插件的泄漏困境

若插件随意调用 window.addEventListener、setInterval 或在 document.body 中注入 DOM 悬浮层,当用户点击“关闭插件”时,由于函数闭包引用未释放,会导致内存泄漏、幽灵弹窗以及事件重复绑定的恶性循环。

2.2 自动资源追踪容器 (Auto-Disposable Context Container)

插件引擎为每个运行中的插件实例分配一个专属的 PluginContext。插件的所有环境调用必须经过该容器:

export interface PluginContext {
  pluginId: string;
  api: PluginContextAPI;
  
  // 自动生命周期追踪容器(卸载时自动清理,无需开发者手动一一注销)
  addEventListener: <K extends keyof DocumentEventMap>(
    target: EventTarget,
    type: K | string,
    listener: (e: any) => void,
    options?: boolean | AddEventListenerOptions
  ) => () => void;
  
  setTimeout: (handler: TimerHandler, timeout?: number) => number;
  setInterval: (handler: TimerHandler, timeout?: number) => number;
  trackDOM: <T extends HTMLElement>(element: T) => T;
  onCleanup: (disposer: () => void) => void;
}

当插件被停用(disablePlugin)时,系统自动执行:

  1. 逆序执行插件注册的 cleanups 自定义析构函数。
  2. 遍历解绑全部记录在案的 addEventListener。
  3. 批量执行 clearTimeout 与 clearInterval。
  4. 从 DOM 树彻底移除插件注入的浮动元素和被 trackDOM 追踪的节点。
  5. 释放上下文引用,实现纯净注销。

3. Prompt Sugar 与 Badge 提示词糖双态机制 (Dual-State Badge & Prompt Sugar)

3.1 提示词糖的本质

所谓的 Badge 胶囊,在本质上是交互层的提示词糖 (Prompt Sugar):

  • 面向用户端 (User View):以极简的芯片胶囊形式挂载于输入框上方或用户消息气泡中,仅展示前 15~25 个字缩略(如 选中文本: "InPage Agent · 浏览器内轻量 ...")及删除按钮,避免冗长段落挤占视觉空间。
  • 面向模型端 (Model View):在消息序列化发送时,将 Badge 内部携带的 fullContent 完整映射入 <user_contexts> 或 <user_attachments> 结构化语义块中:
<user_contexts>
  <context_badge type="selection" label="选中文本: &quot;...&quot;" preview="...">
这里是用户划选或引用的真实全量长文本,提供给大模型作为上下文分析。
  </context_badge>
</user_contexts>

用户输入的实际提问...
  • 历史消息解构回显:当渲染历史记录时,UI 自动捕获 <user_contexts> 块,将其转化为图形化的 .user-context-chip 标签置于气泡顶端,正文仅保留纯文本,兼顾了工程严谨性与界面高审美。

4. 划词智能体设计 (Select to AI, Select2AI)

作为插件体系的旗舰内置扩展,select2ai 展示了完整的划词智能体协同范式:

flowchart LR
    A["用户在任意区域划词选中"] --> B{"选区校验 (非输入框/长度>0)"}
    B -- 是 --> C["精确计算 getBoundingClientRect 坐标"]
    C --> D["浮出三元悬浮菜单 (问AI | 解释 | 翻译)"]
    D --> E1["点击 问AI: 注入 Badge -> 展开侧栏 -> 聚焦输入框"]
    D --> E2["点击 解释: 携带 Badge + 深度解释 Prompt 立即提交"]
    D --> E3["点击 翻译: 携带 Badge + 双语互译 Prompt 立即提交"]
    B -- 否 --> F["悬浮菜单自动隐藏"]

关键细节处理:

  • 点击防丢焦:在三元浮层按钮上阻止 mousedown 默认行为(e.preventDefault()),避免在点击按钮前导致原生划词选区(Selection)发生塌陷丢失。
  • 边界碰撞保护:自动检测屏幕顶部与边缘空间,若顶部视口不足 40px,自动翻转至选区下方展示。
  • 输入域避让:如果选区处于 #chat-input 或其他编辑输入框内部,自动静默抑制浮层,不干扰正常打字与编辑。

5. 插件市场与外部 JS 导入机制 (Plugin Marketplace & Dynamic JS Ingestion)

5.1 预开插件市场 (Pre-Opened Marketplace)

内置插件市场目录包含分类筛选(AI 提效、工具箱、编辑增强、工作流)、评分、下载量、功能列表与一键安装能力。已内置插件与第三方插件共用统一的管理协议。

5.2 外部标准 JS 插件规范

系统支持导入标准 JavaScript 代码(ESM export default 或 CommonJS module.exports):

export default {
  id: 'my-custom-plugin',
  name: '自定义数据解析器',
  version: '1.0.0',
  description: '自动分析表格或文本',
  category: 'tools',
  tags: ['分析', '自定义'],
  
  activate(ctx) {
    // 监听按键或事件
    ctx.addEventListener(window, 'keydown', (e) => {
      // 业务逻辑
    });
    
    // 返回销毁清理函数
    return () => {
      // 清理逻辑
    };
  }
};

系统通过安全的函数工厂与沙箱上下文包装执行,并将源码与启停状态持久化保存在 LocalStorage 中,页面二次加载时自动恢复。


6. 避坑指南与最佳实践 (Pitfalls & Best Practices)

  1. 绝对禁止刷新恢复:任何插件的开关切换必须通过 activeContext.dispose() 彻底移除监听与 DOM。切勿为了“图省事”引入 window.location.reload()。
  2. 异步运行时忙闲检测:在插件一键提交(如快捷解释或翻译)前,先检查 Agent Runtime 是否处于忙碌状态。若仍在生成上轮回答,应先安全中止或排队,防止 sendMessage 被静默丢弃。
  3. 样式隔离与层级约束:插件创建的浮动元素必须挂载在具有独立命名前缀(如 inpage-plugin-floating)或由 ctx.trackDOM 统一纳管的容器上,z-index 保持高位但低于模态对话框遮罩(Modal Overlay)。

本文归档于个人知识库 · 领域:CS & AI · 模块:04 Project_CS_AI/AI apps