Files
skill-lib/docs/L2-核心模块与接口契约层.md
2026-03-06 03:36:12 +08:00

68 KiB
Raw Blame History

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 是叶节点:所有模块依赖 ConfigConfig 不依赖任何业务模块
  • 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 完成用户任务。

模块接口

/// Agent Runtime 核心结构
pub struct AgentRuntime {
    provider: Box<dyn LlmProvider>,
    tools: Vec<Box<dyn Tool>>,          // 包含 BrowserPipeTool + MCP tools
    memory: Arc<dyn Memory>,
    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<String>, // 需人工确认的 action 列表
}

impl AgentRuntime {
    /// 创建 Runtime 实例
    pub fn new(
        provider: Box<dyn LlmProvider>,
        tools: Vec<Box<dyn Tool>>,
        memory: Arc<dyn Memory>,
        critic: Critic,
        config: RuntimeConfig,
    ) -> Self;

    /// 执行用户任务 (核心入口)
    /// 返回任务执行结果或错误
    pub async fn execute_task(&mut self, task: &str) -> Result<TaskResult, AgentError>;

    /// 停止当前任务 (由外部 shutdown 信号触发)
    pub fn abort(&self);
}

/// 任务执行结果
pub struct TaskResult {
    pub success: bool,
    pub summary: String,           // LLM 生成的任务完成摘要
    pub steps: Vec<StepRecord>,    // 所有执行步骤记录
    pub token_usage: TokenUsage,   // token 消耗统计
}

/// 单步执行记录
pub struct StepRecord {
    pub step_num: u32,
    pub thinking: String,          // LLM 的推理内容
    pub action: Option<Action>,    // 选择的操作 (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 框架的核心对接点。

模块接口

/// 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<ToolOutput, ToolError>;
}

/// sgClaw 的浏览器操作工具实现
pub struct BrowserPipeTool {
    pipe_writer: Arc<Mutex<PipeWriter>>,
    pipe_reader: Arc<PipeResponseReceiver>,
    mac_policy: Arc<MacPolicy>,
    seq_counter: AtomicU64,
    hmac_key: [u8; 32],
}

