## Major Features ### Streaming Response System - Implement LlmDriver trait with `stream()` method returning async Stream - Add SSE parsing for Anthropic and OpenAI API streaming - Integrate Tauri event system for frontend streaming (`stream:chunk` events) - Add StreamChunk types: Delta, ToolStart, ToolEnd, Complete, Error ### MCP Protocol Implementation - Add MCP JSON-RPC 2.0 types (mcp_types.rs) - Implement stdio-based MCP transport (mcp_transport.rs) - Support tool discovery, execution, and resource operations ### Browser Hand Implementation - Complete browser automation with Playwright-style actions - Support Navigate, Click, Type, Scrape, Screenshot, Wait actions - Add educational Hands: Whiteboard, Slideshow, Speech, Quiz ### Security Enhancements - Implement command whitelist/blacklist for shell_exec tool - Add SSRF protection with private IP blocking - Create security.toml configuration file ## Test Improvements - Fix test import paths (security-utils, setup) - Fix vi.mock hoisting issues with vi.hoisted() - Update test expectations for validateUrl and sanitizeFilename - Add getUnsupportedLocalGatewayStatus mock ## Documentation Updates - Update architecture documentation - Improve configuration reference - Add quick-start guide updates Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
22 KiB
22 KiB
ZCLAW Kernel 技术参考文档
文档版本: v2.0 更新日期: 2026-03-22 目标: 为 ZCLAW 内部 Kernel 架构提供技术参考
一、项目概述
1.1 基本信息
| 属性 | 值 |
|---|---|
| 项目名称 | ZCLAW |
| 架构 | 8 个 Rust Crates 模块化设计 |
| 定位 | AI Agent 桌面客户端 |
| 许可 | MIT |
1.2 核心特性
┌─────────────────────────────────────────────────────────────────┐
│ ZCLAW 核心特性 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 🚀 内部 Kernel 架构 │
│ ├── 无外部进程依赖 │
│ ├── 单一安装包可运行 │
│ └── 即时启动 │
│ │
│ 🤖 多 LLM Provider 支持 │
│ ├── Kimi Code (kimi) │
│ ├── 百炼/通义千问 (qwen) │
│ ├── DeepSeek (deepseek) │
│ ├── 智谱 GLM (zhipu) │
│ ├── OpenAI (openai) │
│ ├── Anthropic (anthropic) │
│ └── 本地模型 (local/ollama) │
│ │
│ 🔒 UI 配置 │
│ ├── 无需编辑配置文件 │
│ ├── "模型与 API"设置页面配置 │
│ └── 配置存储在 localStorage │
│ │
└─────────────────────────────────────────────────────────────────┘
二、Crate 架构
2.1 模块结构
ZCLAW/
├── crates/ # Rust Workspace
│ │
│ ├── zclaw-types/ # L1: 基础类型(无依赖)
│ │ ├── AgentId, SessionId, Message
│ │ ├── Capability, Tool, Event
│ │ ├── Config, Error
│ │ └── 共享 trait 定义
│ │
│ ├── zclaw-memory/ # L2: 存储层(依赖 types)
│ │ ├── KV Store(Agent 配置、状态)
│ │ ├── Session Manager(对话历史)
│ │ ├── Semantic Search(向量搜索)
│ │ └── SQLite 持久化
│ │
│ ├── zclaw-runtime/ # L3: 运行时(依赖types, memory)
│ │ ├── LLM Drivers(Anthropic, OpenAI, Gemini, Local)
│ │ ├── Tool Runner(23 个内置工具)
│ │ ├── Agent Loop(消息循环、流式处理)
│ │ ├── Loop Guard(防循环)
│ │ └── Session Compactor(上下文压缩)
│ │
│ ├── zclaw-kernel/ # L4: 核心协调(依赖所有下层)
│ │ ├── Agent Registry(注册、生命周期)
│ │ ├── Scheduler(配额、调度)
│ │ ├── Capability Manager(权限)
│ │ ├── Event Bus(事件发布)
│ │ ├── Workflow Engine(工作流)
│ │ ├── Trigger Engine(触发器)
│ │ └── Supervisor(健康监控)
│ │
│ ├── zclaw-skills/ # 技能系统
│ │ ├── Skill Loader(TOML/SKILL.md 解析)
│ │ ├── Skill Runner(Python/WASM/PromptOnly)
│ │ └── Bundled Skills(内置技能)
│ │
│ ├── zclaw-hands/ # Hands 自主能力
│ │ ├── Hand/Trigger trait 定义
│ │ ├── HandRegistry/TriggerRegistry 实现
│ │ ├── Browser Hand
│ │ ├── Collector Hand
│ │ ├── Researcher Hand
│ │ └── 其他 Hands
│ │
│ ├── zclaw-channels/ # 通道适配器
│ │ ├── Channel Trait
│ │ ├── Telegram/Discord/Slack 等
│ │ └── Bridge Manager
│ │
│ └── zclaw-protocols/ # 协议支持
│ ├── MCP Client/Server
│ └── A2A Protocol
│
├── desktop/
│ ├── src-tauri/ # Tauri 应用
│ │ ├── src/
│ │ │ ├── lib.rs # 主入口,Kernel 初始化
│ │ │ ├── kernel_commands.rs # Tauri 命令封装
│ │ │ └── state.rs # 应用状态
│ │ └── Cargo.toml # 依赖内部 crates
│ └── src/ # React 前端
│
└── Cargo.toml # Workspace 定义
2.2 依赖关系
zclaw-types (无依赖)
↑
zclaw-memory (→ types)
↑
zclaw-runtime (→ types, memory)
↑
zclaw-kernel (→ types, memory, runtime)
↑
desktop/src-tauri (→ kernel, skills, hands, channels, protocols)
三、zclaw-types
3.1 核心 ID 类型
pub struct AgentId(Uuid);
pub struct SessionId(Uuid);
pub struct ToolId(String);
3.2 消息类型
pub enum Message {
User { content: String },
Assistant { content: String, thinking: Option<String> },
ToolUse { tool: ToolId, input: Value },
ToolResult { tool: ToolId, output: Value, error: bool },
}
3.3 Agent 配置
pub struct AgentConfig {
pub id: AgentId,
pub name: String,
pub model: ModelConfig,
pub system_prompt: String,
pub capabilities: Vec<Capability>,
pub tools: Vec<ToolId>,
}
pub struct ModelConfig {
pub provider: String,
pub model: String,
pub api_key_env: Option<String>,
pub base_url: Option<String>,
}
3.4 统一错误类型
pub enum ZclawError {
NotFound(String),
PermissionDenied(String),
LlmError(String),
ToolError(String),
StorageError(String),
ConfigError(String),
}
四、zclaw-memory
4.1 MemoryStore
pub struct MemoryStore {
db: Arc<Mutex<Connection>>,
}
impl MemoryStore {
// Agent 配置持久化
pub async fn save_agent(&self, agent: &AgentConfig) -> Result<()>;
pub async fn load_agent(&self, id: &AgentId) -> Result<Option<AgentConfig>>;
pub async fn list_agents(&self) -> Result<Vec<AgentConfig>>;
// 会话管理
pub async fn create_session(&self, agent_id: &AgentId) -> Result<SessionId>;
pub async fn get_session(&self, id: &SessionId) -> Result<Option<Session>>;
pub async fn append_message(&self, session: &SessionId, msg: Message) -> Result<()>;
// KV 存储
pub async fn store(&self, agent: &AgentId, key: &str, value: &Value) -> Result<()>;
pub async fn recall(&self, agent: &AgentId, key: &str) -> Result<Option<Value>>;
// 语义搜索
pub async fn embed(&self, text: &str) -> Result<Vec<f32>>;
pub async fn search_similar(&self, query: &str, limit: usize) -> Result<Vec<MemoryEntry>>;
}
4.2 数据库 Schema
-- Agent 配置表
CREATE TABLE agents (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
config JSON NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 会话表
CREATE TABLE sessions (
id TEXT PRIMARY KEY,
agent_id TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (agent_id) REFERENCES agents(id)
);
-- 消息表
CREATE TABLE messages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT NOT NULL,
role TEXT NOT NULL,
content TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (session_id) REFERENCES sessions(id)
);
五、zclaw-runtime
5.1 LLM Driver Trait
#[async_trait]
pub trait LlmDriver: Send + Sync {
async fn complete(&self, req: CompletionRequest) -> Result<CompletionResponse>;
async fn stream(&self, req: CompletionRequest) -> Result<mpsc::Receiver<StreamEvent>>;
}
5.2 Driver 实现
// Anthropic Claude
pub struct AnthropicDriver {
api_key: SecretString,
}
// OpenAI 兼容(支持 Kimi, Qwen, DeepSeek, Zhipu 等)
pub struct OpenAiDriver {
api_key: SecretString,
base_url: String,
}
impl OpenAiDriver {
pub fn with_base_url(api_key: SecretString, base_url: String) -> Self {
Self { api_key, base_url }
}
}
// Google Gemini
pub struct GeminiDriver {
api_key: SecretString,
}
// 本地模型(Ollama/LMStudio)
pub struct LocalDriver {
base_url: String,
}
5.3 Agent Loop
pub struct AgentLoop {
driver: Arc<dyn LlmDriver>,
tools: Vec<Tool>,
memory: Arc<MemoryStore>,
loop_guard: LoopGuard,
// 模型配置(2026-03-22 添加)
model: String,
system_prompt: Option<String>,
max_tokens: u32,
temperature: f32,
}
impl AgentLoop {
pub fn new(...) -> Self;
pub fn with_model(self, model: impl Into<String>) -> Self;
pub fn with_system_prompt(self, prompt: impl Into<String>) -> Self;
pub fn with_max_tokens(self, max_tokens: u32) -> Self;
pub fn with_temperature(self, temperature: f32) -> Self;
pub async fn run(&self, session: SessionId, input: String) -> Result<AgentLoopResult>;
pub async fn run_streaming(&self, session: SessionId, input: String)
-> Result<mpsc::Receiver<LoopEvent>>;
}
重要: AgentLoop 必须通过 builder 方法配置模型,否则将使用默认值。run() 方法会从 CompletionResponse.content 中提取 ContentBlock::Text 作为响应内容。
六、zclaw-kernel
6.1 Kernel 主结构
pub struct Kernel {
registry: AgentRegistry,
scheduler: Scheduler,
capabilities: CapabilityManager,
events: EventBus,
workflows: WorkflowEngine,
triggers: TriggerEngine,
supervisor: Supervisor,
memory: Arc<MemoryStore>,
config: KernelConfig,
}
6.2 Kernel 方法
impl Kernel {
/// 启动 Kernel
pub async fn boot(config: KernelConfig) -> Result<Self>;
/// 关闭 Kernel
pub async fn shutdown(self) -> Result<()>;
// Agent 生命周期
pub async fn spawn_agent(&self, config: AgentConfig) -> Result<AgentId>;
pub async fn kill_agent(&self, id: &AgentId) -> Result<()>;
pub fn list_agents(&self) -> Vec<AgentInfo>;
pub fn get_agent(&self, id: &AgentId) -> Option<AgentInfo>;
// 消息处理
pub async fn send_message(&self, agent: &AgentId, msg: String)
-> Result<MessageResponse>;
pub async fn send_message_stream(&self, agent: &AgentId, msg: String)
-> Result<mpsc::Receiver<StreamEvent>>;
}
// send_message 实现:从 AgentConfig 获取模型配置传递给 AgentLoop
// 优先级:agent_config.model > kernel_config.default_model
消息处理流程:
send_message(agent_id, message)
│
├── 获取 AgentConfig: registry.get(agent_id)
│
├── 确定模型:
│ if !agent_config.model.model.is_empty():
│ model = agent_config.model.model
│ else:
│ model = kernel_config.default_model
│
├── 创建 AgentLoop (使用 builder 模式):
│ AgentLoop::new(...)
│ .with_model(model)
│ .with_max_tokens(agent_config.max_tokens || kernel_config.max_tokens)
│ .with_temperature(agent_config.temperature || kernel_config.temperature)
│ .with_system_prompt(agent_config.system_prompt) // 可选
│
└── 运行: loop_runner.run(session_id, message)
│
├── 调用 LLM: driver.complete(request)
│
└── 提取响应: 从 ContentBlock::Text 获取内容
// 工作流
pub async fn run_workflow(&self, workflow_id: &str, input: Value) -> Result<Value>;
// 事件
pub fn subscribe(&self) -> broadcast::Receiver<Event>;
}
### 6.3 KernelConfig
```rust
pub struct KernelConfig {
// 数据库
pub database_url: String,
// 默认 Provider 和模型
pub default_provider: String,
pub default_model: String,
// API Keys(从 UI 传入)
pub anthropic_api_key: Option<String>,
pub openai_api_key: Option<String>,
pub gemini_api_key: Option<String>,
pub kimi_api_key: Option<String>,
pub qwen_api_key: Option<String>,
pub deepseek_api_key: Option<String>,
pub zhipu_api_key: Option<String>,
// Base URLs
pub kimi_base_url: String,
pub qwen_base_url: String,
pub deepseek_base_url: String,
pub zhipu_base_url: String,
pub local_base_url: Option<String>,
// 生成参数
pub max_tokens: u32,
pub temperature: f32,
}
6.4 create_driver 方法
impl KernelConfig {
pub fn create_driver(&self) -> Result<Arc<dyn LlmDriver>> {
match self.default_provider.as_str() {
"anthropic" => {
let key = self.anthropic_api_key.clone()
.ok_or_else(|| ZclawError::ConfigError("ANTHROPIC_API_KEY not set".into()))?;
Ok(Arc::new(AnthropicDriver::new(SecretString::new(key))))
}
"kimi" => {
let key = self.kimi_api_key.clone()
.ok_or_else(|| ZclawError::ConfigError("KIMI_API_KEY not set".into()))?;
Ok(Arc::new(OpenAiDriver::with_base_url(
SecretString::new(key),
self.kimi_base_url.clone(),
)))
}
"qwen" => {
let key = self.qwen_api_key.clone()
.ok_or_else(|| ZclawError::ConfigError("QWEN_API_KEY not set".into()))?;
Ok(Arc::new(OpenAiDriver::with_base_url(
SecretString::new(key),
self.qwen_base_url.clone(),
)))
}
"deepseek" => {
let key = self.deepseek_api_key.clone()
.ok_or_else(|| ZclawError::ConfigError("DEEPSEEK_API_KEY not set".into()))?;
Ok(Arc::new(OpenAiDriver::with_base_url(
SecretString::new(key),
self.deepseek_base_url.clone(),
)))
}
"zhipu" => {
let key = self.zhipu_api_key.clone()
.ok_or_else(|| ZclawError::ConfigError("ZHIPU_API_KEY not set".into()))?;
Ok(Arc::new(OpenAiDriver::with_base_url(
SecretString::new(key),
self.zhipu_base_url.clone(),
)))
}
"openai" => {
let key = self.openai_api_key.clone()
.ok_or_else(|| ZclawError::ConfigError("OPENAI_API_KEY not set".into()))?;
Ok(Arc::new(OpenAiDriver::new(SecretString::new(key))))
}
"gemini" => {
let key = self.gemini_api_key.clone()
.ok_or_else(|| ZclawError::ConfigError("GEMINI_API_KEY not set".into()))?;
Ok(Arc::new(GeminiDriver::new(SecretString::new(key))))
}
"local" | "ollama" => {
let base_url = self.local_base_url.clone()
.unwrap_or_else(|| "http://localhost:11434/v1".to_string());
Ok(Arc::new(LocalDriver::new(base_url)))
}
_ => Err(ZclawError::ConfigError(
format!("Unknown provider: {}", self.default_provider)
)),
}
}
}
七、Tauri 集成
7.1 命令封装
// desktop/src-tauri/src/kernel_commands.rs
/// Kernel 状态包装器
pub type KernelState = Arc<Mutex<Option<Kernel>>>;
/// 初始化 Kernel
#[tauri::command]
pub async fn kernel_init(
state: State<'_, KernelState>,
config_request: Option<KernelConfigRequest>,
) -> Result<KernelStatusResponse, String> {
// ...
}
/// 创建 Agent
#[tauri::command]
pub async fn agent_create(
state: State<'_, KernelState>,
request: CreateAgentRequest,
) -> Result<CreateAgentResponse, String> {
// ...
}
/// 发送消息
#[tauri::command]
pub async fn agent_chat(
state: State<'_, KernelState>,
request: ChatRequest,
) -> Result<ChatResponse, String> {
// ...
}
7.2 lib.rs 注册
// desktop/src-tauri/src/lib.rs
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.setup(|app| {
app.manage(kernel_commands::create_kernel_state());
Ok(())
})
.invoke_handler(tauri::generate_handler![
kernel_commands::kernel_init,
kernel_commands::kernel_status,
kernel_commands::kernel_shutdown,
kernel_commands::agent_create,
kernel_commands::agent_list,
kernel_commands::agent_get,
kernel_commands::agent_delete,
kernel_commands::agent_chat,
])
.run(tauri::generate_context!())
}
八、前端集成
8.1 KernelClient
// desktop/src/lib/kernel-client.ts
export class KernelClient {
private config: KernelConfig = {};
private defaultAgentId: string = '';
setConfig(config: KernelConfig): void {
this.config = config;
}
async connect(): Promise<void> {
if (!this.config.provider || !this.config.model || !this.config.apiKey) {
throw new Error('请先在"模型与 API"设置页面配置模型');
}
const status = await invoke<KernelStatus>('kernel_init', {
configRequest: {
provider: this.config.provider,
model: this.config.model,
apiKey: this.config.apiKey,
baseUrl: this.config.baseUrl || null,
},
});
// 创建默认 Agent
const agents = await this.listAgents();
if (agents.length === 0) {
const agent = await this.createAgent({
name: 'Default Agent',
provider: this.config.provider,
model: this.config.model,
});
this.defaultAgentId = agent.id;
}
}
async chat(message: string, opts?: ChatOptions): Promise<ChatResponse> {
return invoke<ChatResponse>('agent_chat', {
request: {
agentId: opts?.agentId || this.defaultAgentId,
message,
},
});
}
}
8.2 ConnectionStore
// desktop/src/store/connectionStore.ts
connect: async (url?: string, token?: string) => {
const useInternalKernel = isTauriRuntime();
if (useInternalKernel) {
const kernelClient = getKernelClient();
const modelConfig = getDefaultModelConfig();
if (!modelConfig) {
throw new Error('请先在"模型与 API"设置页面添加自定义模型配置');
}
kernelClient.setConfig({
provider: modelConfig.provider,
model: modelConfig.model,
apiKey: modelConfig.apiKey,
baseUrl: modelConfig.baseUrl,
});
await kernelClient.connect();
set({ client: kernelClient, gatewayVersion: '0.2.0-internal' });
return;
}
// 非 Tauri 环境...
}
九、支持的 Provider
9.1 Provider 列表
| Provider | Driver | Base URL | API 协议 |
|---|---|---|---|
| kimi | OpenAiDriver | https://api.kimi.com/coding/v1 |
OpenAI 兼容 |
| qwen | OpenAiDriver | https://dashscope.aliyuncs.com/compatible-mode/v1 |
OpenAI 兼容 |
| deepseek | OpenAiDriver | https://api.deepseek.com/v1 |
OpenAI 兼容 |
| zhipu | OpenAiDriver | https://open.bigmodel.cn/api/paas/v4 |
OpenAI 兼容 |
| openai | OpenAiDriver | https://api.openai.com/v1 |
OpenAI |
| anthropic | AnthropicDriver | https://api.anthropic.com |
Anthropic |
| gemini | GeminiDriver | https://generativelanguage.googleapis.com |
Gemini |
| local | LocalDriver | http://localhost:11434/v1 |
OpenAI 兼容 |
9.2 配置示例
// Kimi Code 配置
{
provider: 'kimi',
model: 'kimi-k2-turbo',
apiKey: 'your-kimi-api-key',
baseUrl: 'https://api.kimi.com/coding/v1'
}
// 百炼 Qwen 配置
{
provider: 'qwen',
model: 'qwen-plus',
apiKey: 'your-qwen-api-key',
baseUrl: 'https://dashscope.aliyuncs.com/compatible-mode/v1'
}
十、开发命令
10.1 构建命令
# 构建所有 crates
cargo build
# 构建生产版本
cargo build --release
# 运行测试
cargo test
# 启动开发环境
pnpm start:dev
10.2 常用命令
# 安装依赖
pnpm install
# 类型检查
pnpm tsc --noEmit
# 运行测试
pnpm vitest run
# E2E 测试
pnpm test:e2e
十一、参考资料
11.1 相关文档
11.2 架构演进
| 版本 | 架构 | 说明 |
|---|---|---|
| v1.x | 外部 OpenFang | 需要启动独立后端进程 |
| v2.0 | 内部 Kernel | Kernel 集成在 Tauri 中,无需外部进程 |
文档版本: v2.0 | 更新日期: 2026-03-22