# 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<Value> {
    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<ClaudeMessage> {
        // 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<ClaudeTool> {
        // 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<Message>                                       │  │
│  │  生命周期: 单次任务 (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 节（记忆与自进化）。*
