Files
zclaw_openfang/wiki/routing.md
iven e1af3cca03
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
fix(routing): 消除模型路由链路硬编码不匹配模型名
summarizer_adapter.rs 和 saas-relay-client.ts 中的 fallback 模型名
(glm-4-flash / glm-4-flash-250414) 在 SaaS relay 中不存在,导致请求被拒绝。
改为未配置时明确报错(fail fast),不再静默使用错误模型。
2026-04-11 23:08:06 +08:00

8.0 KiB
Raw Blame History

title, updated, status, tags
title updated status tags
客户端路由 2026-04-11 active
module
routing
connection

客户端路由

index 导航。关联模块: chat saas

设计思想

核心决策: Tauri 桌面端通过 SaaS Token Pool 中转访问 LLM不直连。

为什么?

  1. 集中密钥管理 — 用户不需要自己的 API KeySaaS 维护共享 Key 池
  2. 用量追踪 + 计费 — 每次调用经过 SaaSrecord_usage worker 记录 token 消耗
  3. 模型白名单 — Admin 配置哪些模型可用,listModels() 返回白名单
  4. 降级保障 — SaaS 挂了自动切本地 Kernel桌面端不变砖

代码逻辑

4 分支决策树

入口: connectionStore.ts:349connect(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

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

模型路由

完整链路 (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.rsextraction_adapter.rskernel_init 时配置, 使用与聊天相同的 modelbase_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] 仅用于本地 KernelSaaS relay 不解析别名!

Browser 模式模型路由

createSaaSRelayGatewayClient(saasUrl, getModel)
  │  getModel() 回调 → conversationStore.currentModel || relayModels[0]?.id
  │
  ▼
chatStream() → saasClient.chatCompletion({ model: getModel() })
  │  未获取到模型时 → onError 报错,不发请求
  │
  ▼
POST /api/v1/relay/chat/completions → SSE 流

关联模块

  • 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 注入