Files
zclaw_openfang/wiki/routing.md
iven 36a1c87d87
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
docs(wiki): 重构为模块化知识库 — 按模块组织而非按文档类型
问题: 旧 wiki 按文档类型组织(architecture/data-flows/file-map),
修复 Butler Router 需要读 4 个文件才能拼凑全貌。
且 SaaS Relay 主路径 vs 本地降级的优先级描述不准确。

重构为模块化结构,每个模块页自包含:
- 设计思想: 为什么这样设计
- 代码逻辑: 数据流 + 关键代码
- 关联模块: 依赖关系

新增模块页:
- routing.md: 客户端路由 (明确 SaaS Relay 是主路径,不是本地模式)
- chat.md: 聊天系统 (3种实现 + Token Pool 中转机制)
- butler.md: 管家模式 (路由/冷启动/痛点/双模式UI)
- memory.md: 记忆管道 (提取→FTS5→检索→注入)
- saas.md: SaaS平台 (认证/Token池/计费/Admin)
- middleware.md: 中间件链 (14层 + 优先级)
- hands-skills.md: Hands(9) + Skills(75)
- pipeline.md: Pipeline DSL

删除旧文件: architecture.md, data-flows.md, module-status.md, file-map.md
(内容已分布到对应模块页中)

添加 .gitignore 排除 Obsidian 工作区状态文件
2026-04-11 00:36:26 +08:00

106 lines
3.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: 客户端路由
updated: 2026-04-11
status: active
tags: [module, routing, connection]
---
# 客户端路由
> 从 [[index]] 导航。关联模块: [[chat]] [[saas]]
## 设计思想
**核心决策: Tauri 桌面端通过 SaaS Token Pool 中转访问 LLM不直连。**
为什么?
1. **集中密钥管理** — 用户不需要自己的 API KeySaaS 维护共享 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。
## 关联模块
- [[chat]] — 路由决定使用哪种 ChatStream
- [[saas]] — Token Pool、认证、模型管理
- [[middleware]] — 请求经过中间件链处理
- [[butler]] — 管家模式通过 ButlerRouter 中间件介入
## 关键文件
| 文件 | 职责 |
|------|------|
| `desktop/src/store/connectionStore.ts` | 路由决策核心 (844行) |
| `desktop/src/lib/gateway-client.ts` | WebSocket 客户端 |
| `desktop/src/lib/kernel-chat.ts` | Tauri 内核聊天 |
| `desktop/src/lib/saas-relay-client.ts` | SaaS SSE 中继 |
| `desktop/src/lib/saas-client.ts` | SaaS API 客户端 |
| `desktop/src/store/index.ts` | Store 协调器 + client 注入 |