Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
三层架构增强: - L0 index.md: 用户功能清单+跨模块数据流全景图+导航树增强 (92→143行) - L1 8个模块页标准化: 功能清单/API接口/测试链路/已知问题 routing(252→326) chat(101→157) saas(153→230) memory(182→333) butler(137→179) middleware(121→159) hands-skills(218→257) pipeline(111→156) - L1 新增2页: security.md(157行) data-model.md(180行) - L2 feature-map.md: 33条端到端功能链路映射(408行) 维护机制: CLAUDE.md §8.3 wiki触发规则 5→9条 设计文档: docs/superpowers/specs/2026-04-21-wiki-systematic-overhaul-design.md
13 KiB
13 KiB
title, updated, status, tags
| title | updated | status | tags | |||
|---|---|---|---|---|---|---|
| 客户端路由 | 2026-04-21 | active |
|
客户端路由
设计思想
核心决策: Tauri 桌面端通过 SaaS Token Pool 中转访问 LLM,不直连。
为什么?
- 集中密钥管理 — 用户不需要自己的 API Key,SaaS 维护共享 Key 池
- 用量追踪 + 计费 — 每次调用经过 SaaS,
record_usageworker 记录 token 消耗 - 模型白名单 — Admin 配置哪些模型可用,
listModels()返回白名单 - 降级保障 — SaaS 挂了自动切本地 Kernel,桌面端不变砖
功能清单
| 功能 | 描述 | 入口文件 | 状态 |
|---|---|---|---|
| 连接管理 | 5 分支路由决策 + 自动降级 | connectionStore.ts | ✅ |
| SaaS Relay 中转 | Tauri 通过 SaaS Token Pool 中转 LLM | connectionStore.ts | ✅ |
| 浏览器模式 | SSE 连接 SaaS relay | saas-relay-client.ts | ✅ |
| 本地 Kernel | Tauri 内置 Kernel 直连 LLM | kernel-client.ts | ✅ |
| 外部 Gateway | WebSocket 独立进程 | gateway-client.ts | ✅ |
| Gateway 进程管理 | 启动/停止/重启/状态/诊断 | gateway/commands.rs | ✅ |
| 健康检查 | 端口检测 + 完整诊断 | health_check.rs | ✅ |
| 设备配对 | 设备审批 + 公钥交换 | gateway/commands.rs | ✅ |
| 模型路由 | 白名单验证 + fallback + 别名解析 | connectionStore.ts | ✅ |
代码逻辑
5 分支 + 降级决策树
入口: connectionStore.ts → connect(url?, token?)
connect()
│
├── [1] Admin 强制本地: adminRouting === 'local' && isTauri()
│ → 直接走 Kernel 模式,跳过 SaaS
│
├── [2] SaaS Relay (Tauri 路径): savedMode === 'saas' && isTauri()
│ → KernelClient + baseUrl = saasUrl/api/v1/relay
│ → apiKey = SaaS JWT (不是 LLM Key!)
│ → SaaS 不可达 → 降级到本地 Kernel
│
├── [3] SaaS Relay (Browser 路径): savedMode === 'saas' && !isTauri()
│ → SaaSRelayGatewayClient (SSE)
│ → SaaS 不可达 → 降级到本地 Kernel
│
├── [4] 本地 Kernel: isTauriRuntime() && 非 SaaS 模式
│ → KernelClient + 用户自定义模型配置
│ → 用户需要自己的 API Key
│
└── [5] External Gateway (fallback): !isTauri()
→ GatewayClient via WebSocket/REST
SaaS Relay 主路径 (Tauri 桌面端)
关键代码: connectionStore.ts:482-535
kernelClient.setConfig({
provider: 'custom',
model: modelToUse, // 从 SaaS listModels() 获取
apiKey: session.token, // SaaS JWT,不是 LLM Key
baseUrl: `${session.saasUrl}/api/v1/relay`, // 指向 SaaS relay
apiProtocol: 'openai',
});
注意: Kernel 仍然执行 LLM 调用逻辑,但请求发往 SaaS relay 而非直连 LLM。 SaaS relay 接到请求后,从 Token Pool 中取一个可用 Key,转发给真实 LLM。
SaaS 降级流程
关键代码: connectionStore.ts:446-468
listModels() 失败
→ 401 → session 过期 → logout → 要求重新登录
→ 其他错误 → saasDegraded = true
→ saasStore.saasReachable = false
→ 降级到本地 Kernel 模式
客户端类型
| 客户端 | 传输 | 文件 | 用途 |
|---|---|---|---|
| GatewayClient | WebSocket + REST | lib/gateway-client.ts |
外部 Gateway 进程 |
| KernelClient | Tauri invoke() | lib/kernel-chat.ts |
内置 Kernel (桌面端) |
| SaaSRelayGatewayClient | HTTP SSE | lib/saas-relay-client.ts |
浏览器端 SaaS 中继 |
getClient() 定义: connectionStore.ts:844
所有 Store 通过 initializeStores() (store/index.ts:94) 获取共享 client。
Store 层 (16 根文件 + chat/4 + saas/5 = 25)
desktop/src/store/
├── index.ts Store 协调器 + client 注入
├── agentStore.ts Agent 分身管理
├── browserHandStore.ts 浏览器 Hand 状态
├── chatStore.ts 聊天通用状态
├── classroomStore.ts 课堂模式
├── configStore.ts 配置读写
├── connectionStore.ts 路由决策核心
├── handStore.ts Hand 状态管理
├── industryStore.ts 行业配置 (已接通 ButlerPanel)
├── memoryGraphStore.ts 记忆图谱
├── offlineStore.ts 离线队列
├── saasStore.ts SaaS 认证 (re-export barrel)
├── securityStore.ts 安全状态
├── sessionStore.ts 会话管理
├── uiModeStore.ts 双模式 UI
├── workflowStore.ts 工作流状态
├── chat/
│ ├── artifactStore.ts 聊天产物
│ ├── conversationStore.ts 会话管理
│ ├── messageStore.ts 消息持久化
│ └── streamStore.ts 流式编排
└── saas/ (拆分子模块, 04-17 refactor)
├── index.ts 子模块入口
├── auth.ts 认证逻辑
├── billing.ts 计费逻辑
├── shared.ts 共享状态/工具
└── types.ts 类型定义
lib/ 工具层 (75 个 .ts 文件)
关键分类:
| 类别 | 文件 | 数量 |
|---|---|---|
| Kernel 通信 | kernel-client/kernel-chat/kernel-agent/kernel-skills/kernel-triggers/kernel-hands/... | 8 |
| SaaS 通信 | saas-client/saas-auth/saas-billing/saas-relay/saas-industry/saas-knowledge/... | 12 |
| Gateway | gateway-client/gateway-api/gateway-auth/gateway-config/... | 9 |
| Intelligence | intelligence-backend/intelligence-client/embedding-client/memory-extractor | 4 |
| Viking | viking-client | 1 |
| Pipeline | pipeline-client/pipeline-recommender | 2 |
| Security | crypto-utils/secure-storage/security-audit/security-index/api-key-storage | 5 |
| 工具 | config-parser/logger/utils/error-types/error-utils/json-utils/... | 10+ |
| Tauri 集成 | safe-tauri/tauri-gateway | 2 |
| 工作流 | workflow-builder/ (index + types + yaml-converter) | 3 |
模型路由
完整链路 (Tauri SaaS Relay 主路径)
前端模型选择
│
├─ conversationStore.currentModel (用户上次选择的模型)
│ 持久化到 IndexedDB,跨会话保留
│
├─ connectionStore 连接时获取 SaaS 可用模型
│ saasClient.listModels() → [{id: "deepseek-chat"}, {id: "GLM-4.7"}, ...]
│ relayModels[0]?.id 作为 fallback
│
└─ 最终: preferredModel || fallbackId
│
▼
kernelClient.setConfig({ model: modelToUse })
│
▼
kernel_init (Tauri Command)
│ KernelConfigRequest { model, api_key, base_url }
│ base_url = "https://saas-host/api/v1/relay"
│ api_key = SaaS JWT (不是 LLM Key!)
│
▼
Kernel::boot(config)
│ config.llm.model = modelToUse
│ config.llm.base_url = SaaS relay URL
│
▼
loop_runner → LLM Driver (OpenAI compatible)
│ POST {base_url}/chat/completions
│ body: { model: modelToUse, messages: [...] }
│ header: Authorization: Bearer {SaaS JWT}
│
▼
SaaS Relay Handler (handlers.rs)
│ cache.get_model(model_name) → 精确匹配 model_id
│ ⚠️ 无别名解析! "glm-4-flash" ≠ "deepseek-chat"
│ 找不到 → 400 "模型 xxx 不存在或未启用"
│
▼
Key Pool 轮换
│ priority ASC → last_used_at ASC → cooldown 检查 → RPM/TPM 滑动窗口
│
▼
真实 LLM API
│ 429 → mark cooldown → 切换 key
│ 5xx → exponential backoff
│ model_group → 跨 Provider 故障转移
│
▼
响应 → SSE 流式返回 → 前端
辅助 LLM 调用 (非聊天主路径)
这些 Rust 端组件也通过同一个 relay 发起 LLM 请求:
| 组件 | 文件 | 模型来源 | 触发时机 |
|---|---|---|---|
| 记忆摘要 | summarizer_adapter.rs |
kernel_init 传入的 model | 定期 L0/L1 摘要生成 |
| 记忆提取 | extraction_adapter.rs |
kernel_init 传入的 model | 中间件触发提取 |
| 管家路由 | ButlerRouter via loop_runner | 同聊天模型 | 聊天中间件链 |
关键: summarizer_adapter.rs 和 extraction_adapter.rs 在 kernel_init 时配置,
使用与聊天相同的 model 和 base_url。未配置时会明确报错,不会静默 fallback 到错误模型。
SaaS Relay 模型匹配规则
前端发送 model: "deepseek-chat"
→ SaaS cache 按 model_id 精确匹配
→ 匹配: cache.models["deepseek-chat"] → 命中
→ 不匹配: cache.models["glm-4-flash"] → null → 400 错误
⚠️ config.toml 中的 [llm.aliases] 仅用于本地 Kernel,SaaS relay 不解析别名!
Browser 模式模型路由
createSaaSRelayGatewayClient(saasUrl, getModel)
│ getModel() 回调 → conversationStore.currentModel || relayModels[0]?.id
│
▼
chatStream() → saasClient.chatCompletion({ model: getModel() })
│ 未获取到模型时 → onError 报错,不发请求
│
▼
POST /api/v1/relay/chat/completions → SSE 流
API 接口
Tauri 命令
Gateway 管理 (desktop/src-tauri/src/gateway/commands.rs):
| 命令 | 参数 | 返回值 | 说明 |
|---|---|---|---|
zclaw_status |
— | LocalGatewayStatus |
Kernel 运行状态 |
zclaw_start |
— | LocalGatewayStatus |
启动 Kernel |
zclaw_stop |
— | LocalGatewayStatus |
停止 Kernel |
zclaw_restart |
— | LocalGatewayStatus |
重启 Kernel |
zclaw_local_auth |
— | LocalGatewayAuth |
获取本地认证 token |
zclaw_prepare_for_tauri |
— | LocalGatewayPrepareResult |
更新 Tauri allowed origins |
zclaw_approve_device_pairing |
device_id, public_key_base64, url? | PairingApprovalResult |
设备配对审批 |
zclaw_doctor |
— | String |
诊断报告 |
zclaw_process_list |
— | ProcessListResponse |
进程列表 |
zclaw_process_logs |
pid?, lines? | ProcessLogsResponse |
进程日志 |
zclaw_version |
— | VersionResponse |
版本信息 |
健康检查 (desktop/src-tauri/src/health_check.rs):
| 命令 | 参数 | 返回值 | 说明 |
|---|---|---|---|
zclaw_health_check |
port?, timeout_ms? | HealthCheckResponse |
完整健康检查 |
zclaw_ping |
— | bool |
快速端口检测 |
SaaS Relay 路由 (crates/zclaw-saas/src/relay/)
| 方法 | 路径 | 权限 | 说明 |
|---|---|---|---|
| POST | /api/v1/relay/chat/completions |
认证+配额 | 主聊天中转 |
| GET | /api/v1/relay/models |
认证 | 可用模型列表 |
| GET | /api/v1/relay/tasks |
认证 | 任务列表 |
| GET | /api/v1/relay/tasks/:id |
认证 | 任务详情 |
| POST | /api/v1/relay/tasks/:id/retry |
admin | 重试失败任务 |
Provider Key 管理 (crates/zclaw-saas/src/relay/handlers.rs)
| 方法 | 路径 | 权限 | 说明 |
|---|---|---|---|
| GET | /api/v1/providers/:id/keys |
admin | 列出 Provider Key |
| POST | /api/v1/providers/:id/keys |
admin | 添加加密 Key |
| PUT | /api/v1/providers/:id/keys/:kid/toggle |
admin | 启停 Key |
| DELETE | /api/v1/providers/:id/keys/:kid |
admin | 删除 Key |
测试链路
| 功能 | 测试文件 | 测试数 | 覆盖状态 |
|---|---|---|---|
| Admin 路由解析 | tests/desktop/connectionStore.adminRouting.test.ts |
— | ✅ parseAdminRouting() 纯函数 |
| 连接流程 | tests/desktop/gatewayStore.test.ts |
— | ✅ connect() + 数据加载 |
| GatewayClient | tests/gateway/ws-client.test.ts |
— | ✅ WS 连接/事件/断连 |
| Mock Server | tests/fixtures/zclaw-mock-server.ts |
— | ✅ HTTP+WS 模拟 |
关联模块
- chat — 路由决定使用哪种 ChatStream
- saas — Token Pool、认证、模型管理
- middleware — 请求经过中间件链处理
- butler — 管家模式通过 ButlerRouter 中间件介入
关键文件
| 文件 | 职责 |
|---|---|
desktop/src/store/connectionStore.ts |
路由决策核心 |
desktop/src/lib/gateway-client.ts |
WebSocket 客户端 |
desktop/src/lib/kernel-chat.ts |
Tauri 内核聊天 |
desktop/src/lib/kernel-client.ts |
Kernel 客户端配置 |
desktop/src/lib/saas-relay-client.ts |
SaaS SSE 中继 |
desktop/src/lib/saas-client.ts |
SaaS API 客户端 |
desktop/src/store/index.ts |
Store 协调器 + client 注入 |