commit 4e32b3193f1528235fed884fb788f0137fb6e084 Author: zyl Date: Fri Mar 6 03:36:12 2026 +0800 first commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..0f2e341 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# sgClaw + +sgClaw 项目仓库。 diff --git a/docs/L0-产品白皮书与能力全景层.md b/docs/L0-产品白皮书与能力全景层.md new file mode 100644 index 0000000..1c3cafd --- /dev/null +++ b/docs/L0-产品白皮书与能力全景层.md @@ -0,0 +1,474 @@ +# L0 — 产品白皮书与能力全景层 + +**文档版本**: 1.0 +**适用项目**: sgClaw (业数融合一平台 AI Agent 底座) +**编制日期**: 2026-03-03 + +--- + +## 1. 产品定位 + +sgClaw 是面向国家电网"业数融合一平台"的 **AI 驱动智能代理平台**。它并非一个独立应用程序,而是作为核心能力嵌入 SuperRPA 定制 Chromium 浏览器内核之中,通过浏览器 Side Panel 中的控制按钮一键激活。 + +用户只需用自然语言描述业务意图,sgClaw 即可自主理解指令语义,规划执行步骤,在 ERP、OA、财务、人力资源、经济法务等复杂业务系统中完成跨系统操作——**无需编写任何代码**。 + +> **核心比喻:一位会思考、能学习、永不犯错的数字员工。** + +sgClaw 从浏览器内核层面发起操作,与真实用户行为完全一致,不可被反自动化机制识别,从根本上解决了传统外部 RPA 工具被检测、被拦截的行业痛点。 + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ SuperRPA 定制 Chromium 浏览器 │ +│ │ +│ ┌──────────────────────┐ ┌────────────────────────────────┐ │ +│ │ 浏览器主窗口 │ │ Side Panel 控制区 │ │ +│ │ │ │ │ │ +│ │ ┌────────────────┐ │ │ ┌──────────────────────────┐ │ │ +│ │ │ ERP / OA / │ │ │ │ [启动 Agent] [停止] │ │ │ +│ │ │ 财务 / HR 等 │ │ │ │ │ │ │ +│ │ │ 业务系统页面 │ │ │ │ 指令输入: │ │ │ +│ │ │ │ │ │ │ "导出本月合规报表" │ │ │ +│ │ │ │ │ │ │ │ │ │ +│ │ └────────────────┘ │ │ │ ▼ 任务进度 │ │ │ +│ │ ▲ │ │ │ ████████░░ 80% │ │ │ +│ │ │ 内核级操作 │ │ │ │ │ │ +│ │ │ │ │ │ ✓ 已登录 ERP │ │ │ +│ │ ┌──────┴─────────┐ │ │ │ ✓ 已导出财务报表 │ │ │ +│ │ │ sgClaw 引擎 │◄─┼────┼──│ ► 正在导出合规报表... │ │ │ +│ │ │ (Rust Binary) │ │ │ │ │ │ │ +│ │ └────────────────┘ │ │ └──────────────────────────┘ │ │ +│ └──────────────────────┘ └────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 2. 行业痛点 + +国家电网及大型央企的业务运营高度依赖多套信息系统协同。一线业务人员每天需要在 5 至 10 余套系统之间反复切换,手工搬运数据,面临以下核心痛点: + +### 2.1 效率低下 + +一线员工日常需在 ERP、OA、财务管控、人力资源、经济法务、营销等多套系统间反复登录、切换、手工录入。一项跨系统操作(如合规线索提报)平均需要 **15-30 分钟**,涉及 **3-5 个系统** 的数据交叉核对。全年此类重复操作累计耗费数万人时。 + +### 2.2 人工差错 + +手工跨系统数据搬运极易出错。财务合规场景下,一个数字的录入错误可能导致审计异常,引发合规风险。据行业统计,人工跨系统操作的 **错误率约为 2%-5%**,在高强度、高压力的月末结算期间错误率更高。 + +### 2.3 培训成本高 + +新员工需要 **3-6 个月** 才能熟练掌握多套业务系统的操作流程和业务规则。人员调动频繁时,培训成本成倍增长,且经验难以沉淀、传承。 + +### 2.4 合规风险 + +手工操作缺乏完整的审计轨迹,难以事后追溯"谁在什么时间对哪个系统做了什么操作"。在日趋严格的内控与合规要求下,这构成了显著的制度性风险。 + +### 2.5 重复劳动 + +经调研分析,一线业务人员 **约 80%** 的跨系统操作属于规则明确、流程固定的重复性工作。这些工作本应由自动化工具承担,但因系统间壁垒和技术限制,长期依赖人力完成。 + +### 2.6 传统 RPA 局限 + +外部 RPA 工具(UiPath、BluePrism 等)通过屏幕抓取、模拟点击等方式操控浏览器,存在根本性缺陷: + +- **易被检测**:反自动化机制可识别 WebDriver、Selenium 等注入痕迹 +- **被系统拦截**:越来越多的业务系统部署了 Bot Detection,直接阻断 RPA 操作 +- **需专业脚本**:每个流程需要专门开发自动化脚本,维护成本高 +- **环境依赖**:对操作系统版本、屏幕分辨率、系统界面变更高度敏感 + +--- + +## 3. 核心能力矩阵 + +| 能力维度 | 能力描述 | 关键指标 | +|---------|---------|---------| +| **自然语言驱动** | 用户以自然语言(中文)描述业务意图,Agent 自主理解语义、分解任务、规划步骤并执行 | 支持复杂多步指令,意图识别准确率 > 95% | +| **内核级隐蔽操作** | 从浏览器内核层面发起 DOM 操作与事件派发,与真实用户行为在技术栈上完全一致 | 反自动化检测通过率 100%,零注入痕迹 | +| **自进化学习** | 每次成功执行的操作序列自动沉淀为 Skill,后续同类任务直接复用,无需重复推理 | Skill 复用率随使用时长持续提升 | +| **三层安全防御** | Pipeline 协议层安全 + Rust 命令验证层 + C++ 内核 MAC 强制访问控制 | 纵深防御,任一层均可独立拦截非法操作 | +| **Skill 技能仓库** | 预置覆盖财务合规、风险管控、营销、人力资源、经济法务等业务领域的操作技能包 | 开箱即用,支持自定义扩展 | +| **多模型适配** | 支持 Claude、GPT 系列、本地化模型(Qwen、ChatGLM 等),可按安全等级灵活切换 | 模型切换零代码,响应延迟 < 2s | +| **跨平台支持** | 原生支持 Linux(银河麒麟 V10)与 Windows,满足国产化适配要求 | 信创环境全面兼容 | +| **极致轻量** | Rust 编写的 Agent 引擎,资源占用极低 | 内存 ~5MB,冷启动 < 10ms | + +``` +┌─────────────────────────────────────────────────────────────┐ +│ sgClaw 核心能力全景图 │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ +│ │ 自然语言 │ │ 自进化学习 │ │ 多模型适配 │ │ +│ │ 理解与规划 │ │ Skill 沉淀 │ │ Claude/GPT/Qwen │ │ +│ └──────┬──────┘ └──────┬──────┘ └──────────┬──────────┘ │ +│ │ │ │ │ +│ ▼ ▼ ▼ │ +│ ┌──────────────────────────────────────────────────────┐ │ +│ │ sgClaw Agent 引擎 (Rust) │ │ +│ │ 内存 ~5MB | 冷启动 < 10ms │ │ +│ └───────────────────────┬──────────────────────────────┘ │ +│ │ │ +│ ┌────────────────┼────────────────┐ │ +│ ▼ ▼ ▼ │ +│ ┌────────────┐ ┌─────────────┐ ┌────────────────┐ │ +│ │ Pipeline │ │ Rust 命令 │ │ C++ 内核 MAC │ │ +│ │ 协议层安全 │ │ 验证层 │ │ 强制访问控制 │ │ +│ └────────────┘ └─────────────┘ └────────────────┘ │ +│ │ │ │ │ +│ └────────────────┼────────────────┘ │ +│ ▼ │ +│ ┌──────────────────────────────────────────────────────┐ │ +│ │ 内核级隐蔽操作 (Chromium C++ 层) │ │ +│ │ DOM 操作 · 事件派发 · 与真实用户行为完全一致 │ │ +│ └──────────────────────────────────────────────────────┘ │ +│ │ │ +│ ┌────────────────┼────────────────┐ │ +│ ▼ ▼ ▼ │ +│ ┌────────────┐ ┌─────────────┐ ┌────────────────┐ │ +│ │ Skill 仓库 │ │ 跨平台支持 │ │ 全链路审计 │ │ +│ │ 业务技能包 │ │ 麒麟/Windows │ │ trace_id 追溯 │ │ +│ └────────────┘ └─────────────┘ └────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## 4. 典型业务场景 + +### 4.1 财务合规 + +**场景示例**:合规线索提报与交叉核查 + +用户指令:*"将本月 ERP 中的异常交易记录与财务管控系统的合规规则交叉比对,生成合规线索提报清单。"* + +sgClaw 执行流程: +1. 自动登录 ERP 系统,导航至异常交易模块 +2. 按时间范围筛选并导出本月异常交易数据 +3. 切换至财务管控系统,调取对应合规规则库 +4. 逐条交叉比对,标记命中合规规则的记录 +5. 自动生成合规线索提报清单,填入指定模板 +6. 提交至审批流程,附加完整操作审计记录 + +**业务价值**:原需 2-3 小时的人工操作压缩至 **5-8 分钟**,错误率从 3% 降至 **0%**。 + +### 4.2 风险管控 + +**场景示例**:跨系统风险指标监测与异常预警 + +用户指令:*"每日自动检查 ERP 和风控系统中的关键风险指标,发现异常立即生成预警报告。"* + +sgClaw 执行流程: +1. 定时自动巡检 ERP 系统中的关键财务指标 +2. 同步核查风控系统中的风险阈值配置 +3. 对比分析指标偏离情况,识别异常模式 +4. 异常触发时自动截屏取证、生成预警报告 +5. 推送至相关负责人,并在 OA 系统创建跟踪工单 + +**业务价值**:实现 **7x24 小时** 不间断风险监控,预警响应时间从 "次日发现" 缩短至 **实时告警**。 + +### 4.3 营销 + +**场景示例**:电费异常批量处理与账单核对 + +用户指令:*"批量处理本月电费账单异常记录,对比营销系统与财务系统的数据差异。"* + +sgClaw 执行流程: +1. 进入营销系统,筛选本月标记为异常的电费账单 +2. 逐条提取异常记录的用户编号、金额、异常类型 +3. 在财务系统中查询对应的收费记录 +4. 自动比对金额差异,生成差异明细报表 +5. 对可自动修正的记录执行批量修正操作 +6. 对需人工确认的记录生成待办清单 + +**业务价值**:月均处理量从 **200 条/人日** 提升至 **5000+ 条/小时**,释放大量人力投入高价值工作。 + +### 4.4 人力资源 + +**场景示例**:社保表单自动填报与薪酬数据核验 + +用户指令:*"从 HR 系统导出本月社保基数变更人员名单,自动填入社保申报表并交叉验证薪酬数据。"* + +sgClaw 执行流程: +1. 登录 HR 系统,导出社保基数变更人员明细 +2. 自动填入社保局在线申报表单的对应字段 +3. 同步查询薪酬系统中的工资明细数据 +4. 交叉验证社保基数与实际薪酬的一致性 +5. 标记不一致记录,生成差异报告 +6. 合规记录自动提交,异常记录流转至人工复核 + +**业务价值**:每月社保申报工作从 **3-5 个工作日** 压缩至 **2-4 小时**。 + +### 4.5 经济法务 + +**场景示例**:合同履约监测与法律风险预警 + +用户指令:*"监控即将到期的合同,检查履约状态,对存在违约风险的合同生成法律风险预警。"* + +sgClaw 执行流程: +1. 在合同管理系统中筛选 30 天内到期的合同 +2. 逐一核查合同关键条款的履约状态 +3. 交叉查询 ERP 系统中的付款/交货记录 +4. 识别履约偏差,评估违约风险等级 +5. 生成法律风险预警报告,按风险等级排序 +6. 自动推送至法务部门,创建跟踪任务 + +**业务价值**:合同风险识别从 "事后补救" 转变为 **"事前预警"**,法律纠纷发生率显著降低。 + +### 4.6 协同办公 + +**场景示例**:跨系统数据同步与报表整合 + +用户指令:*"从 ERP、财务、HR 三个系统导出本月关键运营数据,汇总生成月度经营分析报表。"* + +sgClaw 执行流程: +1. 依次登录 ERP、财务、HR 系统 +2. 按预设模板提取各系统的关键运营数据 +3. 自动对齐数据口径,统一格式 +4. 汇总计算关键指标,生成月度经营分析报表 +5. 导出为标准格式,上传至 OA 系统 + +**业务价值**:月度报表整合从 **2-3 天人工汇总** 缩短至 **30 分钟自动生成**。 + +### 4.7 通用场景 + +用户只需一句自然语言指令,sgClaw 即可自主完成端到端的跨系统操作: + +| 自然语言指令 | Agent 自主完成的操作 | +|------------|-------------------| +| "导出本月所有合规报表" | 依次登录各业务系统 → 定位报表模块 → 设定时间范围 → 导出 → 汇总 | +| "检查上周新入职员工的系统权限配置" | HR 系统查询入职名单 → 各业务系统逐一核查权限 → 生成核查报告 | +| "把 ERP 里的采购订单数据同步到财务系统" | ERP 导出订单 → 格式转换 → 财务系统录入 → 数据校验 | +| "统计各部门本季度差旅报销总额" | OA 系统提取差旅审批 → 财务系统核查报销 → 按部门汇总 → 生成报表 | + +--- + +## 5. 技术优势对比 + +### 5.1 综合对比矩阵 + +| 对比维度 | 人工操作 | 传统 RPA (UiPath/BluePrism) | 外部 Agent (OpenClaw) | **sgClaw** | +|---------|---------|---------------------------|---------------------|-----------| +| **架构方式** | N/A | 外部进程控制浏览器 | 外部进程 + WebSocket | **嵌入浏览器内核** | +| **反检测能力** | 天然通过 | 易被检测拦截 | 可被端口扫描发现 | **原生行为,不可检测** | +| **安全层级** | 依赖人员素质 | 应用层安全 | 应用层安全 | **三层纵深防御** | +| **通信方式** | N/A | HTTP / COM | HTTP / WebSocket (端口暴露) | **STDIO Pipe (进程私有)** | +| **内存占用** | N/A | 200-500MB | 394MB+ | **~5MB** | +| **冷启动时间** | N/A | 10-30s | 5-15s | **< 10ms** | +| **技能复用** | 经验口传 | 需重新开发脚本 | 需重新训练 | **复用已有 JS 业务代码** | +| **部署方式** | N/A | 独立安装 + 配置 | 独立安装 + 配置 | **内嵌浏览器,零独立安装** | +| **自然语言** | N/A | 不支持 | 部分支持 | **完整支持中文自然语言** | +| **国产化适配** | N/A | 有限支持 | 不支持 | **银河麒麟 V10 原生支持** | +| **学习门槛** | 3-6 个月 | 需专业 RPA 开发 | 需技术配置 | **自然语言,零学习成本** | + +### 5.2 关键差异化优势 + +``` +┌──────────────────────────────────────────────────────────────────┐ +│ 架构差异:外部控制 vs 内核嵌入 │ +├──────────────────────────────────────────────────────────────────┤ +│ │ +│ 传统 RPA / 外部 Agent 方案: │ +│ │ +│ ┌────────────┐ HTTP/WS ┌──────────────┐ │ +│ │ RPA Engine │ ──────────────→│ 浏览器 │ │ +│ │ (外部进程) │ 端口暴露 │ (被外部控制) │ │ +│ └────────────┘ 可被检测 └──────────────┘ │ +│ 394MB+ 反自动化机制 │ +│ 可识别拦截 │ +│ │ +│ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │ +│ │ +│ sgClaw 方案: │ +│ │ +│ ┌──────────────────────────────────────────────┐ │ +│ │ SuperRPA Chromium 浏览器 │ │ +│ │ │ │ +│ │ ┌──────────┐ STDIO Pipe ┌──────────────┐ │ │ +│ │ │ sgClaw │ ◄──────────► │ Chromium C++ │ │ │ +│ │ │ (Rust) │ 进程私有 │ 内核层 │ │ │ +│ │ │ ~5MB │ 零端口暴露 │ │ │ │ +│ │ └──────────┘ └──────────────┘ │ │ +│ │ │ │ +│ │ 操作 = 原生用户行为,不可被检测 │ │ +│ └──────────────────────────────────────────────┘ │ +│ │ +└──────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 6. 安全与合规保障 + +sgClaw 将安全视为产品基因而非附加功能,构建了从通信层到内核层的 **三层纵深防御体系**。 + +### 6.1 进程隔离通信 + +- 采用 **STDIO Pipe** 作为 Agent 与浏览器内核的唯一通信通道 +- 不开放任何网络端口,外部进程无法探测或连接 +- 通信数据仅存在于父子进程的文件描述符中,操作系统级别的隐私保护 + +### 6.2 MAC 强制访问控制 + +- 浏览器 C++ 内核层实施 **Mandatory Access Control** +- 严格的域名白名单机制:Agent 仅可操作授权的业务系统域名 +- 敏感操作(如支付、审批)需额外的内核级权限校验 +- 白名单策略由管理员统一配置,Agent 无法自行绕过 + +### 6.3 凭证安全保护 + +- 用户凭证由浏览器 Zombie Session Pool 统一管理 +- 凭证信息 **永远不会通过 Pipe 协议传输** 至 Agent 进程 +- Agent 通过 BrowserAction API 间接使用已建立的会话,无需接触明文密码 + +### 6.4 人工激活机制 + +- Agent 功能 **默认关闭**,需用户在 Side Panel 中显式点击启动按钮 +- 每次启动均需用户确认,杜绝后台无感自动运行 +- 用户可随时一键停止 Agent 的所有操作 + +### 6.5 全链路审计追溯 + +- 每次 Agent 会话分配唯一 **trace_id** +- 所有操作步骤(页面导航、元素点击、数据读取、表单提交)均有完整日志记录 +- 日志包含操作时间戳、目标系统、操作类型、执行结果 +- 支持事后审计回溯与合规举证 + +### 6.6 防失控熔断机制 + +- 内置 **Circuit Breaker** 机制,防止 Agent 进入死循环或失控状态 +- 单次任务设置最大步骤数上限 +- 连续失败自动熔断,暂停执行并通知用户 +- 关键操作设置人工确认断点(human-in-the-loop) + +--- + +## 7. 产品形态与交付方式 + +### 7.1 产品形态 + +| 组件 | 形态 | 规格 | +|------|------|------| +| Agent 引擎 | Rust 编译二进制 | 约 8.8MB | +| 宿主环境 | SuperRPA 定制 Chromium 浏览器 | 集成交付 | +| 用户界面 | 浏览器 Side Panel 控制区 | 启停按钮 + 指令输入 + 任务进度 | +| Skill 仓库 | JSON 格式技能定义文件 | 随浏览器内置,支持在线更新 | +| 运行时依赖 | 无 | Rust 静态编译,零外部依赖 | + +### 7.2 交付方式 + +- **Linux (银河麒麟 V10)**:集成于 `superrpa-chromium` .deb 安装包 +- **Windows**:集成于 `superrpa-chromium` .exe 安装包 +- **无需独立安装**:随浏览器一并部署,无额外配置步骤 +- **无需独立升级**:随浏览器版本统一升级管理 + +### 7.3 用户交互流程 + +``` +用户操作流程: + + 打开 SuperRPA 浏览器 + │ + ▼ + 访问业务系统(自动登录) + │ + ▼ + 打开 Side Panel ──→ 看到 sgClaw 控制区 + │ + ▼ + 点击 [启动 Agent] 按钮 + │ + ▼ + 输入自然语言指令 ──→ "导出本月所有合规报表" + │ + ▼ + Agent 自主执行 ──→ Side Panel 实时显示进度 + │ + ▼ + 执行完成 ──→ 结果展示 / 文件下载 + │ + ▼ + (可选)点击 [停止] 终止任务 +``` + +--- + +## 8. 与 SuperRPA 浏览器的协同关系 + +sgClaw 并非独立产品,而是与 SuperRPA 浏览器深度耦合的 **智能增强层**。两者各司其职,协同构成完整的"智能数字员工"平台。 + +### 8.1 能力分工 + +``` +┌────────────────────────────────────────────────────────────────────┐ +│ "智能数字员工" 完整能力栈 │ +├────────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌──────────────────────────────────────────────────────────────┐ │ +│ │ sgClaw 智能增强层 │ │ +│ │ │ │ +│ │ ┌────────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │ │ +│ │ │ LLM 智能 │ │ 自然语言 │ │ 多步自主 │ │ 自进化学习 │ │ │ +│ │ │ 推理引擎 │ │ 理解 │ │ 任务执行 │ │ Skill 沉淀 │ │ │ +│ │ └────────────┘ └──────────┘ └──────────┘ └──────────────┘ │ │ +│ │ │ │ +│ └──────────────────────────┬───────────────────────────────────┘ │ +│ │ STDIO Pipe │ +│ ┌──────────────────────────┴───────────────────────────────────┐ │ +│ │ SuperRPA 浏览器基础设施层 │ │ +│ │ │ │ +│ │ ┌────────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │ │ +│ │ │ Zombie │ │ SDK │ │ Browser │ │ 凭证与会话 │ │ │ +│ │ │ Session │ │ 注入引擎 │ │ Action │ │ 安全管理 │ │ │ +│ │ │ Pool │ │ │ │ API │ │ │ │ │ +│ │ └────────────┘ └──────────┘ └──────────┘ └──────────────┘ │ │ +│ │ │ │ +│ │ ┌────────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │ │ +│ │ │ 反检测 │ │ 多标签页 │ │ 域名 │ │ C++ 内核 │ │ │ +│ │ │ 指纹伪装 │ │ 并发管理 │ │ 白名单 │ │ MAC 控制 │ │ │ +│ │ └────────────┘ └──────────┘ └──────────┘ └──────────────┘ │ │ +│ │ │ │ +│ └──────────────────────────────────────────────────────────────┘ │ +│ │ +├────────────────────────────────────────────────────────────────────┤ +│ 协同价值 │ +│ │ +│ SuperRPA 提供: sgClaw 增加: │ +│ ├─ Zombie Session Pool 会话池 ├─ LLM 智能推理能力 │ +│ ├─ SDK 注入与 JS 执行环境 ├─ 自然语言理解与意图解析 │ +│ ├─ BrowserAction API 操作接口 ├─ 自主多步任务规划与执行 │ +│ ├─ 凭证管理与自动登录 ├─ 自进化学习与 Skill 积累 │ +│ ├─ 反自动化检测基础设施 ├─ 跨系统业务流程编排 │ +│ └─ 内核级安全强制控制 └─ 业务语义理解与异常处理 │ +│ │ +│ 单独的 SuperRPA = 强大的自动化浏览器 │ +│ SuperRPA + sgClaw = 会思考的智能数字员工 │ +│ │ +└────────────────────────────────────────────────────────────────────┘ +``` + +### 8.2 典型协同流程 + +以"自动完成月度合规报表导出"为例: + +| 步骤 | 执行者 | 操作 | +|------|-------|------| +| 1 | SuperRPA | Zombie Session Pool 提供已登录的各系统会话 | +| 2 | sgClaw | LLM 理解用户指令,规划任务步骤 | +| 3 | sgClaw | 通过 BrowserAction API 向浏览器发送操作指令 | +| 4 | SuperRPA | SDK 注入层执行 DOM 操作(内核级,不可检测) | +| 5 | SuperRPA | C++ 内核 MAC 校验操作合法性(域名白名单) | +| 6 | sgClaw | 解析操作结果,决定下一步行动 | +| 7 | sgClaw | 任务完成后将操作序列沉淀为 Skill | +| 8 | SuperRPA | 记录完整操作审计日志(含 trace_id) | + +### 8.3 价值总结 + +sgClaw 与 SuperRPA 浏览器的结合,实现了 **"能力 + 智能"** 的完整闭环: + +- **SuperRPA 浏览器** 解决了 "如何安全、隐蔽地操作业务系统" 的基础设施问题 +- **sgClaw** 解决了 "如何智能地理解业务意图并自主执行" 的上层智能问题 +- 两者结合,使"业数融合一平台"真正具备 **"理解自然语言 → 自主规划 → 安全执行 → 持续进化"** 的完整智能数字员工能力 + +--- + +> **sgClaw — 让每一位员工都拥有一位永不疲倦、永不犯错的智能数字助手。** diff --git a/docs/L1-系统架构与安全模型层.md b/docs/L1-系统架构与安全模型层.md new file mode 100644 index 0000000..69102bd --- /dev/null +++ b/docs/L1-系统架构与安全模型层.md @@ -0,0 +1,996 @@ +# L1 — 系统架构与安全模型层 + +**文档版本**: 1.0 +**适用项目**: sgClaw (业数融合一平台 AI Agent 底座) +**编制日期**: 2026-03-03 + +--- + +## 1. 全局架构拓扑 + +### 1.1 完整架构图 + +以下是 sgClaw 系统的完整进程与组件拓扑。图中标注了新增组件(New)与已有组件(Existing), +以便评估改造范围。 + +``` +superrpa-chromium.sh (wrapper, 启动入口) + │ + ├─ watchdog (background) ─────────────────────────── [Existing, 不变] + │ └─ node local_service [Existing, 不变] + │ ├─ 本地 HTTP API (端口 9527) + │ └─ 文件上传 / 下载辅助 + │ + └─ chrome (main process, foreground) ─────────────── [Existing, 不变] + │ + ├─ SgClawProcessHost ────────────────────────── [New, Singleton in Browser Process] + │ │ + │ ├── STDIO Pipe (stdin/stdout) ──────────→ sgclaw (Rust child process) + │ │ │ + │ │ ├─ Agent Runtime ──────── ZeroClaw ReAct Loop + │ │ │ └─ think → act → observe → repeat + │ │ │ + │ │ ├─ LLM Provider ──────── Claude / GPT / 本地模型 + │ │ │ ├─ streaming 支持 + │ │ │ └─ token 用量统计 + │ │ │ + │ │ ├─ BrowserPipeTool ──── 自定义 Tool trait 实现 + │ │ │ └─ 将 Agent action 序列化为 pipe command + │ │ │ + │ │ ├─ SkillLoader ──────── 加载 JS 业务技能脚本 + │ │ │ ├─ 技能签名校验 + │ │ │ └─ 沙箱执行环境 + │ │ │ + │ │ ├─ MAC Policy ──────── 域 / Action 白名单 + │ │ │ └─ rules.json 配合浏览器侧双重校验 + │ │ │ + │ │ ├─ Critic ─────────── 输出质量评估 + │ │ │ └─ Circuit Breaker (熔断器) + │ │ │ + │ │ ├─ Memory ─────────── SQLite + Vector 向量存储 + │ │ │ ├─ 短期对话记忆 + │ │ │ └─ 长期任务知识库 + │ │ │ + │ │ └─ MCP Client ─────── rmcp (Rust MCP SDK) + │ │ └─ 连接外部 MCP Server 获取工具 + │ │ + │ ├─ PipeListener (async read loop) + │ │ └─ 解析 JSON Line,分发到 MAC 校验 + │ │ + │ ├─ MAC Whitelist Check ──────────────────── [New, ~100 lines C++] + │ │ ├─ 校验 action 是否在 pipe 允许列表 + │ │ └─ 校验 expected_domain 是否匹配当前页 + │ │ + │ └─→ CommandRouter ──────────────────────── [Existing, 40+ actions, 不变] + │ ├─ CdpCommandExecutor → 页面操作 + │ ├─ ZombiePageManager → 后台会话池 + │ └─ SessionManager → 会话状态机 + │ + ├─ CdpBridgeManager ────────────────────────── [Existing, 页内 JS SDK, 不变] + │ └─ 页面 JS → CDP binding → CommandRouter + │ + ├─ ZombiePageManager ───────────────────────── [Existing, 后台会话池, 不变] + │ └─ 最多 5 个 zombie page,自动回收 + │ + ├─ SessionManager ──────────────────────────── [Existing, 会话状态机, 不变] + │ └─ login → active → expired → re-login + │ + └─ RpaGlobalStorage ────────────────────────── [Existing, 跨页存储, 不变] + └─ key-value 存储,持久化到磁盘 +``` + +### 1.2 简化四组件视图 + +从系统集成的角度,sgClaw 的架构可简化为四个核心组件的交互: + +``` +┌─────────────────────┐ STDIO Pipe ┌─────────────────────┐ +│ │ ◄──────────────────────────► │ │ +│ SuperRPA Browser │ JSON Line Protocol │ sgClaw (Rust) │ +│ (C++ Chromium) │ 单连接、进程私有 │ 基于 ZeroClaw │ +│ │ │ │ +│ • CommandRouter │ │ • Agent Runtime │ +│ • CdpBridgeManager│ │ • BrowserPipeTool │ +│ • ZombiePageMgr │ │ • SkillLoader │ +│ • SessionManager │ │ • Memory │ +│ • SgClawProcessHost│ │ • MAC Policy │ +│ (New) │ │ • Critic │ +└─────────┬───────────┘ └──────┬──┬──────────┘ + │ │ │ + │ 页面渲染 API 调用 │ │ 加载技能 + │ 用户交互 │ │ + ▼ ▼ ▼ +┌─────────────────────┐ ┌────────────────────────────┐ +│ │ │ │ +│ 用户 (前台浏览器) │ │ LLM Cloud / Local │ +│ │ │ ├─ Claude API │ +│ • Side Panel UI │ │ ├─ GPT API │ +│ • 启动/停止按钮 │ │ └─ 本地模型 (Ollama 等) │ +│ • 任务输入框 │ │ │ +│ • 执行日志 │ ├────────────────────────────┤ +│ │ │ │ +└─────────────────────┘ │ Skill Repository │ + │ ├─ 内置技能 (OA 审批等) │ + │ ├─ 用户自定义技能 │ + │ └─ 签名校验 + 版本管理 │ + │ │ + └────────────────────────────┘ +``` + +--- + +## 2. 技术选型决策记录 + +### 2.1 基底框架选型: ZeroClaw + +在确定 sgClaw 的 Agent Runtime 底座时,团队对 Rust 生态中主流 Agent 框架进行了系统评估。 +评估维度包括:社区活跃度、内存占用、可嵌入性(是否支持替换浏览器控制层)、以及与我们 +STDIO Pipe 架构的兼容性。 + +| Framework | Language | Stars | Runtime Memory | Embeddability | Browser Layer Replaceable | 备注 | +|------------|----------|-------|----------------|----------------------|---------------------------|---------------------------| +| **ZeroClaw** | Rust | 17K | ~5 MB | High (trait-driven) | **Best** | 活跃社区,trait 抽象完善 | +| Rig | Rust | 6.2K | Minimal | Best (cargo add) | Full custom | 轻量但 Agent loop 需自建 | +| Moltis | Rust | - | ~40 MB | Medium | Via MCP only | 内存偏高,嵌入需改造 | +| OpenFang | Rust | New | ~40 MB | Low (API only) | Difficult | API Server 架构,不适合嵌入 | +| MicroClaw | Rust | 152 | - | Low (fork needed) | Via MCP only | 社区不活跃,需 fork 维护 | + +**决策:ZeroClaw** + +核心理由: + +1. **trait-driven 架构**:ZeroClaw 将 Tool、Provider、Memory、Security 全部定义为 trait, + 允许我们用自定义的 `BrowserPipeTool` 替换默认的浏览器控制层(如 Playwright/Puppeteer), + 同时复用其 Agent Runtime、Provider 抽象、Memory 系统和安全模块。 + +2. **内存优势**:~5 MB 的运行时内存占用,在 8 GB 总内存预算内几乎可以忽略不计。 + 相比 Moltis/OpenFang 的 ~40 MB,差异显著。 + +3. **ReAct Loop 成熟**:ZeroClaw 内置 think → act → observe 循环,支持 streaming、 + multi-turn、tool-use,无需从零构建。 + +4. **MCP 生态兼容**:内置 rmcp client,可在需要时连接外部 MCP Server 扩展工具集。 + +### 2.2 通信协议选型: STDIO Pipe + +sgClaw (Rust) 与 SuperRPA Browser (C++) 之间的 IPC 通道是整个架构的关键路径。 +团队评估了四种 IPC 机制: + +| 方式 | Linux 支持 | Windows 支持 | 安全性 | 多连接支持 | 延迟 | +|------------------|-------------|---------------------|---------------------------------|-----------|----------| +| **STDIO Pipe** | fd inherit | HANDLE inherit | **最高** (进程私有,无法外部连接) | 否 | ~0.1 ms | +| Unix Domain Socket| /tmp/sock | 不支持 | 高 (文件权限控制) | 是 | ~0.2 ms | +| Named Pipe | 不支持 | \\\\.\pipe\ | 高 (DACL) | 是 | ~0.2 ms | +| TCP localhost | 支持 | 支持 | **低** (任意进程可连) | 是 | ~0.5 ms | + +**决策:STDIO Pipe** + +核心理由: + +1. **安全性最高**:STDIO Pipe 是进程私有的文件描述符(Linux)或句柄(Windows), + 只有父子进程间可以访问。本机其他进程无法连接、监听或注入命令。 + 这从物理层面杜绝了 Pipe Hijack 攻击。 + +2. **跨平台统一**:Chromium 的 `base::LaunchProcess` 已经抽象了跨平台的管道创建, + Linux 使用 `pipe()` + `fork()`/`exec()`,Windows 使用 `CreatePipe()` + `CreateProcess()`。 + 我们不需要编写任何平台特定代码。 + +3. **单连接足够**:sgClaw 是 Browser 的唯一 Rust 子进程,一条 STDIO Pipe 即可满足 + 全部双向通信需求。不需要多客户端并发连接的能力。 + +4. **延迟最低**:内核态 pipe buffer(Linux 默认 64 KB)的读写延迟约 0.1 ms, + 远低于 socket 方案。 + +### 2.3 Why Rust + +为什么 sgClaw 选择 Rust 而非 C++/Python/Node.js: + +1. **内存安全无 GC**:在 8 GB 总内存限制下,GC 语言的内存开销和暂停不可接受。 + Rust 的零成本抽象确保 ~5 MB 运行时内存,无 GC 停顿。 + +2. **跨平台编译**:目标平台包括 Linux 银河麒麟 V10 SP1 (x86_64) 和 Windows 10/11。 + Rust 的 cross-compilation 工具链 (`cross`, `cargo-zigbuild`) 可直接生成两个平台的二进制。 + +3. **极小二进制体积**:release 构建约 8.8 MB(含 LLM provider、memory、MCP client 全部功能), + Windows 约 9 MB。无需额外运行时依赖。 + +4. **冷启动极快**:< 10 ms 内完成进程初始化和 pipe handshake,用户点击"启动"后几乎无感。 + +5. **ZeroClaw 生态**:ZeroClaw 本身及其核心依赖(rmcp、tokenizers 等)均为 Rust 实现, + 选择 Rust 可直接复用,无需 FFI 桥接。 + +--- + +## 3. 进程模型与生命周期 + +### 3.1 进程层次结构 + +sgClaw 嵌入 SuperRPA 浏览器的既有进程树中,作为 Chrome 主进程的子进程存在。 + +``` +superrpa-chromium.sh (wrapper, PID 1001) + │ + ├─ watchdog (PID 1002, background, 守护进程) + │ └─ node local_service (PID 1003, HTTP API) + │ ├─ 监听 127.0.0.1:9527 + │ ├─ 文件上传/下载 + │ └─ 与浏览器通过 HTTP 通信 + │ + └─ chrome (PID 1004, foreground, 用户交互主进程) + │ + │ 当用户在 Side Panel 点击 [启动] 按钮时: + │ SgClawProcessHost::Start() 被调用 + │ + └─ sgclaw (PID 1005, child of chrome) + │ + ├─ stdin ← chrome stdout (接收命令响应/事件) + ├─ stdout → chrome stdin (发送命令请求) + │ + ├─ Agent Runtime (ReAct loop) + ├─ LLM Provider (网络出口) + └─ Memory (SQLite 文件 I/O) +``` + +关键特征: + +- sgclaw 是 chrome 的**直接子进程**,继承 chrome 的 UID/GID 权限 +- STDIO Pipe 的文件描述符在 `fork()`/`exec()` 时自动继承,无需额外传递 +- 当 chrome 进程退出时,sgclaw 会收到 SIGPIPE/EOF,自行退出 +- watchdog 和 local_service 与 sgclaw **无直接通信**,完全独立 + +### 3.2 生命周期状态机 + +``` + ┌──────────────────────────────────┐ + │ Side Panel UI: [启动] 按钮 │ + └──────────────┬───────────────────┘ + │ 用户点击 + ▼ + ┌──────────────────────────────────┐ + │ SgClawProcessHost::Start() │ + │ │ + │ 1. Check: sgclaw 二进制存在? │ + │ → 不存在: 报错,终止 │ + │ │ + │ 2. Check: 是否已有实例运行? │ + │ → 已运行: 直接返回 │ + │ │ + │ 3. 创建 pipe pair (读端+写端) │ + │ │ + │ 4. base::LaunchProcess( │ + │ "sgclaw", pipe_opts) │ + │ │ + │ 5. 启动 PipeReader async loop │ + │ │ + │ 6. 发送 handshake: │ + │ {"type":"init","version":"1.0"}│ + │ │ + │ 7. 等待 sgClaw ack (超时 5s) │ + │ → 超时: Kill + 报错 │ + │ │ + │ 8. Notify UI: Running │ + └──────────────┬───────────────────┘ + │ + ┌──────────────┴───────────────────┐ + │ Running (正常运行) │ + │ │ + │ • PipeReader 持续监听响应 │ + │ • Agent Runtime 接受用户任务 │ + │ • 命令经 pipe 发往 Browser │ + └──────┬──────────┬──────────┬─────┘ + │ │ │ + 用户点击 浏览器 sgClaw + [停止] 正常退出 异常崩溃 + │ │ │ + ▼ ▼ ▼ + ┌──────────┐ ┌─────────┐ ┌──────────────────┐ + │ Stop() │ │Shutdown()│ │ OnProcessCrash() │ + │ │ │ │ │ │ + │ 发送 │ │ 发送 │ │ 1. 记录崩溃日志 │ + │ shutdown │ │ SIGTERM │ │ 2. 收集 stderr │ + │ 命令 │ │ + 等待 │ │ 3. 通知 UI 异常 │ + │ 等待退出 │ │ 2s 超时 │ │ 4. 关闭 pipe │ + │ Kill │ │ SIGKILL │ │ │ + └────┬─────┘ └────┬────┘ │ *** 不自动重启 ***│ + │ │ └────────┬─────────┘ + ▼ ▼ ▼ + ┌──────────────────────────────────┐ + │ Stopped (已停止) │ + │ │ + │ • Pipe 已关闭 │ + │ • 子进程已退出 │ + │ • UI 显示 [启动] 按钮 │ + │ • 用户可再次点击启动 │ + └──────────────────────────────────┘ +``` + +**关键决策:崩溃不自动重启** + +当 sgclaw 进程崩溃时(如 panic、OOM、非零退出码),系统**不会**自动重启。 +用户必须显式点击 [启动] 按钮才能再次启动。理由: + +- 防止恶意 prompt injection 导致的无限崩溃重启循环 +- 防止异常状态下的资源耗尽(内存泄漏累积、日志磁盘写满) +- 给用户明确的故障感知,而非静默恢复后行为异常 + +### 3.3 内存预算 (8 GB 约束) + +目标部署环境为 8 GB 内存的银河麒麟工作站。以下是各组件的内存分配: + +``` +┌───────────────────────────────────┬────────────┬──────────┐ +│ 组件 │ 内存占用 │ 备注 │ +├───────────────────────────────────┼────────────┼──────────┤ +│ OS + Desktop (银河麒麟 V10) │ ~2.0 GB │ 固定开销 │ +│ Browser Process (主进程) │ ~0.2 GB │ 已有 │ +│ Foreground Tabs (1-3 tabs) │ ~0.3-0.9 GB│ 用户页面 │ +│ Side Panel (Agent UI) │ ~0.05 GB │ Vue SPA │ +│ Zombie Page Pool (max 5) │ ~0.15-0.25 GB│ 后台会话 │ +│ local_service (Node.js) │ ~0.1 GB │ 已有 │ +│ sgClaw (Rust binary) │ ~0.005 GB │ 5 MB │ +│ LLM context cache (对话历史) │ ~0.05 GB │ 内存缓存 │ +│ SQLite + vector memory │ ~0.02 GB │ 磁盘为主 │ +├───────────────────────────────────┼────────────┼──────────┤ +│ **Total** │ **~3.0-3.6 GB** │ │ +│ **Headroom** │ **~4.4-5.0 GB** │ >50% │ +└───────────────────────────────────┴────────────┴──────────┘ +``` + +sgClaw 的 ~5 MB 内存占用仅为总预算的 0.06%,对系统几乎零负担。 +即使在极端场景下(3 个前台标签 + 5 个 zombie page + LLM 长对话), +总内存使用也不超过 4 GB,保留超过 50% 的余量供操作系统和突发需求使用。 + +--- + +## 4. 安全架构 + +### 4.1 三层纵深防御模型 + +sgClaw 的安全架构采用纵深防御(Defense in Depth)策略,从内到外设置三道独立的安全层。 +任何单一层被突破时,上层仍能有效阻止攻击。 + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ │ +│ Layer 3: 浏览器内核层 (C++ Browser Process) │ +│ 最后一道防线 —— 不信任外部一切输入 │ +│ │ +│ ┌─ MAC 强制访问控制 ───────────────────────────────────────────┐ │ +│ │ │ │ +│ │ • 域白名单: 仅 rules.json 中配置的域允许特权操作 │ │ +│ │ → 未知域的 click/type 等操作直接拒绝 │ │ +│ │ │ │ +│ │ • Action 白名单: pipe 来源命令受限于安全子集 │ │ +│ │ → eval、executeJsInPage 等危险操作禁止通过 pipe 调用 │ │ +│ │ │ │ +│ │ • 凭证隔离: credential_store 数据永不通过 pipe 暴露 │ │ +│ │ → 登录操作由 SessionManager 内部完成 │ │ +│ │ │ │ +│ │ • 速率限制: 单域每秒最多 N 次特权操作 (可配置,默认 10) │ │ +│ │ → 超限后暂停该域操作 30 秒 │ │ +│ │ │ │ +│ └──────────────────────────────────────────────────────────────┘ │ +│ │ +├─────────────────────────────────────────────────────────────────────────┤ +│ │ +│ Layer 2: Rust 中枢层 (sgClaw Agent) │ +│ 核心原则 —— 不信任 LLM 输出 │ +│ │ +│ ┌─ 命令校验沙箱 ──────────────────────────────────────────────┐ │ +│ │ │ │ +│ │ • JSON Schema 严格校验: LLM 输出必须符合预定义的命令格式 │ │ +│ │ → 非法字段、类型错误、缺失必要参数均被拒绝 │ │ +│ │ │ │ +│ │ • 禁止危险命令: LLM 不可生成 eval / executeJsInPage 类命令 │ │ +│ │ → BrowserPipeTool 的 action 枚举中不包含这些操作 │ │ +│ │ │ │ +│ │ • Human-in-the-loop: 敏感操作弹窗确认 │ │ +│ │ → sessionLogin / sessionLogout / 大批量操作 │ │ +│ │ │ │ +│ │ • 序列号 + HMAC: 每条 pipe 消息携带递增 sequence_id │ │ +│ │ → 防止消息重放和篡改 │ │ +│ │ │ │ +│ │ • 熔断器 (Circuit Breaker): 连续失败 N 次自动停止 Agent │ │ +│ │ → 默认阈值 10 次,指数退避冷却 │ │ +│ │ │ │ +│ └──────────────────────────────────────────────────────────────┘ │ +│ │ +├─────────────────────────────────────────────────────────────────────────┤ +│ │ +│ Layer 1: 管道传输层 (STDIO Pipe) │ +│ 物理隔离 —— 传输通道本身不可被第三方访问 │ +│ │ +│ ┌─ 安全通道 ──────────────────────────────────────────────────┐ │ +│ │ │ │ +│ │ • STDIO Pipe: 进程私有 fd/HANDLE,无法被外部进程连接 │ │ +│ │ → 不经过文件系统、不绑定端口、不暴露地址 │ │ +│ │ │ │ +│ │ • Handshake 校验: 启动时双向版本握手 │ │ +│ │ → 版本不匹配则立即断开 │ │ +│ │ │ │ +│ │ • 递增 sequence_id: 所有消息附带全局递增序列号 │ │ +│ │ → 检测到乱序或重复即报警并断开 │ │ +│ │ │ │ +│ │ • 格式约束: JSON Line 格式,单消息最大 1 MB │ │ +│ │ → 超大消息直接丢弃,防止内存 DoS │ │ +│ │ │ │ +│ └──────────────────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────────────┘ +``` + +### 4.2 各层防御详解 + +**Layer 1 — 管道传输层** + +本层的设计目标是确保 sgClaw 与 Browser 之间的通信通道从物理层面不可被第三方窃听或注入。 + +防御对象: +- 本机恶意进程尝试连接或嗅探 IPC 通道 +- 中间人攻击(消息篡改、注入伪造命令) +- 消息重放(录制合法命令序列后重新发送) + +实现机制: +- STDIO Pipe 使用操作系统内核的匿名管道,文件描述符仅在父子进程间继承, + `/proc/[pid]/fd/` 对其他用户不可见(取决于 procfs 挂载选项,银河麒麟默认安全) +- 启动时的 handshake 消息包含协议版本号,防止二进制版本不匹配导致的解析错误 +- 每条消息的 `sequence_id` 全局递增,接收方校验连续性,检测到跳号或重复立即断开 pipe + +**Layer 2 — Rust 中枢层** + +本层的核心假设是 **LLM 的输出不可信**。无论是 prompt injection 还是 hallucination, +LLM 可能生成任何格式、任何内容的输出。sgClaw 必须在将命令发往 Browser 之前进行严格校验。 + +防御对象: +- Prompt injection:恶意网页内容被 LLM 读取后,诱导其生成危险操作 +- Hallucination:LLM 生成不存在的 action 或格式错误的参数 +- 无限循环:LLM 陷入 retry 死循环,持续消耗资源 + +实现机制: +- `BrowserPipeTool` 定义了一个封闭的 action 枚举(enum),LLM 只能选择预定义的安全操作 +- 每个 action 都有对应的 JSON Schema,参数类型、范围、必填项均有严格约束 +- Critic 模块在 Agent 每步 observe 后评估输出质量,异常时触发终止 +- Circuit Breaker 在连续 10 次操作失败后自动停止 Agent loop,需用户手动恢复 +- 敏感操作(涉及登录、批量删除等)通过 Side Panel UI 弹窗请求用户确认 + +**Layer 3 — 浏览器内核层** + +本层是安全架构的最后防线。即使 sgClaw 的 Rust 层被完全绕过(理论上几乎不可能), +Browser 的 C++ 代码仍会独立执行 MAC 检查。 + +防御对象: +- 已突破 Layer 2 的命令(假设 sgClaw 被完全控制) +- 未授权域上的操作(sgClaw 试图操作不在白名单中的网站) +- 凭证窃取(任何试图通过 pipe 读取存储密码的操作) + +实现机制: +- `rules.json` 白名单由管理员配置,列出允许 Agent 操作的域名(如 `oa.example.com`) +- 每条 pipe 命令到达 Browser 后,先校验 `expected_domain` 是否与当前页面的实际域名一致 +- `credential_store`(Linux Keyring / Windows Credential Manager)的 API 在 pipe handler 中 + 没有任何暴露点——登录凭证只能由 SessionManager 在 Browser 内部使用 +- 速率限制器独立于 sgClaw 的 Circuit Breaker,即使后者被禁用,Browser 侧的限速仍然生效 + +--- + +## 5. 威胁模型与对抗策略 + +### 5.1 威胁分类总表 + +| # | 威胁名称 | 描述 | 影响 | 防御层 | 对抗策略 | +|----|------------------------|------------------------------------------------|----------------------------|-----------------|---------------------------------------------------------| +| T1 | Pipe Hijack | 本机攻击者劫持 STDIO Pipe | 完全控制浏览器操作 | Layer 1 | STDIO 进程私有;fd 不经过文件系统;procfs 受限 | +| T2 | LLM Prompt Injection | 恶意页面内容注入 LLM prompt | 执行未授权操作 | Layer 2 | JSON Schema 校验;Action 枚举白名单;禁止 eval | +| T3 | Skill Poisoning | 注入恶意 Skill 脚本 | 持久化后门 | Layer 2 + 3 | Skill 签名校验;沙箱执行;MAC 域名限制 | +| T4 | Credential Leakage | 通过 pipe 读取浏览器存储的密码 | 账户泄露 | Layer 3 | credential_store API 不暴露给 pipe;凭证仅内部使用 | +| T5 | Replay Attack | 录制合法命令后重放 | 重复执行敏感操作 | Layer 2 | sequence_id 递增校验 + HMAC 签名 | +| T6 | Retry Storm | LLM 无限重试导致资源耗尽 | CPU/内存/网络耗尽 | Layer 2 | Circuit Breaker (阈值 10);指数退避;最大重试限制 | + +### 5.2 各威胁详细分析 + +**T1: Pipe Hijack(管道劫持)** + +攻击路径:同一台机器上的恶意进程尝试读取/写入 sgClaw 与 Browser 之间的 STDIO Pipe。 + +分析:STDIO Pipe 基于操作系统匿名管道实现,没有文件系统路径,没有网络端口。 +攻击者需要获取目标进程的 fd(Linux)或 HANDLE(Windows),这在标准安全配置下 +需要 root 权限或 `ptrace` 能力。银河麒麟 V10 默认启用 `kernel.yama.ptrace_scope=1`, +非父进程无法 ptrace。 + +残余风险:root 用户可以访问任何进程的 fd。但在本部署场景中,root 被视为可信实体。 + +**T2: LLM Prompt Injection(提示注入)** + +攻击路径:用户访问的恶意网页中包含精心构造的文本,当 sgClaw 读取页面内容并发送给 LLM 时, +这些文本被 LLM 误解为指令,导致 Agent 执行非预期操作。 + +分析:这是 AI Agent 面临的最常见威胁。sgClaw 的防御不依赖于 LLM 本身的抗注入能力 +(这是不可靠的),而是在 Rust 层进行硬编码限制: +- LLM 只能输出预定义的 action 枚举值,任何不在枚举中的操作被直接拒绝 +- 最危险的 `eval`、`executeJsInPage` 不在枚举中,从根本上不可能被 LLM 触发 +- `expected_domain` 字段要求 LLM 明确声明目标域名,Browser 侧校验实际域名是否匹配 + +**T3: Skill Poisoning(技能投毒)** + +攻击路径:攻击者将恶意 JavaScript Skill 注入到 Skill Repository 中, +当 sgClaw 加载该 Skill 时执行恶意代码。 + +分析:Skill 文件在加载前经过以下校验链: +1. 签名校验:每个 Skill 文件必须附带由构建系统生成的数字签名 +2. 沙箱执行:Skill 代码在受限的 JavaScript 沙箱中运行,无法访问文件系统或网络 +3. MAC 限制:即使 Skill 生成了恶意命令,Browser 侧的域白名单仍会阻止未授权操作 + +**T4: Credential Leakage(凭证泄漏)** + +攻击路径:通过 pipe 发送特制命令,尝试读取 Browser 中存储的登录密码。 + +分析:`credential_store` 使用 Linux Keyring(银河麒麟)或 Windows Credential Manager +存储加密凭证。其 C++ API 仅在 `SessionManager::DoLogin()` 内部调用, +没有任何 CommandRouter action 会暴露凭证数据。pipe handler 的代码路径中 +完全不存在读取凭证的逻辑。 + +**T5: Replay Attack(重放攻击)** + +攻击路径:攻击者(假设已突破 Layer 1)录制合法的 pipe 命令序列,稍后重放。 + +分析:每条消息携带递增的 `sequence_id` 和基于共享密钥的 HMAC。 +接收方维护已处理的最大 sequence_id,拒绝任何小于或等于该值的消息。 +HMAC 密钥在每次 sgclaw 启动时通过 handshake 动态协商,重启后旧命令的 HMAC 无效。 + +**T6: Retry Storm(重试风暴)** + +攻击路径:LLM 因 hallucination 或 prompt injection 陷入无限循环, +持续生成失败命令并重试。 + +分析:sgClaw 内置两级防护: +1. Agent Runtime 层:单次任务最大步数限制(默认 50 步),超出自动终止 +2. Circuit Breaker:连续 10 次操作失败后,断路器打开,停止所有 pipe 通信, + 需要用户手动恢复。冷却期间采用指数退避(1s → 2s → 4s → ... → 30s max) + +--- + +## 6. 通信架构 + +### 6.1 Pipe 协议概览 + +sgClaw 与 Browser 之间使用 JSON Line 格式通过 STDIO Pipe 通信。 +每条消息占一行(以 `\n` 结尾),单消息最大 1 MB。 + +**消息类型一览:** + +``` +sgClaw → Browser: + ├─ command (请求执行浏览器操作) + └─ ack (确认收到事件) + +Browser → sgClaw: + ├─ response (命令执行结果) + ├─ event (主动推送的页面事件) + └─ init_ack (handshake 响应) +``` + +**Request 格式(sgClaw → Browser):** + +```json +{ + "seq": 1, + "type": "command", + "action": "click", + "params": { + "selector": "#submit-btn", + "wait_after": 1000 + }, + "security": { + "expected_domain": "oa.example.com", + "hmac": "a3f8c2..." + } +} +``` + +字段说明: +- `seq`: 递增序列号,全局唯一,用于匹配 response 和防重放 +- `type`: 消息类型,`command` 表示操作请求 +- `action`: 操作名称,必须在允许列表中 +- `params`: 操作参数,每个 action 有独立的 JSON Schema +- `security.expected_domain`: 期望的目标域名,Browser 侧校验 +- `security.hmac`: 消息完整性签名 + +**Response 格式(Browser → sgClaw):** + +```json +{ + "seq": 1, + "type": "response", + "success": true, + "data": { + "clicked": true, + "element_text": "提交" + }, + "aom_snapshot": [ + {"role": "button", "name": "提交", "bounds": [100, 200, 80, 30]} + ], + "timing": { + "queue_ms": 2, + "exec_ms": 45 + } +} +``` + +字段说明: +- `seq`: 与请求对应的序列号 +- `success`: 操作是否成功 +- `data`: 操作结果数据,格式因 action 而异 +- `aom_snapshot`: 操作后的 AOM(Accessibility Object Model)快照, + 供 Agent 的 observe 阶段使用,理解页面当前状态 +- `timing`: 性能计时信息 + +**Event 格式(Browser → sgClaw,主动推送):** + +```json +{ + "type": "event", + "event": "page_navigated", + "data": { + "url": "https://oa.example.com/approval/list", + "title": "审批列表 - OA系统", + "domain": "oa.example.com" + }, + "timestamp": 1709452800000 +} +``` + +支持的事件类型: +- `page_navigated`: 页面导航完成 +- `page_loaded`: 页面 DOMContentLoaded +- `dialog_appeared`: 浏览器对话框弹出(alert/confirm/prompt) +- `session_expired`: SessionManager 检测到会话过期 +- `zombie_ready`: Zombie page 初始化完成 + +### 6.2 与 CommandRouter 的对接 + +sgClaw 的命令最终汇入 SuperRPA 已有的 CommandRouter,这是一个统一的命令分发器, +支持 40+ 种浏览器操作。sgClaw 作为新增的命令来源,与已有的 JS SDK 并行存在。 + +**sgClaw Pipe 路径(新增):** + +``` +sgClaw (Rust) + │ + │ JSON Line over STDIO Pipe + │ + ▼ +SgClawProcessHost (C++, Browser Process) + │ + ▼ +PipeListener (async read, parse JSON) + │ + ├─── MAC Whitelist Check ─────────────────────────────┐ + │ │ │ + │ ├─ action in allowed_pipe_actions? │ + │ │ → No: reject with error response │ + │ │ │ + │ ├─ expected_domain matches current page? │ + │ │ → No: reject with domain_mismatch error │ + │ │ │ + │ └─ rate limit not exceeded? │ + │ → Exceeded: reject with rate_limit error │ + │ │ + ▼ │ +CommandRouter (Existing, 40+ actions) ◄──────────────────┘ + │ + ├───────────┬──────────────┬──────────────┐ + ▼ ▼ ▼ ▼ +CdpCommand ZombiePage Session RpaGlobal +Executor Manager Manager Storage + │ + ▼ +Page DOM Operations (click, type, getText, etc.) +``` + +**已有 JS SDK 路径(不变):** + +``` +页面内 JavaScript + │ + │ CDP binding (chrome.runtime.sendMessage) + │ + ▼ +CdpBridgeManager (C++, Renderer → Browser IPC) + │ + ▼ +CommandRouter (Same instance!) ◄── 同一个 CommandRouter + │ + ├───────────┬──────────────┬──────────────┐ + ▼ ▼ ▼ ▼ +CdpCommand ZombiePage Session RpaGlobal +Executor Manager Manager Storage +``` + +两条路径汇聚于同一个 CommandRouter 实例。sgClaw 只是新增了一个命令来源, +不改变 CommandRouter 的任何现有逻辑。区别仅在于: + +- JS SDK 路径:无 MAC whitelist check(页面 JS 已通过 CSP 和 rules.json 限制) +- Pipe 路径:增加 MAC whitelist check(因为 sgClaw 被视为外部进程,需额外校验) + +### 6.3 Pipe 允许的 Action 子集 + +并非所有 CommandRouter 支持的 40+ 个 action 都允许通过 pipe 调用。 +出于安全考虑,pipe 来源的命令被限制在以下安全子集中: + +**允许通过 Pipe 调用(无需确认):** + +| Action | 描述 | 备注 | +|------------------|--------------------|---------------------------------| +| click | 点击元素 | 需 expected_domain 匹配 | +| type | 输入文本 | 需 expected_domain 匹配 | +| navigate | 导航到 URL | URL 必须在域白名单内 | +| getText | 获取元素文本 | 只读操作,安全 | +| getHtml | 获取元素 HTML | 只读操作,安全 | +| waitForSelector | 等待元素出现 | 只读操作,安全 | +| pageScreenshot | 页面截图 | 返回 base64,用于 Agent observe | +| storageSet | 写 RpaGlobalStorage | key 前缀限制为 sgclaw.* | +| storageGet | 读 RpaGlobalStorage | key 前缀限制为 sgclaw.* | +| zombieSpawn | 创建 zombie page | 受池大小限制(max 5) | +| zombieKill | 销毁 zombie page | 只能销毁自己创建的 zombie | +| select | 下拉框选择 | 需 expected_domain 匹配 | +| scrollTo | 滚动页面 | 需 expected_domain 匹配 | +| getAomSnapshot | 获取 AOM 快照 | 只读操作,核心 observe 能力 | + +**禁止通过 Pipe 调用(硬编码拒绝):** + +| Action | 描述 | 禁止原因 | +|--------------------|--------------------|--------------------------------------------| +| eval | 执行任意 JS | 最高危操作,可绕过一切安全限制 | +| executeJsInPage | 在页面执行 JS | 等同于 eval,prompt injection 核心攻击面 | +| registerJsFunction | 注册 JS 回调 | 可植入持久化代码 | +| setRequestInterceptor | 拦截网络请求 | 可窃取请求中的 token/cookie | +| exportCookies | 导出所有 cookie | 可窃取会话凭证 | + +**需要用户确认的操作(Human-in-the-loop):** + +| Action | 描述 | 确认原因 | +|------------------|--------------------|---------------------------------| +| sessionLogin | 执行登录流程 | 涉及凭证使用,需用户知情 | +| sessionLogout | 登出会话 | 可能中断用户正在进行的工作 | +| clearStorage | 清空存储 | 不可逆操作 | + +--- + +## 7. 跨平台策略 + +### 7.1 STDIO Pipe 跨平台实现 + +STDIO Pipe 的跨平台实现依赖 Chromium 已有的 `base::LaunchProcess` 抽象层, +sgClaw 团队无需编写平台特定的管道代码。 + +**Linux 实现路径:** + +``` +Browser Process (C++) sgClaw (Rust) +───────────────────── ───────────── +1. pipe(fds) → fds[0](读), fds[1](写) +2. pipe(fds2) → fds2[0](读), fds2[1](写) +3. fork() +4. 子进程: + dup2(fds[0], STDIN_FILENO) + dup2(fds2[1], STDOUT_FILENO) + exec("sgclaw") ──────────────────→ main() +5. 父进程: std::io::stdin() → 读取命令 + write(fds[1], ...) ──────────────→ std::io::stdout() → 写回响应 + read(fds2[0], ...) ◄──────────── BufReader + BufWriter +``` + +Chromium 的 `base::LaunchProcess` 封装了上述 `pipe()` + `fork()` + `exec()` 流程, +通过 `LaunchOptions::fds_to_remap` 配置 fd 映射。 + +**Windows 实现路径:** + +``` +Browser Process (C++) sgClaw (Rust) +───────────────────── ───────────── +1. CreatePipe(&hRead1, &hWrite1, ...) +2. CreatePipe(&hRead2, &hWrite2, ...) +3. SetHandleInformation(hRead1, INHERIT) +4. SetHandleInformation(hWrite2, INHERIT) +5. CreateProcess("sgclaw.exe", + STARTUPINFO { + hStdInput = hRead1, main() + hStdOutput = hWrite2 std::io::stdin() → ReadFile + }) std::io::stdout() → WriteFile +6. WriteFile(hWrite1, ...) ──────────→ + ReadFile(hRead2, ...) ◄──────── +``` + +Chromium 的 `base::LaunchProcess` 在 Windows 下使用 `CreateProcess` + `STARTUPINFO` +实现句柄继承,与 Linux 的 fd 继承语义一致。 + +**Rust 侧统一代码:** + +无论 Linux 还是 Windows,sgclaw 的 Rust 代码完全一致: + +```rust +// sgclaw 端无需区分平台 +let reader = BufReader::new(std::io::stdin()); +let writer = BufWriter::new(std::io::stdout()); + +for line in reader.lines() { + let msg: PipeMessage = serde_json::from_str(&line?)?; + let response = handle_message(msg).await?; + writeln!(writer, "{}", serde_json::to_string(&response)?)?; + writer.flush()?; +} +``` + +### 7.2 目标平台 + +| 平台 | 操作系统 | CPU 架构 | 状态 | 备注 | +|-----------|--------------------------|----------|------------------|-----------------------------| +| Primary | 银河麒麟 V10 SP1 | x86_64 | 目标部署环境 | 政企客户主要使用平台 | +| Secondary | Windows 10 / 11 | x86_64 | 开发 + 部分部署 | 开发团队日常使用 + 部分客户 | +| Future | 银河麒麟 V10 SP1 | aarch64 | 规划中 | 国产 ARM 服务器/终端 | + +**银河麒麟 V10 SP1 特殊注意事项:** + +- 基于 Ubuntu 内核,但定制了安全策略(SELinux/AppArmor 变体) +- `procfs` 默认配置限制跨进程 fd 访问,有利于 Pipe Hijack 防御 +- `kernel.yama.ptrace_scope=1`,非父进程无法 ptrace,增强安全性 +- glibc 版本需确认兼容(通常 >= 2.31),Rust musl 静态链接可规避 + +### 7.3 sgClaw 二进制分发 + +sgClaw 编译为单一静态链接的 Rust 二进制,无外部运行时依赖。 + +``` +sgclaw 二进制产物 +├─ Linux: sgclaw (~8.8 MB, musl 静态链接, strip) +│ target: x86_64-unknown-linux-musl +│ 无 glibc 依赖,银河麒麟直接运行 +│ +└─ Windows: sgclaw.exe (~9.0 MB, MSVC 链接, strip) + target: x86_64-pc-windows-msvc + 无额外 DLL 依赖 +``` + +构建命令: + +```bash +# Linux (musl 静态链接,确保银河麒麟兼容) +cargo build --release --target x86_64-unknown-linux-musl +strip target/x86_64-unknown-linux-musl/release/sgclaw + +# Windows (交叉编译) +cargo build --release --target x86_64-pc-windows-msvc +``` + +部署方式:将 `sgclaw` / `sgclaw.exe` 放置在 SuperRPA 浏览器安装目录下: + +``` +superrpa-browser/ +├─ chrome (浏览器主程序) +├─ superrpa-chromium.sh (启动脚本) +├─ sgclaw (AI Agent, 新增) +├─ sgclaw-skills/ (技能目录, 新增) +│ ├─ oa-approval.js +│ └─ ... +└─ resources/ + └─ rules.json (域白名单, 已有) +``` + +--- + +## 8. 与 SuperRPA 已有子系统的关系 + +### 8.1 复用与新增总览 + +sgClaw 的核心设计原则之一是**最小侵入**——最大限度复用 SuperRPA 浏览器的已有基础设施, +将新增代码量控制在最低水平。 + +``` +┌─────────────────────────────────────────────────────────────────────┐ +│ SuperRPA Browser 已有子系统 │ +│ (全部保持不变,零修改) │ +│ │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ +│ │ Command │ │ CdpBridge │ │ ZombiePage │ │ +│ │ Router │ │ Manager │ │ Manager │ │ +│ │ (40+ acts) │ │ (JS SDK) │ │ (5 pools) │ │ +│ └──────┬──────┘ └─────────────┘ └─────────────┘ │ +│ │ │ +│ ┌──────┴──────┐ ┌─────────────┐ ┌─────────────┐ │ +│ │ CdpCommand │ │ Session │ │ RpaGlobal │ │ +│ │ Executor │ │ Manager │ │ Storage │ │ +│ │ │ │ (状态机) │ │ (KV store) │ │ +│ └─────────────┘ └─────────────┘ └─────────────┘ │ +│ │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ +│ │ credential │ │ rules.json │ │ BrowserAction│ │ +│ │ _store │ │ (URL白名单) │ │ API │ │ +│ │ (Keyring) │ │ │ │ (JS 调度) │ │ +│ └─────────────┘ └─────────────┘ └─────────────┘ │ +│ │ +├─────────────────────────────────────────────────────────────────────┤ +│ sgClaw 新增组件 │ +│ (~500 lines C++ in Browser + Rust binary) │ +│ │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ SgClawProcessHost (C++, ~200-300 lines) │ │ +│ │ ├─ 进程生命周期管理 (Start / Stop / OnCrash) │ │ +│ │ ├─ PipeListener (~150 lines, async read loop) │ │ +│ │ ├─ MAC Whitelist Check (~100 lines) │ │ +│ │ └─ FunctionsUI handlers for start/stop (~50 lines) │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Side Panel UI Controls (Vue, ~100 lines) │ │ +│ │ ├─ [启动] / [停止] 按钮 │ │ +│ │ ├─ 状态指示 (Running / Stopped / Error) │ │ +│ │ ├─ 任务输入框 │ │ +│ │ └─ 执行日志 (streaming output) │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ sgclaw Binary (Rust, based on ZeroClaw) │ │ +│ │ ├─ Agent Runtime (ReAct loop) │ │ +│ │ ├─ BrowserPipeTool (custom Tool trait impl) │ │ +│ │ ├─ SkillLoader (JS business skills) │ │ +│ │ ├─ LLM Provider (Claude / GPT / local) │ │ +│ │ ├─ Memory (SQLite + vector) │ │ +│ │ ├─ MAC Policy (domain / action whitelist) │ │ +│ │ ├─ Critic + Circuit Breaker │ │ +│ │ └─ MCP Client (rmcp) │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────────┘ +``` + +### 8.2 复用子系统详细说明 + +| 子系统 | 代码位置 | sgClaw 如何复用 | 是否修改 | +|---------------------|----------------------|---------------------------------------------------|---------| +| CommandRouter | browser/rpa/command/ | sgClaw 通过 pipe → PipeListener → CommandRouter | 不修改 | +| CdpBridgeManager | browser/rpa/cdp/ | 独立运行,JS SDK 路径不受影响 | 不修改 | +| ZombiePageManager | browser/rpa/zombie/ | sgClaw 通过 zombieSpawn/zombieKill action 使用 | 不修改 | +| SessionManager | browser/rpa/session/ | sgClaw 可触发 sessionLogin (需确认) | 不修改 | +| RpaGlobalStorage | browser/rpa/storage/ | sgClaw 通过 storageSet/storageGet 读写 | 不修改 | +| BrowserAction API | browser/rpa/action/ | 已有 JS SDK 继续使用,与 sgClaw 独立 | 不修改 | +| credential_store | browser/rpa/cred/ | 仅 SessionManager 内部使用,sgClaw 无法访问 | 不修改 | +| rules.json | resources/ | sgClaw 的 MAC Policy 也读取此文件进行域校验 | 不修改 | + +### 8.3 新增代码量估算 + +| 组件 | 语言 | 估计行数 | 位置 | +|-----------------------------|--------|-------------|-------------------------------| +| SgClawProcessHost | C++ | ~200-300 | browser/rpa/sgclaw/ | +| PipeListener | C++ | ~150 | browser/rpa/sgclaw/ | +| MAC Whitelist Check (Pipe) | C++ | ~100 | browser/rpa/sgclaw/ | +| FunctionsUI handlers | C++ | ~50 | browser/rpa/functions_ui/ | +| Side Panel UI controls | Vue | ~100 | resources/side_panel/ | +| **Browser 侧合计** | | **~500-600**| | +| sgclaw binary | Rust | ~3000-5000 | 独立仓库 sgclaw/ | + +### 8.4 影响评估 + +**对已有功能的影响:零。** + +具体论证: + +1. **CommandRouter 不变**:sgClaw 只是新增了一个命令来源(pipe),CommandRouter 本身的 + dispatch 逻辑、action 处理函数、错误处理均不修改。 + +2. **JS SDK 不变**:CdpBridgeManager 独立于 SgClawProcessHost,两者通过不同的 IPC 路径 + 到达同一个 CommandRouter。JS SDK 的用户不会感知到 sgClaw 的存在。 + +3. **会话管理不变**:SessionManager 的状态机(login → active → expired → re-login) + 完全不变。sgClaw 只能通过 CommandRouter 触发 sessionLogin action(需用户确认), + 不直接操作 SessionManager 内部状态。 + +4. **存储隔离**:sgClaw 通过 RpaGlobalStorage 的 key 被限制在 `sgclaw.*` 命名空间下, + 不会与已有的 JS SDK 存储的 key 冲突。 + +5. **二进制独立**:sgclaw 是独立二进制,不链接 Chromium 的任何库。 + 即使 sgclaw 构建失败或缺失,浏览器仍然正常工作——只是 Side Panel 的 [启动] 按钮 + 会报错"sgclaw binary not found"。 + +--- + +*文档结束。本文档为 sgClaw L1 层架构设计参考,后续 L2(详细设计)、L3(实现规范) +将在此基础上展开。* diff --git a/docs/L2-核心模块与接口契约层.md b/docs/L2-核心模块与接口契约层.md new file mode 100644 index 0000000..963355d --- /dev/null +++ b/docs/L2-核心模块与接口契约层.md @@ -0,0 +1,2009 @@ +# L2 — 核心模块与接口契约层 + +**文档版本**: 1.0 +**适用项目**: sgClaw (业数融合一平台 AI Agent 底座) +**编制日期**: 2026-03-03 + +**读者**: 各组组长(Rust 组、浏览器 C++ 组、前端组)—— 需要理解模块边界、接口契约、依赖关系,指导各组并行开发。 + +--- + +## 1. 模块总览与依赖拓扑 + +### 1.1 全局模块图 + +sgClaw 系统由三个独立的代码域组成:Rust 端(sgclaw binary)、C++ 端(Browser Process 新增模块)、前端(Side Panel Vue 组件)。以下拓扑展示了所有模块及其依赖关系。 + +``` +┌──────────────────────────────────────────────────────────────────────────┐ +│ sgClaw Rust Binary │ +│ │ +│ ┌──────────┐ ┌──────────────┐ ┌───────────┐ ┌────────────────┐ │ +│ │ main.rs │──→│ Agent Runtime│──→│ Critic │ │ Config/ │ │ +│ │ (入口) │ │ (ReAct) │ │ (评估器) │ │ Settings │ │ +│ └────┬─────┘ └──────┬───────┘ └─────┬─────┘ └────────────────┘ │ +│ │ │ │ │ +│ │ ┌──────┴───────┐ ┌──────┴──────┐ │ +│ │ │ BrowserPipe │ │ Circuit │ │ +│ │ │ Tool │ │ Breaker │ │ +│ │ │ (Tool trait) │ └─────────────┘ │ +│ │ └──────┬───────┘ │ +│ │ │ │ +│ ┌────┴─────┐ ┌──────┴───────┐ ┌───────────┐ ┌────────────────┐ │ +│ │ Pipe │ │ MAC Policy │ │ Memory │ │ LLM Provider │ │ +│ │ Protocol │ │ (安全策略) │ │ (记忆层) │ │ (模型适配) │ │ +│ └──────────┘ └──────────────┘ └───────────┘ └────────────────┘ │ +│ │ +│ ┌──────────────┐ ┌──────────────┐ │ +│ │ SkillLoader │ │ MCP Client │ │ +│ │ (技能加载) │ │ (rmcp) │ │ +│ └──────────────┘ └──────────────┘ │ +│ │ +└───────────────────────────────┬──────────────────────────────────────────┘ + │ STDIO Pipe (JSON Line) + │ +┌───────────────────────────────┴──────────────────────────────────────────┐ +│ SuperRPA Browser Process (C++ 新增) │ +│ │ +│ ┌─────────────────────────┐ │ +│ │ SgClawProcessHost │ Singleton, 进程生命周期管理 │ +│ │ ├─ PipeListener │ 异步读取循环, 解析 JSON Line │ +│ │ ├─ MAC Whitelist Check │ action + domain 白名单校验 │ +│ │ └─→ CommandRouter │ [Existing] 40+ actions, 零修改 │ +│ └─────────────────────────┘ │ +│ │ +└──────────────────────────────────────────────────────────────────────────┘ + │ FunctionsUI IPC + │ +┌───────────────────────────────┴──────────────────────────────────────────┐ +│ Side Panel (Vue 前端) │ +│ │ +│ ┌─────────────────────────┐ │ +│ │ AgentControlPanel.vue │ 启停按钮 + 指令输入 + 状态显示 + 执行日志 │ +│ └─────────────────────────┘ │ +│ │ +└──────────────────────────────────────────────────────────────────────────┘ +``` + +### 1.2 模块依赖矩阵 + +以下矩阵展示 Rust 端各模块之间的依赖关系(行依赖列): + +``` + Runtime BrowserPipe Protocol MAC Memory LLM Skill Critic MCP Config +Runtime - ✓ - - ✓ ✓ ✓ ✓ ✓ ✓ +BrowserPipe - - ✓ ✓ - - - - - - +Protocol - - - - - - - - - ✓ +MAC Policy - - - - - - - - - ✓ +Memory - - - - - - - - - ✓ +LLM Provider - - - - - - - - - ✓ +SkillLoader - - - - - - - - - ✓ +Critic - - - - ✓ - - - - ✓ +MCP Client - - - - - - - - - ✓ +Config - - - - - - - - - - +``` + +设计原则: +- **Config 是叶节点**:所有模块依赖 Config,Config 不依赖任何业务模块 +- **Runtime 是根节点**:Agent Runtime 协调所有模块,是唯一的"上帝模块" +- **BrowserPipeTool 仅依赖 Protocol 和 MAC**:确保 Tool 实现的纯粹性 +- **无循环依赖**:依赖图是 DAG(有向无环图) + +### 1.3 并行开发分工 + +基于模块依赖关系,三个组可以并行开发: + +| 组别 | 负责模块 | 依赖条件 | 可并行阶段 | +|------|---------|---------|-----------| +| **Rust 组** | Agent Runtime, BrowserPipeTool, Protocol, MAC Policy, Memory, LLM Provider, SkillLoader, Critic, MCP Client, Config | 无外部依赖,可先行 | 第一阶段即可开始 | +| **C++ 组** | SgClawProcessHost, PipeListener, MAC Whitelist Check | 需与 Rust 组对齐 Pipe 协议格式 | 协议 JSON Schema 确定后开始 | +| **前端组** | AgentControlPanel.vue | 需 C++ 组提供 FunctionsUI handler | C++ 组 handler 就绪后开始 | + +--- + +## 2. sgClaw Rust 端模块详细设计 + +### 2.1 main.rs — 进程入口 + +**职责**:初始化 Pipe I/O、配置加载、tokio 异步运行时启动、模块组装、handshake 握手。 + +**启动序列**: + +``` +main() + │ + ├── 1. 初始化 tracing (日志) + │ └── 日志输出到 stderr (stdout 保留给 pipe) + │ + ├── 2. 加载 Config + │ ├── 默认值 (编译时内嵌) + │ ├── 配置文件 (sgclaw.toml, 可选) + │ └── 环境变量覆盖 (SGCLAW_*) + │ + ├── 3. 初始化 tokio runtime + │ └── multi_thread, worker_threads = 2 + │ + ├── 4. 创建 Pipe I/O + │ ├── stdin → BufReader (接收 Browser 消息) + │ └── stdout → BufWriter (发送消息到 Browser) + │ + ├── 5. Handshake + │ ├── 等待 Browser 的 init 消息 (超时 5s) + │ ├── 校验协议版本 + │ ├── 交换 HMAC 密钥种子 + │ └── 发送 init_ack 确认 + │ + ├── 6. 组装模块 + │ ├── MAC Policy ← Config (rules 路径) + │ ├── Protocol ← Config (max_message_size) + │ ├── BrowserPipeTool ← Protocol + MAC + │ ├── LLM Provider ← Config (model, api_key) + │ ├── Memory ← Config (db_path) + │ ├── SkillLoader ← Config (skills_dir) + │ ├── MCP Client ← Config (mcp_servers) + │ ├── Critic ← Config (thresholds) + Memory + │ └── Agent Runtime ← 以上全部 + │ + ├── 7. 启动 Agent 消息循环 + │ └── 监听 pipe 输入,分发到 Runtime + │ + └── 8. 优雅退出 + ├── 收到 shutdown 命令 / EOF / SIGTERM + ├── 停止 Agent loop + ├── flush Memory 到磁盘 + └── 退出进程 (exit code 0) +``` + +**关键设计决策**: + +- **stdout 专用于 pipe**:所有日志、调试信息通过 `tracing` 输出到 stderr。stdout 严格保留给 JSON Line 协议,防止日志混入 pipe 流导致解析失败。 +- **tokio worker_threads = 2**:在 8 GB 内存约束下,2 个 worker 线程足以处理 pipe I/O + LLM HTTP 请求。不需要更多并发。 +- **模块组装使用依赖注入**:所有模块通过构造函数接收依赖,便于测试时替换为 mock 实现。 + +### 2.2 Agent Runtime — ReAct 循环 + +**职责**:实现 think → act → observe 循环,协调 LLM、Tool、Memory、Critic 完成用户任务。 + +**模块接口**: + +```rust +/// Agent Runtime 核心结构 +pub struct AgentRuntime { + provider: Box, + tools: Vec>, // 包含 BrowserPipeTool + MCP tools + memory: Arc, + critic: Critic, + config: RuntimeConfig, +} + +/// Runtime 配置 +pub struct RuntimeConfig { + pub max_steps: u32, // 单次任务最大步数 (默认 50) + pub max_thinking_tokens: u32, // 单步 thinking 最大 token (默认 4096) + pub system_prompt_template: String, // 系统提示模板路径 + pub human_confirm_actions: Vec, // 需人工确认的 action 列表 +} + +impl AgentRuntime { + /// 创建 Runtime 实例 + pub fn new( + provider: Box, + tools: Vec>, + memory: Arc, + critic: Critic, + config: RuntimeConfig, + ) -> Self; + + /// 执行用户任务 (核心入口) + /// 返回任务执行结果或错误 + pub async fn execute_task(&mut self, task: &str) -> Result; + + /// 停止当前任务 (由外部 shutdown 信号触发) + pub fn abort(&self); +} + +/// 任务执行结果 +pub struct TaskResult { + pub success: bool, + pub summary: String, // LLM 生成的任务完成摘要 + pub steps: Vec, // 所有执行步骤记录 + pub token_usage: TokenUsage, // token 消耗统计 +} + +/// 单步执行记录 +pub struct StepRecord { + pub step_num: u32, + pub thinking: String, // LLM 的推理内容 + pub action: Option, // 选择的操作 (None = 任务完成) + pub observation: String, // 操作结果观察 + pub duration_ms: u64, +} +``` + +**ReAct 循环伪码**: + +``` +execute_task(user_instruction): + context = memory.load_context(user_instruction) + system_prompt = render_template(config.system_prompt_template, { + available_tools: tools.descriptions(), + skills: skill_loader.list_skills(), + context: context, + }) + + for step in 1..=config.max_steps: + // === THINK === + llm_response = provider.chat(system_prompt, messages, tools) + + if llm_response.is_final_answer(): + return TaskResult { success: true, summary: llm_response.text } + + // === ACT === + action = llm_response.tool_call + if action.name in config.human_confirm_actions: + confirmation = request_human_confirm(action) // 通过 pipe 发送确认请求 + if not confirmation: + messages.push(observation: "用户拒绝了此操作") + continue + + result = tools.execute(action) + + // === OBSERVE === + observation = format_observation(result) + messages.push(observation) + + // === CRITIC === + critic_verdict = critic.evaluate(step, action, result) + if critic_verdict.should_abort: + return TaskResult { success: false, summary: critic_verdict.reason } + + memory.record_step(step, action, result) + + return TaskResult { success: false, summary: "达到最大步数限制" } +``` + +### 2.3 BrowserPipeTool — 自定义 Tool trait 实现 + +**职责**:将 Agent 的 action 请求序列化为 Pipe JSON 命令,发送到 Browser 并等待响应。这是 sgClaw 与 ZeroClaw 框架的核心对接点。 + +**模块接口**: + +```rust +/// ZeroClaw 的 Tool trait (框架定义) +#[async_trait] +pub trait Tool: Send + Sync { + fn name(&self) -> &str; + fn description(&self) -> &str; + fn parameters_schema(&self) -> serde_json::Value; + async fn execute(&self, params: serde_json::Value) -> Result; +} + +/// sgClaw 的浏览器操作工具实现 +pub struct BrowserPipeTool { + pipe_writer: Arc>, + pipe_reader: Arc, + mac_policy: Arc, + seq_counter: AtomicU64, + hmac_key: [u8; 32], +} + +impl BrowserPipeTool { + pub fn new( + pipe_writer: Arc>, + pipe_reader: Arc, + mac_policy: Arc, + hmac_key: [u8; 32], + ) -> Self; +} + +#[async_trait] +impl Tool for BrowserPipeTool { + fn name(&self) -> &str { "browser_action" } + + fn description(&self) -> &str { + "在浏览器中执行操作。支持的操作包括:click, type, navigate, \ + getText, getHtml, waitForSelector, pageScreenshot, select, \ + scrollTo, getAomSnapshot, storageSet, storageGet, \ + zombieSpawn, zombieKill" + } + + fn parameters_schema(&self) -> serde_json::Value { + // 返回所有 action 的 JSON Schema (oneOf 结构) + // 详见 §5 接口契约 + } + + async fn execute(&self, params: serde_json::Value) -> Result { + // 1. 解析 action + params + let action: Action = serde_json::from_value(params)?; + + // 2. MAC 策略校验 + self.mac_policy.check(&action)?; + + // 3. 构造 pipe message + let seq = self.seq_counter.fetch_add(1, Ordering::SeqCst); + let msg = PipeMessage::command(seq, &action, &self.hmac_key); + + // 4. 发送到 Browser + self.pipe_writer.lock().await.send(&msg)?; + + // 5. 等待对应 seq 的 response (超时 30s) + let response = self.pipe_reader.recv(seq, Duration::from_secs(30)).await?; + + // 6. 转换为 ToolOutput + Ok(ToolOutput::from_pipe_response(response)) + } +} +``` + +**Action 枚举(白名单)**: + +```rust +/// 允许通过 pipe 的操作类型 (封闭枚举) +#[derive(Debug, Serialize, Deserialize)] +#[serde(tag = "action", content = "params")] +pub enum Action { + #[serde(rename = "click")] + Click { selector: String, wait_after: Option }, + + #[serde(rename = "type")] + Type { selector: String, text: String, clear_first: Option }, + + #[serde(rename = "navigate")] + Navigate { url: String }, + + #[serde(rename = "getText")] + GetText { selector: String }, + + #[serde(rename = "getHtml")] + GetHtml { selector: String, outer: Option }, + + #[serde(rename = "waitForSelector")] + WaitForSelector { selector: String, timeout_ms: Option }, + + #[serde(rename = "pageScreenshot")] + PageScreenshot { full_page: Option }, + + #[serde(rename = "select")] + Select { selector: String, value: String }, + + #[serde(rename = "scrollTo")] + ScrollTo { selector: Option, x: Option, y: Option }, + + #[serde(rename = "getAomSnapshot")] + GetAomSnapshot { root_selector: Option }, + + #[serde(rename = "storageSet")] + StorageSet { key: String, value: String }, + + #[serde(rename = "storageGet")] + StorageGet { key: String }, + + #[serde(rename = "zombieSpawn")] + ZombieSpawn { url: String }, + + #[serde(rename = "zombieKill")] + ZombieKill { page_id: String }, +} +``` + +### 2.4 SkillLoader — 技能加载器 + +**职责**:发现、校验、加载 JavaScript 业务技能脚本,将其注册到 Agent 的工具集中。 + +**模块接口**: + +```rust +/// 技能加载器 +pub struct SkillLoader { + skills_dir: PathBuf, + registry: HashMap, + signature_verifier: SignatureVerifier, +} + +/// 技能定义 +pub struct Skill { + pub name: String, // 技能名称 (如 "oa-approval") + pub version: String, // 语义版本 (如 "1.2.0") + pub description: String, // 自然语言描述 (供 LLM 选择) + pub target_domains: Vec,// 适用域名列表 + pub parameters: JsonSchema, // 输入参数 schema + pub script: String, // JS 脚本内容 + pub signature: String, // 数字签名 (Ed25519) +} + +/// 技能清单文件 (registry.json) +pub struct SkillRegistry { + pub version: String, + pub skills: Vec, +} + +pub struct SkillManifestEntry { + pub name: String, + pub file: String, // 相对路径 (如 "builtin/oa-approval.js") + pub hash: String, // SHA-256 文件哈希 + pub signature: String, // Ed25519 签名 +} + +impl SkillLoader { + /// 创建加载器并扫描技能目录 + pub fn new(skills_dir: PathBuf, public_key: &[u8]) -> Result; + + /// 获取所有已加载技能的描述 (供 LLM system prompt) + pub fn list_skills(&self) -> Vec; + + /// 获取指定技能的 JS 脚本 + pub fn get_script(&self, name: &str) -> Option<&str>; + + /// 运行时重新加载技能 (热更新) + pub fn reload(&mut self) -> Result<(), SkillError>; +} +``` + +**技能加载流程**: + +``` +SkillLoader::new(skills_dir) + │ + ├── 1. 读取 skills/registry.json + │ └── 解析技能清单 + │ + ├── 2. 遍历每个 SkillManifestEntry + │ │ + │ ├── 2a. 读取技能文件 + │ │ + │ ├── 2b. 计算文件 SHA-256 + │ │ └── 与 registry.json 中的 hash 比对 + │ │ └── 不匹配 → 跳过 + 警告日志 + │ │ + │ ├── 2c. 验证 Ed25519 签名 + │ │ └── 使用编译时内嵌的公钥 + │ │ └── 签名无效 → 跳过 + 警告日志 + │ │ + │ └── 2d. 解析技能元数据 (JS 文件头部注释) + │ └── 注册到 registry HashMap + │ + └── 3. 日志: "Loaded N skills, skipped M (signature failed)" +``` + +**技能文件格式** (JS 文件头部): + +```javascript +/** + * @skill oa-approval + * @version 1.2.0 + * @description OA系统审批单自动处理:查询待审批列表、批量审批、添加审批意见 + * @domains oa.example.com, oa-test.example.com + * @params { + * "type": "object", + * "properties": { + * "action": { "enum": ["list", "approve", "reject"] }, + * "opinion": { "type": "string" } + * } + * } + */ +async function execute(params, browserAction) { + // browserAction 是 BrowserAction 函数的引用 + // 技能代码使用 browserAction 调用浏览器操作 + + if (params.action === 'list') { + await browserAction('navigate', 'https://oa.example.com/approval/pending'); + await browserAction('waitForSelector', '.approval-list'); + const items = await browserAction('getText', '.approval-list'); + return { success: true, data: items }; + } + // ... +} +``` + +### 2.5 MAC Policy — 强制访问控制策略 + +**职责**:在 Rust 端实施域名白名单和 Action 白名单校验,作为安全架构 Layer 2 的核心组件。 + +**模块接口**: + +```rust +/// MAC 策略引擎 +pub struct MacPolicy { + allowed_domains: HashSet, // 允许操作的域名集合 + allowed_actions: HashSet, // 允许的 action 名称集合 + blocked_actions: HashSet, // 硬性禁止的 action 集合 + confirm_actions: HashSet, // 需人工确认的 action 集合 + storage_key_prefix: String, // 存储 key 前缀限制 (默认 "sgclaw.") + rate_limits: HashMap, // 每域速率限制 +} + +/// 速率限制配置 +pub struct RateLimit { + pub max_per_second: u32, // 每秒最大操作数 + pub cooldown_seconds: u32, // 超限后冷却时间 +} + +/// MAC 校验结果 +pub enum MacVerdict { + Allow, // 允许执行 + NeedConfirm(String), // 需要用户确认 (附确认提示) + Deny(MacDenyReason), // 拒绝执行 +} + +/// 拒绝原因 +pub enum MacDenyReason { + ActionNotAllowed(String), // action 不在白名单 + ActionBlocked(String), // action 在黑名单 (eval 等) + DomainNotAllowed(String), // 域名不在白名单 + StorageKeyViolation(String), // storage key 前缀不合规 + RateLimitExceeded(String, u32), // 超过速率限制 (域名, 当前速率) +} + +impl MacPolicy { + /// 从 rules.json 加载策略 + pub fn from_rules_file(path: &Path) -> Result; + + /// 校验 action 是否允许 + pub fn check(&self, action: &Action) -> MacVerdict; + + /// 校验域名是否在白名单 + pub fn check_domain(&self, domain: &str) -> bool; + + /// 记录一次操作 (用于速率统计) + pub fn record_access(&self, domain: &str); +} +``` + +**rules.json 策略文件格式**: + +```json +{ + "version": "1.0", + "domains": { + "allowed": [ + "oa.example.com", + "erp.example.com", + "hr.example.com", + "finance.example.com" + ] + }, + "pipe_actions": { + "allowed": [ + "click", "type", "navigate", "getText", "getHtml", + "waitForSelector", "pageScreenshot", "select", "scrollTo", + "getAomSnapshot", "storageSet", "storageGet", + "zombieSpawn", "zombieKill" + ], + "blocked": [ + "eval", "executeJsInPage", "registerJsFunction", + "setRequestInterceptor", "exportCookies" + ], + "need_confirm": [ + "sessionLogin", "sessionLogout", "clearStorage" + ] + }, + "storage": { + "key_prefix": "sgclaw." + }, + "rate_limits": { + "default": { "max_per_second": 10, "cooldown_seconds": 30 }, + "overrides": { + "finance.example.com": { "max_per_second": 5, "cooldown_seconds": 60 } + } + } +} +``` + +### 2.6 Critic — 输出质量评估器 + +**职责**:在 Agent 每步 observe 后评估操作结果质量,检测异常模式,触发熔断保护。 + +**模块接口**: + +```rust +/// 输出质量评估器 +pub struct Critic { + circuit_breaker: CircuitBreaker, + memory: Arc, + config: CriticConfig, +} + +pub struct CriticConfig { + pub consecutive_failure_threshold: u32, // 连续失败熔断阈值 (默认 10) + pub same_action_repeat_limit: u32, // 同一 action 重复上限 (默认 5) + pub max_task_duration_secs: u64, // 单任务最大时长 (默认 600) +} + +/// 评估结果 +pub struct CriticVerdict { + pub should_abort: bool, // 是否应终止任务 + pub reason: Option, // 终止原因 + pub warning: Option, // 警告 (不终止但需记录) +} + +/// 熔断器 +pub struct CircuitBreaker { + state: CircuitState, + consecutive_failures: AtomicU32, + last_failure_time: Mutex>, + config: CircuitBreakerConfig, +} + +pub enum CircuitState { + Closed, // 正常 — 允许操作 + Open, // 断开 — 拒绝所有操作 + HalfOpen, // 半开 — 允许一次试探 +} + +pub struct CircuitBreakerConfig { + pub failure_threshold: u32, // 失败次数阈值 (默认 10) + pub cooldown_base_secs: u64, // 基础冷却时间 (默认 1) + pub cooldown_max_secs: u64, // 最大冷却时间 (默认 30) + pub reset_on_success: bool, // 成功后是否重置计数 (默认 true) +} + +impl Critic { + pub fn new(memory: Arc, config: CriticConfig) -> Self; + + /// 评估单步结果 + pub fn evaluate( + &self, + step: u32, + action: &Action, + result: &ToolOutput, + elapsed: Duration, + ) -> CriticVerdict; + + /// 重置熔断器 (用户手动恢复时调用) + pub fn reset(&self); +} +``` + +**评估逻辑**: + +``` +evaluate(step, action, result, elapsed): + // 1. 检查操作是否失败 + if result.is_error(): + circuit_breaker.record_failure() + if circuit_breaker.state == Open: + return Verdict { should_abort: true, reason: "连续失败次数达到熔断阈值" } + + // 2. 检查是否在重复同一操作 + recent_actions = memory.get_recent_actions(config.same_action_repeat_limit) + if all_same(recent_actions, action): + return Verdict { should_abort: true, reason: "检测到死循环:连续重复相同操作" } + + // 3. 检查任务时长 + if elapsed > config.max_task_duration_secs: + return Verdict { should_abort: true, reason: "任务执行超时" } + + // 4. 成功时重置计数 + if result.is_success() && config.reset_on_success: + circuit_breaker.reset_failures() + + return Verdict { should_abort: false } +``` + +### 2.7 Memory — 记忆系统 + +**职责**:管理 Agent 的短期对话记忆和长期任务知识库,支持上下文检索和经验沉淀。 + +**模块接口**: + +```rust +/// Memory trait (ZeroClaw 框架定义) +#[async_trait] +pub trait Memory: Send + Sync { + /// 存储对话消息 + async fn store_message(&self, msg: &Message) -> Result<(), MemoryError>; + + /// 检索相关上下文 (语义搜索) + async fn retrieve(&self, query: &str, limit: usize) -> Result, MemoryError>; + + /// 清除当前会话记忆 + async fn clear_session(&self) -> Result<(), MemoryError>; +} + +/// sgClaw 的复合记忆实现 +pub struct CompositeMemory { + short_term: ShortTermMemory, // Ring Buffer 短期记忆 + long_term: LongTermMemory, // SQLite 长期记忆 +} + +/// 短期记忆 (Ring Buffer) +pub struct ShortTermMemory { + buffer: VecDeque, + max_size: usize, // 默认 50 条消息 + max_tokens: usize, // 默认 8000 tokens +} + +/// 长期记忆 (SQLite + 简单向量) +pub struct LongTermMemory { + db: SqlitePool, // SQLite 连接池 + embedding_dim: usize, // 向量维度 (默认 384) +} + +/// 记忆条目 +pub struct MemoryEntry { + pub id: String, + pub content: String, + pub entry_type: MemoryType, + pub relevance_score: f32, // 与查询的相关度 (0.0 ~ 1.0) + pub created_at: DateTime, +} + +pub enum MemoryType { + Conversation, // 对话历史 + TaskResult, // 任务执行结果 + SkillExperience, // 技能执行经验 + UserPreference, // 用户偏好 +} + +impl CompositeMemory { + pub fn new(config: MemoryConfig) -> Result; + + /// 获取最近 N 步的 action 记录 (供 Critic 查重) + pub fn get_recent_actions(&self, n: usize) -> Vec; + + /// 保存任务执行经验 (供自进化学习) + pub async fn save_task_experience( + &self, + task: &str, + result: &TaskResult, + ) -> Result<(), MemoryError>; +} +``` + +**存储 Schema** (SQLite): + +```sql +-- 长期记忆表 +CREATE TABLE memory_entries ( + id TEXT PRIMARY KEY, + content TEXT NOT NULL, + entry_type TEXT NOT NULL, -- 'conversation' | 'task_result' | 'skill_exp' | 'user_pref' + embedding BLOB, -- f32 向量 (384 维 = 1536 bytes) + metadata TEXT, -- JSON 附加信息 + created_at TEXT NOT NULL, + session_id TEXT -- 关联的 trace_id +); + +-- 向量索引 (简单线性扫描,数据量小时足够) +CREATE INDEX idx_memory_type ON memory_entries(entry_type); +CREATE INDEX idx_memory_session ON memory_entries(session_id); + +-- 技能执行记录表 +CREATE TABLE skill_executions ( + id TEXT PRIMARY KEY, + skill_name TEXT NOT NULL, + input_hash TEXT NOT NULL, -- 输入参数 hash (用于精确匹配) + success INTEGER NOT NULL, + duration_ms INTEGER, + created_at TEXT NOT NULL +); +``` + +### 2.8 LLM Provider — 模型适配层 + +**职责**:统一封装不同 LLM 服务的调用接口,支持 streaming、tool-use、token 计量。 + +**模块接口**: + +```rust +/// LLM Provider trait (ZeroClaw 框架定义) +#[async_trait] +pub trait LlmProvider: Send + Sync { + /// 发送聊天请求 + async fn chat( + &self, + messages: &[Message], + tools: &[ToolDefinition], + config: &ChatConfig, + ) -> Result; + + /// 发送 streaming 请求 + async fn chat_stream( + &self, + messages: &[Message], + tools: &[ToolDefinition], + config: &ChatConfig, + ) -> Result>>>, LlmError>; + + /// 获取 provider 信息 + fn info(&self) -> ProviderInfo; +} + +/// 聊天配置 +pub struct ChatConfig { + pub max_tokens: u32, // 最大生成 token 数 + pub temperature: f32, // 温度 (默认 0.1, Agent 场景偏低) + pub stop_sequences: Vec, // 停止序列 +} + +/// LLM 响应 +pub struct LlmResponse { + pub content: Option, // 文本回复 + pub tool_calls: Vec, // 工具调用请求 + pub usage: TokenUsage, // token 使用量 + pub finish_reason: FinishReason, +} + +/// Token 使用统计 +pub struct TokenUsage { + pub prompt_tokens: u32, + pub completion_tokens: u32, + pub total_tokens: u32, +} + +/// Provider 信息 +pub struct ProviderInfo { + pub name: String, // "claude" | "openai" | "ollama" + pub model: String, // 如 "claude-sonnet-4-20250514" + pub max_context_tokens: u32, // 最大上下文窗口 + pub supports_tools: bool, + pub supports_streaming: bool, +} +``` + +**已实现的 Provider**: + +| Provider | 模块 | 支持功能 | 适用场景 | +|----------|------|---------|---------| +| **Claude** | `llm/claude.rs` | streaming + tool-use + vision | 默认推荐,综合能力最强 | +| **OpenAI** | `llm/openai.rs` | streaming + tool-use | 兼容 GPT-4 及 API 兼容服务 | +| **Ollama** | `llm/ollama.rs` | streaming + tool-use (部分模型) | 本地部署,离线场景 | + +**Provider 选择逻辑**: + +```rust +/// 根据配置创建 Provider 实例 +pub fn create_provider(config: &LlmConfig) -> Result, LlmError> { + match config.provider.as_str() { + "claude" => Ok(Box::new(ClaudeProvider::new( + &config.api_key, + &config.model, // 如 "claude-sonnet-4-20250514" + config.base_url.as_deref(), + )?)), + "openai" => Ok(Box::new(OpenAiProvider::new( + &config.api_key, + &config.model, // 如 "gpt-4o" + config.base_url.as_deref(), + )?)), + "ollama" => Ok(Box::new(OllamaProvider::new( + &config.model, // 如 "qwen2.5:32b" + config.base_url.as_deref().unwrap_or("http://localhost:11434"), + )?)), + other => Err(LlmError::UnknownProvider(other.to_string())), + } +} +``` + +### 2.9 MCP Client — 外部工具扩展 + +**职责**:通过 rmcp(Rust MCP SDK)连接外部 MCP Server,动态获取额外工具供 Agent 使用。 + +**模块接口**: + +```rust +/// MCP 客户端管理器 +pub struct McpClientManager { + clients: HashMap, // server_name → client + config: McpConfig, +} + +pub struct McpConfig { + pub servers: Vec, +} + +pub struct McpServerConfig { + pub name: String, // 服务器标识名 + pub command: String, // 启动命令 + pub args: Vec, // 命令参数 + pub env: HashMap, // 环境变量 +} + +impl McpClientManager { + /// 创建并连接所有配置的 MCP Server + pub async fn new(config: McpConfig) -> Result; + + /// 获取所有 MCP Server 提供的工具列表 + pub fn list_tools(&self) -> Vec; + + /// 调用 MCP 工具 + pub async fn call_tool( + &self, + server: &str, + tool: &str, + params: serde_json::Value, + ) -> Result; + + /// 关闭所有连接 + pub async fn shutdown(&mut self); +} +``` + +--- + +## 3. 浏览器 C++ 端模块详细设计 + +### 3.1 SgClawProcessHost — 进程宿主 + +**职责**:Singleton,管理 sgclaw 子进程的完整生命周期(Start / Stop / OnCrash),创建和持有 STDIO Pipe。 + +**接口声明**: + +```cpp +// sg_claw_process_host.h + +#ifndef CHROME_BROWSER_SUPERRPA_SGCLAW_SG_CLAW_PROCESS_HOST_H_ +#define CHROME_BROWSER_SUPERRPA_SGCLAW_SG_CLAW_PROCESS_HOST_H_ + +#include +#include +#include "base/process/launch.h" +#include "base/process/process.h" + +namespace superrpa { +namespace sgclaw { + +class PipeListener; + +// sgClaw 进程状态 +enum class ProcessState { + kStopped, // 初始状态 / 已停止 + kStarting, // 正在启动 (等待 handshake) + kRunning, // 正常运行 + kStopping, // 正在停止 + kCrashed, // 异常退出 +}; + +// 进程事件观察者 +class ProcessObserver { + public: + virtual ~ProcessObserver() = default; + virtual void OnStateChanged(ProcessState new_state) = 0; + virtual void OnPipeMessage(const std::string& json_line) = 0; + virtual void OnProcessCrash(int exit_code, const std::string& stderr_output) = 0; +}; + +// sgClaw 进程宿主 (Singleton) +class SgClawProcessHost { + public: + static SgClawProcessHost* GetInstance(); + + // 启动 sgClaw 子进程 + // 成功返回 true,失败返回 false 并通过 observer 通知 + bool Start(); + + // 优雅停止 sgClaw + void Stop(); + + // 发送消息到 sgClaw + bool SendMessage(const std::string& json_line); + + // 获取当前状态 + ProcessState GetState() const; + + // 注册/移除观察者 + void AddObserver(ProcessObserver* observer); + void RemoveObserver(ProcessObserver* observer); + + private: + SgClawProcessHost(); + ~SgClawProcessHost(); + + // 禁止拷贝 + SgClawProcessHost(const SgClawProcessHost&) = delete; + SgClawProcessHost& operator=(const SgClawProcessHost&) = delete; + + // 内部方法 + base::FilePath GetSgClawBinaryPath() const; + void OnChildProcessExited(int exit_code); + void DoHandshake(); + + // 成员 + ProcessState state_ = ProcessState::kStopped; + base::Process child_process_; + std::unique_ptr pipe_listener_; + + // Pipe 文件描述符 (Linux: fd, Windows: HANDLE) + base::ScopedFD pipe_write_fd_; // Browser → sgClaw + base::ScopedFD pipe_read_fd_; // sgClaw → Browser + + std::vector observers_; +}; + +} // namespace sgclaw +} // namespace superrpa + +#endif +``` + +**关键实现说明**: + +- **Singleton**:使用 `base::NoDestructor` 实现进程全局唯一实例 +- **Start() 流程**:检查二进制存在 → 创建 pipe pair → `base::LaunchProcess` → 启动 PipeListener → 发送 handshake → 等待 ack (5s 超时) → 状态切换到 Running +- **Stop() 流程**:发送 `{"type":"shutdown"}` → 等待进程退出 (2s 超时) → SIGTERM → 再等 2s → SIGKILL +- **OnChildProcessExited()**:检查退出码,非零且非主动 Stop → 视为 Crash → 通知 observers → 不自动重启 +- **线程安全**:所有方法在 Browser UI 线程调用,Pipe 读取在 IO 线程 + +### 3.2 PipeListener — 异步读取循环 + +**职责**:在 IO 线程上异步读取 sgClaw 的 stdout 输出,按行解析 JSON,分发到 MAC 校验和 CommandRouter。 + +**接口声明**: + +```cpp +// pipe_listener.h + +#ifndef CHROME_BROWSER_SUPERRPA_SGCLAW_PIPE_LISTENER_H_ +#define CHROME_BROWSER_SUPERRPA_SGCLAW_PIPE_LISTENER_H_ + +#include +#include +#include "base/files/file_descriptor_watcher_posix.h" // Linux +// Windows: base/win/object_watcher.h + +namespace superrpa { +namespace sgclaw { + +class PipeListener { + public: + using MessageCallback = std::function; + using ErrorCallback = std::function; + + PipeListener(base::ScopedFD read_fd, + MessageCallback on_message, + ErrorCallback on_error); + ~PipeListener(); + + // 开始异步监听 + void StartListening(); + + // 停止监听 + void StopListening(); + + private: + void OnReadable(); // fd 可读回调 + void ProcessBuffer(); // 从 buffer 中提取完整行 + bool ValidateMessageSize(const std::string& line); // 检查消息大小 (<=1MB) + + base::ScopedFD read_fd_; + std::string read_buffer_; // 累积读取缓冲 + MessageCallback on_message_; + ErrorCallback on_error_; + + // Linux: FileDescriptorWatcher + std::unique_ptr fd_watcher_; +}; + +} // namespace sgclaw +} // namespace superrpa + +#endif +``` + +**读取流程**: + +``` +StartListening() + │ + └── 注册 fd_watcher_ 监听 read_fd_ 可读事件 + │ + ▼ +OnReadable() [IO 线程回调] + │ + ├── read(read_fd_, temp_buffer, 4096) + │ → 返回 0: EOF → 通知 process exited + │ → 返回 -1: EAGAIN → 忽略 (非阻塞 I/O) + │ → 返回 N: 追加到 read_buffer_ + │ + └── ProcessBuffer() + │ + ├── 在 read_buffer_ 中查找 '\n' + │ → 找到: 提取该行作为 json_line + │ → 未找到: 等待下次 OnReadable() + │ + ├── ValidateMessageSize(json_line) + │ → > 1MB: 丢弃 + 日志警告 + │ + └── on_message_(json_line) + │ + └── [UI 线程] MAC 校验 → CommandRouter +``` + +### 3.3 MAC Whitelist Check — Pipe 来源 MAC 校验 + +**职责**:对通过 Pipe 收到的命令进行强制访问控制校验,作为安全架构 Layer 3 的实现。 + +**接口声明**: + +```cpp +// mac_whitelist_check.h + +#ifndef CHROME_BROWSER_SUPERRPA_SGCLAW_MAC_WHITELIST_CHECK_H_ +#define CHROME_BROWSER_SUPERRPA_SGCLAW_MAC_WHITELIST_CHECK_H_ + +#include +#include +#include "base/values.h" + +namespace superrpa { +namespace sgclaw { + +// MAC 校验结果 +enum class MacResult { + kAllow, // 允许 + kDeny, // 拒绝 + kNeedConfirm, // 需用户确认 +}; + +struct MacCheckResult { + MacResult result; + std::string reason; // 拒绝/确认原因 +}; + +class MacWhitelistCheck { + public: + // 从 rules.json 加载策略 + static std::unique_ptr CreateFromRules( + const base::FilePath& rules_path); + + // 校验 pipe 命令 + MacCheckResult Check(const std::string& action, + const std::string& expected_domain, + const std::string& current_page_domain) const; + + private: + MacWhitelistCheck(); + + std::set allowed_actions_; + std::set blocked_actions_; + std::set confirm_actions_; + std::set allowed_domains_; +}; + +} // namespace sgclaw +} // namespace superrpa + +#endif +``` + +**校验流程**: + +``` +Check(action, expected_domain, current_page_domain): + │ + ├── 1. action 在 blocked_actions_ 中? + │ → 是: return Deny("Action is blocked: " + action) + │ + ├── 2. action 不在 allowed_actions_ 中? + │ → 是: return Deny("Action not in pipe whitelist: " + action) + │ + ├── 3. expected_domain 不在 allowed_domains_ 中? + │ → 是: return Deny("Domain not in whitelist: " + expected_domain) + │ + ├── 4. expected_domain != current_page_domain? + │ → 是: return Deny("Domain mismatch: expected " + expected_domain + │ + " but current is " + current_page_domain) + │ + ├── 5. action 在 confirm_actions_ 中? + │ → 是: return NeedConfirm("操作需要用户确认: " + action) + │ + └── 6. return Allow +``` + +--- + +## 4. 前端 Side Panel 新增 UI + +### 4.1 AgentControlPanel.vue + +**职责**:在 Side Panel 中提供 Agent 控制面板,包括启停按钮、指令输入、状态显示、执行日志流。 + +**组件接口**: + +``` +AgentControlPanel.vue +│ +├── Props: 无 (独立组件) +│ +├── State: +│ ├── agentState: 'stopped' | 'starting' | 'running' | 'error' +│ ├── taskInput: string // 用户输入的自然语言指令 +│ ├── logs: LogEntry[] // 执行日志条目 +│ ├── currentTask: string // 当前执行的任务描述 +│ └── errorMessage: string // 错误信息 +│ +├── Methods: +│ ├── startAgent() // 调用 FunctionsUI → SgClawProcessHost::Start() +│ ├── stopAgent() // 调用 FunctionsUI → SgClawProcessHost::Stop() +│ ├── submitTask(instruction) // 发送任务到 Agent +│ ├── confirmAction(actionId) // 用户确认 human-in-the-loop 操作 +│ └── rejectAction(actionId) // 用户拒绝操作 +│ +└── Events (from C++ via FunctionsUI): + ├── onStateChanged(state) // Agent 状态变更 + ├── onLogEntry(entry) // 新日志条目 + ├── onConfirmRequired(action) // 需要用户确认的操作 + └── onTaskCompleted(result) // 任务完成 +``` + +**UI 布局**: + +``` +┌─────────────────────────────────────┐ +│ sgClaw Agent │ +├─────────────────────────────────────┤ +│ │ +│ 状态: ● 运行中 │ +│ │ +│ ┌─────────────────────────────┐ │ +│ │ 请输入业务指令... │ │ +│ │ │ │ +│ └─────────────────────────────┘ │ +│ [ 发送 ] │ +│ │ +│ ─── 执行日志 ─────────────────── │ +│ │ +│ ▸ [10:23:01] 正在分析指令... │ +│ ▸ [10:23:03] 导航至 ERP 系统 │ +│ ▸ [10:23:05] 点击"财务报表"菜单 │ +│ ▸ [10:23:08] 设置时间范围: 本月 │ +│ ▸ [10:23:10] 正在导出数据... │ +│ │ +│ ─── 确认请求 ─────────────────── │ +│ │ +│ ⚠ Agent 请求执行登录操作 │ +│ 目标系统: erp.example.com │ +│ [ 允许 ] [ 拒绝 ] │ +│ │ +├─────────────────────────────────────┤ +│ [ 启动 Agent ] [ 停止 Agent ] │ +└─────────────────────────────────────┘ +``` + +**与 C++ 层的通信**: + +前端通过 FunctionsUI 机制(SuperRPA 已有的 JS → C++ IPC)与 SgClawProcessHost 交互。 + +```javascript +// 启动 Agent +window.sgFunctionsUI('sgclaw_start', {}, (response) => { + // response: { success: true } 或 { success: false, error: "..." } +}); + +// 停止 Agent +window.sgFunctionsUI('sgclaw_stop', {}, (response) => { ... }); + +// 发送任务 +window.sgFunctionsUI('sgclaw_submit_task', { + instruction: "导出本月合规报表" +}, (response) => { ... }); + +// 确认/拒绝操作 +window.sgFunctionsUI('sgclaw_confirm', { + action_id: "abc123", + approved: true +}, (response) => { ... }); +``` + +**事件接收**(C++ → JS 推送): + +```javascript +// C++ 通过 ExecuteJavaScript 推送事件到 Side Panel +// 前端注册全局事件处理器 +window.sgClawEvents = { + onStateChanged: function(state) { + // state: 'stopped' | 'starting' | 'running' | 'error' + vm.agentState = state; + }, + onLogEntry: function(entry) { + // entry: { time: "10:23:01", message: "正在分析指令...", level: "info" } + vm.logs.push(entry); + }, + onConfirmRequired: function(action) { + // action: { id: "abc123", type: "sessionLogin", domain: "erp.example.com" } + vm.pendingConfirm = action; + }, + onTaskCompleted: function(result) { + // result: { success: true, summary: "已成功导出3份报表" } + vm.currentTask = null; + } +}; +``` + +--- + +## 5. 接口契约 + +### 5.1 Pipe JSON 协议完整 Schema + +以下定义了 sgClaw 与 Browser 之间所有 Pipe 消息的完整 JSON Schema。 + +**Handshake — Browser → sgClaw**: + +```json +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PipeInitMessage", + "type": "object", + "required": ["type", "version", "hmac_seed"], + "properties": { + "type": { "const": "init" }, + "version": { + "type": "string", + "pattern": "^\\d+\\.\\d+$", + "description": "协议版本号 (如 1.0)" + }, + "hmac_seed": { + "type": "string", + "minLength": 32, + "maxLength": 64, + "description": "HMAC 密钥种子 (hex 编码)" + }, + "capabilities": { + "type": "array", + "items": { "type": "string" }, + "description": "Browser 支持的扩展能力列表" + } + } +} +``` + +**Handshake — sgClaw → Browser**: + +```json +{ + "title": "PipeInitAckMessage", + "type": "object", + "required": ["type", "version", "agent_id"], + "properties": { + "type": { "const": "init_ack" }, + "version": { + "type": "string", + "description": "sgClaw 协议版本 (必须与 init.version 一致)" + }, + "agent_id": { + "type": "string", + "description": "Agent 实例唯一标识 (UUID v4)" + }, + "supported_actions": { + "type": "array", + "items": { "type": "string" }, + "description": "sgClaw 可使用的 action 列表" + } + } +} +``` + +**Command — sgClaw → Browser**: + +```json +{ + "title": "PipeCommandMessage", + "type": "object", + "required": ["seq", "type", "action", "params", "security"], + "properties": { + "seq": { + "type": "integer", + "minimum": 1, + "description": "全局递增序列号" + }, + "type": { "const": "command" }, + "action": { + "type": "string", + "enum": [ + "click", "type", "navigate", "getText", "getHtml", + "waitForSelector", "pageScreenshot", "select", "scrollTo", + "getAomSnapshot", "storageSet", "storageGet", + "zombieSpawn", "zombieKill" + ] + }, + "params": { + "type": "object", + "description": "操作参数,格式取决于 action (见 §5.2)" + }, + "security": { + "type": "object", + "required": ["expected_domain", "hmac"], + "properties": { + "expected_domain": { + "type": "string", + "description": "期望操作的目标域名" + }, + "hmac": { + "type": "string", + "description": "HMAC-SHA256 签名 (hex)" + } + } + } + } +} +``` + +**Response — Browser → sgClaw**: + +```json +{ + "title": "PipeResponseMessage", + "type": "object", + "required": ["seq", "type", "success"], + "properties": { + "seq": { + "type": "integer", + "description": "对应 command 的序列号" + }, + "type": { "const": "response" }, + "success": { "type": "boolean" }, + "data": { + "type": "object", + "description": "操作结果数据 (成功时)" + }, + "error": { + "type": "object", + "properties": { + "code": { "type": "string" }, + "message": { "type": "string" } + }, + "description": "错误信息 (失败时)" + }, + "aom_snapshot": { + "type": "array", + "items": { + "type": "object", + "properties": { + "role": { "type": "string" }, + "name": { "type": "string" }, + "bounds": { + "type": "array", + "items": { "type": "integer" }, + "minItems": 4, + "maxItems": 4, + "description": "[x, y, width, height]" + }, + "value": { "type": "string" }, + "children": { "type": "array" } + } + }, + "description": "操作后的 AOM 快照" + }, + "timing": { + "type": "object", + "properties": { + "queue_ms": { "type": "integer" }, + "exec_ms": { "type": "integer" } + } + } + } +} +``` + +### 5.2 各 Action 参数 Schema + +**click**: + +```json +{ + "type": "object", + "required": ["selector"], + "properties": { + "selector": { + "type": "string", + "description": "CSS 选择器" + }, + "wait_after": { + "type": "integer", + "minimum": 0, + "maximum": 30000, + "default": 1000, + "description": "点击后等待时间 (ms)" + } + } +} +``` + +**type**: + +```json +{ + "type": "object", + "required": ["selector", "text"], + "properties": { + "selector": { + "type": "string", + "description": "CSS 选择器" + }, + "text": { + "type": "string", + "maxLength": 10000, + "description": "要输入的文本" + }, + "clear_first": { + "type": "boolean", + "default": true, + "description": "输入前是否清空已有内容" + } + } +} +``` + +**navigate**: + +```json +{ + "type": "object", + "required": ["url"], + "properties": { + "url": { + "type": "string", + "format": "uri", + "description": "目标 URL (必须在域白名单内)" + } + } +} +``` + +**getText**: + +```json +{ + "type": "object", + "required": ["selector"], + "properties": { + "selector": { + "type": "string", + "description": "CSS 选择器" + } + } +} +``` + +**getHtml**: + +```json +{ + "type": "object", + "required": ["selector"], + "properties": { + "selector": { + "type": "string" + }, + "outer": { + "type": "boolean", + "default": false, + "description": "true 返回 outerHTML, false 返回 innerHTML" + } + } +} +``` + +**waitForSelector**: + +```json +{ + "type": "object", + "required": ["selector"], + "properties": { + "selector": { + "type": "string" + }, + "timeout_ms": { + "type": "integer", + "minimum": 100, + "maximum": 30000, + "default": 5000, + "description": "等待超时 (ms)" + } + } +} +``` + +**pageScreenshot**: + +```json +{ + "type": "object", + "properties": { + "full_page": { + "type": "boolean", + "default": false, + "description": "是否截取整页 (true) 或仅可视区域 (false)" + } + } +} +``` + +**select**: + +```json +{ + "type": "object", + "required": ["selector", "value"], + "properties": { + "selector": { + "type": "string", + "description": "select 元素的 CSS 选择器" + }, + "value": { + "type": "string", + "description": "要选择的 option 的 value 值" + } + } +} +``` + +**scrollTo**: + +```json +{ + "type": "object", + "properties": { + "selector": { + "type": "string", + "description": "滚动到指定元素 (优先于 x/y)" + }, + "x": { + "type": "integer", + "description": "水平滚动位置 (px)" + }, + "y": { + "type": "integer", + "description": "垂直滚动位置 (px)" + } + } +} +``` + +**getAomSnapshot**: + +```json +{ + "type": "object", + "properties": { + "root_selector": { + "type": "string", + "description": "从指定元素开始获取 AOM 子树 (默认整页)" + } + } +} +``` + +**storageSet**: + +```json +{ + "type": "object", + "required": ["key", "value"], + "properties": { + "key": { + "type": "string", + "pattern": "^sgclaw\\.", + "description": "存储键名 (必须以 sgclaw. 开头)" + }, + "value": { + "type": "string", + "maxLength": 65536, + "description": "存储值" + } + } +} +``` + +**storageGet**: + +```json +{ + "type": "object", + "required": ["key"], + "properties": { + "key": { + "type": "string", + "pattern": "^sgclaw\\.", + "description": "存储键名 (必须以 sgclaw. 开头)" + } + } +} +``` + +**zombieSpawn**: + +```json +{ + "type": "object", + "required": ["url"], + "properties": { + "url": { + "type": "string", + "format": "uri", + "description": "Zombie page 加载的 URL (必须在域白名单内)" + } + } +} +``` + +**zombieKill**: + +```json +{ + "type": "object", + "required": ["page_id"], + "properties": { + "page_id": { + "type": "string", + "description": "要销毁的 zombie page ID" + } + } +} +``` + +### 5.3 错误码体系 + +所有错误响应使用统一的错误码格式: + +```json +{ + "seq": 5, + "type": "response", + "success": false, + "error": { + "code": "MAC_DOMAIN_MISMATCH", + "message": "Expected domain erp.example.com but current page is oa.example.com" + } +} +``` + +**错误码分类**: + +| 错误码前缀 | 来源 | 描述 | +|-----------|------|------| +| `PIPE_*` | PipeListener | Pipe 传输层错误 | +| `MAC_*` | MAC Whitelist Check | 安全策略拒绝 | +| `CMD_*` | CommandRouter | 命令执行错误 | +| `SESSION_*` | SessionManager | 会话相关错误 | +| `INTERNAL_*` | 内部 | 系统内部错误 | + +**完整错误码列表**: + +| 错误码 | 描述 | 建议处理 | +|--------|------|---------| +| `PIPE_INVALID_JSON` | JSON 解析失败 | 检查消息格式 | +| `PIPE_MESSAGE_TOO_LARGE` | 消息超过 1MB 限制 | 减小消息体积 | +| `PIPE_SEQ_DUPLICATE` | 序列号重复 | 检查 seq 生成逻辑 | +| `PIPE_SEQ_OUT_OF_ORDER` | 序列号乱序 | 检查消息发送顺序 | +| `PIPE_HMAC_INVALID` | HMAC 签名校验失败 | 检查密钥和签名算法 | +| `MAC_ACTION_BLOCKED` | Action 在黑名单中 | 使用允许的 action | +| `MAC_ACTION_NOT_ALLOWED` | Action 不在白名单中 | 检查 action 名称 | +| `MAC_DOMAIN_NOT_ALLOWED` | 域名不在白名单中 | 联系管理员添加域名 | +| `MAC_DOMAIN_MISMATCH` | 期望域名与实际不匹配 | 确认当前页面域名 | +| `MAC_RATE_LIMIT` | 超过速率限制 | 降低操作频率 | +| `MAC_NEED_CONFIRM` | 操作需要用户确认 | 等待用户确认 | +| `CMD_SELECTOR_NOT_FOUND` | 选择器未匹配到元素 | 检查选择器或等待元素 | +| `CMD_SELECTOR_TIMEOUT` | 等待元素超时 | 增加超时时间 | +| `CMD_NAVIGATION_FAILED` | 页面导航失败 | 检查 URL 可达性 | +| `CMD_ZOMBIE_POOL_FULL` | Zombie 池已满 (max 5) | 先销毁不需要的 zombie | +| `CMD_ZOMBIE_NOT_FOUND` | 指定的 zombie page 不存在 | 检查 page_id | +| `SESSION_EXPIRED` | 会话已过期 | 触发重新登录 | +| `SESSION_LOGIN_FAILED` | 登录失败 | 检查凭证或网络 | +| `INTERNAL_UNKNOWN` | 未知内部错误 | 查看日志 | + +### 5.4 Handshake 协议流程 + +``` +Browser sgClaw + │ │ + │ (1) Start child process │ + │ ──────────────────────────────────→ │ + │ │ + │ (2) init message │ + │ {"type":"init", │ + │ "version":"1.0", │ + │ "hmac_seed":"a1b2c3..."} │ + │ ──────────────────────────────────→ │ + │ │ 校验版本号 + │ │ 派生 HMAC 密钥 + │ (3) init_ack │ + │ {"type":"init_ack", │ + │ "version":"1.0", │ + │ "agent_id":"uuid-v4", │ + │ "supported_actions":[...]} │ + │ ◄────────────────────────────────── │ + │ │ + │ 校验版本号 │ + │ 记录 agent_id (审计) │ + │ 状态 → Running │ + │ │ + │ (4) 正常通信开始 │ + │ ◄═══════════════════════════════════►│ + │ │ +``` + +**超时处理**: +- Browser 发送 init 后等待 5 秒 +- 超时未收到 init_ack → Kill 子进程 → 状态切换到 Crashed → 通知 UI + +**版本不匹配处理**: +- sgClaw 收到的 version 与自身版本不一致 → 发送 error ack → 主动退出 +- Browser 收到的 version 与自身不一致 → Kill 子进程 → 报错 + +--- + +## 6. 跨模块交互时序 + +### 6.1 用户启动 Agent 完整时序 + +``` +用户 Side Panel C++ (Browser) Rust (sgClaw) + │ │ │ │ + │ 点击 [启动] │ │ │ + │ ──────────────→ │ │ │ + │ │ sgclaw_start │ │ + │ │ ────────────────→ │ │ + │ │ │ Start() │ + │ │ │ ├ 检查二进制 │ + │ │ │ ├ 创建 pipe pair │ + │ │ │ ├ LaunchProcess │ + │ │ │ │ ─────────────────→ │ main() + │ │ │ │ │ ├ 初始化 + │ │ │ │ init message │ ├ 等待 init + │ │ │ │ ─────────────────→ │ + │ │ │ │ │ ├ 校验版本 + │ │ │ │ init_ack │ ├ 派生 HMAC + │ │ │ │ ◄───────────────── │ + │ │ │ ├ 状态 → Running │ + │ │ { success: true } │ │ + │ │ ◄──────────────── │ │ + │ 状态: ● 运行中 │ │ │ + │ ◄────────────── │ │ │ +``` + +### 6.2 用户提交任务完整时序 + +``` +用户 Side Panel C++ (Browser) Rust (sgClaw) LLM + │ │ │ │ │ + │ "导出合规报表" │ │ │ │ + │ ───────────→ │ │ │ │ + │ │ sgclaw_submit_task │ │ │ + │ │ ──────────────────→ │ │ │ + │ │ │ Pipe: submit_task │ │ + │ │ │ ──────────────────→ │ │ + │ │ │ │ execute_task() │ + │ │ │ │ │ + │ │ │ │ ┌─ ReAct Loop ───┤ + │ │ │ │ │ │ + │ │ │ │ │ THINK │ + │ │ │ │ │ ──────────────→│ + │ │ │ │ │ LLM response │ + │ │ │ │ │ ◄──────────────│ + │ │ │ │ │ │ + │ │ │ │ │ ACT │ + │ │ │ Pipe: click │ │ │ + │ │ │ ◄────────────────── │ │ │ + │ │ │ MAC Check → Allow │ │ │ + │ │ │ CommandRouter.exec │ │ │ + │ │ │ Pipe: response │ │ │ + │ │ │ ──────────────────→ │ │ │ + │ │ │ │ │ OBSERVE │ + │ │ │ │ │ CRITIC │ + │ │ │ │ │ │ + │ │ │ │ │ (repeat...) │ + │ │ │ │ └────────────────┤ + │ │ │ │ │ + │ │ │ Pipe: task_complete │ │ + │ │ │ ◄────────────────── │ │ + │ │ onTaskCompleted │ │ │ + │ │ ◄────────────────── │ │ │ + │ "已导出3份报表" │ │ │ │ + │ ◄─────────── │ │ │ │ +``` + +### 6.3 Human-in-the-loop 确认时序 + +``` +Rust (sgClaw) C++ (Browser) Side Panel 用户 + │ │ │ │ + │ Pipe: command │ │ │ + │ action=sessionLogin │ │ │ + │ ──────────────────→ │ │ │ + │ │ MAC: NeedConfirm │ │ + │ │ push onConfirmReq │ │ + │ │ ──────────────────→ │ │ + │ │ │ 显示确认对话框 │ + │ │ │ ───────────────→ │ + │ │ │ │ + │ │ │ 用户点击 [允许] │ + │ │ │ ◄─────────────── │ + │ │ sgclaw_confirm │ │ + │ │ ◄────────────────── │ │ + │ │ │ │ + │ │ CommandRouter.exec │ │ + │ │ (sessionLogin) │ │ + │ Pipe: response │ │ │ + │ ◄────────────────── │ │ │ + │ │ │ │ +``` + +--- + +## 7. 配置体系 + +### 7.1 sgclaw.toml 配置文件 + +```toml +# sgClaw 配置文件 +# 路径: 与 sgclaw 二进制同目录, 或 $SGCLAW_CONFIG 指定 + +[general] +# Agent 日志级别: trace | debug | info | warn | error +log_level = "info" + +[llm] +# LLM Provider: claude | openai | ollama +provider = "claude" +model = "claude-sonnet-4-20250514" +# API Key (建议通过环境变量 SGCLAW_LLM_API_KEY 设置) +# api_key = "sk-..." +# 自定义 API 端点 (可选) +# base_url = "https://api.example.com/v1" + +[llm.config] +max_tokens = 4096 +temperature = 0.1 + +[agent] +# 单次任务最大步数 +max_steps = 50 +# 系统提示模板路径 (相对于 sgclaw 二进制) +system_prompt_template = "prompts/system.txt" + +[memory] +# SQLite 数据库路径 +db_path = "data/memory.db" +# 短期记忆最大条数 +short_term_max_messages = 50 +# 短期记忆最大 token 数 +short_term_max_tokens = 8000 + +[security] +# rules.json 路径 (域白名单 + action 白名单) +rules_path = "rules.json" +# Skill 公钥路径 (Ed25519) +skill_public_key_path = "keys/skill_verify.pub" + +[skills] +# 技能目录路径 +skills_dir = "sgclaw-skills" + +[circuit_breaker] +# 连续失败熔断阈值 +failure_threshold = 10 +# 基础冷却时间 (秒) +cooldown_base_secs = 1 +# 最大冷却时间 (秒) +cooldown_max_secs = 30 + +[mcp] +# MCP Server 配置 (可选) +# [[mcp.servers]] +# name = "filesystem" +# command = "npx" +# args = ["-y", "@anthropic/mcp-server-filesystem", "/tmp"] +``` + +### 7.2 环境变量覆盖 + +所有配置均可通过 `SGCLAW_` 前缀的环境变量覆盖: + +| 环境变量 | 覆盖配置项 | 示例 | +|---------|-----------|------| +| `SGCLAW_LOG_LEVEL` | `general.log_level` | `debug` | +| `SGCLAW_LLM_PROVIDER` | `llm.provider` | `ollama` | +| `SGCLAW_LLM_MODEL` | `llm.model` | `qwen2.5:32b` | +| `SGCLAW_LLM_API_KEY` | `llm.api_key` | `sk-...` | +| `SGCLAW_LLM_BASE_URL` | `llm.base_url` | `http://localhost:11434` | +| `SGCLAW_MAX_STEPS` | `agent.max_steps` | `100` | +| `SGCLAW_RULES_PATH` | `security.rules_path` | `/etc/sgclaw/rules.json` | + +优先级:环境变量 > 配置文件 > 编译时默认值。 + +--- + +*文档结束。本文档为 sgClaw L2 层模块设计与接口契约参考,各组应基于此文档中的接口定义 +进行并行开发。接口变更需经三方组长会审后更新本文档。* diff --git a/docs/L3-数据流与Skill体系层.md b/docs/L3-数据流与Skill体系层.md new file mode 100644 index 0000000..f5920ad --- /dev/null +++ b/docs/L3-数据流与Skill体系层.md @@ -0,0 +1,1361 @@ +# L3 — 数据流与 Skill 体系层 + +**文档版本**: 1.0 +**适用项目**: sgClaw (业数融合一平台 AI Agent 底座) +**编制日期**: 2026-03-03 + +**读者**: 高级开发者、Skill 开发者 —— 需要理解 Agent 内部数据流转、编写业务 Skill、集成 LLM、 +调优感知层和记忆系统。 + +--- + +## 1. 核心数据流时序 + +### 1.1 端到端数据流全景 + +一次完整的用户任务执行涉及以下数据流转路径。从用户在 Side Panel 输入自然语言指令开始, +到最终任务完成结果返回,数据流经前端 → C++ → Pipe → Rust → LLM → Pipe → C++ → DOM。 + +``` +用户输入 结果展示 +"导出本月合规报表" "已导出3份报表" + │ ▲ + ▼ │ +┌─────────────────────────────────────────────────────────────────────────────┐ +│ Side Panel (Vue) │ +│ AgentControlPanel.vue │ +│ ├─ sgFunctionsUI('sgclaw_submit_task', { instruction: "..." }) │ +│ └─ 接收 onLogEntry / onTaskCompleted 事件 │ +└──────────────┬────────────────────────────────────────────────┬────────────┘ + │ FunctionsUI IPC ▲ ExecuteJS push + ▼ │ +┌─────────────────────────────────────────────────────────────────────────────┐ +│ C++ Browser Process │ +│ │ +│ SgClawProcessHost │ +│ ├─ 将 instruction 封装为 pipe submit_task 消息 │ +│ ├─ PipeListener 接收 sgClaw 的 command 消息 │ +│ ├─ MAC Whitelist Check 校验 │ +│ └─→ CommandRouter → CdpCommandExecutor → 页面 DOM 操作 │ +│ │ +│ 响应路径: │ +│ CommandRouter result → PipeWriter → sgClaw │ +│ + AOM Snapshot (操作后的页面状态快照) │ +└──────────────┬────────────────────────────────────────────────┬────────────┘ + │ STDIO Pipe (JSON Line) ▲ + ▼ │ +┌─────────────────────────────────────────────────────────────────────────────┐ +│ sgClaw Rust Process │ +│ │ +│ ┌──────────────────────────────────────────────────────────────┐ │ +│ │ Agent Runtime (ReAct Loop) │ │ +│ │ │ │ +│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ +│ │ │ THINK │───→│ ACT │───→│ OBSERVE │──┐ │ │ +│ │ │ │ │ │ │ │ │ │ │ +│ │ │ LLM 推理 │ │ 执行工具 │ │ 解析结果 │ │ │ │ +│ │ │ 生成计划 │ │ 发送命令 │ │ 更新记忆 │ │ │ │ +│ │ └──────────┘ └──────────┘ └──────────┘ │ │ │ +│ │ ▲ │ │ │ +│ │ └──── Critic 评估 ◄──────────────────────┘ │ │ +│ │ │ OK → 继续循环 │ │ +│ │ │ Abort → 终止任务 │ │ +│ └──────────────────────────────────────────────────────────────┘ │ +│ │ +│ 数据交互: │ +│ ├─ BrowserPipeTool ──→ Pipe ──→ Browser (command) │ +│ ├─ LLM Provider ──→ HTTP(S) ──→ Claude / GPT / Ollama │ +│ ├─ Memory ──→ SQLite (本地文件) │ +│ └─ SkillLoader ──→ 文件系统 (技能脚本) │ +│ │ +└─────────────────────────────────────────────────────────────────────────────┘ +``` + +### 1.2 单步数据流详解 + +Agent 每一步(ReAct loop 的一次迭代)的数据流如下: + +``` +Step N 开始 +│ +├── 1. 构造 LLM 输入 +│ ├── system_prompt (模板渲染) +│ │ ├── 可用工具描述 (BrowserPipeTool + MCP tools) +│ │ ├── 可用技能列表 (SkillLoader.list_skills()) +│ │ ├── 安全约束说明 +│ │ └── 当前会话 trace_id +│ │ +│ ├── messages (对话历史) +│ │ ├── 短期记忆 (最近 N 条, token 截断) +│ │ └── 长期记忆检索结果 (语义相关的历史经验) +│ │ +│ └── tools (工具定义) +│ ├── browser_action: { 参数 JSON Schema } +│ └── mcp_tools: [外部工具列表] +│ +├── 2. 调用 LLM (THINK) +│ ├── → HTTP(S) → Claude API / OpenAI API / Ollama +│ ├── ← streaming chunks (thinking + tool_call) +│ └── 解析 LLM 输出: +│ ├── 纯文本 → 最终回答 (任务完成) +│ └── tool_call → 继续执行 +│ +├── 3. 执行工具调用 (ACT) +│ │ +│ ├── [browser_action] BrowserPipeTool.execute() +│ │ ├── MAC Policy 校验 (Rust 层) +│ │ ├── 构造 pipe command JSON +│ │ │ { seq: N, type: "command", action: "click", +│ │ │ params: { selector: "#btn" }, +│ │ │ security: { expected_domain: "erp.example.com", hmac: "..." } } +│ │ ├── 写入 stdout (→ pipe → Browser) +│ │ └── 等待 response (← pipe ← Browser) +│ │ { seq: N, type: "response", success: true, +│ │ data: { clicked: true }, +│ │ aom_snapshot: [...] } +│ │ +│ └── [mcp_tool] McpClientManager.call_tool() +│ ├── STDIO → MCP Server +│ └── ← tool result JSON +│ +├── 4. 处理结果 (OBSERVE) +│ ├── 格式化 observation 文本 +│ │ ├── 操作成功/失败 +│ │ ├── 结果数据摘要 +│ │ └── AOM 快照 (页面状态描述) +│ │ +│ ├── 存入短期记忆 +│ └── 追加到 messages 历史 +│ +├── 5. 质量评估 (CRITIC) +│ ├── 检查操作成功/失败 +│ ├── 检查是否重复操作 (死循环检测) +│ ├── 检查任务时长 +│ └── 更新 Circuit Breaker 状态 +│ +└── Step N 结束 → Step N+1 或 任务完成 +``` + +### 1.3 数据格式在各阶段的变换 + +``` +阶段 数据格式 示例 +──────────── ──────────────────────────── ────────────────────────────── +用户输入 自然语言字符串 "导出本月合规报表" + │ + ▼ +LLM 输入 Message[] + ToolDef[] { role: "user", content: "..." } + │ + ▼ +LLM 输出 ToolCall { name: "browser_action", + │ arguments: { action: "click", ... } } + ▼ +Pipe 命令 JSON Line {"seq":1,"type":"command","action":"click",...} + │ + ▼ +C++ 内部 base::Value (Dict) CommandRouter 参数字典 + │ + ▼ +DOM 操作 CDP / JS 执行 document.querySelector('#btn').click() + │ + ▼ +DOM 结果 CDP 返回值 { "result": { "type": "undefined" } } + │ + ▼ +Pipe 响应 JSON Line {"seq":1,"type":"response","success":true,...} + │ + ▼ +Observation 格式化文本 "操作成功:点击了'提交'按钮。\n当前页面..." + │ + ▼ +LLM 下一轮 Message (role: "tool") { role: "tool", content: "操作成功:..." } +``` + +--- + +## 2. Agent 循环详解 + +### 2.1 ReAct 循环模型 + +sgClaw 采用 ReAct(Reasoning + Acting)循环模型,这是当前 AI Agent 领域最成熟的执行范式。 +核心思想:让 LLM 交替进行推理(Reasoning)和行动(Acting),每次行动后观察结果, +再决定下一步。 + +``` +┌─────────────────────────────────────────────────────────┐ +│ ReAct 循环 │ +│ │ +│ ┌──────────┐ │ +│ │ 用户指令 │ │ +│ └─────┬────┘ │ +│ │ │ +│ ▼ │ +│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ +│ │ │ │ │ │ │ │ +│ ┌─→│ THINK │────→│ ACT │────→│ OBSERVE │──┐ │ +│ │ │ │ │ │ │ │ │ │ +│ │ │ 推理下一步│ │ 调用工具 │ │ 读取结果 │ │ │ +│ │ │ 生成计划 │ │ 执行操作 │ │ 理解状态 │ │ │ +│ │ └──────────┘ └──────────┘ └──────────┘ │ │ +│ │ │ │ +│ │ ┌──────────┐ │ │ +│ └─────────│ CRITIC │◄─────────────────────────────┘ │ +│ │ │ │ +│ │ 质量评估 │ │ +│ │ 熔断检查 │ │ +│ └────┬─────┘ │ +│ │ │ +│ OK → 继续 │ +│ Abort → 终止 │ +│ Done → 返回结果 │ +│ │ +└─────────────────────────────────────────────────────────┘ +``` + +### 2.2 System Prompt 模板 + +Agent 的行为由 System Prompt 定义。以下是 sgClaw 的 System Prompt 模板结构: + +``` +你是 sgClaw,一个运行在 SuperRPA 浏览器中的 AI 助手。你的任务是帮助用户在企业业务系统 +中完成自动化操作。 + +## 身份与角色 +- 你运行在国家电网"业数融合一平台"的 SuperRPA 浏览器中 +- 你可以操控浏览器中打开的业务系统页面(ERP、OA、财务、HR 等) +- 你的每个操作都会被审计记录,trace_id: {{trace_id}} + +## 可用工具 +你有一个核心工具 `browser_action`,支持以下操作: +{{#each tools}} +### {{this.name}} +{{this.description}} +参数: {{this.parameters}} +{{/each}} + +## 可用技能 +以下是预置的业务技能脚本,当任务匹配时优先使用: +{{#each skills}} +- **{{this.name}}** (v{{this.version}}): {{this.description}} + 适用域名: {{this.domains}} +{{/each}} + +## 安全约束 +1. 只能操作以下域名的页面: {{allowed_domains}} +2. 每个 browser_action 调用必须指定 expected_domain +3. 禁止尝试执行 eval、executeJsInPage 等操作 +4. 涉及登录、登出、清空存储的操作会请求用户确认 +5. 不要尝试读取或猜测用户密码 + +## 执行策略 +1. 先观察当前页面状态 (getAomSnapshot) +2. 制定执行计划,分步骤完成 +3. 每步操作后检查结果 +4. 遇到异常时尝试恢复(最多重试 3 次) +5. 任务完成后提供简明的执行摘要 + +## 输出格式 +- 思考过程用自然语言描述 +- 操作通过 tool_call 执行 +- 最终结果用中文文本总结 +``` + +### 2.3 消息历史管理策略 + +Agent 的 LLM 调用需要发送对话历史(messages),但上下文窗口有限。 +sgClaw 采用分层记忆策略管理消息历史: + +``` +┌──────────────────────────────────────────────────────────────┐ +│ LLM 上下文窗口 (如 200K tokens) │ +│ │ +│ ┌─ System Prompt ────────────────────────────────────────┐ │ +│ │ ~2000 tokens (固定) │ │ +│ │ 工具定义 + 技能列表 + 安全约束 + 执行策略 │ │ +│ └────────────────────────────────────────────────────────┘ │ +│ │ +│ ┌─ 长期记忆检索 ─────────────────────────────────────────┐ │ +│ │ ~1000 tokens (按相关度检索) │ │ +│ │ 相似任务的历史经验、成功的操作路径 │ │ +│ └────────────────────────────────────────────────────────┘ │ +│ │ +│ ┌─ 短期对话记忆 ─────────────────────────────────────────┐ │ +│ │ 动态大小 (剩余 token 预算) │ │ +│ │ │ │ +│ │ ┌─ 最近 N 步完整记录 ──────────────────────────────┐ │ │ +│ │ │ Step K: thinking + action + observation │ │ │ +│ │ │ Step K+1: thinking + action + observation │ │ │ +│ │ │ ... │ │ │ +│ │ │ Step N: thinking + action + observation │ │ │ +│ │ └──────────────────────────────────────────────────┘ │ │ +│ │ │ │ +│ │ ┌─ 更早步骤的压缩摘要 ─────────────────────────────┐ │ │ +│ │ │ "Steps 1-5: 登录 ERP,导航到报表模块, │ │ │ +│ │ │ 设置了时间范围为本月" │ │ │ +│ │ └──────────────────────────────────────────────────┘ │ │ +│ │ │ │ +│ └────────────────────────────────────────────────────────┘ │ +│ │ +│ ┌─ 当前用户指令 ─────────────────────────────────────────┐ │ +│ │ ~100 tokens │ │ +│ │ "导出本月合规报表" │ │ +│ └────────────────────────────────────────────────────────┘ │ +│ │ +└──────────────────────────────────────────────────────────────┘ +``` + +**Token 预算分配**: + +| 区域 | Token 预算 | 策略 | +|------|-----------|------| +| System Prompt | ~2000 | 固定,编译时确定 | +| 长期记忆检索 | ~1000 | 按语义相关度排序取 top-3 | +| 短期对话记忆 | 剩余空间 | 最近步骤完整保留,早期步骤压缩 | +| 用户指令 | ~100 | 原文保留 | +| LLM 生成预留 | ~4096 | max_tokens 参数 | + +**短期记忆截断算法**: + +``` +truncate_messages(messages, token_budget): + // 1. 保留最近 5 步完整记录 + recent = messages[-5:] + recent_tokens = count_tokens(recent) + + // 2. 如果最近 5 步已超预算,减少保留数 + while recent_tokens > token_budget * 0.8 and len(recent) > 2: + recent = recent[-len(recent)+1:] + recent_tokens = count_tokens(recent) + + // 3. 对更早的步骤生成压缩摘要 + remaining_budget = token_budget - recent_tokens + older = messages[:-len(recent)] + if older: + summary = compress_steps(older, remaining_budget) + return [summary_message] + recent + else: + return recent +``` + +### 2.4 任务生命周期状态机 + +``` + ┌────────────────────────────┐ + │ TaskState Machine │ + └────────────────────────────┘ + + ┌─────────┐ + │ Idle │ + └────┬────┘ + │ submit_task(instruction) + ▼ + ┌─────────┐ + │Planning │ ← LLM 分析指令,生成初步计划 + └────┬────┘ + │ 计划生成完成 + ▼ + ┌─────────┐ + ┌───→│Executing│◄───┐ + │ └────┬────┘ │ + │ │ │ + │ ┌────┴────┐ │ + │ │Step N │ │ + │ │executing│ │ + │ └────┬────┘ │ + │ │ │ + │ ┌────┴────┐ │ + │ │Waiting │ │ step OK, more to do + │ │for resp │ │ + │ └────┬────┘ │ + │ │ │ + │ ┌────┴────┐ │ + │ │Observing│────┘ + │ └────┬────┘ + │ │ + │ ┌────┴────┐ + │ │Confirm? │ ← human-in-the-loop + │ └──┬───┬──┘ + │ yes │ │ no (auto-proceed) + │ │ │ + │ ┌──┴───┴──┐ + └────│Evaluate │ + └────┬────┘ + │ + ┌─────────┼─────────┐ + │ │ │ + ▼ ▼ ▼ + ┌─────────┐ ┌──────┐ ┌────────┐ + │Completed│ │Failed│ │Aborted │ + │ (成功) │ │(失败) │ │(用户终止)│ + └─────────┘ └──────┘ └────────┘ +``` + +--- + +## 3. Skill 体系 + +### 3.1 Skill 定义格式 + +每个 Skill 是一个 JavaScript 文件,包含元数据头和执行函数。 + +**完整 Skill 文件结构**: + +```javascript +/** + * @skill erp-monthly-report + * @version 1.0.0 + * @description 从ERP系统导出月度财务报表。支持按部门、科目筛选, + * 自动处理分页数据,合并导出为完整报表。 + * @domains erp.example.com, erp-test.example.com + * @author sgClaw Team + * @params { + * "type": "object", + * "required": ["month"], + * "properties": { + * "month": { + * "type": "string", + * "pattern": "^\\d{4}-\\d{2}$", + * "description": "报表月份 (如 2026-03)" + * }, + * "department": { + * "type": "string", + * "description": "部门名称 (可选, 不填则导出全部)" + * }, + * "format": { + * "type": "string", + * "enum": ["xlsx", "csv", "pdf"], + * "default": "xlsx", + * "description": "导出格式" + * } + * } + * } + */ + +/** + * 技能执行入口 + * @param {object} params - 输入参数 (符合上方 @params 定义) + * @param {function} browserAction - BrowserAction 调用函数 + * @returns {object} 执行结果 { success: boolean, data?: any, error?: string } + */ +async function execute(params, browserAction) { + const { month, department, format = 'xlsx' } = params; + + try { + // Step 1: 导航到 ERP 报表页面 + await browserAction('navigate', 'https://erp.example.com/report/finance'); + await browserAction('waitForSelector', '.report-filter', 5000); + + // Step 2: 设置筛选条件 + await browserAction('click', '#month-picker'); + await browserAction('type', '#month-input', month); + + if (department) { + await browserAction('click', '#dept-selector'); + await browserAction('type', '#dept-search', department); + await browserAction('click', `.dept-option[data-name="${department}"]`); + } + + // Step 3: 选择导出格式 + await browserAction('select', '#export-format', format); + + // Step 4: 点击导出 + await browserAction('click', '#export-btn'); + + // Step 5: 等待导出完成 + await browserAction('waitForSelector', '.export-success', 30000); + + // Step 6: 获取结果信息 + const resultText = await browserAction('getText', '.export-result'); + + return { + success: true, + data: { + message: resultText, + month: month, + department: department || '全部', + format: format + } + }; + + } catch (error) { + return { + success: false, + error: `导出报表失败: ${error.message}` + }; + } +} +``` + +### 3.2 Skill 仓库结构 + +``` +sgclaw-skills/ +├── registry.json # 技能清单 (签名 + 哈希索引) +├── builtin/ # 内置技能 (随产品交付) +│ ├── erp-monthly-report.js # ERP 月度报表导出 +│ ├── erp-anomaly-check.js # ERP 异常交易检查 +│ ├── oa-approval.js # OA 审批单处理 +│ ├── oa-meeting-schedule.js # OA 会议日程管理 +│ ├── finance-compliance.js # 财务合规线索提报 +│ ├── finance-reconciliation.js # 财务对账 +│ ├── hr-social-insurance.js # 人力社保申报 +│ ├── hr-salary-check.js # 薪酬数据核验 +│ ├── legal-contract-monitor.js # 合同履约监测 +│ └── cross-system-sync.js # 跨系统数据同步 +│ +├── custom/ # 用户自定义技能 +│ └── (用户创建的 .js 文件) +│ +└── keys/ + └── skill_verify.pub # Ed25519 公钥 (校验签名) +``` + +**registry.json 格式**: + +```json +{ + "version": "1.0", + "updated_at": "2026-03-03T00:00:00Z", + "skills": [ + { + "name": "erp-monthly-report", + "file": "builtin/erp-monthly-report.js", + "version": "1.0.0", + "hash": "sha256:a1b2c3d4e5f6...", + "signature": "ed25519:x7y8z9...", + "enabled": true + }, + { + "name": "oa-approval", + "file": "builtin/oa-approval.js", + "version": "1.2.0", + "hash": "sha256:f6e5d4c3b2a1...", + "signature": "ed25519:z9y8x7...", + "enabled": true + } + ] +} +``` + +### 3.3 Skill 生命周期 + +``` + ┌─────────────┐ + │ 开发阶段 │ + │ (JS 编写) │ + └──────┬──────┘ + │ + ▼ + ┌─────────────┐ + │ 签名阶段 │ + │ 构建系统 │ + │ Ed25519 签名│ + │ SHA-256 哈希│ + └──────┬──────┘ + │ + ▼ + ┌─────────────┐ + │ 发布阶段 │ + │ 更新 │ + │ registry │ + │ .json │ + └──────┬──────┘ + │ + ▼ + ┌─────────────┐ + │ 部署阶段 │ + │ 放入 │ + │ sgclaw- │ + │ skills/ │ + └──────┬──────┘ + │ + ▼ + ┌─────────────┐ + │ 加载阶段 │ + │ sgClaw 启动 │ + │ 校验签名 │ + │ 校验哈希 │ + │ 解析元数据 │ + │ 注册到 Agent│ + └──────┬──────┘ + │ + ▼ + ┌─────────────┐ + │ 执行阶段 │ ← Agent ReAct 循环中 LLM 选择使用 + │ Agent 调用 │ + │ 传入 params │ + │ + browser │ + │ Action 引用 │ + └──────┬──────┘ + │ + ▼ + ┌─────────────┐ + │ 沉淀阶段 │ ← 执行结果存入 Memory + │ 记录经验 │ + │ 成功率统计 │ + │ 供后续复用 │ + └─────────────┘ +``` + +### 3.4 从现有 JS 场景代码迁移到 Skill + +SuperRPA 已有大量 agent-vue 中的 JS 场景代码(49 个文件,208+ 个调用点)。 +这些代码可以迁移为 sgClaw Skill,实现复用。 + +**迁移策略**: + +| 阶段 | 内容 | 说明 | +|------|------|------| +| **Phase 1: 直接复用** | 现有 JS 代码通过 BrowserAction API 调用 | sgClaw 发送 pipe 命令 → Browser 执行 → JS SDK 代码无需改动 | +| **Phase 2: 技能化封装** | 将高频场景代码提取为 Skill | 添加元数据头、参数 schema、错误处理 | +| **Phase 3: LLM 增强** | Skill 作为 Agent 的工具选项 | LLM 根据用户指令自动选择合适的 Skill | + +**迁移示例**:将 agent-vue 中的 OA 审批代码迁移为 Skill + +现有代码(agent-vue 中): + +```javascript +// agent-vue/src/scenes/oa-approval.js +async function approveAll() { + await sgBrowser.page.navigate('https://oa.example.com/approval/pending'); + await sgBrowser.page.waitForSelector('.approval-list'); + const items = await sgBrowser.page.getText('.approval-list .item'); + for (const item of items) { + await sgBrowser.input.click(`.item[data-id="${item.id}"] .approve-btn`); + await sgBrowser.page.waitForSelector('.confirm-dialog'); + await sgBrowser.input.click('.confirm-dialog .ok-btn'); + } +} +``` + +迁移后的 Skill: + +```javascript +/** + * @skill oa-approval + * @version 1.0.0 + * @description OA系统待审批单据批量处理。支持查看待审批列表、 + * 批量审批、批量驳回、添加审批意见。 + * @domains oa.example.com, oa-test.example.com + * @params { + * "type": "object", + * "required": ["action"], + * "properties": { + * "action": { "enum": ["list", "approve_all", "reject", "approve_one"] }, + * "item_id": { "type": "string", "description": "单据ID (approve_one/reject 时需要)" }, + * "opinion": { "type": "string", "description": "审批意见 (可选)" } + * } + * } + */ +async function execute(params, browserAction) { + const { action, item_id, opinion } = params; + + // 导航到审批页面 + await browserAction('navigate', 'https://oa.example.com/approval/pending'); + await browserAction('waitForSelector', '.approval-list', 5000); + + switch (action) { + case 'list': { + const text = await browserAction('getText', '.approval-list'); + return { success: true, data: { items: text } }; + } + case 'approve_all': { + // 获取所有待审批项 + const snapshot = await browserAction('getAomSnapshot', '.approval-list'); + let count = 0; + + // 逐个审批 (带错误处理) + for (const item of snapshot) { + try { + await browserAction('click', `.item[data-id="${item.name}"] .approve-btn`); + await browserAction('waitForSelector', '.confirm-dialog', 3000); + if (opinion) { + await browserAction('type', '.opinion-input', opinion); + } + await browserAction('click', '.confirm-dialog .ok-btn'); + await browserAction('waitForSelector', '.success-toast', 3000); + count++; + } catch (e) { + // 单条失败不影响整体 + continue; + } + } + + return { success: true, data: { approved: count, total: snapshot.length } }; + } + // ... 其他 action + } +} +``` + +**迁移改动点总结**: + +| 改动项 | 说明 | +|-------|------| +| 添加元数据头 | `@skill`, `@version`, `@description`, `@domains`, `@params` | +| 函数签名统一 | `async function execute(params, browserAction)` | +| API 调用方式 | `sgBrowser.page.navigate(url)` → `browserAction('navigate', url)` | +| 错误处理 | 添加 try/catch,返回统一格式 `{ success, data, error }` | +| 参数化 | 硬编码值改为 params 输入 | + +### 3.5 Skill 执行沙箱 + +Skill 的 JS 脚本需要在受限环境中执行,防止恶意代码突破安全边界。 + +**沙箱约束**: + +| 能力 | 是否允许 | 说明 | +|------|---------|------| +| `browserAction()` | 允许 | 唯一的外部交互方式,受 MAC 策略约束 | +| `console.log/error` | 允许 | 输出到 Agent 日志 | +| `JSON.parse/stringify` | 允许 | 数据处理必需 | +| `Promise / async-await` | 允许 | 异步控制流必需 | +| `setTimeout / setInterval` | 允许 (受限) | 最大延迟 30s,用于等待 | +| `fetch / XMLHttpRequest` | **禁止** | 网络请求必须通过 browserAction | +| `require / import` | **禁止** | 不允许加载外部模块 | +| `process / child_process` | **禁止** | 不允许访问系统进程 | +| `fs / path` | **禁止** | 不允许访问文件系统 | +| `eval / Function()` | **禁止** | 不允许动态代码执行 | + +**实现方案**: + +sgClaw 使用轻量级 JS 引擎(如 Boa 或 embedded V8 via Rusty_V8)运行 Skill 脚本, +在创建执行上下文时仅注入允许的全局对象: + +```rust +// Skill 沙箱执行 (概念代码) +fn execute_skill(script: &str, params: Value, browser_tool: &BrowserPipeTool) -> Result { + let mut context = JsContext::new(); + + // 仅注入允许的全局对象 + context.register_global("browserAction", |args| { + // 代理到 BrowserPipeTool.execute() + browser_tool.execute(args) + }); + context.register_global("console", ConsoleProxy::new()); + context.register_global("JSON", JsonGlobal::new()); + context.register_global("Promise", PromiseGlobal::new()); + + // 禁止所有其他全局对象 + // (JsContext 默认不提供 Node.js / Browser API) + + // 执行技能脚本 + context.eval(script)?; + + // 调用 execute 函数 + let result = context.call("execute", &[params, browser_action_ref])?; + + Ok(result) +} +``` + +--- + +## 4. 感知层数据格式 + +### 4.1 AOM(Accessibility Object Model)快照 + +AOM 快照是 Agent 理解页面状态的核心数据源。每次浏览器操作后,响应中附带当前页面的 +AOM 快照,供 Agent 的 OBSERVE 阶段使用。 + +**AOM 快照格式**: + +```json +{ + "aom_snapshot": [ + { + "role": "navigation", + "name": "主导航栏", + "bounds": [0, 0, 1920, 60], + "children": [ + { "role": "link", "name": "首页", "bounds": [20, 10, 60, 40] }, + { "role": "link", "name": "财务报表", "bounds": [100, 10, 80, 40], "focused": true }, + { "role": "link", "name": "系统设置", "bounds": [200, 10, 80, 40] } + ] + }, + { + "role": "main", + "name": "报表筛选区", + "bounds": [0, 60, 1920, 200], + "children": [ + { + "role": "combobox", + "name": "月份选择", + "value": "2026-03", + "bounds": [20, 80, 200, 40], + "selector": "#month-picker" + }, + { + "role": "combobox", + "name": "部门筛选", + "value": "", + "bounds": [240, 80, 200, 40], + "selector": "#dept-selector" + }, + { + "role": "button", + "name": "导出报表", + "bounds": [460, 80, 100, 40], + "selector": "#export-btn", + "disabled": false + } + ] + }, + { + "role": "table", + "name": "报表数据", + "bounds": [0, 260, 1920, 600], + "row_count": 25, + "children": [ + { + "role": "row", + "name": "表头", + "children": [ + { "role": "columnheader", "name": "科目编号" }, + { "role": "columnheader", "name": "科目名称" }, + { "role": "columnheader", "name": "借方金额" }, + { "role": "columnheader", "name": "贷方金额" } + ] + } + ] + } + ] +} +``` + +**AOM 快照字段说明**: + +| 字段 | 类型 | 说明 | +|------|------|------| +| `role` | string | ARIA 角色 (button, link, textbox, table, etc.) | +| `name` | string | 元素的可访问名称 (label text, aria-label, etc.) | +| `bounds` | [x, y, w, h] | 元素的视口坐标和尺寸 (px) | +| `value` | string | 元素当前值 (input, select 等) | +| `selector` | string | CSS 选择器 (供后续操作使用) | +| `focused` | boolean | 是否获得焦点 | +| `disabled` | boolean | 是否禁用 | +| `checked` | boolean | 是否选中 (checkbox, radio) | +| `children` | array | 子元素列表 | +| `row_count` | number | 表格行数 (仅 table 角色) | + +**AOM 快照与 LLM 的关系**: + +AOM 快照被格式化为结构化文本后,作为 observation 的一部分发送给 LLM: + +``` +操作结果: 点击"财务报表"链接成功。 + +当前页面状态: +- 导航栏: 首页 | [财务报表] (当前) | 系统设置 +- 报表筛选区: + - 月份选择: 2026-03 + - 部门筛选: (未选择) + - [导出报表] 按钮 (可用) +- 报表数据: 25行数据已加载 + - 表头: 科目编号 | 科目名称 | 借方金额 | 贷方金额 +``` + +### 4.2 SoM(Set-of-Mark)标注 + +在需要视觉辅助时,sgClaw 可以请求带 SoM 标注的页面截图。SoM 在页面截图上为每个 +可交互元素叠加数字标签,便于 LLM 通过编号引用元素。 + +**请求方式**: + +```json +{ + "seq": 10, + "type": "command", + "action": "pageScreenshot", + "params": { + "full_page": false, + "som_overlay": true + }, + "security": { "expected_domain": "erp.example.com", "hmac": "..." } +} +``` + +**响应**: + +```json +{ + "seq": 10, + "type": "response", + "success": true, + "data": { + "image_base64": "/9j/4AAQSkZJRg...", + "som_labels": [ + { "id": 1, "selector": "#month-picker", "name": "月份选择", "bounds": [20, 80, 200, 40] }, + { "id": 2, "selector": "#dept-selector", "name": "部门筛选", "bounds": [240, 80, 200, 40] }, + { "id": 3, "selector": "#export-btn", "name": "导出报表", "bounds": [460, 80, 100, 40] } + ] + } +} +``` + +**LLM 使用 SoM 的方式**: + +LLM 看到截图后可以引用标签编号: + +``` +我看到页面上有以下可交互元素: +[1] 月份选择 - 当前值 2026-03 +[2] 部门筛选 - 未选择 +[3] 导出报表按钮 + +我需要点击 [3] 导出报表按钮来导出数据。 +``` + +**SoM 使用策略**: + +| 场景 | 使用方式 | 优先级 | +|------|---------|--------| +| 正常操作 | AOM 快照(文本) | 默认,token 开销低 | +| AOM 不可用或不完整 | SoM 截图 | 兜底方案 | +| 复杂视觉布局 | AOM + SoM 结合 | 特殊场景 | +| 表格/图表分析 | SoM 截图 | 视觉信息丰富时 | + +### 4.3 感知数据选择策略 + +Agent 在 OBSERVE 阶段根据当前状态自动选择感知方式: + +``` +OBSERVE(pipe_response): + │ + ├── 1. 解析 pipe_response.aom_snapshot + │ │ + │ ├── AOM 不为空且完整度 > 80%? + │ │ → 使用 AOM 文本格式作为 observation + │ │ + │ └── AOM 为空或不完整? + │ → 请求 SoM 截图作为 observation + │ + ├── 2. AOM 完整度判断 + │ ├── 页面有可交互元素但 AOM 未列出 → 不完整 + │ ├── AOM 元素数为 0 但页面非空白 → 不完整 + │ └── 其他情况 → 完整 + │ + └── 3. 格式化 observation + ├── AOM: 结构化文本描述 + └── SoM: 截图 (base64) + 标签列表 +``` + +--- + +## 5. LLM 集成 + +### 5.1 Provider 适配层 + +sgClaw 通过 ZeroClaw 的 Provider trait 抽象对接不同的 LLM 服务。 +每个 Provider 实现 HTTP 调用、streaming 解析、错误重试等逻辑。 + +**Provider 统一行为要求**: + +| 行为 | 要求 | 说明 | +|------|------|------| +| 超时 | 连接 10s,首 token 30s,总体 120s | 防止挂起 | +| 重试 | 最多 3 次,指数退避 (1s, 2s, 4s) | 仅重试 5xx 和网络错误 | +| Streaming | 必须支持 | 用于实时日志显示 | +| Tool-use | 必须支持 | Agent ReAct 的核心能力 | +| Token 计量 | 每次调用后上报 usage | 审计和成本控制 | +| 错误分类 | 区分可重试/不可重试错误 | 401/403 不重试 | + +### 5.2 Claude Provider 实现要点 + +```rust +pub struct ClaudeProvider { + client: reqwest::Client, + api_key: String, + model: String, + base_url: String, +} + +impl ClaudeProvider { + const DEFAULT_BASE_URL: &'static str = "https://api.anthropic.com"; + const API_VERSION: &'static str = "2023-06-01"; + + /// 将 sgClaw 的 Message 格式转换为 Claude API 格式 + fn convert_messages(messages: &[Message]) -> Vec { + // Message::User → { role: "user", content: [...] } + // Message::Assistant → { role: "assistant", content: [...] } + // Message::Tool → { role: "user", content: [{ type: "tool_result", ... }] } + } + + /// 将 sgClaw 的 ToolDefinition 转换为 Claude tool 格式 + fn convert_tools(tools: &[ToolDefinition]) -> Vec { + // ToolDefinition → { name, description, input_schema } + } +} +``` + +### 5.3 输出约束 + +LLM 的输出必须遵循以下约束: + +**Tool-use 输出格式** (Claude API): + +```json +{ + "role": "assistant", + "content": [ + { + "type": "thinking", + "thinking": "用户要导出本月合规报表。我需要先导航到 ERP 系统的报表模块..." + }, + { + "type": "tool_use", + "id": "toolu_01xyz", + "name": "browser_action", + "input": { + "action": "navigate", + "params": { + "url": "https://erp.example.com/report/compliance" + }, + "expected_domain": "erp.example.com" + } + } + ] +} +``` + +**输出校验规则**: + +``` +LLM 输出校验: +│ +├── 1. 是否为合法 JSON? +│ → 否: 请求 LLM 重新生成 (最多 2 次) +│ +├── 2. tool_call 格式是否正确? +│ ├── name 必须是 "browser_action" 或已注册的 MCP 工具 +│ ├── input 必须包含 action 字段 +│ └── action 必须在 Action 枚举中 +│ +├── 3. 参数是否通过 JSON Schema 校验? +│ → 否: 将校验错误信息反馈给 LLM,请求修正 +│ +├── 4. 是否包含 expected_domain? +│ → 否: 自动从上下文推断 (当前页面域名) +│ +└── 5. 通过所有校验 + → 交给 BrowserPipeTool 执行 +``` + +### 5.4 System Prompt 中的工具描述 + +提供给 LLM 的工具描述需要精确、简洁,帮助 LLM 正确选择操作: + +```json +{ + "name": "browser_action", + "description": "在浏览器中执行操作。可以点击元素、输入文本、导航页面、获取页面内容等。每次调用需要指定 action 类型和对应参数。", + "input_schema": { + "type": "object", + "required": ["action", "expected_domain"], + "properties": { + "action": { + "type": "string", + "enum": ["click", "type", "navigate", "getText", "getHtml", "waitForSelector", "pageScreenshot", "select", "scrollTo", "getAomSnapshot", "storageSet", "storageGet", "zombieSpawn", "zombieKill"], + "description": "要执行的操作类型" + }, + "params": { + "type": "object", + "description": "操作参数,根据 action 类型不同而不同" + }, + "expected_domain": { + "type": "string", + "description": "操作目标页面的域名(安全校验用)" + } + } + } +} +``` + +--- + +## 6. 记忆与自进化 + +### 6.1 记忆分层架构 + +``` +┌─────────────────────────────────────────────────────────────┐ +│ 记忆系统架构 │ +│ │ +│ ┌─ L0: 即时记忆 ─────────────────────────────────────────┐ │ +│ │ LLM 上下文窗口内的消息历史 │ │ +│ │ 生命周期: 单次 LLM 调用 │ │ +│ │ 容量: max_tokens (如 4096) │ │ +│ │ 用途: 当前步骤的推理依据 │ │ +│ └────────────────────────────────────────────────────────┘ │ +│ │ +│ ┌─ L1: 短期记忆 (Ring Buffer) ────────────────────────────┐ │ +│ │ VecDeque │ │ +│ │ 生命周期: 单次任务 (execute_task 调用) │ │ +│ │ 容量: 50 条消息 / 8000 tokens │ │ +│ │ 用途: 当前任务的完整对话历史 │ │ +│ │ 淘汰策略: FIFO,超限时压缩最早的消息为摘要 │ │ +│ └────────────────────────────────────────────────────────┘ │ +│ │ +│ ┌─ L2: 长期记忆 (SQLite) ─────────────────────────────────┐ │ +│ │ 持久化存储 │ │ +│ │ 生命周期: 跨任务、跨会话 │ │ +│ │ 容量: 磁盘空间限制 (建议 < 100MB) │ │ +│ │ 用途: 任务经验、用户偏好、技能执行记录 │ │ +│ │ 检索: 语义相似度 (向量) + 关键词匹配 │ │ +│ └────────────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +### 6.2 记忆读写流程 + +**写入流程**(任务执行过程中): + +``` +Agent 每步操作后: +│ +├── 1. 写入短期记忆 (L1) +│ ├── 追加 thinking 消息 +│ ├── 追加 action 消息 +│ └── 追加 observation 消息 +│ +├── 2. 检查短期记忆容量 +│ ├── 消息数 > 50? → 压缩最早 10 条为摘要 +│ └── token 数 > 8000? → 压缩直至 < 8000 +│ +└── 3. (任务结束时) 写入长期记忆 (L2) + ├── 保存任务摘要 (type: TaskResult) + ├── 保存成功步骤序列 (type: SkillExperience) + └── 更新技能执行统计 (skill_executions 表) +``` + +**读取流程**(新任务开始时): + +``` +execute_task(instruction): +│ +├── 1. 清空短期记忆 (新任务) +│ +├── 2. 从长期记忆检索相关经验 +│ ├── 语义搜索: embedding(instruction) vs memory_entries +│ ├── 取 top-3 相关条目 +│ └── 格式化为 "历史经验" 上下文 +│ +├── 3. 检查是否有精确匹配的 Skill 经验 +│ ├── 查询 skill_executions 表 +│ ├── 找到成功执行记录? +│ │ → 作为推荐步骤加入 system_prompt +│ └── 未找到? +│ → 不影响执行 +│ +└── 4. 组装初始上下文 + ├── system_prompt + 长期记忆检索结果 + ├── 用户指令 + └── 开始 ReAct 循环 +``` + +### 6.3 自进化学习机制 + +sgClaw 的自进化核心思想:每次成功执行的任务都是一个学习样本。 +通过记录和检索这些样本,Agent 在后续遇到相似任务时可以更快、更准确地完成。 + +**自进化循环**: + +``` + ┌─────────────────────────────┐ + │ 首次执行新任务 │ + │ │ + │ LLM 从零推理 │ + │ 多步试探 │ + │ 可能遇到错误和回退 │ + │ 平均 8-15 步完成 │ + └──────────┬──────────────────┘ + │ 成功完成 + │ + ▼ + ┌─────────────────────────────┐ + │ 经验沉淀 │ + │ │ + │ 记录成功步骤序列 │ + │ 记录关键决策点 │ + │ 计算向量嵌入 │ + │ 存入长期记忆 (L2) │ + └──────────┬──────────────────┘ + │ + ▼ + ┌─────────────────────────────┐ + │ 再次执行相似任务 │ + │ │ + │ 检索到历史经验 │ + │ LLM 参考已有路径 │ + │ 跳过试探阶段 │ + │ 平均 4-6 步完成 │ + └──────────┬──────────────────┘ + │ + ▼ + ┌─────────────────────────────┐ + │ 技能提炼 (可选) │ + │ │ + │ 高频任务 (>5 次相似执行) │ + │ 提取为正式 Skill 脚本 │ + │ 后续直接调用,1-2 步完成 │ + └─────────────────────────────┘ +``` + +**经验记录格式**: + +```json +{ + "id": "exp-2026-03-03-001", + "type": "task_result", + "content": "任务: 导出本月ERP合规报表\n\n执行路径:\n1. navigate → erp.example.com/report\n2. click → #month-picker → 设置为2026-03\n3. click → #compliance-tab\n4. click → #export-btn\n5. waitForSelector → .export-success\n\n结果: 成功导出,文件名 compliance-2026-03.xlsx", + "metadata": { + "task_instruction": "导出本月合规报表", + "steps": 5, + "duration_ms": 12500, + "domains_visited": ["erp.example.com"], + "skills_used": [], + "success": true + }, + "embedding": [0.12, -0.34, 0.56, ...], + "created_at": "2026-03-03T10:30:00Z", + "session_id": "trace-abc123" +} +``` + +### 6.4 向量检索实现 + +长期记忆的语义检索使用简单的余弦相似度计算。在数据量较小时(< 10000 条), +线性扫描足够快(< 10ms)。 + +**嵌入向量生成**: + +``` +文本 → 嵌入方式: +│ +├── 在线模型可用时: +│ └── 调用 LLM Provider 的 embedding API +│ ├── Claude: 不提供独立 embedding API,使用 summarize + hash +│ ├── OpenAI: text-embedding-3-small (1536 维) +│ └── Ollama: nomic-embed-text (384 维) +│ +└── 离线/无 embedding API 时: + └── 简单 TF-IDF 向量 + 关键词匹配 + ├── 中文分词 (jieba-rs) + ├── 计算词频向量 + └── 余弦相似度排序 +``` + +**检索优化策略**: + +| 策略 | 说明 | 适用场景 | +|------|------|---------| +| 类型过滤 | 先按 entry_type 过滤,减少扫描量 | 总是使用 | +| 时间窗口 | 优先检索最近 30 天的记忆 | 默认策略 | +| 域名过滤 | 按当前任务涉及的域名过滤 | 域名已知时 | +| 混合排序 | 0.7 * 向量相似度 + 0.3 * 时间衰减 | 综合排序 | + +--- + +## 7. 审计与追溯 + +### 7.1 Trace ID 体系 + +每次 Agent 会话分配唯一的 `trace_id`,贯穿所有模块和组件,用于事后审计追溯。 + +**trace_id 格式**: + +``` +sgclaw-{date}-{random} +示例: sgclaw-20260303-a1b2c3d4 +``` + +**trace_id 传播路径**: + +``` +SgClawProcessHost::Start() + │ 生成 trace_id + │ + ├─→ init message: { trace_id: "sgclaw-20260303-a1b2c3d4" } + │ + ├─→ sgClaw Runtime: 所有日志附带 trace_id + │ ├─→ LLM 调用记录 + │ ├─→ Pipe 命令记录 + │ └─→ Memory 条目关联 + │ + ├─→ PipeListener: 所有 pipe 消息日志附带 trace_id + │ + └─→ CommandRouter: 操作审计日志附带 trace_id +``` + +### 7.2 审计日志格式 + +**sgClaw 端日志** (输出到 stderr,结构化 JSON): + +```json +{ + "timestamp": "2026-03-03T10:23:05.123Z", + "level": "INFO", + "trace_id": "sgclaw-20260303-a1b2c3d4", + "module": "agent::runtime", + "event": "step_completed", + "data": { + "step": 3, + "action": "click", + "selector": "#export-btn", + "domain": "erp.example.com", + "success": true, + "duration_ms": 245 + } +} +``` + +**Browser 端日志** (SuperRPA 已有日志系统): + +``` +[2026-03-03 10:23:05.123] [sgclaw] [trace:sgclaw-20260303-a1b2c3d4] + PIPE_CMD seq=3 action=click selector=#export-btn domain=erp.example.com + MAC_CHECK: ALLOW + CMD_EXEC: OK (245ms) +``` + +### 7.3 日志采集与存储 + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ 日志采集架构 │ +│ │ +│ sgClaw (Rust) Browser (C++) │ +│ ┌───────────┐ ┌───────────┐ │ +│ │ tracing │ │ LOG() │ │ +│ │ → stderr │ │ → file │ │ +│ └─────┬─────┘ └─────┬─────┘ │ +│ │ │ │ +│ ▼ ▼ │ +│ sgclaw.log chrome_debug.log │ +│ (JSON Lines) (SuperRPA 格式) │ +│ │ │ │ +│ └───────────┬───────────────────┘ │ +│ │ │ +│ ▼ │ +│ 审计查询接口 │ +│ (按 trace_id 关联检索) │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +**日志保留策略**: + +| 日志类型 | 保留时长 | 存储位置 | +|---------|---------|---------| +| sgClaw 操作日志 | 90 天 | 本地文件 + 可选远程上传 | +| Pipe 通信记录 | 30 天 | 本地文件 | +| LLM 调用记录 | 90 天 | 本地文件 (含 token 用量) | +| 长期记忆 (SQLite) | 永久 | 本地数据库 | +| 浏览器审计日志 | 按已有策略 | SuperRPA 日志系统 | + +--- + +*文档结束。本文档为 sgClaw L3 层数据流与 Skill 体系参考。Skill 开发者应重点关注 +第 3 节(Skill 体系)和第 4 节(感知层数据格式),高级开发者应关注第 2 节(Agent 循环) +和第 6 节(记忆与自进化)。* diff --git a/docs/L4-工程实现与部署拓扑层.md b/docs/L4-工程实现与部署拓扑层.md new file mode 100644 index 0000000..422cdc2 --- /dev/null +++ b/docs/L4-工程实现与部署拓扑层.md @@ -0,0 +1,1577 @@ +# L4 -- 工程实现与部署拓扑层 + +**文档版本**: 1.0 +**适用项目**: sgClaw (业数融合一平台 AI Agent 底座) +**编制日期**: 2026-03-03 + +**读者**: 开发者、DevOps、测试工程师 -- 需要搭建开发环境、理解构建流程、执行测试、部署发布。 + +--- + +## 1. 仓库结构 + +sgClaw 系统的代码分布在两个仓库中:独立的 sgClaw Rust 仓库和 SuperRPA Chromium 仓库。 +前者承载 Agent 核心逻辑,后者承载浏览器侧集成代码和前端 UI。 + +### 1.1 sgClaw 仓库 (Rust 端) + +``` +/home/zyl/projects/sgClaw/ +├── Cargo.toml # 项目配置、依赖声明、编译优化 +├── Cargo.lock # 依赖版本锁定 +├── src/ +│ ├── main.rs # 入口: pipe I/O 初始化, tokio runtime 启动 +│ ├── agent/ +│ │ ├── mod.rs # Agent 模块导出 +│ │ ├── runtime.rs # ZeroClaw ReAct 循环 (think -> act -> observe) +│ │ └── critic.rs # 输出质量评估 + Circuit Breaker 熔断器 +│ ├── pipe/ +│ │ ├── mod.rs # Pipe 模块导出 +│ │ ├── protocol.rs # JSON Line 编解码, 消息类型定义 +│ │ ├── handshake.rs # 版本协商 + HMAC 密钥交换 +│ │ └── browser_tool.rs # BrowserPipeTool: 自定义 Tool trait 实现 +│ ├── skill/ +│ │ ├── mod.rs # Skill 模块导出 +│ │ ├── loader.rs # 技能发现 + 签名校验 + 沙箱加载 +│ │ └── registry.rs # 技能注册表 (名称 -> 技能映射) +│ ├── llm/ +│ │ ├── mod.rs # LLM 模块导出 +│ │ ├── provider.rs # Provider trait 定义 (统一接口) +│ │ ├── claude.rs # Claude API 实现 (streaming) +│ │ ├── openai.rs # OpenAI/兼容 API 实现 +│ │ └── ollama.rs # 本地 Ollama 实现 +│ ├── memory/ +│ │ ├── mod.rs # Memory 模块导出 +│ │ ├── short_term.rs # Ring Buffer 短期对话记忆 +│ │ └── long_term.rs # SQLite + 向量存储 长期知识库 +│ ├── security/ +│ │ ├── mod.rs # Security 模块导出 +│ │ ├── mac_policy.rs # 域名 / Action 白名单校验 +│ │ └── hmac.rs # HMAC-SHA256 消息签名与验证 +│ └── config/ +│ ├── mod.rs # Config 模块导出 +│ └── settings.rs # 配置文件加载 (TOML / 环境变量) +├── skills/ # 内置技能目录 +│ ├── builtin/ # 预置业务技能脚本 +│ └── registry.json # 技能清单 + 签名哈希 +├── tests/ +│ ├── integration/ # 集成测试 (pipe 协议, 端到端) +│ └── fixtures/ # 测试数据 (mock LLM 响应, JSON 样例) +├── docs/ # 设计文档 (L0-L4 分层) +├── scripts/ +│ ├── build-linux.sh # Linux musl 静态链接构建脚本 +│ ├── build-windows.sh # Windows MSVC 交叉编译脚本 +│ └── install-to-superrpa.sh # 二进制部署到 SuperRPA 目录 +└── .github/ + └── workflows/ # CI/CD 流水线定义 +``` + +### 1.2 SuperRPA 新增文件 (C++ 端) + +在已有 SuperRPA 仓库中新增一个 `sgclaw/` 子目录,约 500-600 行 C++ 代码。 +所有新增文件位于 `src/chrome/browser/superrpa/sgclaw/` 下,与已有子系统平级。 + +``` +src/chrome/browser/superrpa/ +├── BUILD.gn # [修改] 新增 :sgclaw source_set 依赖 +├── sgclaw/ # [新增目录] +│ ├── sg_claw_process_host.h # 进程宿主: 生命周期管理 (Start/Stop/OnCrash) +│ ├── sg_claw_process_host.cc # ~200-300 行, Singleton, 管道创建与进程启动 +│ ├── pipe_listener.h # 异步读取循环: 解析 JSON Line 消息 +│ ├── pipe_listener.cc # ~150 行, base::FileDescriptorWatcher +│ ├── mac_whitelist_check.h # Pipe 来源 MAC 校验: action + domain 白名单 +│ ├── mac_whitelist_check.cc # ~100 行, 与已有 rules.json 集成 +│ └── BUILD.gn # 子模块构建声明 +``` + +已有文件的修改点 (最小侵入): + +| 文件 | 修改内容 | 行数变更 | +|------|---------|---------| +| `superrpa/BUILD.gn` | deps 新增 `":sgclaw"` | +1 行 | +| `ui/ui_page_controller.cc` | 新增 Agent 启停的 FunctionsUI handler | +~50 行 | + +### 1.3 Side Panel 新增文件 (前端) + +前端变更位于 agent-vue 应用中,新增一个 Vue 组件用于 Agent 控制面板。 +agent-vue 基于 Vue 2.6 + Element UI,构建产物部署到 Side Panel 的 agent/ 目录。 + +``` +agent-vue/src/ +├── components/ +│ └── SgClawControl.vue # [新增] Agent 启停控制 + 状态展示 + 任务输入 +``` + +构建产物部署位置: + +``` +src/chrome/browser/resources/superrpa/panels/apps/agent/ +├── css/ +├── js/ +├── index.html +└── ... # npm run build:production 输出 +``` + +--- + +## 2. 构建系统 + +### 2.1 Rust 构建 (sgClaw) + +#### 2.1.1 Cargo.toml 核心配置 + +```toml +[package] +name = "sgclaw" +version = "0.1.0" +edition = "2021" +description = "AI Agent engine for SuperRPA browser" + +[dependencies] +zeroclaw = { version = "0.x", features = ["react-loop", "mcp"] } +rmcp = { version = "0.x", features = ["client", "transport-stdio"] } +tokio = { version = "1", features = ["full"] } +serde = { version = "1", features = ["derive"] } +serde_json = "1" +rusqlite = { version = "0.31", features = ["bundled"] } +hmac = "0.12" +sha2 = "0.10" +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["json"] } +clap = { version = "4", features = ["derive"] } +thiserror = "1" +anyhow = "1" + +[profile.release] +opt-level = "z" # 最小体积优化 +lto = true # Link-Time Optimization +codegen-units = 1 # 单 codegen unit, 更好的优化 +strip = true # 去除符号表 +panic = "abort" # abort 而非 unwind, 减小体积 +``` + +#### 2.1.2 构建命令 + +```bash +# --- 开发构建 (快速编译, 含调试信息) --- +cargo build + +# --- Release Linux (银河麒麟 V10 目标) --- +# musl 静态链接, 无 glibc 依赖 +cargo build --release --target x86_64-unknown-linux-musl +strip target/x86_64-unknown-linux-musl/release/sgclaw + +# 验证产物 +file target/x86_64-unknown-linux-musl/release/sgclaw +# 预期输出: ELF 64-bit LSB executable, x86-64, statically linked +ls -lh target/x86_64-unknown-linux-musl/release/sgclaw +# 预期大小: ~8.8 MB + +# --- Release Windows --- +cargo build --release --target x86_64-pc-windows-msvc + +# 验证产物 +ls -lh target/x86_64-pc-windows-msvc/release/sgclaw.exe +# 预期大小: ~9.0 MB +``` + +#### 2.1.3 构建产物位置 + +``` +target/ +├── debug/ +│ └── sgclaw # 开发构建 (~30-50 MB, 含调试符号) +├── x86_64-unknown-linux-musl/ +│ └── release/ +│ └── sgclaw # Linux release (~8.8 MB) +└── x86_64-pc-windows-msvc/ + └── release/ + └── sgclaw.exe # Windows release (~9.0 MB) +``` + +#### 2.1.4 交叉编译配置 + +在开发机上为 Linux musl 目标编译需要安装 musl 工具链: + +```bash +# Ubuntu / 银河麒麟 +sudo apt install musl-tools + +# 添加 Rust target +rustup target add x86_64-unknown-linux-musl + +# 如果需要 Windows 交叉编译 (从 Linux 编译 Windows 产物) +# 需要 cargo-xwin 或在 Windows CI 上构建 +cargo install cargo-xwin +cargo xwin build --release --target x86_64-pc-windows-msvc +``` + +### 2.2 C++ 构建 (SuperRPA 新增部分) + +#### 2.2.1 BUILD.gn 配置 + +新增 `sgclaw/BUILD.gn`: + +```gn +# src/chrome/browser/superrpa/sgclaw/BUILD.gn + +source_set("sgclaw") { + sources = [ + "sg_claw_process_host.cc", + "sg_claw_process_host.h", + "pipe_listener.cc", + "pipe_listener.h", + "mac_whitelist_check.cc", + "mac_whitelist_check.h", + ] + deps = [ + "//base", + "//content/public/browser", + ] +} + +source_set("sgclaw_unit_tests") { + testonly = true + sources = [ + "sg_claw_process_host_unittest.cc", + "pipe_listener_unittest.cc", + "mac_whitelist_check_unittest.cc", + ] + deps = [ + ":sgclaw", + "//base", + "//base/test:test_support", + "//testing/gtest", + ] +} +``` + +在父级 `superrpa/BUILD.gn` 中添加依赖: + +```gn +source_set("superrpa") { + # ... 已有 sources ... + deps = [ + ":ai", + ":sgclaw", # <-- 新增 + "//base", + # ... 已有 deps ... + ] +} +``` + +#### 2.2.2 构建命令 + +```bash +# 进入 Chromium 源码目录 +cd /home/zyl/projects/superRpa/src + +# 生成构建配置 (仅首次或 BUILD.gn 变更时) +gn gen out/Default + +# 增量编译 Chrome (24 并发) +autoninja -j 24 -C out/Default chrome + +# 单独编译 sgclaw 单元测试 +autoninja -j 24 -C out/Default sgclaw_unittests + +# 运行 sgclaw 单元测试 +./out/Default/sgclaw_unittests +``` + +### 2.3 前端构建 (agent-vue) + +agent-vue 使用 Vue CLI 构建,产物部署到浏览器资源目录。 + +```bash +# 进入 agent-vue 目录 +cd /home/zyl/projects/superRpa/agent-vue + +# 安装依赖 (首次) +npm install + +# 开发模式 +npm run serve + +# 生产构建 +npm run build:production + +# 构建产物位于 dist/ 目录 +ls dist/ +# css/ js/ index.html favicon.ico ... +``` + +前端技术栈: +- Vue 2.6.14 + Vue Router 3.5 + Vuex 3.6 +- Element UI 2.15 +- Axios 1.9 (HTTP 请求) +- 无新增依赖,SgClawControl.vue 使用已有框架组件 + +### 2.4 联合构建流程 + +完整的从零构建步骤,适用于全新开发环境或 CI 流水线: + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ 联合构建流程 │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ Step 1: 构建 sgclaw (Rust) │ +│ ┌──────────────────────────────────────────────────────────┐ │ +│ │ cd /home/zyl/projects/sgClaw │ │ +│ │ cargo build --release --target x86_64-unknown-linux-musl │ │ +│ │ strip target/x86_64-unknown-linux-musl/release/sgclaw │ │ +│ │ cargo test │ │ +│ └──────────────────────────────┬───────────────────────────┘ │ +│ │ sgclaw (~8.8 MB) │ +│ ▼ │ +│ Step 2: 复制二进制到 SuperRPA 构建目录 │ +│ ┌──────────────────────────────────────────────────────────┐ │ +│ │ cp target/x86_64-unknown-linux-musl/release/sgclaw \ │ │ +│ │ /home/zyl/projects/superRpa/repack_work/ │ │ +│ │ extract_YYYYMMDD/opt/chromium.org/chromium/sgclaw │ │ +│ └──────────────────────────────┬───────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ Step 3: 构建 SuperRPA (C++) │ +│ ┌──────────────────────────────────────────────────────────┐ │ +│ │ cd /home/zyl/projects/superRpa/src │ │ +│ │ gn gen out/Default │ │ +│ │ autoninja -j 24 -C out/Default chrome │ │ +│ └──────────────────────────────┬───────────────────────────┘ │ +│ │ chrome (Chromium 主程序) │ +│ ▼ │ +│ Step 4: 构建 agent-vue (前端) │ +│ ┌──────────────────────────────────────────────────────────┐ │ +│ │ cd /home/zyl/projects/superRpa/agent-vue │ │ +│ │ npm install && npm run build:production │ │ +│ └──────────────────────────────┬───────────────────────────┘ │ +│ │ dist/ (HTML + CSS + JS) │ +│ ▼ │ +│ Step 5: 部署前端到 Side Panel 资源目录 │ +│ ┌──────────────────────────────────────────────────────────┐ │ +│ │ cp -r agent-vue/dist/* \ │ │ +│ │ src/chrome/browser/resources/superrpa/panels/ │ │ +│ │ apps/agent/ │ │ +│ └──────────────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +自动化构建脚本 (`scripts/build-linux.sh`): + +```bash +#!/bin/bash +set -euo pipefail + +SGCLAW_DIR="/home/zyl/projects/sgClaw" +SUPERRPA_DIR="/home/zyl/projects/superRpa" +TARGET="x86_64-unknown-linux-musl" + +echo "=== [1/4] Building sgclaw (Rust) ===" +cd "$SGCLAW_DIR" +cargo build --release --target "$TARGET" +strip "target/$TARGET/release/sgclaw" +echo " Binary size: $(du -h target/$TARGET/release/sgclaw | cut -f1)" + +echo "=== [2/4] Running sgclaw tests ===" +cargo test --release --target "$TARGET" + +echo "=== [3/4] Building SuperRPA (C++) ===" +cd "$SUPERRPA_DIR/src" +autoninja -j 24 -C out/Default chrome + +echo "=== [4/4] Building agent-vue ===" +cd "$SUPERRPA_DIR/agent-vue" +npm run build:production + +echo "=== Build complete ===" +``` + +--- + +## 3. 依赖管理 + +### 3.1 Rust 依赖 (Cargo.toml) + +| 依赖 | 版本 | 用途 | 可选 | +|------|------|------|------| +| `zeroclaw` | latest | Agent Runtime: ReAct 循环, Tool/Provider trait | 否 | +| `rmcp` | latest | MCP Client: 连接外部 MCP Server 获取工具 | features: client, transport-stdio | +| `tokio` | 1.x | 异步运行时: 事件循环, I/O, 定时器 | features: full | +| `serde` | 1.x | 序列化框架: derive 宏 | features: derive | +| `serde_json` | 1.x | JSON 编解码: pipe 协议消息 | 否 | +| `rusqlite` | 0.31+ | SQLite 存储: 长期记忆, Skill 缓存 | features: bundled (内嵌 SQLite) | +| `hmac` | 0.12 | HMAC 消息认证码: pipe 消息签名 | 否 | +| `sha2` | 0.10 | SHA-256 哈希: 配合 hmac 使用 | 否 | +| `tracing` | 0.1 | 结构化日志: span, event, 链路追踪 | 否 | +| `tracing-subscriber` | 0.3 | 日志输出: JSON 格式, 文件/stderr 输出 | features: json | +| `clap` | 4.x | 命令行参数解析: 配置文件路径, 日志级别 | features: derive | +| `thiserror` | 1.x | 错误类型定义: 自定义 Error enum | 否 | +| `anyhow` | 1.x | 错误传播: 顶层错误处理 | 否 | + +依赖锁定策略: +- `Cargo.lock` 提交到版本控制,确保可重复构建 +- 依赖更新通过 `cargo update` 手动触发,经 CI 验证后合并 +- `rusqlite` 使用 `bundled` feature,内嵌 SQLite 源码编译,避免系统库版本差异 + +### 3.2 C++ 依赖 + +sgClaw 的 C++ 部分仅使用 Chromium 已有的基础库,无新增第三方依赖: + +| 依赖 | GN 路径 | 用途 | +|------|---------|------| +| base | `//base` | 线程 (SequencedTaskRunner), 回调 (OnceCallback), 进程管理 (LaunchProcess), 文件监听 (FileDescriptorWatcher) | +| content | `//content/public/browser` | BrowserContext, WebContents 交互接口 | + +无需引入 Mojo IPC -- sgClaw 使用更轻量的 STDIO Pipe 通信。 +Side Panel UI 通过已有的 `rpa_page_handler` Mojo 接口发送启停命令, +该接口已在 SuperRPA 中实现,仅需新增 2 个 handler method。 + +### 3.3 前端依赖 + +无新增 npm 依赖。SgClawControl.vue 使用 agent-vue 已有的技术栈: + +| 依赖 | 版本 | 用途 | +|------|------|------| +| `vue` | 2.6.14 | 组件框架 | +| `element-ui` | 2.15.14 | UI 组件库 (Button, Input, Message) | +| `vuex` | 3.6.2 | 状态管理 (Agent 运行状态) | +| `axios` | 1.9.0 | HTTP 请求 (如需与 local_service 通信) | + +--- + +## 4. 打包与发布 + +### 4.1 Linux .deb 打包 + +SuperRPA 以 `.deb` 包形式发布到银河麒麟 V10 SP1。sgClaw 的产物集成到现有打包流程中。 + +当前 .deb 包结构 (`repack_work/extract_YYYYMMDD/`): + +``` +DEBIAN/ +├── control # 包元数据 (版本、大小、描述) +└── postinst # 安装后脚本 (chmod +x) +opt/chromium.org/chromium/ +├── chrome # Chromium 主程序 (~1.6 GB) +├── superrpa-chromium # 启动脚本 (wrapper) +├── node/ # Node.js 运行时 +├── local_service/ # 本地 HTTP 服务 +├── locales/ # 语言包 +├── *.pak # 资源文件 +├── sgclaw # [新增] AI Agent 引擎 +└── sgclaw-skills/ # [新增] 技能目录 + ├── builtin/ # 内置技能脚本 + └── registry.json # 技能清单 +usr/bin/ +└── superrpa-chromium # 系统级启动链接 +``` + +#### 4.1.1 修改 repack-with-service.sh + +在现有 `scripts/repack-with-service.sh` 中新增 sgclaw 安装步骤: + +```bash +# --- 新增: Install sgclaw binary --- +echo "[N/N] Installing sgclaw AI Agent..." +SGCLAW_BIN="/home/zyl/projects/sgClaw/target/x86_64-unknown-linux-musl/release/sgclaw" +SGCLAW_SKILLS="/home/zyl/projects/sgClaw/skills" + +if [ -f "$SGCLAW_BIN" ]; then + cp "$SGCLAW_BIN" "$CHROMIUM_DIR/sgclaw" + chmod +x "$CHROMIUM_DIR/sgclaw" + echo " sgclaw binary: $CHROMIUM_DIR/sgclaw ($(du -h "$SGCLAW_BIN" | cut -f1))" +fi + +if [ -d "$SGCLAW_SKILLS" ]; then + mkdir -p "$CHROMIUM_DIR/sgclaw-skills" + cp -r "$SGCLAW_SKILLS/"* "$CHROMIUM_DIR/sgclaw-skills/" + echo " sgclaw skills: $CHROMIUM_DIR/sgclaw-skills/" +fi +``` + +#### 4.1.2 postinst 修改 + +```bash +#!/bin/bash +set -e + +CHROMIUM_DIR=/opt/chromium.org/chromium + +# 已有权限设置 +chmod +x "$CHROMIUM_DIR/node/bin/node" 2>/dev/null || true +chmod +x "$CHROMIUM_DIR/superrpa-chromium" 2>/dev/null || true + +# 新增: sgclaw 权限 +chmod +x "$CHROMIUM_DIR/sgclaw" 2>/dev/null || true + +echo "SuperRPA Chromium with LocalService installed successfully" +``` + +#### 4.1.3 包大小影响 + +``` +当前 .deb 包大小: ~447 MB (含 Node.js local_service) +sgclaw 二进制: + ~8.8 MB +sgclaw-skills/: + ~0.2 MB (JSON + JS 技能脚本) +──────────────────────────────────────── +预计新 .deb 包大小: ~456 MB (增加约 2%) +``` + +### 4.2 Windows 安装包 + +Windows 端的 sgclaw.exe 放置在 SuperRPA 浏览器安装目录下: + +``` +C:\Program Files\SuperRPA Chromium\ +├── chrome.exe +├── sgclaw.exe # [新增] ~9 MB +├── sgclaw-skills\ # [新增] 技能目录 +│ ├── builtin\ +│ └── registry.json +└── ... +``` + +安装程序修改要点: +- 在 NSIS/Inno Setup 脚本中新增 sgclaw.exe 和 sgclaw-skills/ 的文件拷贝项 +- 无需注册服务或添加注册表项 -- sgclaw 由 chrome.exe 按需启动 +- 卸载时一并删除 sgclaw 相关文件和 SQLite 数据库 + +### 4.3 升级策略 + +#### 4.3.1 sgclaw 二进制升级 + +sgclaw 二进制本身无状态(运行时状态在内存中,持久状态在 SQLite 中), +升级策略为直接覆盖: + +```bash +# 停止 Agent (如果正在运行,由 SgClawProcessHost::Stop() 完成) +# 替换二进制 +cp sgclaw_new /opt/chromium.org/chromium/sgclaw +chmod +x /opt/chromium.org/chromium/sgclaw +# 下次用户点击 [启动] 时加载新版本 +``` + +#### 4.3.2 Skills 仓库更新 + +技能文件支持增量更新: + +``` +更新流程: + 1. 下载新版 registry.json (含每个技能的 SHA-256 哈希) + 2. 对比本地 registry.json, 识别变更的技能文件 + 3. 仅下载变更的 .js 文件 + 4. 校验每个文件的 SHA-256 签名 + 5. 原子替换 (写入临时文件 -> rename) +``` + +#### 4.3.3 数据库迁移 + +SQLite 数据库使用 schema 版本管理: + +```sql +-- 内置 user_version pragma 记录 schema 版本 +PRAGMA user_version; -- 返回当前版本号 + +-- sgclaw 启动时检查并执行迁移 +-- migration_001.sql: 初始 schema +-- migration_002.sql: 新增索引 +-- migration_003.sql: 新增表 +``` + +迁移规则: +- 向前兼容: 新版 sgclaw 可读取旧版数据库 +- 自动迁移: 启动时检测 user_version,依次执行未应用的迁移脚本 +- 备份优先: 迁移前自动备份 `.db` 文件为 `.db.bak.{timestamp}` + +--- + +## 5. 测试策略 + +### 5.1 测试金字塔 + +``` + ┌───────────────────┐ + │ E2E 验收测试 │ 少量, 模拟完整用户场景 + │ (mock LLM) │ 确保端到端可用 + ├───────────────────┤ + │ │ + │ Pipe 协议集成 │ 真实 sgclaw 进程 + │ 测试 │ 验证 pipe 通信正确性 + ├───────────────────┤ + │ │ + │ C++ 单元测试 │ SgClawProcessHost + │ sgclaw_unittests│ PipeListener / MAC Check + ├───────────────────┤ + │ │ + │ Rust 单元测试 │ 大量, 覆盖核心逻辑 + │ cargo test │ mock LLM + mock pipe + │ │ + └───────────────────┘ +``` + +测试比例指导: +- Rust 单元测试: ~70% (快速、隔离、可重复) +- C++ 单元测试: ~15% (进程管理、协议解析) +- Pipe 协议集成测试: ~10% (真实进程间通信) +- E2E 验收测试: ~5% (端到端场景,mock LLM 保证可重复) + +### 5.2 Rust 单元测试 + +#### 5.2.1 Agent Runtime 测试 + +```bash +# 运行全部 Rust 测试 +cargo test + +# 运行特定模块测试 +cargo test agent::runtime +cargo test agent::critic +``` + +测试内容: +- ReAct 循环: mock LLM 返回固定输出, 验证 think -> act -> observe 流转 +- Circuit Breaker: 模拟连续失败, 验证熔断触发与恢复 +- 最大步数限制: 验证超出 50 步自动终止 + +```rust +// 测试示例: Circuit Breaker 在连续 10 次失败后触发 +#[tokio::test] +async fn test_circuit_breaker_triggers_after_threshold() { + let mut breaker = CircuitBreaker::new(10); + for _ in 0..10 { + breaker.record_failure(); + } + assert!(breaker.is_open()); +} +``` + +#### 5.2.2 BrowserPipeTool 测试 + +测试内容: +- 命令序列化: Rust struct -> JSON Line 格式正确 +- 响应反序列化: JSON Line -> Rust struct 解析正确 +- 未知 action 拒绝: 非枚举值被正确拒绝 +- HMAC 签名: 签名生成与校验一致 + +```bash +cargo test pipe::protocol +cargo test pipe::browser_tool +``` + +#### 5.2.3 MAC Policy 测试 + +测试内容: +- 域名白名单: 允许 / 拒绝 / 通配符匹配 +- Action 白名单: 允许的 action 通过, eval/executeJsInPage 被拒绝 +- 空白名单: 默认拒绝所有 + +```bash +cargo test security::mac_policy +``` + +#### 5.2.4 Memory 测试 + +测试内容: +- 短期记忆 Ring Buffer: 容量溢出时正确淘汰最旧条目 +- 长期记忆 SQLite: CRUD 操作, schema 迁移, 并发读写安全 +- 向量搜索: 相似度计算正确性 + +```bash +cargo test memory::short_term +cargo test memory::long_term +``` + +### 5.3 C++ 单元测试 + +新增 `sgclaw_unittests` target, 集成到 SuperRPA 现有测试体系。 + +```bash +# 构建 +autoninja -j 24 -C out/Default sgclaw_unittests + +# 运行 +./out/Default/sgclaw_unittests +``` + +#### 5.3.1 SgClawProcessHost 测试 + +``` +测试用例: +├─ Start() 在二进制存在时成功启动子进程 +├─ Start() 在二进制不存在时返回错误 +├─ Start() 重复调用不创建多个进程 (Singleton 保证) +├─ Stop() 发送 shutdown 命令后等待退出 +├─ Stop() 超时后强制 kill +├─ OnProcessCrash() 记录崩溃日志, 不自动重启 +└─ 析构函数确保清理子进程 +``` + +#### 5.3.2 PipeListener 测试 + +``` +测试用例: +├─ 正确解析单行 JSON 消息 +├─ 正确解析多行连续消息 (JSON Line 流) +├─ 拒绝超过 1 MB 的消息 (防内存 DoS) +├─ 拒绝非 JSON 格式的行 +├─ 管道断开时触发 OnPipeDisconnected 回调 +└─ sequence_id 乱序时触发告警 +``` + +#### 5.3.3 MAC Whitelist Check 测试 + +``` +测试用例: +├─ 允许列表内的 action (click, type, navigate) 通过 +├─ 禁止列表的 action (eval, executeJsInPage) 被拒绝 +├─ expected_domain 与当前页面域名匹配时通过 +├─ expected_domain 与当前页面域名不匹配时拒绝 +├─ 速率限制: 同一域超过 10 次/秒后拒绝 +└─ rules.json 加载失败时默认拒绝所有 +``` + +### 5.4 Pipe 协议集成测试 + +集成测试启动真实的 sgclaw 进程,通过 pipe 发送命令并验证响应。 +位于 `tests/integration/` 目录。 + +```bash +# 运行 Rust 集成测试 (需要先构建 sgclaw) +cargo test --test integration +``` + +#### 5.4.1 测试场景 + +``` +┌─────────────────────────────────────────────────────────┐ +│ Pipe 协议集成测试架构 │ +│ │ +│ Test Harness (Rust) │ +│ ┌───────────────────────────────────────────────┐ │ +│ │ │ │ +│ │ 1. 创建 pipe pair │ │ +│ │ 2. spawn sgclaw 子进程 │ │ +│ │ 3. 发送 handshake init 消息 │ │ +│ │ 4. 验证 init_ack 响应 │ │ +│ │ 5. 发送 command 消息 │ │ +│ │ 6. 验证 response 格式和内容 │ │ +│ │ 7. 发送 shutdown │ │ +│ │ 8. 验证进程正常退出 │ │ +│ │ │ │ +│ │ sgclaw 使用 mock LLM (--llm-mock 标志) │ │ +│ │ mock LLM 返回固定 action 序列 │ │ +│ │ │ │ +│ └───────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────┘ +``` + +测试用例: +- Handshake 成功: init -> init_ack, 版本匹配 +- Handshake 版本不匹配: sgclaw 返回错误并退出 +- 命令执行: command -> response, 验证 seq 匹配 +- 错误处理: 发送格式错误的 JSON, 验证 error response +- 管道断开: 关闭写端, 验证 sgclaw 退出码 0 +- 大消息拒绝: 发送 > 1 MB 的消息, 验证被丢弃 + +### 5.5 E2E 验收测试 + +E2E 测试覆盖完整的用户场景: 从浏览器启动 Agent 到任务执行完成。 +使用 mock LLM 确保测试可重复、无外部依赖。 + +#### 5.5.1 测试环境 + +``` +┌─────────────────────────────────────────────────────────┐ +│ E2E 测试环境 │ +│ │ +│ SuperRPA Chrome (headless 模式) │ +│ ├─ SgClawProcessHost 启动 sgclaw │ +│ ├─ sgclaw 使用 mock LLM (固定输出) │ +│ └─ 目标页面: 本地 test HTTP server (固定 HTML) │ +│ │ +│ 验证链: │ +│ 用户输入 -> Agent 理解 -> 生成 action -> Pipe 传输 │ +│ -> MAC 校验 -> CommandRouter 执行 -> 页面 DOM 变更 │ +│ -> 响应返回 -> Agent observe -> 任务完成 │ +└─────────────────────────────────────────────────────────┘ +``` + +#### 5.5.2 验收场景 + +| 场景 | 用户输入 | 预期行为 | 验收标准 | +|------|---------|---------|---------| +| 基本点击 | "点击提交按钮" | Agent 生成 click action | 按钮状态变为 disabled | +| 表单填写 | "在用户名框输入 admin" | Agent 生成 type action | input.value === "admin" | +| 页面导航 | "打开审批列表页面" | Agent 生成 navigate action | URL 变更为目标地址 | +| 域名拦截 | "访问 evil.com" | MAC 拒绝, Agent 报告失败 | 未发生导航 | +| 熔断触发 | 连续 10 次无效 action | Circuit Breaker 打开 | Agent 自动停止 | + +### 5.6 测试运行汇总 + +```bash +# === Rust 全量测试 === +cd /home/zyl/projects/sgClaw +cargo test # 单元测试 + 集成测试 +cargo test --release # Release 模式测试 (更贴近生产) + +# === C++ 全量测试 === +cd /home/zyl/projects/superRpa/src +autoninja -j 24 -C out/Default sgclaw_unittests +./out/Default/sgclaw_unittests + +# === 已有 SuperRPA 测试 (回归验证) === +autoninja -j 24 -C out/Default superrpa_unittests +./out/Default/superrpa_unittests # 14 test files + +autoninja -j 24 -C out/Default superrpa_ai_unittests +./out/Default/superrpa_ai_unittests # 3 test files + +# === 代码质量检查 === +cargo clippy -- -D warnings # Rust lint +cargo fmt -- --check # Rust 格式化检查 +cd /home/zyl/projects/superRpa/src && git cl format # C++ 格式化 +``` + +--- + +## 6. 开发环境搭建 + +### 6.1 Rust 开发环境 + +#### 6.1.1 基础安装 + +```bash +# 安装 Rust 工具链 (stable channel) +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh +source ~/.cargo/env + +# 验证安装 +rustc --version # 预期: rustc 1.7x.x (stable) +cargo --version # 预期: cargo 1.7x.x + +# 安装编译目标 +rustup target add x86_64-unknown-linux-musl # Linux 静态链接 +rustup target add x86_64-pc-windows-msvc # Windows 交叉编译 (可选) + +# 安装 musl 工具链 (Linux 静态链接依赖) +sudo apt update && sudo apt install -y musl-tools + +# 安装开发工具 +rustup component add clippy # Lint 工具 +rustup component add rustfmt # 格式化工具 +``` + +#### 6.1.2 项目克隆与首次构建 + +```bash +# 克隆项目 +git clone /home/zyl/projects/sgClaw +cd /home/zyl/projects/sgClaw + +# 开发构建 (首次编译需下载依赖,约 2-5 分钟) +cargo build + +# 运行测试 +cargo test + +# 运行代码检查 +cargo clippy -- -D warnings + +# 格式化代码 +cargo fmt +``` + +#### 6.1.3 开发模式运行 + +sgclaw 通过 STDIO 与浏览器通信,开发时可直接在终端模拟 pipe 输入: + +```bash +# 方式一: 直接运行,手动输入 JSON Line +cargo run + +# 在终端中输入 (模拟 Browser 发来的 init 消息): +{"type":"init","version":"1.0","hmac_key":"dev-test-key"} + +# 方式二: 使用文件作为输入 +cargo run < tests/fixtures/sample_session.jsonl + +# 方式三: 使用 echo + pipe +echo '{"type":"init","version":"1.0"}' | cargo run +``` + +### 6.2 C++ 开发环境 + +C++ 开发依赖已有的 SuperRPA 开发环境。以下假设 depot_tools 和 Chromium 源码已就绪。 + +```bash +# 确认 depot_tools 在 PATH 中 +which gn # /path/to/depot_tools/gn +which autoninja # /path/to/depot_tools/autoninja + +# Chromium 源码目录 +ls /home/zyl/projects/superRpa/src/chrome/browser/superrpa/ +# 预期看到: BUILD.gn, ai/, cdp/, router/, session/, zombie/, ... + +# 新增 sgclaw 文件后,重新生成构建配置 +cd /home/zyl/projects/superRpa/src +gn gen out/Default + +# 增量编译 (仅编译变更的文件) +autoninja -j 24 -C out/Default chrome + +# 编译并运行 sgclaw 单元测试 +autoninja -j 24 -C out/Default sgclaw_unittests +./out/Default/sgclaw_unittests +``` + +### 6.3 联合调试 + +#### 6.3.1 sgclaw 独立调试 (Rust 侧) + +```bash +# 使用 RUST_LOG 控制日志级别 +RUST_LOG=debug cargo run 2>sgclaw-debug.log + +# 使用 lldb 或 gdb 调试 +cargo build +lldb target/debug/sgclaw + +# VS Code 调试 (需要 CodeLLDB 扩展) +# launch.json 配置见 6.4 节 +``` + +#### 6.3.2 附加到浏览器进程调试 (C++ 侧) + +```bash +# 启动 SuperRPA 浏览器 +cd /home/zyl/projects/superRpa/src +./out/Default/chrome --no-sandbox + +# 在另一终端查找 sgclaw 子进程 PID +pgrep -a sgclaw + +# 使用 gdb 附加 +gdb -p + +# 或使用 gdb 附加到 chrome 主进程,在 sgclaw 相关代码设断点 +gdb -p +(gdb) break SgClawProcessHost::Start +(gdb) continue +``` + +#### 6.3.3 日志查看 + +```bash +# sgclaw Rust 日志 (输出到 stderr, 不干扰 pipe 通信) +# 日志格式: JSON (tracing-subscriber json feature) +RUST_LOG=sgclaw=debug ./sgclaw 2>agent.log + +# 实时查看日志 +tail -f agent.log | python3 -m json.tool + +# Chrome 日志 (含 SgClawProcessHost 日志) +./out/Default/chrome --no-sandbox --enable-logging --v=1 2>&1 | grep -i sgclaw +``` + +### 6.4 IDE 配置 + +#### 6.4.1 VS Code + rust-analyzer (推荐) + +`.vscode/settings.json`: + +```json +{ + "rust-analyzer.cargo.target": "x86_64-unknown-linux-musl", + "rust-analyzer.check.command": "clippy", + "rust-analyzer.check.extraArgs": ["--", "-D", "warnings"], + "rust-analyzer.inlayHints.typeHints.enable": true, + "rust-analyzer.inlayHints.parameterHints.enable": true, + "[rust]": { + "editor.formatOnSave": true, + "editor.defaultFormatter": "rust-lang.rust-analyzer" + } +} +``` + +`.vscode/launch.json` (调试配置): + +```json +{ + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "Debug sgclaw", + "cargo": { + "args": ["build", "--bin=sgclaw"] + }, + "args": [], + "env": { + "RUST_LOG": "debug" + }, + "stdio": [null, null, null] + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests", + "cargo": { + "args": ["test", "--no-run"] + } + } + ] +} +``` + +推荐扩展: +- `rust-lang.rust-analyzer` -- Rust 语言支持 +- `vadimcn.vscode-lldb` -- LLDB 调试器 +- `tamasfe.even-better-toml` -- TOML 语法高亮 +- `serayuzgur.crates` -- Cargo.toml 依赖版本提示 + +#### 6.4.2 CLion + Rust 插件 + +CLion 原生支持 Rust (通过内置插件)。打开 `Cargo.toml` 即可识别项目。 + +关键配置: +- File -> Settings -> Languages -> Rust -> Cargo target: `x86_64-unknown-linux-musl` +- Run Configuration -> Cargo Command: 选择 `run` 或 `test` +- External Tool: 可配置 `cargo clippy` 为保存时自动运行 + +--- + +## 7. 代码规范 + +### 7.1 Rust 规范 + +#### 7.1.1 格式化 + +使用 `rustfmt` 统一代码格式。配置文件 `rustfmt.toml`: + +```toml +edition = "2021" +max_width = 100 +tab_spaces = 4 +use_field_init_shorthand = true +use_try_shorthand = true +``` + +执行命令: + +```bash +# 格式化所有代码 +cargo fmt + +# 检查格式 (CI 中使用) +cargo fmt -- --check +``` + +#### 7.1.2 Lint + +使用 `clippy` 进行静态分析,CI 中以 `-D warnings` 模式运行 (warnings 即 error): + +```bash +cargo clippy -- -D warnings +``` + +#### 7.1.3 命名约定 + +| 元素 | 风格 | 示例 | +|------|------|------| +| 变量 / 函数 | snake_case | `process_message`, `pipe_reader` | +| 类型 / Trait | CamelCase | `BrowserPipeTool`, `MacPolicy` | +| 常量 | SCREAMING_SNAKE_CASE | `MAX_MESSAGE_SIZE`, `DEFAULT_TIMEOUT` | +| 模块 | snake_case | `browser_tool`, `mac_policy` | +| 文件名 | snake_case | `browser_tool.rs`, `mac_policy.rs` | + +#### 7.1.4 错误处理 + +- 库级别错误: 使用 `thiserror` 定义具体的 Error enum +- 应用级别传播: 使用 `anyhow::Result` 简化错误链 +- 禁止 `unwrap()` / `expect()` 出现在非测试代码中 (clippy 规则) + +```rust +// 正确: 定义具体错误类型 +#[derive(thiserror::Error, Debug)] +pub enum PipeError { + #[error("message too large: {size} bytes (max {max})")] + MessageTooLarge { size: usize, max: usize }, + + #[error("invalid JSON: {0}")] + InvalidJson(#[from] serde_json::Error), + + #[error("pipe disconnected")] + Disconnected, +} + +// 正确: 使用 ? 传播 +fn read_message(reader: &mut BufReader) -> anyhow::Result { + let line = reader.read_line()?; + let msg: PipeMessage = serde_json::from_str(&line)?; + Ok(msg) +} +``` + +#### 7.1.5 日志 + +使用 `tracing` 框架,提供结构化日志和链路追踪: + +```rust +use tracing::{info, warn, error, debug, instrument}; + +#[instrument(skip(self), fields(seq = msg.seq))] +async fn handle_message(&self, msg: PipeMessage) -> Result { + info!(action = %msg.action, "processing command"); + // ... + if let Err(e) = result { + error!(error = %e, "command failed"); + } +} +``` + +### 7.2 C++ 规范 (延续 SuperRPA) + +延续 Chromium / SuperRPA 现有编码风格: + +| 规则 | 说明 | +|------|------| +| 缩进 | 2 空格 | +| 命名 | CamelCase (类型), lower_case (变量), kCamelCase (常量) | +| 头文件 | `#ifndef` 守卫,使用 `#pragma once` 亦可 | +| 智能指针 | `std::unique_ptr` 优先,`scoped_refptr` 用于引用计数对象 | +| 线程安全 | `base::SequencedTaskRunner` 绑定,避免直接使用锁 | +| 格式化 | `git cl format` (调用 clang-format) | + +```bash +# 格式化 C++ 代码 +cd /home/zyl/projects/superRpa/src +git cl format +``` + +### 7.3 Git 规范 + +#### 7.3.1 分支策略 + +``` +main (保护分支, 始终可部署) + │ + ├── feature/sgclaw-pipe-protocol # 功能分支 + ├── feature/sgclaw-mac-check # 功能分支 + ├── fix/sgclaw-crash-on-large-msg # 修复分支 + └── release/v1.0.0 # 发布分支 +``` + +规则: +- `main` 分支受保护,仅通过 PR 合并 +- 功能分支从 `main` 创建,完成后合并回 `main` +- 命名格式: `{type}/{scope}-{brief-description}` +- type: feature, fix, refactor, test, docs + +#### 7.3.2 Commit Message 格式 + +采用 Conventional Commits 规范: + +``` +(): + + + +