异步探测与密钥可用性运维服务
1. 建设目标
该服务将 CPA 每次真实账号请求视为一次被动探测结果,在不阻塞请求采集链路的前提下,持续评估公益密钥和 CPA 账号的可用性,并按全局策略或单密钥策略执行以下动作:
- 根据成功、失败和连续健康度自动提升或降低路由优先级。
- 在有效、待观察、无效、禁用、过期等状态之间自动调整。
- 将账号状态和优先级同步到 CPA,完成账号上线、下线和运营路由控制。
- 对有明确过期时间的账号直接执行过期禁用;对无过期时间的账号持续通过真实请求观察。
- 在
/charitable词元中心提供策略配置、探测统计和动作审计界面。
2. 总体架构
CPA 请求上报
-> collector 写入 usage_events
-> collector 将同批事件投递到有界异步探测队列
-> probe.Manager 归一化并生成 probe_results
-> 匹配 cpa_auth_detail 与单密钥 probe_policy
-> 计算连续成功/失败、状态、优先级和续期动作
-> 更新 cpa_auth_detail
-> 可选调用 CPA Management API 上下线账号
-> 写入 probe_action_logs 供统计和审计探测队列与请求采集解耦。队列已满时不会阻塞 collector,而是记录 dropped_batches;运维人员可在探测统计页观察队列深度、丢弃批次和最后错误。
3. 数据模型
3.1 渠道与提供商基础目录
cpa_channel_info 与 cpa_provider_info 均新增 description TEXT NOT NULL DEFAULT '',用于记录用途、来源和运营说明。迁移会对已有数据库幂等补列,并保留已有自定义记录。
系统启动时固定校准以下渠道记录。如果 channel_id=1/2/3 已被其他记录占用,会更新为预置定义并恢复为有效状态;其他 ID 的自定义渠道保持不变。
channel_id | 渠道名称 | 状态 |
|---|---|---|
1 | 内置渠道商 | 1 |
2 | 官方渠道商 | 1 |
3 | 第三方渠道商 | 1 |
系统同时按照规范化后的 base_url 补齐 CPA 原生提供商目录。匹配时忽略大小写、首尾空白和末尾 /,不依赖 provider_id 或名称;已经存在相同端点时保留原记录的名称、状态、渠道、策略和参数。当前预置端点包括 Codex、OpenAI、Anthropic、Gemini、Gemini CLI、Grok 和 Kimi,缺失项默认归属“官方渠道商”。
3.2 cpa_auth_detail 扩展字段
| 字段 | 类型 | 语义 |
|---|---|---|
expires_at_ms | INTEGER NULL | Unix 毫秒过期时间;空值或小于等于 0 表示没有明确过期时间。 |
probe_policy | TEXT NOT NULL DEFAULT '{}' | 单密钥 JSON 策略;空对象表示继承全局配置。 |
3.3 probe_results
每条采集请求最多形成一条去重后的探测结果,保存请求时间、账号标识、密钥哈希、关联密钥和提供商、模型、端点、状态码、延迟、错误及本次自动动作。event_hash 唯一索引用于避免重复消费。
3.4 probe_action_logs
保存状态调整、优先级调整、过期、续期和 CPA 上下线等副作用的执行结果。动作日志同时记录成功标记和错误信息,便于区分“策略已命中”和“外部 CPA 调用已成功”。
3.5 服务配置
全局配置以 settings.probe_service_config_v1 保存,主要参数如下:
enabled:探测服务总开关。autoPriorityEnabled:自动调整密钥优先级。autoStatusEnabled:自动调整密钥状态。autoCpaAccountEnabled:状态变化时自动推送 CPA 上下线。renewExpiryOnSuccess:成功使用后自动延长明确过期时间。renewalSeconds:每次成功续期时长。windowSeconds:统计与策略观察窗口。failureThreshold/recoveryThreshold:连续失败和恢复门槛。priorityBoost/priorityPenalty:成功和失败的优先级变化量。minPriority/maxPriority:自动优先级边界。maxActionsPerBatch:单批次允许执行的最大副作用数量。
提供商 probe_policy 是策略运营的主要配置单元,可选择继承全局、自定义或不参与探测,并可覆盖自动优先级、自动状态、CPA 推送、成功续期及各项阈值。历史单密钥 probe_policy 继续保留为兼容覆盖层。
策略解析顺序为:
系统全局策略 -> 提供商策略 -> 历史单密钥策略提供商设置为“不参与探测”时,其下密钥不能通过单密钥策略反向启用。
/charitable/token 的提供商新增与编辑面板提供代理服务器选择器,数据来源为 /charitable/proxies 中状态为有效的代理记录。选中后将代理 URI 和记录 ID 分别写入提供商 param.proxy_url、param.proxy_id;也允许直接输入自定义代理 URI。
提供商策略选择“继承全局”时,策略抽屉会展示当前系统全局策略;切换为自定义时以该全局策略作为编辑初始值。策略页查看提供商密钥后,可直接切换到对应密钥编辑面板,提供商编辑同样通过页内回调完成,不依赖重复 URL 导航。
策略运营和提供商列表中的“查看密钥”使用原地抽屉展示,不切换 /charitable/token 的主标签。点击密钥编辑时,密钥列表抽屉暂时让位给按密钥 ID 重新查询的编辑抽屉;保存或取消后恢复原密钥列表,并在保存成功后重新加载列表。策略页“编辑提供商”同样按提供商 ID 重新查询并在独立抽屉中保存,完成后直接更新当前策略列表和已打开的提供商信息。
密钥批量探测会为每个请求独立生成随机大整数求和题目,避免所有探测请求重复使用固定提示词;单密钥探测仍允许在面板中手动调整提示词。
密钥导入会根据每条配置中的 type / provider 和 base_url 自动关联提供商:优先按规范化 Base URL 匹配,其次在没有 Base URL 时按类型别名匹配;仍未找到且存在可用 Base URL 时,自动创建归属官方渠道的提供商。所有导入密钥初始状态统一为未知(0),等待真实使用或探测结果确认。
批量探测完成后集中写回状态:成功结果设置为有效(1);HTTP 401、402、403 直接设置为失效(-1);其他失败设置为未知(0)继续观察。人工禁用(-2)的密钥不会被批量探测覆盖。
密钥主表除“探测所选”外,还支持“探测全部筛选结果”。前端会保留当前搜索、提供商、状态和优先级过滤条件,以每页 500 条循环读取全部分页结果后执行探测,不受当前页勾选范围限制。策略运营和提供商页面共用的密钥明细抽屉同时提供单行探测与当前提供商全部密钥探测。
多密钥导入按每条凭证自身的类型和显式 Base URL 分别解析提供商。显式 Base URL 优先精确匹配;没有显式地址时优先按类型别名匹配,避免 Gemini、AI Studio 等共享规范端点的不同凭证类型被错误合并。导入完成后立即将本次匹配或创建的提供商写入前端缓存,再刷新密钥列表,因此新记录会直接展示提供商名称而不是临时 ID。
4. 状态与优先级规则
密钥状态约定:
| 状态值 | 含义 | 自动探测行为 |
|---|---|---|
>= 1 | 有效 | 继续观察;失败达到门槛后可转为探测管理的失败状态。 |
0 | 未知、待观察 | 保留在探测范围内;成功达到恢复门槛后可转为有效。 |
-1 | 无效 | 视为人工或业务状态,不由探测自动恢复。 |
-2 | 禁用 | 不作为可路由账号使用。 |
-3 | 已过期 | 由过期扫描直接设置。 |
< -99 | 探测管理的 HTTP 失败状态 | 例如 HTTP 401 对应 -401;连续成功达到门槛后可恢复为 1。 |
自动状态只恢复“待观察”或探测产生的失败状态,不覆盖 -1、-2、-3 等人工或明确生命周期状态。优先级调整始终限制在 minPriority 与 maxPriority 之间。
5. 过期时间策略
5.1 明确过期时间
- 后台扫描发现
expires_at_ms <= 当前时间后,直接将状态设置为-3。 - 不再等待新的失败请求,也不再主动探测该账号。
- 开启 CPA 自动推送时,同步将对应账号下线。
5.2 未设置过期时间
- 不做时间驱动的直接禁用。
- 每次真实请求继续作为被动探测,依据连续成功和失败调整健康状态。
- 状态未知
0表示待观察,可通过成功请求恢复为有效。
5.3 成功续期
开启 renewExpiryOnSuccess 后,成功使用可按 renewalSeconds 延长过期时间。该能力适用于“持续使用即续期”的账号,不建议用于具有供应商硬性到期日的凭证。
6. CPA 推送与安全边界
- 自动下线只在能够解析到真实 CPA auth-file 快照时执行,避免根据模糊名称误操作账号。
- “保存并推送 CPA”会将当前密钥策略中的优先级和禁用状态同步到 CPA 提供商配置。
- CPA 调用失败不会回滚已经记录的探测结果;失败会写入动作日志,供人工重试或排查。
- 建议先仅开启探测统计,观察阈值效果后再开启自动状态、自动优先级和 CPA 自动推送。
7. 前端入口
/charitable/policy:提供商策略运营。按提供商统一配置其下密钥的自动探测和高可用策略,并可通过密钥抽屉查看匹配账号。/charitable/token的提供商页:每个提供商提供“查看密钥”操作,通过抽屉分页展示当前关联密钥,并可继续进入密钥编辑。/charitable/probe:探测统计。查看服务状态、汇总指标、账号健康、探测明细和动作日志。/system/config:系统设置。开启或关闭探测服务,并配置全局阈值、优先级、续期和 CPA 推送策略。
8. HTTP API
| 方法 | 路径 | 用途 |
|---|---|---|
GET / PUT | /api/charitable/probe/config | 查询或保存全局探测配置。 |
GET | /api/charitable/probe/status | 查询队列、处理量、动作量和最后错误。 |
GET | /api/charitable/probe/summary | 查询指定窗口的探测汇总。 |
GET | /api/charitable/probe/results | 分页查询探测结果,支持账号、密钥、提供商、成功状态和时间过滤。 |
GET | /api/charitable/probe/stats | 查询按账号或密钥聚合的健康统计。 |
GET | /api/charitable/probe/actions | 分页查询自动动作日志。 |
密钥详情的过期时间和 probe_policy 继续通过 charitable 密钥 CRUD 接口读取和保存。
9. 请求监控 SSE 联动
/realtime/request 使用 /v0/management/usage/realtime/stream 建立带管理密钥认证的 SSE 长连接。服务端在 usage_events 最新游标增长时发送 usage 事件,前端收到通知后按当前筛选条件重新加载请求页。
- SSE 只发送变更游标和时间,不在长连接中复制完整请求数据。
- 前端对 SSE 通知做 300ms 防抖,并串行合并高频刷新,避免并发 REST 覆盖结果;SSE 触发的表格刷新使用 quiet 模式,不打断表格 loading 状态。
- 实时页默认不请求
/v0/management/usage/summary,只按当前筛选拉取/v0/management/usage/realtime分页;筛选下拉在无 summary facets 时从当前 realtime 行推导。 - 监控中心等需要汇总卡片的页面仍会通过
useUsageData默认拉取 summary。 - 连接一旦断开(关闭、错误、空闲超时)会立即进入重连循环,并按 1 秒到 15 秒有界退避;网络恢复(
online)或标签页重新可见且未处于 live 时会强制打断退避并立刻重连。 - 前端对 SSE 施加 45 秒空闲超时(超过服务端 15 秒 heartbeat),心跳注释也算活性,避免半开连接假活。
- 页面“刷新配置”按钮只刷新账号配置、元数据、模型价格和 API Key 别名,不再手工刷新请求记录。
- 请求状态调试 JSON 会解析
raw_json或rawJson对象,并将其字段提升到一级;冲突时内部原始字段覆盖外层字段。
10. 运维与验证建议
- 首次启用后先确认探测统计中的队列深度能够回落、
dropped_batches持续为 0。 - 使用较大的失败门槛观察真实流量,避免瞬时网络错误批量下线账号。
- 开启 CPA 自动推送前,核对 auth index、auth-file 和提供商关联是否准确。
- 对供应商明确到期的账号设置
expires_at_ms,并关闭不符合业务规则的成功续期。 - 定期检查动作日志中的失败记录和 Usage Service 的最后错误。
建议验证命令:
npm run type-check
cd services
env GOCACHE=/tmp/agent-tools-gocache CGO_ENABLED=0 go test ./internal/core/store ./internal/core/httpapi ./internal/core/probe