ScriptCat Agent 子系统架构深度解析:服务组合、工具注册表与 LLM Tool Loop 全链路
前端开发者工具插件系统【免费下载链接】scriptcatScriptCat, a browser extension that can execute userscript; 脚本猫一个可以执行用户脚本的浏览器扩展项目地址https://gitcode.com/gh_mirrors/sc/scriptcat点击查看免费下载ScriptCat脚本猫在五个既有运行时上下文service worker、content、inject、offscreen、sandbox之上构建了一套完整的 AI Agent 子系统src/app/service/agent/。本文以仓库文档 docs/references/architecture-agent.md 为骨架结合源码逐层拆解其服务组合方式、全局/会话级工具注册表、LLM 流式调用与重试压缩机制、后台会话与子代理生命周期、存储选型与页面自动化权限边界。读完你将掌握Agent 功能在 ScriptCat 中如何组装、为什么需要双层工具注册表、tool loop 如何驱动一轮完整对话以及新增工具、MCP 工具和子代理类型的标准扩展路径。1. 定位构建在五上下文之上的 Agent 层从 docs/architecture.md 可知ScriptCat 是一个 Manifest V3 浏览器扩展运行在 service worker、content、inject、offscreen、sandbox 五个相互隔离的沙箱 realm 中彼此通过 packages/message 的消息通道通信。Agent 子系统不是第六个上下文而是叠加在这五个上下文之上的一层 AI 代理能力代码统一位于src/app/service/agent/按与上下文无关的core/与service_worker 组装层两部分组织用户脚本通过 content 侧的CAT.agent.*API 使用对话能力技能Skill脚本执行时复用 offscreen/sandbox 的脚本执行通道不另起炉灶。核心组装点位于 ServiceWorkerManagerconst agent new AgentService(this.api.group(agent), this.offscreenSend, resource); agent.init(); // 注入 AgentService 到 GMApi使 Agent API 走权限验证通道 const gmApi runtime.getGMApi(); if (gmApi) { gmApi.setAgentService(agent); }AgentService通过this.api.group(agent)挂载消息 action与 architecture.md 中描述的其他服务的 RPC 模式一致——Agent 层的差异在于内部组合方式而非接入Group/Server的方式。2. Service-worker 组合AgentService 是装配器而非巨类AgentService在构造器中只注入它真正需要的依赖Group、MessageSend、ResourceService随后组合出一组职责单一的窄服务。每个子服务只拿自己需要的依赖而不是统一注入一套Group/IMessageQueue/DAO 三元组。完整清单如下服务文件职责ChatServicechat_service.ts聊天请求生命周期构建 system prompt、为每个请求装配SessionToolRegistry、把 tool loop 委托给编排器AgentTaskServicetask_service.tsAgentTask的 CRUD 与调度类 cron 触发器通过同一 tool-loop 编排器运行任务SkillServiceskill_service.ts从.md或.zip源安装/更新/列出技能parseSkillMd/parseSkillZip由SkillRepo持久化AgentModelServicemodel_service.ts模型配置 CRUD以及默认/摘要模型选择由AgentModelRepo持久化MCPServicemcp.ts按配置管理MCPClient连接并把工具注册/注销到共享ToolRegistryBackgroundSessionManagerbackground_session_manager.ts跟踪后台运行中的对话流式状态、listener、待处理的ask_user提问供 UI 重新附加到进行中的会话SubAgentServicesub_agent_service.ts通过共享 tool loop 运行子代理对话带按类型区分的工具排除列表CompactServicecompact_service.ts用专门的 compact prompt 对长对话历史做摘要压缩AgentDomServicedom.tsdom_cdp.ts辅助页面自动化见下文默认模式 vs 可信模式的拆分AgentOPFSServiceopfs_service.ts同时服务 content 脚本不支持 Blob与 offscreen支持 Blob的CAT.agent.opfs请求按调用方是否携带sender分派当前实现清单可通过git grep -n export class -- src/app/service/agent/service_worker/验证。以AgentService构造器中的组装为例ToolLoopOrchestrator不持有工具注册表而是由调用方在每次callLLMWithToolLoop时传入通常是SessionToolRegistry从而保证并发会话的工具注册互相隔离this.toolLoopOrchestrator new ToolLoopOrchestrator( { // callLLM 通过 lambda 注入确保测试 spy 可以拦截 service.callLLM callLLM: (model, params, sendEvent, signal) this.callLLM(model, params, sendEvent, signal), autoCompact: (convId, generation, model, msgs, sendEvent, signal) this.compactService.autoCompact(convId, generation, model, msgs, sendEvent, signal), }, agentChatRepo );3. 工具注册表全局ToolRegistry与会话级SessionToolRegistry3.1 全局注册表与 ToolSource 分类ToolRegistry是进程级全局注册表持有的工具在进程生命周期内持续存在并按ToolSource分类builtin启动期永久注册的内置工具例如web_fetch、web_search、opfs_*以及标签页工具list_tabs、open_tab、get_tab_content、close_tab、activate_tabmcp来自MCPService管理的 MCP server 的工具skill技能元工具load_skill、execute_skill_script、read_referencesession按对话注册的工具任务工具、ask_user、agent子代理、execute_scriptscript用户脚本通过conv.chat传入的自定义工具不存入 Map而是通过回调ScriptToolCallback分派执行。从源码可见注册表的完整操作面register(source, definition, executor)、unregister(name)、unregisterBySource(source)MCP server 断开时批量清理、listBySource(source)、getDefinitions(extraTools)以及executeTools()对内置工具与脚本工具的分流处理。其中脚本工具若无回调可用会返回带可用工具列表的错误提示并引导 LLM 自我纠正例如提示若这是 skill script请改用 execute_skill_script 工具。注意工具名与其源文件不一定同名。定义在sub_agent.ts的子代理工具注册名是agenttab_tools.ts注册的get_tab_content/list_tabs/open_tab/close_tab/activate_tab没有任何共享前缀。阅读代码时要读name:字段而不是文件名。3.2 为什么需要 SessionToolRegistrySessionToolRegistry持有对全局ToolRegistry的只读引用外加自己的会话级Map。它存在的根本原因若把同名内置工具任务工具、ask_user、agent直接注册到全局注册表并发会话会用彼此的闭包互相覆盖——会话级工具必须绑定到自己的conversationId/sendEvent。其行为契约register()只写 session 自己的Map不污染 parentgetDefinitions()合并 session parent 工具session 同名遮蔽 parentextraTools最后并入且不覆盖前两者execute()构建合并 Map 后复用parent.executeTools()使附件持久化等共享逻辑不被重复实现。会话结束时该实例超出作用域被 GC 回收即完成清理无需显式 unregister 循环。4. LLM 调用链路流式、重试、自动压缩与 Tool Loop4.1 ToolLoopOrchestrator统一的一轮对话驱动ToolLoopOrchestrator驱动一次会话轮次调用模型 → 执行模型请求的工具调用 → 把结果回喂 → 重复直到模型不再调用工具或用户通过 Loop Guard/取消中止。它依赖注入的callLLM与autoCompact函数而非直接 import 具体客户端因此测试可以用 spy 替换。UI 对话与脚本驱动的对话共用同一条边界不存在两套 tool loop 实现。关键行为均有源码佐证上下文预算按模型完整上下文窗口计算使用率getContextInputTokens/getContextWindow达到 80% 时触发autoCompact而不是用固定估算提前触发工具轮持久化assistant 消息与全部 tool 消息通过commitToolRound一次性原子提交避免持久化历史暴露半轮状态若提交失败checkToolRoundDurability区分已落盘/未落盘/不确定三态——只有正向证实未落盘才允许回收本轮租约态附件not_durable确认读失败indeterminate时宁可保留租约、以persist_indeterminate终止也不误删仍被引用的文件取消终态化emitCancelled统一收敛取消路径持久化一条终态记录并发送唯一终态事件携带累计 usage/耗时落库失败不阻塞事件发送Loop Guardtool_call_guard的循环检测连续命中GUARD_ESCALATION_STRIKES 2次时暂停并向用户询问是否继续仅 UI 对话传入askUserForGuard定时任务与子代理保持仅告警不暂停。4.2 重试与错误分类retry_utils.ts定义了可重试错误的判定与退避策略export function isRetryableError(e: Error): boolean { const msg e.message; return /429|5\d\d|network|fetch|ECONNRESET/i.test(msg) !/40[0134]/.test(msg); }匹配429、5xx或网络类信号network/fetch/ECONNRESET判为可重试同时排除400、401、403、404——注意是这四个具体状态码而非所有 4xxwithRetry默认最多重试 3 次maxRetries 3指数退避1000 * 2^attempt 随机抖动调用方的AbortSignal触发时立即退出。classifyErrorCode则把错误归一为结构化错误码内置受信任码context_too_large/persist_indeterminate/tool_timeout优先其余按文案匹配context_too_large、rate_limit、auth、tool_timeout兜底api_error——这样 UI 与自动压缩可以用同一套上下文超限处理逻辑响应。4.3 Provider 归一化Provider 特定的请求/响应整形位于core/providers/anthropic.ts、openai.ts、registry.ts使编排器保持 provider 无关。以上下文输入 token 计算为例Anthropic 把缓存命中/写入 token 与断点后的input_tokens分开返回而 OpenAI 的prompt_tokens已含缓存部分因此getContextInputTokens对 anthropic 做了单独累加。5. 三种运行生命周期后台会话、子代理、定时任务5.1 后台会话Background sessionBackgroundSessionManager维护一个RunningConversation流式缓冲、已发生的工具调用、待处理的ask_user状态、abort controller其存在与否不依赖 UI 是否在监听因此 popup/options 页面可以对同一进行中的会话 attach、detach 再 reattach。handleAttach会先发送sync快照当前流式内容、pending ask_user、任务列表再把 UI 连接注册为 listener。状态机细节stop()只置为cancelling并 abort不广播终态事件真正携带累计 usage/耗时的终态事件由 orchestrator 的emitCancelled在 promise 落定后广播。清理带 30 秒延迟窗口cleanupIfDone给迟到的重连者留出机会。5.2 子代理Sub-agentSubAgentService通过与顶层聊天相同的callLLMWithToolLoop契约运行嵌套对话但通过resolveSubAgentType/getExcludeToolsForTypecore/sub_agent_types.ts解析按类型区分的工具排除列表。内置三种类型类型说明工具边界超时researcher研究型搜索/抓取/读页面只读不操作 DOM白名单web_fetch、web_search、get_tab_content、open_tab、list_tabs、close_tab、opfs_*600spage_operator页面操作标签导航、页面自动化白名单get_tab_content、list_tabs、open_tab、close_tab、activate_tab、execute_script、web_fetch、opfs_*600sgeneral通用全部工具默认类型黑名单排除ask_user、agent不能再派生子代理、不能问用户600s任务工具create_task/update_task/list_tasks对所有子代理类型始终可用用于与主代理共享任务进度。白名单模式下排除allowedTools之外的所有工具未知类型名直接抛错而非静默降级防止攻击者传任意类型名获得更宽权限。子代理事件通过subAgent字段回标到父会话父会话的流式状态更新会忽略子代理事件。chat_service.ts中创建子代理时还会组合AbortSignal.any([父信号, AbortSignal.timeout(typeConfig.timeoutMs)])并为其创建完全独立的SessionToolRegistry。5.3 定时任务Scheduled taskAgentTaskService持久化AgentTask定义AgentTaskRepo与运行记录AgentTaskRunRepo下次触发时间由core/task_scheduler.ts与 pkg/utils/cron 计算。service worker 的chrome.alarmshandleragentTaskScheduler在src/app/service/service_worker/index.ts中注册调用agent.onSchedulerTick()把到期任务通过与交互式聊天相同的 tool loop驱动执行。AgentTaskRepo使用带 generation/revision 的乐观并发控制RevisionConflictError写入经navigator.locks回退到stackAsyncTask串行化。6. 存储选型按数据形状选择后端Agent 子系统并不采用单一持久化模式而是按数据特征匹配 docs/references/architecture-data.md 的选型RepoTchrome.storage.localAgentModelRepo小型配置对象、AgentTaskRepo任务定义见 src/app/repo/agent_task.tsOPFSRepoOrigin Private File SystemAgentChatRepo会话历史可能增长很大且持有附件、AgentTaskRunRepo任务运行历史、SkillRepo技能.md/脚本包MCPServerRepoRepoTMCP server 配置。7. 页面/offscreen/sandbox 委派与权限边界7.1 用户脚本侧的 CAT.agent APIcontent 侧 src/app/service/content/gm_api/cat_agent.ts 向用户脚本暴露CAT.agent.*API。ConversationInstance包装一个会话并分派由调用脚本注册的工具调用 handler。它走的是与传统 GM API 相同的GMContext.API/PermissionVerify.API/grant注册路径带点号 grant 名与基于connect()的聊天流式传输——具体差异见 docs/references/architecture-gm-api.md。7.2 DOM 自动化默认模式 vs 可信模式并非统一开关DOM 自动化统一由 service worker 中的单个AgentDomServicedom.ts负责处理全部动作navigate、readPage、screenshot、click、fill、scroll、waitFor、executeScript、标签页监控并在需要chrome.debugger时委托给从dom_cdp.ts导入的 CDP 辅助函数cdpClick、cdpFill、cdpScreenshot、cdpStartMonitor/cdpStopMonitor/cdpPeekMonitor。dom_cdp.ts是dom.ts调用的辅助模块不是拥有独立请求路径的服务。关键点默认 vs 可信的拆分在不同动作上并不一致需要逐个核对导航与标签页簿记navigate、update、create、query永远走chrome.tabs绝不走 CDPclick/fill真正按调用方的trusted选项分支——默认模式用chrome.scripting.executeScript驱动trusted 模式委托 CDP 产生真实合成输入isTrusted: trueCDP 调用失败时回退到非 trusted 路径screenshot与trusted标志无关有独立逻辑——selector限定区域的截图永远走 CDP后台非激活标签优先 CDP、失败回退chrome.tabs.captureVisibleTab无 selector 的激活标签直接用chrome.tabs.captureVisibleTab标签页监控startMonitor/stopMonitor/peekMonitor无条件走 CDP根本不存在非 CDP 路径。CDP 会向标签附加 debugger随之带来chrome.debugger的额外权限与用户可见横幅影响其适用范围取决于具体动作而不是一个二元的默认/可信总开关。navigate还通过dom_policy.ts的assertDomUrlAllowed校验目标 URL 是否命中黑名单。7.3 OPFS 与技能脚本OPFS 访问按调用方分派AgentOPFSService.handleOPFSApi检查请求是否携带sendercontent 脚本不支持 Blob还是经postMessage到来offscreen支持 Blob据此调整行为而不是假设单一执行上下文技能脚本通过core/skill_script_executor.ts执行与普通后台/定时脚本相同的方式委托给 Sandbox见 docs/references/architecture-execution.md——Agent 子系统不引入并行的脚本执行路径。8. 测试约定service_worker/下的测试文件名并不都与源文件一一对应部分按行为分组background.test.ts覆盖background_session_manager.tsretry.test.ts覆盖retry_utils.tsautocompact.test.ts覆盖压缩触发路径。因此某个source.test.ts缺失并不代表覆盖缺口。当前清单可用git ls-tree --name-only HEAD src/app/service/agent/service_worker/ | grep test验证core/遵循同目录*.test.ts约定。整体 Vitest 约定见 docs/references/develop-testing.md。9. 扩展 Agent 子系统三条标准路径新增工具放在core/tools/下以合适的ToolSource注册builtin在启动期session在相关服务的会话装配中并实现ToolExecutor。不要把会话级工具注册到全局ToolRegistry——使用SessionToolRegistry否则并发会话会互相覆盖闭包新增 MCP 后端工具走MCPService而非手动注册。它已处理连接、命名mcp_server_tool格式源码中为mcp_${safeName}_${encodedId}_${toolName}用码点编码 server id 避免a-b/a_b碰撞与断开清理unregisterBySource(mcp)批量注销新增子代理类型扩展core/sub_agent_types.ts的SUB_AGENT_TYPES注册表配置白名单/黑名单与systemPromptAddition角色说明而不是在SubAgentService里写特判。结语ScriptCat 的 Agent 子系统是一套精心设计的组合式架构AgentService只做装配十个窄服务各司其职全局ToolRegistry承载进程级工具SessionToolRegistry保证并发会话隔离ToolLoopOrchestrator以注入依赖的方式驱动流式调用、指数退避重试、80% 上下文触发压缩与工具轮原子提交后台会话、子代理与定时任务共用同一条 tool loop。理解这些边界尤其是默认/可信模式的非统一拆分与持久化三态判定是安全、正确地扩展 ScriptCat Agent 能力的起点。赞分享前端开发者工具插件系统【免费下载链接】scriptcatScriptCat, a browser extension that can execute userscript; 脚本猫一个可以执行用户脚本的浏览器扩展项目地址https://gitcode.com/gh_mirrors/sc/scriptcat点击查看免费下载相关推荐Agent Tool Registry (ATR) 深度解析用 Python 构建类型安全、去中心化的 Agent 工具注册表Agent Tool Registry ATR 深度解析用 Python 构建类型安全、去中心化的 Agent 工具注册表 ATRAgent Tool Re人工智能AI AgentAI 安全治理策略引擎认证鉴权Agent 沙箱可观测性OpenHuman 工具层深度解析Tool 特质、默认注册表与内置工具体系OpenHuman 工具层深度解析Tool 特质、默认注册表与内置工具体系 本篇文章基于开源项目 OpenHuman面向 Mac、Windows 与 Lin人工智能AI 应用本地部署AI Agent交互助手深度研究Kilo 核心工具架构解析Tool 表示、Location 注册与结算机制深度指南Kilo 核心工具架构解析Tool 表示、Location 注册与结算机制深度指南 导读 本文以 packages/core/src/tool/AGENTS.人工智能大模型AI Agent代码智能体工具调用交互助手CLI上一篇开源语音识别工具 AsrTools一键实现高效音频转字幕的智能解决方案下一篇终极杀戮尖塔模组管理器ModTheSpire完全指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考