CC Switch 模型检查(Stream Check)实战指南:配置项、检查参数与可达性探测实现原理
【免费下载链接】cc-switchA cross-platform desktop All-in-One assistant for Claude Code, Codex, OpenCode, OpenClaw, Grok Build & Hermes Agent. Only official website: ccswitch.io项目地址: https://gitcode.com/GitHub_Trending/cc/cc-switch
模型检查(Stream Check,界面中也称"连通检测")是 CC Switch 中用于验证第三方供应商端点是否可用的内置诊断功能,位于设置的高级面板中,并与本地代理的自动故障转移(Auto Failover)体系配合工作。读完本文,你将掌握该功能在 CC Switch 中的完整使用方式——如何配置检查参数、如何在供应商卡片上执行检查、如何解读健康状态,以及从源码层面理解"可达性探测"的判定逻辑、各应用base_url的解析规则,还有它和故障转移熔断器之间的职责边界。
一、功能定位:模型检查能回答什么问题
按用户手册 4.5 模型检查 的定义,模型检查(Stream Check)用于验证供应商配置的模型是否可用,早期实现通过发送真实的 API 请求来测试:
- 模型是否存在
- API Key 是否有效
- 端点是否正常响应
- 响应延迟是否正常
- 流式响应首字节时间(TTFB)
自 v3.13.0 起,Stream Check 的覆盖范围扩展到Claude / Codex / Gemini / OpenCode / OpenClaw,包括 OpenClaw 的全部协议变体(openai-completions等);OpenCode 通过 npm 包映射自动识别;OpenClaw 支持自定义auth-header检测,并处理了 Bedrock 错误消息、baseURL回退等边界情况(见 CHANGELOG v3.13.0 条目 "OpenCode / OpenClaw Stream Check Coverage")。
需要特别说明的是,当前仓库的实现已经演进为轻量级可达性探测。从 v3.16.3 的 "Lightweight Provider Health Check" 变更开始,检查不再发送真实的大模型流式请求(很多第三方供应商会用 401/403/WAF 拦截这类探测,造成误判),而是对供应商base_url执行一次轻量 HTTP 探测:
- 收到任意HTTP 响应(200/4xx/5xx)即判定"可达"——证明端口通、网关存活;
- 仅 DNS 失败、连接被拒、TLS 错误、超时等网络级错误判定"不可达";
- 延迟 = 收到响应头的耗时,即 TTFB,是真实往返。
这一设计取舍在源码头部注释中有明确表述:"可达 ≠ 配置正确"——它刻意不验证鉴权或模型,因此不会被第三方供应商的鉴权拦截、模型校验误判为不可用;代价是它无法告诉你鉴权对不对、模型存不存在。相关实现见 services/stream_check.rs。
二、打开设置面板与执行检查
2.1 参数设置面板
入口为设置 → 高级 → 模型测试(界面文案已从 "model test" 统一更名为 "connectivity check")。该面板由 ConnectivityCheckConfigPanel.tsx 实现,包含三项数字输入和一段固定语义说明:
"连通检测仅探测供应商地址是否可达,不发送真实模型请求。收到任意响应即视为'可达'——这不代表鉴权或模型配置一定正确。"
2.2 手动测试
在供应商卡片上点击「测试」按钮即可触发单次检查,前端经由 useStreamCheck.ts 调用 Tauri 命令,并根据结果弹出三类提示(operational绿色成功 /degraded黄色警告 / 失败红色错误,错误提示会附带"无法建立连接(DNS / 连接 / TLS / 超时)。请检查 base_url 与网络"的排查指引)。
三、检查参数配置:默认值、取值范围与判定逻辑
3.1 参数总览
手册中的原始参数表如下(历史版本默认值):
| 参数 | 说明 | 文档默认值 | 文档范围 |
|---|---|---|---|
| 超时时间 | 单次请求超时 | 45 秒 | 10-120 秒 |
| 最大重试 | 失败后重试次数 | 2 次 | 0-5 次 |
| 降级阈值 | 响应超过此时间标记为降级 | 6000ms | 1000-30000ms |
以当前源码为准,由于探测对象从"真实模型请求"换成了只读响应头的小请求,超时默认值从 45 秒大幅缩短为 8 秒、重试从 2 次降为 1 次。后端StreamCheckConfig的默认实现见 services/stream_check.rs,并有单测test_default_config_uses_reachability_friendly_values锁定这三个值:
| 参数 | 字段 | 当前默认值 | UI 输入范围 | 说明 |
|---|---|---|---|---|
| 超时时间 | timeoutSecs | 8 秒 | 2-60 秒 | 单次探测超时;远小于旧版真实请求检查的 45 秒 |
| 最大重试 | maxRetries | 1 次 | 0-5 次 | 仅对超时类失败生效 |
| 降级阈值 | degradedThresholdMs | 6000ms | 1000-30000ms(步进 1000) | TTFB 超过该值判定"较慢",标为降级 |
UI 输入范围定义在 ConnectivityCheckConfigPanel.tsx(min/max属性)。保存时若输入为空则回落到上述默认值(parseNum逻辑,见同文件 L49-L60)。
手册中的两条调参经验仍然适用:超时设置过短可能导致误判,过长会延迟故障检测;网络不稳定时建议增加重试次数。
3.2 健康状态判定:一行代码讲清楚
状态判定逻辑在determine_status(services/stream_check.rs):
fn determine_status(latency_ms: u64, threshold: u64) -> HealthStatus { if latency_ms <= threshold { HealthStatus::Operational } else { HealthStatus::Degraded } }即:延迟 ≤ 阈值 → Operational(健康);延迟 > 阈值 → Degraded(降级,仍可用);探测失败 → Failed。注意边界条件是"小于等于"算健康——单测test_determine_status验证了latency == threshold时返回Operational。
3.3 重试策略:只重试"值得重试"的失败
check_with_retry对每次失败调用should_retry(services/stream_check.rs):
fn should_retry(msg: &str) -> bool { let lower = msg.to_lowercase(); lower.contains("timeout") || lower.contains("abort") || lower.contains("timed out") }只有超时 / abort 类网络抖动才会继续重试;连接被拒、DNS 失败等确定性错误立即返回,避免无谓等待。
四、可达性探测的底层实现
4.1 探测请求长什么样
核心函数probe_reachability(services/stream_check.rs)构造一个 GET 请求打到base_url:
GET base_url,带超时;请求头为accept: */*、accept-encoding: identity(不读 body,省流量);- 供应商级自定义 User-Agent(
meta.customUserAgent)原样复用——部分网关按 UA 白名单放行,探测与转发路径共用同一口径(custom_user_agent,L290-L295); send()在收到响应头时即返回,因此计时天然是 TTFB;- reqwest 对任何 HTTP 状态码都返回
Ok,只有网络级错误进Err——这正是"任何响应都算可达、只有连不上才算失败"语义的来源。单测test_build_result_any_http_status_is_reachable用 200/401/403/404/429/500/503 七种状态码逐一验证了该行为。
4.2 各应用的base_url提取规则
不同应用的settings_config结构互不相同,resolve_base_url(services/stream_check.rs)按应用分派:
| 应用 | 提取方式 |
|---|---|
| Claude / Codex / Gemini 等 | 走代理适配器get_adapter(app_type).extract_base_url() |
| Claude Desktop | ClaudeAdapter::extract_base_url() |
| OpenCode | options.baseURL优先;未填时按npm包名回退到 SDK 默认端点:@ai-sdk/openai→https://api.openai.com/v1,@ai-sdk/anthropic→https://api.anthropic.com,@ai-sdk/google→https://generativelanguage.googleapis.com;@ai-sdk/openai-compatible无默认端点,必须显式填写(resolve_opencode_base_url,L337-L359) |
| OpenClaw | camelCase 的baseUrl字段(L300-L314) |
| Hermes | snake_case 的base_url字段(L317-L331) |
| Pi | pi_config::provider_base_url() |
这一套提取逻辑有完整单测覆盖:test_resolve_opencode_base_url_explicit_wins(显式地址优先)、test_resolve_opencode_base_url_falls_back_for_known_npm(npm 回退)、test_resolve_opencode_base_url_errors_for_openai_compatible_without_url(缺 URL 报错)、test_extract_openclaw_base_url_missing_errors等(services/stream_check.rs)。
4.3 命令入口:单个与批量
Tauri 命令定义在 commands/stream_check.rs:
stream_check_provider(L17-L45):单个供应商检查,完成后把结果写入stream_check_logs表;stream_check_all_providers(L48-L112):批量检查,proxy_targets_only = true时只检查"当前激活供应商 + 故障转移队列"内的条目,且跳过所有category == "official"的供应商;get_stream_check_config/save_stream_check_config(L114-L127):读取/保存全局检查配置。
前端 API 封装在 lib/api/connectivity-check.ts,类型定义与 Rust 侧通过 serde 的camelCase命名一一对应。
五、测试结果与健康状态
5.1 状态表
手册中的状态表与当前实现完全一致,对应 Rust 侧HealthStatus枚举(services/stream_check.rs,序列化为小写字符串operational / degraded / failed):
| 状态 | 图标 | 说明 |
|---|---|---|
| 健康 | 🟢 | 响应正常,延迟在阈值内 |
| 降级 | 🟡 | 响应正常,但延迟超过阈值(仍可使用) |
| 不可用 | 🔴 | 请求失败或超时(DNS / 连接 / TLS / 超时) |
5.2 结果字段
StreamCheckResult返回:status(健康状态)、success、message(失败时为错误描述)、response_time_ms(TTFB 毫秒)、http_status(可达时的 HTTP 状态码)、tested_at(时间戳)、retry_count(实际重试次数)。
两个字段值得注意:model_used是为兼容旧版stream_check_logs表结构保留的字段,可达性检查下恒为空串;error_category在连通性检查中恒为None(不再细分错误类别)。早期"发送简短 prompt(如 'Hi')、限制最大输出 token(10-50)"的真实请求行为属于旧实现,现已移除——这也是"模型检查"更名为"连通检测"的原因。
六、与故障转移(Auto Failover)的关系
手册描述的集成方式为:开启代理后系统定期对故障转移队列中的供应商执行健康检查并更新状态,熔断供应商恢复时也通过检查验证可用性。
当前实现有一条关键不变量:连通性检查绝不触碰故障转移熔断器。源码注释明确写道(services/stream_check.rs):
一个返回 403/401 的供应商在本检查里算"可达",但它对真实流量是坏的。熔断器只由
proxy/forwarder.rs转发真实流量的成败驱动(被动)。两者职责分离——可达性检查回答"能不能到",真实流量回答"能不能用"。
也就是说:手动/批量连通性检查不会把被熔断的供应商切回线上;自动故障转移仍完全由 proxy/forwarder.rs 中真实转发流量的成败驱动。批量检查的proxy_targets_only参数则把探测范围收敛为"当前供应商 + 故障转移队列",避免无谓地全量打请求。
七、特殊场景与边界情况
7.1 官方供应商不参与探测
官方供应商(category == "official",如内置的 Claude / Codex / Gemini 官方预设)走 OAuth、base_url留空,没有可靠的探测目标。后端resolve_base_url对其直接报错(L168-L173),批量检查直接跳过(commands/stream_check.rs),前端在 ProviderCard.tsx 中也隐藏了连通性检测按钮——CHANGELOG 将此记为 "Skip Reachability Probes for Official Providers" 的纵深防御,并有回归测试覆盖。
7.2 GitHub Copilot 动态端点
Copilot 供应商的端点随 OAuth token 动态变化,命令层在探测前先通过 OAuth 管理器解析出base_url(按绑定账号或默认端点),以base_url_override传入服务层(resolve_copilot_base_url_override,commands/stream_check.rs);is_full_url的供应商已是完整地址,无需解析。
7.3 全局配置替代了逐供应商覆盖
早期版本允许在供应商表单里配置testConfig(每供应商独立的超时/重试/降级阈值)与测试模型。这些字段已在后续版本中从表单、元数据和连通性检查合并逻辑中全部移除——轻量base_url探测统一使用全局连通性检查配置;自动故障转移另有独立的代理超时与熔断设置(CHANGELOG "Provider Connectivity Configuration Simplified" 条目)。
八、常见问题(FAQ)
测试失败但实际可用
可能原因:探测目标(base_url)与实际调用路径不同;OpenCode 供应商未填options.baseURL且 SDK 包无默认端点;Hermes 配置缺base_url。解决方法:核对各应用字段命名(OpenClaw 是 camelCasebaseUrl,Hermes 是 snake_casebase_url);OpenCode 显式填写baseURL。
"可达"但真实请求报错
这是预期行为:可达性检查收到 401/403 也判定"可达",它不验证鉴权。若代理转发时报鉴权错误,请检查 API Key 与模型配置本身,而非依赖连通性结果。
延迟过高(降级)
可能原因:网络延迟、供应商服务器负载高、跨境链路慢。解决方法:调整降级阈值(1000-30000ms)、考虑更换更快的镜像端点、或检查本地网络。
频繁超时
可能原因:超时时间设置过短(当前默认仅 8 秒)、网络不稳定、供应商服务不稳定。解决方法:调大超时(UI 上限 60 秒)、增加重试次数(上限 5 次,仅对超时类失败生效)、检查本地网络连接与 TLS 配置。
九、注意事项
- 模型检查(连通检测)只发轻量 HEAD 语义请求(GET 后丢弃 body),配额消耗远低于旧版真实请求检查,但仍建议避免过于频繁的批量探测;
- 官方供应商不显示检测按钮,属正常现象,不要试图为其配置探测目标;
- 连通性结果不改变故障转移熔断状态——供应商能否被自动切回,只看真实流量成败;
- 不同供应商对自定义 User-Agent 的白名单策略可能不同,若转发正常但探测报 403,可尝试在供应商高级设置中配置与转发一致的 UA。
十、延伸阅读
- 用户手册原文:4.5 模型检查
- 后端探测服务与单测:services/stream_check.rs
- Tauri 命令层(含 Copilot 端点解析):commands/stream_check.rs
- 前端配置面板:ConnectivityCheckConfigPanel.tsx
- 前端 Hook 与 API 封装:useStreamCheck.ts、connectivity-check.ts
- 版本演进记录:CHANGELOG.md(v3.12.0 恢复 Stream Check UI、v3.13.0 扩展 OpenCode/OpenClaw 覆盖、v3.16.3 轻量化可达性探测)
【免费下载链接】cc-switchA cross-platform desktop All-in-One assistant for Claude Code, Codex, OpenCode, OpenClaw, Grok Build & Hermes Agent. Only official website: ccswitch.io项目地址: https://gitcode.com/GitHub_Trending/cc/cc-switch
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考