第三章 · RUNTIME & FLOW

运行时 · 状态机 · 流程

zeroclaw 支持三种截然不同的运行时进程模型,每种模型有独立的启动路径、并发策略和生命周期管理。 本页通过 CSS 流程图详解 Chat 状态机、Gateway 路由链路、SOP 执行流程、WASM 插件调用链,以及 tokio 并发模型。

⚡ 三种进程模型对比

💬 Chat 模式
进程类型
单进程,前台运行
I/O 模型
阻塞 TUI,同步读用户输入
异步运行时
tokio(工具执行并发)
生命周期
用户退出即终止
会话持久化
退出时写入 SQLite
适用场景
个人开发、本地使用
🌐 Gateway 模式
进程类型
长驻服务进程
I/O 模型
全异步 HTTP + WebSocket
异步运行时
tokio multi-thread
生命周期
持续运行,SIGTERM 优雅退出
会话持久化
实时写入,多租户隔离
适用场景
应用后端 API、MCP 端点
👻 Daemon 模式
进程类型
后台常驻,systemd 托管
I/O 模型
Webhook 触发,无 stdin
异步运行时
tokio + watchdog 线程
生命周期
开机自启,崩溃自恢复
会话持久化
全程持久化,任务日志
适用场景
自治 Agent、定时任务

🔄 Agent 循环与状态机

以下是 Chat 模式下 agent/loop.rs 的完整状态机,覆盖从用户输入到响应渲染的全部状态转换。

INIT · 启动初始化
加载 config · 检测 LLM 提供商 · 恢复历史会话
READY · TUI 就绪,等待用户输入
用户按下 Enter 提交
SAFETY_CHECK · 风险预评估
risk = low → 自动通过  |  risk = medium → 提示确认  |  risk = high → 必须显式确认
LLM_CALL · 调用 LLM 提供商 API
provider_router.complete() → 流式接收 token
PARSE_TOOLS · 解析响应中的 tool_calls
有 tool_calls → YES
TOOL_DISPATCH
tokio::spawn 并发执行
各工具在沙箱中执行
TOOL_RESULTS
结果 append 到上下文
↑ 返回 LLM_CALL(继续循环)
无 tool_calls → NO(最终回复)
RENDER_OUTPUT
TUI 渲染 / stdout 输出
PERSIST_SESSION
写入 SQLite
READY / EXIT
ℹ️
工具调用循环:LLM_CALL → PARSE_TOOLS → TOOL_DISPATCH → TOOL_RESULTS → LLM_CALL 这个内循环 可能迭代多次,直到 LLM 返回不含 tool_calls 的最终文本响应为止。 循环上限由 agent.max_tool_rounds 配置(默认 20)。

🌐 Gateway 路由链路

Gateway 模式下每个客户端请求经过以下处理链路,全程异步,多客户端并发互不阻塞。

Client · HTTP POST /v1/chat
JSON body: { message, session_id? }
gateway/server.rs · axum 路由接收
gateway/rpc.rs · JSON-RPC 2.0 解析
gateway/session.rs · 会话查找 / 创建
session_id 存在 → 恢复上下文;否则 → 新建会话
tokio::spawn → Agent Task(独立任务)
每个请求独立 task,互不阻塞
provider_router → LLM Provider API
tool_dispatch → 沙箱工具执行
SSE 流式响应 → Client(实时 token 推送)
Gateway API 端点 方法 说明
/v1/chat POST 发送消息,返回 SSE 流式响应
/v1/sessions GET 列出所有活跃会话
/v1/sessions/:id GET 获取指定会话历史
/v1/sessions/:id DELETE 删除会话
/ws WS WebSocket 双向通信,JSON-RPC 2.0
/health GET 健康检查端点

📋 SOP 执行流程(Daemon 模式)

Daemon 模式接收 Webhook 或定时触发的任务后,交由 sop_engine 进行任务分解与有序执行。

Task 到达 · Webhook / 定时触发
sop_engine · 任务分解为有序 Steps
分析任务依赖关系,构建执行 DAG
For each Step(按依赖顺序)
scheduler.queue(step)
agent_loop.execute(step)
调用 LLM + 工具,完成单步任务
verify result · 验证结果
验证通过 → 继续下一步;失败 → 重试或报错
所有步骤完成 → 生成执行报告

🧩 WASM 插件调用链

