--- title: 客户端路由 updated: 2026-04-11 status: active tags: [module, routing, connection] --- # 客户端路由 > 从 [[index]] 导航。关联模块: [[chat]] [[saas]] ## 设计思想 **核心决策: Tauri 桌面端通过 SaaS Token Pool 中转访问 LLM,不直连。** 为什么? 1. **集中密钥管理** — 用户不需要自己的 API Key,SaaS 维护共享 Key 池 2. **用量追踪 + 计费** — 每次调用经过 SaaS,`record_usage` worker 记录 token 消耗 3. **模型白名单** — Admin 配置哪些模型可用,`listModels()` 返回白名单 4. **降级保障** — SaaS 挂了自动切本地 Kernel,桌面端不变砖 ## 代码逻辑 ### 4 分支决策树 入口: `connectionStore.ts:349` → `connect(url?, token?)` ``` connect() │ ├── [1] Admin 强制路由: localStorage llm_routing │ ├── "relay" → 强制 SaaS Relay 模式 │ └── "local" → 强制本地 Kernel (adminForceLocal=true) │ ├── [2] SaaS Relay 模式: localStorage('zclaw-connection-mode') === 'saas' │ ├── Tauri: KernelClient + baseUrl = saasUrl/api/v1/relay │ │ apiKey = SaaS JWT (不是 LLM Key!) │ ├── Browser: SaaSRelayGatewayClient (SSE) │ └── SaaS 不可达 → 降级到本地 Kernel │ ├── [3] 本地 Kernel: isTauriRuntime() === true │ KernelClient + 用户自定义模型配置 │ 用户需要自己的 API Key │ └── [4] External Gateway (fallback) GatewayClient via WebSocket/REST ``` ### SaaS Relay 主路径 (Tauri 桌面端) 关键代码: `connectionStore.ts:482-535` ```ts 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 层 (17 文件 + chat/4) ``` desktop/src/store/ ├── index.ts Store 协调器 + client 注入 ├── agentStore.ts Agent 分身管理 ├── browserHandStore.ts 浏览器 Hand 状态 ├── chatStore.ts 聊天通用状态 ├── classroomStore.ts 课堂模式 ├── configStore.ts 配置读写 ├── connectionStore.ts 路由决策核心 ├── handStore.ts Hand 状态管理 ├── memoryGraphStore.ts 记忆图谱 ├── offlineStore.ts 离线队列 ├── saasStore.ts SaaS 认证 ├── securityStore.ts 安全状态 ├── sessionStore.ts 会话管理 ├── uiModeStore.ts 双模式 UI ├── workflowBuilderStore.ts 工作流构建器 ├── workflowStore.ts 工作流状态 └── chat/ ├── artifactStore.ts 聊天产物 ├── conversationStore.ts 会话管理 ├── messageStore.ts 消息持久化 └── streamStore.ts 流式编排 ``` ### lib/ 工具层 (85 个文件) 关键分类: | 类别 | 文件 | 数量 | |------|------|------| | Kernel 通信 | kernel-chat/kernel-client/kernel-agent/... | 7 | | SaaS 通信 | saas-client/saas-auth/saas-billing/... | 9 | | Gateway | gateway-client/gateway-api/gateway-auth/... | 6 | | Intelligence | intelligence-client/ + 5 fallback | 7 | | 工具 | config-parser/crypto-utils/logger/utils | 10+ | | Tauri 集成 | safe-tauri/tauri-gateway/secure-storage | 3 | ## 关联模块 - [[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 注入 |