Skip to content

Latest commit

 

History

History
194 lines (134 loc) · 11.8 KB

File metadata and controls

194 lines (134 loc) · 11.8 KB

自动切换时机与任务连续性策略

文档定位

本文档只处理一个专题:当账号额度接近或显示耗尽时,Helper 应在什么时候切换账号,才能既避免任务中断,又充分使用当前账号在正在执行轮次中的可用尾部额度。

这项能力是商业可靠性门槛,但不应挤占整体产品化目标的主线。总目标只需引用本文档并把验收作为发布阻断条件。

问题背景

Codex 存在一个重要运行特性:即使额度界面已经显示接近耗尽或耗尽,只要当前轮任务仍在执行,生成、工具调用和收尾动作可能继续进行,直到当前轮自然结束、上下文压缩、明确错误或其它中断事件发生。

因此,最后 1% 不是简单的“立即不可用”信号。它可能承载一个相当长的在途任务。如果系统仅根据额度阈值或过早的额度错误提示立即写入新 auth 并重启 Codex,会产生以下损失:

  • 正在执行的任务被中断,用户看见无故换号或工作断流。
  • 当前账号仍能完成的在途轮次被浪费。
  • 新账号额度被更早消耗,账号池总有效工作量下降。
  • 审计将“保护任务”与“消耗额度”混成一次简单切换,无法解释损失原因。

当前实现基线

目前 Helper 已有任务保护骨架:

  • native-helper/CodexPlusLocalHelper.cs 中的自动切换循环先得到 usage 或运行日志触发,再调用 IsSafeToAutoSwitch(...);自动切换配置模型位于 native-helper/AutoSwitchConfig.cs
  • CodexLogRuntimeMonitor 会读取 Codex 日志,识别 activecoolingidleunknown 与待处理触发。
  • quotaauthaccount_disabled 当前都属于硬触发;在 cooling 状态下,硬触发可以继续进入换号路径。
  • Worker 负责确认切换条件、选候选账号和保存审计;真正写 auth、重启 Codex 的动作在 Helper。

这说明代码已在尝试避免任务中切换,但仍需专门验证并修正一个核心语义:

额度信号只能说明“需要准备换号”,不能单独证明“当前轮已不能继续运行”。

产品原则

  1. 自动切换必须发生在安全轮次边界,而不是单纯发生在额度阈值命中时。
  2. 正在产生有效输出或执行工具的任务,默认不因额度低、额度为零或用量接口异常而被强制切换。
  3. auth 已明确失效且当前请求已经失败结束时,可以尽快切换;仍不能通过破坏在途任务来推测其已失败。
  4. 状态不确定时采用保守策略:延后自动切换,并允许用户手动确认。
  5. 用户必须能看懂为什么仍未切换、什么时候会切换、最终节约或消耗了什么。

需要先查清的事实

实现调整前,应通过真实 Codex 桌面运行和可重复测试获取以下证据,不凭关键词猜测:

  • 当额度为 1%0% 或用量接口返回限流时,当前轮是否仍持续产生输出、工具调用或完成事件。
  • 额度耗尽提示与 response.completedresponse.failedturn.completed、上下文压缩、用户中止之间的事件顺序。
  • app-server 状态、logs_2.sqlite 状态与窗口可见任务状态在长任务期间是否一致。
  • 当前实现发生误切时,Helper 判定的 statetriggerTypependingSwitchAtSafeToSwitch 和实际写 auth 时间。
  • 是否存在日志静默间隔,使一个仍会继续执行的任务被误判为 coolingidle

所有排查记录不得包含 access token、refresh token 或完整 auth 内容。

目标状态机

自动切换应将“触发”与“执行”彻底分开。