当 agent_loop 决定调用一个 WASM 插件提供的工具时,完整调用链如下。整个过程对 agent_loop 透明, 与内置工具调用接口完全一致。

Step 1 · 工具查找
agent_loop 决定调用工具 "my-wasm-tool", 通过 tool_registry 查找,发现该工具由 WASM 插件提供。
Step 2 · 插件实例化
wasm_runtime.instantiate(plugin.wasm) 通过 Wasmtime 加载 .wasm 文件, 初始化隔离的 WebAssembly 实例。若插件已缓存则复用实例。
Step 3 · 沙箱配置
应用插件声明的权限:限制可访问的文件系统路径(通过 WASI 配置), 限制网络访问范围,拒绝未声明的系统调用。
Step 4 · JSON-RPC 调用
通过 JSON-RPC 2.0 协议调用插件导出的函数,传入工具参数(JSON 格式)。 插件内部处理逻辑完全在 WASM 沙箱中执行。
Step 5 · 结果返回
插件通过 JSON-RPC 返回结果(JSON 格式), wasm_runtime 解包结果并返回给 tool_registry
Step 6 · 上下文注入
工具结果 append 到 agent_loop 的对话上下文中,作为下一轮 LLM 调用的输入,继续 Agent 循环。
# WASM 插件调用伪代码(简化)
agent_loop:
  tool_name = "my-wasm-tool"
  tool_args = { "url": "https://example.com" }

tool_registry:
  plugin = find_wasm_plugin(tool_name)  // 找到对应插件

wasm_runtime:
  instance = wasmtime::instantiate("my-wasm-tool.wasm")
  apply_sandbox_policy(instance, plugin.permissions)
  request  = json_rpc_request(tool_name, tool_args)
  response = instance.call_export("handle", request)  // 在沙箱内执行
  result   = json_rpc_parse(response)

agent_loop:
  context.append_tool_result(tool_name, result)
  // 继续下一轮 LLM_CALL

🔀 并发模型:tokio async

zeroclaw 全程使用 tokio 异步运行时。工具调用通过 tokio::spawn 并发分发, 结果通过 join_all 聚合,每个任务独立携带沙箱策略。

tokio 运行时内部任务分布(简化视图)
tokio Runtime (multi-thread)
Worker Thread #1
agent_loop task
tool: file_read
Worker Thread #2
tool: shell_exec
tool: web_fetch
Worker Thread #3
wasm plugin task
session persist
Worker Thread #N
LLM stream recv
tool: grep
工具调用的并发流程:
1. agent_loop 解析到多个 tool_calls → tokio::spawn 为每个工具创建独立 task
2. 每个 task 在自己的异步上下文中执行,由 tokio 调度器分配到工作线程
3. 所有 task 完成后通过 futures::future::join_all(tasks).await 聚合结果
4. 每个 task 在创建时携带独立的沙箱策略(Safety Engine 在 spawn 前注入)
// agent/loop.rs 内部工具并发调用(简化)
async fn dispatch_tools(tool_calls: Vec<ToolCall>) -> Vec<ToolResult> {
    // 为每个工具创建沙箱策略并并发 spawn
    let tasks: Vec<_> = tool_calls
        .into_iter()
        .map(|call| {
            let policy = safety_engine.policy_for(&call);
            tokio::spawn(async move {
                // 在沙箱内执行工具
                sandbox.run(policy, || tool_registry.execute(call)).await
            })
        })
        .collect();

    // 等待全部完成并聚合结果
    futures::future::join_all(tasks).await
        .into_iter()
        .filter_map(|r| r.ok())
        .collect()
}

📊 运行时关键参数

参数 配置键 默认值 影响
最大工具调用轮数 agent.max_tool_rounds 20 防止 Agent 无限循环
LLM 请求超时 agent.llm_timeout_secs 120s 单次 LLM 调用最长等待时间
工具执行超时 tools.shell_timeout 30s 单个工具调用最长执行时间
最大并发会话(Gateway) gateway.max_sessions 50 Gateway 模式最大并发客户端数
WASM 内存上限 wasm.memory_limit_mb 64MB 单个 WASM 插件实例最大内存
Daemon watchdog 间隔 daemon.watchdog_interval_secs 10s 进程健康检测频率
向量检索 Top-K memory.retrieval_top_k 5 每次跨会话记忆检索返回的条目数