impl BrowserPipeTool {
    pub fn new(
        pipe_writer: Arc<Mutex<PipeWriter>>,
        pipe_reader: Arc<PipeResponseReceiver>,
        mac_policy: Arc<MacPolicy>,
        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<ToolOutput, ToolError> {
        // 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 枚举(白名单)

/// 允许通过 pipe 的操作类型 (封闭枚举)
#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "action", content = "params")]
pub enum Action {
    #[serde(rename = "click")]
    Click { selector: String, wait_after: Option<u64> },

    #[serde(rename = "type")]
    Type { selector: String, text: String, clear_first: Option<bool> },

    #[serde(rename = "navigate")]
    Navigate { url: String },

    #[serde(rename = "getText")]
    GetText { selector: String },

    #[serde(rename = "getHtml")]
    GetHtml { selector: String, outer: Option<bool> },

    #[serde(rename = "waitForSelector")]
    WaitForSelector { selector: String, timeout_ms: Option<u64> },

    #[serde(rename = "pageScreenshot")]
    PageScreenshot { full_page: Option<bool> },

    #[serde(rename = "select")]
    Select { selector: String, value: String },

    #[serde(rename = "scrollTo")]
    ScrollTo { selector: Option<String>, x: Option<i32>, y: Option<i32> },

    #[serde(rename = "getAomSnapshot")]
    GetAomSnapshot { root_selector: Option<String> },

    #[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 的工具集中。

模块接口

/// 技能加载器
pub struct SkillLoader {
    skills_dir: PathBuf,
    registry: HashMap<String, Skill>,
    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<String>,// 适用域名列表
    pub parameters: JsonSchema,     // 输入参数 schema
    pub script: String,             // JS 脚本内容
    pub signature: String,          // 数字签名 (Ed25519)
}

/// 技能清单文件 (registry.json)
pub struct SkillRegistry {
    pub version: String,
    pub skills: Vec<SkillManifestEntry>,
}

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<Self, SkillError>;

    /// 获取所有已加载技能的描述 (供 LLM system prompt)
    pub fn list_skills(&self) -> Vec<SkillDescription>;

    /// 获取指定技能的 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 文件头部)

/**
 * @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 的核心组件。

模块接口

/// MAC 策略引擎
pub struct MacPolicy {
    allowed_domains: HashSet<String>,     // 允许操作的域名集合
    allowed_actions: HashSet<String>,     // 允许的 action 名称集合
    blocked_actions: HashSet<String>,     // 硬性禁止的 action 集合
    confirm_actions: HashSet<String>,     // 需人工确认的 action 集合
    storage_key_prefix: String,           // 存储 key 前缀限制 (默认 "sgclaw.")
    rate_limits: HashMap<String, RateLimit>, // 每域速率限制
}

/// 速率限制配置
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<Self, PolicyError>;

    /// 校验 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 策略文件格式

{
  "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 后评估操作结果质量,检测异常模式,触发熔断保护。

模块接口

/// 输出质量评估器
pub struct Critic {
    circuit_breaker: CircuitBreaker,
    memory: Arc<dyn Memory>,
    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<String>,         // 终止原因
    pub warning: Option<String>,        // 警告 (不终止但需记录)
}

/// 熔断器
pub struct CircuitBreaker {
    state: CircuitState,
    consecutive_failures: AtomicU32,
    last_failure_time: Mutex<Option<Instant>>,
    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<dyn Memory>, 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 的短期对话记忆和长期任务知识库,支持上下文检索和经验沉淀。

模块接口

/// 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<Vec<MemoryEntry>, 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<Message>,
    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<Utc>,
}

pub enum MemoryType {
    Conversation,                       // 对话历史
    TaskResult,                         // 任务执行结果
    SkillExperience,                    // 技能执行经验
    UserPreference,                     // 用户偏好
}

impl CompositeMemory {
    pub fn new(config: MemoryConfig) -> Result<Self, MemoryError>;

    /// 获取最近 N 步的 action 记录 (供 Critic 查重)
    pub fn get_recent_actions(&self, n: usize) -> Vec<Action>;

    /// 保存任务执行经验 (供自进化学习)
    pub async fn save_task_experience(
        &self,
        task: &str,
        result: &TaskResult,
    ) -> Result<(), MemoryError>;
}

存储 Schema (SQLite):

-- 长期记忆表
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 计量。

模块接口

/// LLM Provider trait (ZeroClaw 框架定义)
#[async_trait]
pub trait LlmProvider: Send + Sync {
    /// 发送聊天请求
    async fn chat(
        &self,
        messages: &[Message],
        tools: &[ToolDefinition],
        config: &ChatConfig,
    ) -> Result<LlmResponse, LlmError>;

    /// 发送 streaming 请求
    async fn chat_stream(
        &self,
        messages: &[Message],
        tools: &[ToolDefinition],
        config: &ChatConfig,
    ) -> Result<Pin<Box<dyn Stream<Item = Result<StreamChunk, LlmError>>>>, LlmError>;

    /// 获取 provider 信息
    fn info(&self) -> ProviderInfo;
}

/// 聊天配置
pub struct ChatConfig {
    pub max_tokens: u32,              // 最大生成 token 数
    pub temperature: f32,             // 温度 (默认 0.1, Agent 场景偏低)
    pub stop_sequences: Vec<String>,  // 停止序列
}

/// LLM 响应
pub struct LlmResponse {
    pub content: Option<String>,       // 文本回复
    pub tool_calls: Vec<ToolCall>,     // 工具调用请求
    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 选择逻辑

/// 根据配置创建 Provider 实例
pub fn create_provider(config: &LlmConfig) -> Result<Box<dyn LlmProvider>, 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 — 外部工具扩展

职责:通过 rmcpRust MCP SDK连接外部 MCP Server动态获取额外工具供 Agent 使用。

模块接口

/// MCP 客户端管理器
pub struct McpClientManager {
    clients: HashMap<String, McpClient>,    // server_name → client
    config: McpConfig,
}

pub struct McpConfig {
    pub servers: Vec<McpServerConfig>,
}

pub struct McpServerConfig {
    pub name: String,                       // 服务器标识名
    pub command: String,                    // 启动命令
    pub args: Vec<String>,                  // 命令参数
    pub env: HashMap<String, String>,       // 环境变量
}

impl McpClientManager {
    /// 创建并连接所有配置的 MCP Server
    pub async fn new(config: McpConfig) -> Result<Self, McpError>;

    /// 获取所有 MCP Server 提供的工具列表
    pub fn list_tools(&self) -> Vec<ToolDefinition>;

    /// 调用 MCP 工具
    pub async fn call_tool(
        &self,
        server: &str,
        tool: &str,
        params: serde_json::Value,
    ) -> Result<serde_json::Value, McpError>;

    /// 关闭所有连接
    pub async fn shutdown(&mut self);
}

3. 浏览器 C++ 端模块详细设计

3.1 SgClawProcessHost — 进程宿主

职责Singleton管理 sgclaw 子进程的完整生命周期Start / Stop / OnCrash创建和持有 STDIO Pipe。

接口声明

// 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 <memory>
#include <string>
#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<PipeListener> pipe_listener_;

  // Pipe 文件描述符 (Linux: fd, Windows: HANDLE)
  base::ScopedFD pipe_write_fd_;    // Browser → sgClaw
  base::ScopedFD pipe_read_fd_;     // sgClaw → Browser

  std::vector<ProcessObserver*> observers_;
};

}  // namespace sgclaw
}  // namespace superrpa

#endif

关键实现说明

  • Singleton:使用 base::NoDestructor<SgClawProcessHost> 实现进程全局唯一实例
  • 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。

接口声明

// pipe_listener.h

#ifndef CHROME_BROWSER_SUPERRPA_SGCLAW_PIPE_LISTENER_H_
#define CHROME_BROWSER_SUPERRPA_SGCLAW_PIPE_LISTENER_H_

#include <string>
#include <functional>
#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<void(const std::string& json_line)>;
  using ErrorCallback = std::function<void(const std::string& error)>;

  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<base::FileDescriptorWatcher::Controller> 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 的实现。

接口声明

// mac_whitelist_check.h

#ifndef CHROME_BROWSER_SUPERRPA_SGCLAW_MAC_WHITELIST_CHECK_H_
#define CHROME_BROWSER_SUPERRPA_SGCLAW_MAC_WHITELIST_CHECK_H_

#include <string>
#include <set>
#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<MacWhitelistCheck> 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<std::string> allowed_actions_;
  std::set<std::string> blocked_actions_;
  std::set<std::string> confirm_actions_;
  std::set<std::string> 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 交互。

// 启动 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 推送):

// 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

{
  "$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

{
  "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

{
  "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

{
  "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

{
  "type": "object",
  "required": ["selector"],
  "properties": {
    "selector": {
      "type": "string",
      "description": "CSS 选择器"
    },
    "wait_after": {
      "type": "integer",
      "minimum": 0,
      "maximum": 30000,
      "default": 1000,
      "description": "点击后等待时间 (ms)"
    }
  }
}

type

{
  "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

{
  "type": "object",
  "required": ["url"],
  "properties": {
    "url": {
      "type": "string",
      "format": "uri",
      "description": "目标 URL (必须在域白名单内)"
    }
  }
}

getText

{
  "type": "object",
  "required": ["selector"],
  "properties": {
    "selector": {
      "type": "string",
      "description": "CSS 选择器"
    }
  }
}

getHtml

{
  "type": "object",
  "required": ["selector"],
  "properties": {
    "selector": {
      "type": "string"
    },
    "outer": {
      "type": "boolean",
      "default": false,
      "description": "true 返回 outerHTML, false 返回 innerHTML"
    }
  }
}

waitForSelector

{
  "type": "object",
  "required": ["selector"],
  "properties": {
    "selector": {
      "type": "string"
    },
    "timeout_ms": {
      "type": "integer",
      "minimum": 100,
      "maximum": 30000,
      "default": 5000,
      "description": "等待超时 (ms)"
    }
  }
}

pageScreenshot

{
  "type": "object",
  "properties": {
    "full_page": {
      "type": "boolean",
      "default": false,
      "description": "是否截取整页 (true) 或仅可视区域 (false)"
    }
  }
}

select

{
  "type": "object",
  "required": ["selector", "value"],
  "properties": {
    "selector": {
      "type": "string",
      "description": "select 元素的 CSS 选择器"
    },
    "value": {
      "type": "string",
      "description": "要选择的 option 的 value 值"
    }
  }
}

scrollTo

{
  "type": "object",
  "properties": {
    "selector": {
      "type": "string",
      "description": "滚动到指定元素 (优先于 x/y)"
    },
    "x": {
      "type": "integer",
      "description": "水平滚动位置 (px)"
    },
    "y": {
      "type": "integer",
      "description": "垂直滚动位置 (px)"
    }
  }
}

getAomSnapshot

{
  "type": "object",
  "properties": {
    "root_selector": {
      "type": "string",
      "description": "从指定元素开始获取 AOM 子树 (默认整页)"
    }
  }
}

storageSet

{
  "type": "object",
  "required": ["key", "value"],
  "properties": {
    "key": {
      "type": "string",
      "pattern": "^sgclaw\\.",
      "description": "存储键名 (必须以 sgclaw. 开头)"
    },
    "value": {
      "type": "string",
      "maxLength": 65536,
      "description": "存储值"
    }
  }
}

storageGet

{
  "type": "object",
  "required": ["key"],
  "properties": {
    "key": {
      "type": "string",
      "pattern": "^sgclaw\\.",
      "description": "存储键名 (必须以 sgclaw. 开头)"
    }
  }
}

zombieSpawn

{
  "type": "object",
  "required": ["url"],
  "properties": {
    "url": {
      "type": "string",
      "format": "uri",
      "description": "Zombie page 加载的 URL (必须在域白名单内)"
    }
  }
}

zombieKill

{
  "type": "object",
  "required": ["page_id"],
  "properties": {
    "page_id": {
      "type": "string",
      "description": "要销毁的 zombie page ID"
    }
  }
}

5.3 错误码体系

所有错误响应使用统一的错误码格式:

{
  "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 配置文件

# 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 层模块设计与接口契约参考,各组应基于此文档中的接口定义 进行并行开发。接口变更需经三方组长会审后更新本文档。