状态 含义 允许动作
healthy 当前额度和任务状态正常 周期检查,不写切换审计流水
tail_observing 额度低于预警阈值,但没有确定失败 显示预警,准备候选,不切换
switch_pending 已出现额度耗尽、限流或授权异常信号 记录待切原因,持续观察当前轮
draining_active_turn 待切状态下当前任务仍活跃或可能继续 禁止自动写 auth 与重启
boundary_confirming 检测到轮次结束或明确失败,正在短时确认稳定边界 再次核验当前 auth、任务状态和候选
switching 边界已确认,开始切换 执行 payload、写 auth、恢复 Codex
held_unknown 无法可靠判断轮次边界 不自动切,提示用户检查或手动操作
cooldown 已切换或用户取消后保护期 阻止重复换号

切换判定规则

额度阈值

  • fiveHourThresholdoneWeekThreshold 只进入 tail_observingswitch_pending,不得直接成为写 auth 的充分条件。
  • 额度接近阈值时可预取候选摘要,但不得提前下发可写入的明文 auth,也不得提前更新 last_switch_at

额度耗尽或限流

  • 发现额度耗尽、429 或标准 usage-limit 信号时,记录待切原因。
  • 若当前轮仍是 active、工具执行中、流式输出中或状态无法确认,则进入 draining_active_turn
  • 只有出现可信的任务完成、任务失败、用户中止或已证实不再可继续的边界事件,并通过短时间稳定确认后,才允许自动切换。
  • 可见的额度提示、usage 百分比或短暂日志静默,不能单独作为轮次结束证据。

授权失效与账号停用

  • 授权失效、账号停用属于更高优先级触发,但仍应先确认当前轮已经结束或失败。
  • 若错误事件自身明确结束当前请求,例如可信的 response.failed,则可以在短确认期后进入切换。
  • 若只是后台预检查失败,不得中断正在成功运行的当前轮。

手动切换

  • 用户主动点击切换时,如存在活跃任务,先给出明确提示:“当前任务仍在运行,立即切换可能中断本轮并浪费剩余额度”。
  • 提供“等待当前任务完成后自动切换”和“仍然立即切换”两个清晰选择。
  • 强制切换必须记录为用户动作,不能伪装成智能切换策略执行。

建议设置项

设置页可在“智能切换”中增加高级项,默认值保护任务连续性:

  • switchTimingMode: after_turn | immediate_when_idle | manual_confirm
  • protectActiveTailUsage: 默认 true,表示额度耗尽后仍等待当前轮结束。
  • boundaryConfirmSeconds: 安全边界稳定确认时间,默认建议 1530 秒,需基于真实测试确定。
  • unknownStateAction: hold | notify_only,默认 hold
  • allowManualForceSwitch: 默认 true,但必须展示风险确认。

after_turn 应为默认和推荐模式。不能把“立即切换”作为普通用户无感知的默认行为。

UI 与文案

Helper 主窗口和云控制台应显示可理解的阶段,而不是笼统的“切换中”:

  • 额度接近上限,继续观察当前任务
  • 额度已耗尽,正在保护当前运行任务
  • 任务已结束,正在确认安全切换时机
  • 状态无法确认,自动切换已暂停
  • 安全边界已确认,正在切换账号

界面应展示:

  • 待切原因,例如 5H 额度耗尽授权失效
  • 当前保护状态,例如 当前轮仍在执行,不会打断
  • 待切开始时间和最近一次可信任务事件。
  • 实际切换发生时间与目标账号。

账号详情不应展示每次轮询检查;只展示一次触发、一次延后摘要和最终切换结果。

审计与可观测性

新增或规范化审计事件,保证运营侧能判断是否误切:

  • auto-switch-armed: 达到触发条件,仅建立待切计划。
  • auto-switch-deferred-active-turn: 因当前轮仍活跃而延后,按一次保护周期聚合,不按轮询频率刷屏。
  • auto-switch-boundary-confirmed: 已找到可信安全边界。
  • auto-switch-switched: 实际完成切换,携带触发类型和保护持续时长。
  • auto-switch-manual-forced: 用户明确强制切换。
  • auto-switch-held-unknown: 无法判断状态而暂停。

需要记录但可脱敏的字段:

  • accountIddeviceKeyHinttriggerTypetriggerAt
  • runtimeStateruntimeSourcelastTaskEventboundaryEvidence
  • protectedDurationMsswitchedAttargetAccountId

严禁记录 token、完整 auth payload 或第三方响应中的秘密数据。

实现落点

Helper

  • 将现有“硬触发在 cooling 时可切换”的判定重构为安全边界判定,不把 quota 信号本身视为已安全。
  • 扩展 CodexLogRuntimeMonitor,明确标记在途轮次、结束边界和不确定状态的证据来源。
  • 将 pending switch 持久化到本机配置或状态文件,Helper 重启后不丢失待切原因,但仍要重新核验任务状态。
  • 切换前再核验当前 auth、目标账号、Codex 状态与触发有效性。

Worker

  • 将“触发资格”和“切换提交”分离:Worker 可以确认候选,但只有 Helper 报告安全边界并执行成功后才记录切换完成。
  • 对延后事件做去重和聚合,避免 D1 审计被轮询写满。
  • 返回候选时保留触发和边界证据字段,以便审计追踪。

控制台

  • 设置页暴露切换时机策略与任务保护开关。
  • Helper 诊断区展示 保护当前任务等待安全边界状态未知暂停
  • 设备页自动切换阶段卡已落地:基于 Helper 上报的 pending_switch_reasonsafe_to_switch、最近任务事件和 auto_switch.last_result,展示触发、边界证据、最近结果和下一步动作,不新增后台轮询或 D1 写入。
  • 手动切换保护弹窗已接入账号详情、账号列表和智能选择入口:当 Helper 上报 safe_to_switch === false 时默认拦截切换,用户可选择等待安全边界后自动切换,或明确强制立即切换;强制路径会写入 manualForce 审计元数据。
  • Helper 0.4.7 已将待切原因、触发类型、来源、开始时间和本机 auth 指纹持久化到本机配置;Helper 重启后控制台显示“恢复待切计划”,但持久记录不会直接授权换号,必须通过新一轮实时额度和安全边界核验后才可写入 auth。
  • 运行记录按业务语义展示聚合结果,不把预警显示成已切换。

测试方案

必须覆盖真实运行验证与自动测试。

真实场景

  1. 额度剩余约 1% 时启动长输出任务,确认触发后仍不会中断当前轮。
  2. 额度显示 0% 但流式输出或工具调用仍继续,确认 Helper 只进入保护状态。
  3. 当前轮自然完成后,确认只切换一次并恢复 Codex。
  4. 当前轮因可信 usage-limit 或 auth 错误明确失败后,确认能够尽快切换。
  5. 上下文压缩、用户中止、工具等待确认、日志短暂静默等场景不发生误切。
  6. Helper 重启或离线恢复后,待切状态可解释且不会重复消耗账号。

自动化

  • 状态机单元测试:事件序列输入后只能在安全边界输出 shouldSwitch=true
  • 回归测试:阈值命中、usage 错误、response.completedresponse.failed、未知状态、手动强制切换。
  • Worker 审计测试:延后去重、完成切换后才写成功、没有 token 泄露。
  • UI 测试:保护状态、风险确认、聚合审计文案可见且不刷屏;scripts/verify-panels-ui.cjs 覆盖保护当前任务、安全边界已确认和自动切换失败三类阶段。

验收标准

  • 额度低或显示耗尽时,正在执行的当前轮默认不会被自动切换打断。
  • 只有可信轮次边界确认后,自动切换才改写 auth 并重启 Codex。
  • 状态无法确认时不会冒险切换,用户可看见暂停原因和手动选项。
  • 真实测试证明最后尾部额度能被当前任务继续使用,不因提前换号浪费。
  • 自动切换相关日志、审计和界面文案能区分触发、保护、确认边界和完成切换。
  • 高频状态检查不会污染账号详情或运营审计。