From 6a2760bd03267177835e9b8159c3e52ac9496c8e Mon Sep 17 00:00:00 2001 From: vibe-code Date: Thu, 13 Aug 2026 06:08:26 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=AF=8F=E6=97=A5=E6=B6=88=E8=80=97?= =?UTF-8?q?=E4=BB=BB=E5=8A=A1=E8=A7=A6=E5=8F=91=E6=97=B6=E7=82=B9=2023:00?= =?UTF-8?q?=20=E6=94=B9=E4=B8=BA=2023:20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - volc_limit_sync:_maybe_trigger_daily_consume 改分钟级判断(23:20 起触发), 线程内 0-10 分钟随机抖动保留,实际执行窗口 23:20~23:30 - volc_consume:main 手动运行门卫同步 23:20 窗口(--force/--dry-run 不受限) - docs/volc-quota-notes §9:consume clamp2 余额快照滞后在最新思路(§4.2 量化 L=149,616)下确认为必改项,方向待定;消耗任务口径同步收紧标记 [x] - 全仓文档/注释 23:00 统一同步为 23:20(AGENTS/README/docker-compose/两 docs) --- AGENTS.md | 10 +++++----- README.md | 2 +- docker-compose.yml | 2 +- docs/merge-auto-consume.md | 26 +++++++++++++------------- docs/volc-quota-notes.md | 16 +++++++++------- volc_consume.py | 16 +++++++++------- volc_limit_sync.py | 9 +++++---- 7 files changed, 43 insertions(+), 38 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index a35d760..9bcb3d1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,10 +5,10 @@ ## 1. 项目上下文 此仓库包含用于 **LiteLLM** 的自定义回调脚本,核心功能是限制模型用量(特别是火山引擎/VolcEngine)。 - **核心逻辑**:通过 `quota_interceptor` 拦截请求,并通过 `DailyLimitHandler` 记录用量到 Redis。后台 `active_monitor_loop` 守护线程定期从火山引擎 API 同步配额到 Redis。 -- **架构**:主逻辑在 `volc_limit_sync.py`(限额 + 同步守护 + 每日消耗调度),消耗任务拆分在 `volc_consume.py`(每日北京时间 23:00 由守护线程延迟导入触发;见 `merge-auto-consume.md`)。LiteLLM 通过 `config.yaml` 中 `callbacks: ["volc_limit_sync.proxy_handler_instance"]` 加载回调,模块加载时自动启动后台同步线程。 +- **架构**:主逻辑在 `volc_limit_sync.py`(限额 + 同步守护 + 每日消耗调度),消耗任务拆分在 `volc_consume.py`(每日北京时间 23:20 由守护线程延迟导入触发;见 `merge-auto-consume.md`)。LiteLLM 通过 `config.yaml` 中 `callbacks: ["volc_limit_sync.proxy_handler_instance"]` 加载回调,模块加载时自动启动后台同步线程。 - **关键文件**: - - `volc_limit_sync.py`: **核心文件**,包含同步守护线程(`active_monitor_loop`)、LiteLLM 回调(`DailyLimitHandler` + `quota_interceptor`)、限额计算(v5:`stable_limit = usage + balance − BUFFER`,`effective_limit = min(2M 采集配额, stable_limit)`;当日初始免费池守恒,扣费判定见 `docs/volc-quota-notes.md`)、模型屏蔽/恢复、以及每日 23:00 消耗任务调度(`_maybe_trigger_daily_consume`,SETNX 去重)。 - - `volc_consume.py`: 消耗任务模块(每日 23:00 触发;手动 `python volc_consume.py --dry-run` / `--force`)。复用 volc_limit_sync 的拉包/归一化/用量记账设施,直连 Ark API 洗即将过期的免费资源包(防 30 天过期蒸发)。 + - `volc_limit_sync.py`: **核心文件**,包含同步守护线程(`active_monitor_loop`)、LiteLLM 回调(`DailyLimitHandler` + `quota_interceptor`)、限额计算(v5:`stable_limit = usage + balance − BUFFER`,`effective_limit = min(2M 采集配额, stable_limit)`;当日初始免费池守恒,扣费判定见 `docs/volc-quota-notes.md`)、模型屏蔽/恢复、以及每日 23:20 消耗任务调度(`_maybe_trigger_daily_consume`,SETNX 去重)。 + - `volc_consume.py`: 消耗任务模块(每日 23:20 触发;手动 `python volc_consume.py --dry-run` / `--force`)。复用 volc_limit_sync 的拉包/归一化/用量记账设施,直连 Ark API 洗即将过期的免费资源包(防 30 天过期蒸发)。 - `config.yaml`: LiteLLM 配置文件,包含模型定义和回调配置。 - `docker-compose.yml`: 容器编排,仅挂载 `config.yaml`、`volc_limit_sync.py`、`volc_consume.py`。 - `docs/`: 设计文档与额度机制速查(`merge-auto-consume.md` 消耗任务/限额设计、`volc-quota-notes.md` 火山额度机制事实基础、`merge-volc-limit-into-newapi.md` 未实施的迁移方案)。 @@ -45,9 +45,9 @@ pip install litellm redis requests ```bash # 需要 HUOSHAN_API_KEY(Ark API Key)与 HUOSHAN_BASE_URL python volc_consume.py --dry-run # 只计算每个模型的消耗计划,不发送请求 - python volc_consume.py --force # 立即执行(绕过 23:00 时间限制) + python volc_consume.py --force # 立即执行(绕过 23:20 时间限制) ``` - 每日 23:00 由 `volc_limit_sync.py` 守护线程自动触发(`consume:last_run:{日期}` 去重)。 + 每日 23:20 由 `volc_limit_sync.py` 守护线程自动触发(`consume:last_run:{日期}` 去重)。 ## 3. 代码风格指南 (Code Style) diff --git a/README.md b/README.md index ab1b681..e370322 100644 --- a/README.md +++ b/README.md @@ -237,7 +237,7 @@ litellm-config/ ├── config.yaml # 纳入监控模型的主入口 ├── docker-compose.yml # 容器编排(挂载 config.yaml / volc_limit_sync.py / volc_consume.py) ├── volc_limit_sync.py # 当前生效的监控、同步、限额脚本(v4 冻结快照限额) -├── volc_consume.py # 每日 23:00 自动消耗任务(防免费资源包过期蒸发) +├── volc_consume.py # 每日 23:20 自动消耗任务(防免费资源包过期蒸发) ├── AGENTS.md # 仓库 Agent 协议(代码风格/运行/提交规范) ├── README.md ├── docs/ diff --git a/docker-compose.yml b/docker-compose.yml index 71e7dba..3edd339 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,7 +9,7 @@ services: - ./config.yaml:/app/config.yaml # 【关键】挂载自定义脚本到容器根目录 - ./volc_limit_sync.py:/app/volc_limit_sync.py - # 自动消耗任务(每日 23:00 由 volc_limit_sync 调度触发) + # 自动消耗任务(每日 23:20 由 volc_limit_sync 调度触发) - ./volc_consume.py:/app/volc_consume.py # 消耗任务语料:宿主真实系统日志(只读) - /var/log:/host-var-log:ro diff --git a/docs/merge-auto-consume.md b/docs/merge-auto-consume.md index 69a715e..6a5271d 100644 --- a/docs/merge-auto-consume.md +++ b/docs/merge-auto-consume.md @@ -1,7 +1,7 @@ # 火山免费额度自动消耗:auto_consume 合入 volc_limit_sync 设计方案 > 日期:2026-08-11 -> 背景:`/root/workspace/huoshan-api-auto_consume/`(自动消耗脚本)与 `/root/workspace/litellm-config/`(LiteLLM 限流回调 + 火山余额同步)合并,使每日 23:00 自动消耗免费额度,防止资源包过期蒸发,避免真实扣费。 +> 背景:`/root/workspace/huoshan-api-auto_consume/`(自动消耗脚本)与 `/root/workspace/litellm-config/`(LiteLLM 限流回调 + 火山余额同步)合并,使每日 23:20 自动消耗免费额度,防止资源包过期蒸发,避免真实扣费。 > 前置文档:《merge-volc-limit-into-newapi.md》(2026-08-05,方向相反:把 volc_limit_sync 功能迁往 new-api,未实施)。 --- @@ -12,7 +12,7 @@ ``` 每天任意时刻 真实用户 + 消耗任务 的消耗 = 当日采集量(火山实时统计,每模型单独计算) -每天 23:00 CST 消耗任务补齐缺口(可选,见 §4) +每天 23:20 CST 消耗任务补齐缺口(可选,见 §4) 次日 00:00 起 火山按昨日采集量发放资源包(不定时,可能延迟到上午) ``` @@ -122,7 +122,7 @@ API 请求的 token 消耗不可精确控制(输入按字符估算、输出不 **为什么均分优于一次性洗光**(在新模型下): 1. **最小干扰真实用户**:只在额度即将死亡(进窗口)后才每天接管 1/D,其余留给真实用户 → 免费额度利用率最大化,空窗风险最小 2. **请求负载分摊**:每天 3–5 次请求(而非 25–40 次),速率伪装自然,风控友好 -3. **余额下降平缓**:23:00 后余额仍保留大部分 → 扣费只发生在余额真正归零时,均分下脚本几乎不可能制造归零 +3. **余额下降平缓**:23:20 后余额仍保留大部分 → 扣费只发生在余额真正归零时,均分下脚本几乎不可能制造归零 4. **FIFO 自动一致**:效期统一 30 天 → 获得早 = 过期早 → 队列头部 = 窗口内包,计划与执行不打架(分包明细仅用于定总量) **与日返还循环的契合**:旧脚本本就是"每天跑一次、分 D 天烧完"的设计;自动化后每天重算(余额/天数变化 → div 递减 → 当日份额递增),节奏天然吻合。9 天窗口 + 1.1 系数 + 24h 冲刺:只要每天触发成功,任何进窗口的包都赶在过期前清空。 @@ -149,9 +149,9 @@ daily_usage ≥ 2M → 停(采集上限,再烧不返还 ### 3.6 触发调度 -- 北京时间 **23:00 ± 随机抖动**(防固定时间被风控识别),Redis `consume:last_run:{日期}` 去重,当天未跑过才触发 +- 北京时间 **23:20 ± 随机抖动**(防固定时间被风控识别),Redis `consume:last_run:{日期}` 去重,当天未跑过才触发 - 守护线程(`active_monitor_loop` 已有主循环)检测时点 → spawn daemon 线程执行,不阻塞主循环 -- 23:00 的时点语义:距 00:00 采集结算 1 小时,此时窗口内包状态已定(真实用户当天用量已发生),洗包目标最准 +- 23:20 的时点语义:距 00:00 采集结算 40 分钟,此时窗口内包状态已定(真实用户当天用量已发生),洗包目标最准 --- @@ -193,7 +193,7 @@ HARD_DAILY_LIMITS → 统一 2M(采集上限;doubao-seed-code-preview-251028 - 返还:balance 跳涨 → 池涨 → 限额次日自动解锁(无需 RECHARGE)✓ - 过期:balance 跳跌 → 池跌 → 限额收紧(无需 DROP)✓ - 余额滞后(实测 20 万内不动):池虚高 ≤ 20 万 → BUFFER 30 万覆盖 ✓ -- 消耗任务 23:00 烧(usage 计入):池不变 → 滞后窗口内限额偏松 ≤ BUFFER ✓ +- 消耗任务 23:20 烧(usage 计入):池不变 → 滞后窗口内限额偏松 ≤ BUFFER ✓ - 余额耗尽:池 − 30 万 ≤ 0 → 限额 0 → 全拦,扣费结构性杜绝 ✓ **fallback(stable_limit 缺失窗口)**:免费模型余额常态 < 2M(昨日采多少返多少),若 dynamic 缺失(首次同步前/新增模型/TTL 过期)时裸奔到静态 2M,余额不足的模型会被烧穿扣费。故 fallback 用保守值 `FALLBACK_LIMIT = min(static, 90万)`(env 可调),宁可短时收紧不可裸奔。 @@ -211,7 +211,7 @@ HARD_DAILY_LIMITS → 统一 2M(采集上限;doubao-seed-code-preview-251028 ### 4.4 空窗期防扣费闭环(消耗量计入 daily_usage 的理由) -23:00 消耗任务把额度洗掉后余额可能很低,此时真实用户请求必须被拦。拦截依据 = `daily_usage vs effective_limit`: +23:20 消耗任务把额度洗掉后余额可能很低,此时真实用户请求必须被拦。拦截依据 = `daily_usage vs effective_limit`: - **若消耗量不计入 daily_usage**:限额看到"真实用户才用 100k,未超限" → 放行 → **扣费** - **若计入**:daily_usage ≈ 真实 + 消耗任务 ≈ 2M → 超限拦截 → 空窗期安全 @@ -266,7 +266,7 @@ effective_limit = min(HARD_DAILY_LIMITS, stable_limit) ``` litellm-config/ ├── volc_limit_sync.py # 现状:签名/拉取/归一化/限额/守护线程 -│ # 新增:23:00 调度钩子(检测时点 + spawn 消耗线程 + 去重) +│ # 新增:23:20 调度钩子(检测时点 + spawn 消耗线程 + 去重) ├── volc_consume.py # 新增:消耗任务(从 auto_consume 移植 + 改造) │ ├── load_base_logs / generate_dynamic_payload / _sanitize_line # 原样 │ ├── calculate_plan # 保留 + 两个 clamp @@ -291,7 +291,7 @@ litellm-config/ ### 5.3 消耗任务执行流程 ``` -23:00 触发(守护线程) +23:20 触发(守护线程) → consume:last_run:{今天} 已存在? → 跳过 → 遍历 MONITORED_MODELS(config.yaml 发现的 volcengine 模型) → fetch_all_packages(复用)→ 按模型过滤窗口内包 @@ -313,7 +313,7 @@ litellm-config/ | 语料生成/脱敏/合成 | auto_consume | 原样移植 | | 速率伪装/退避/失败上限/6h 上限 | auto_consume | 原样移植 | | `_record_usage` 的 key 计算(`get_usage_key`) | volc_limit_sync | 消耗任务复用做 INCR | -| 调度钩子 | 新增 | 守护线程 23:00 检测 + 去重 | +| 调度钩子 | 新增 | 守护线程 23:20 检测 + 去重 | --- @@ -324,8 +324,8 @@ litellm-config/ | 空窗期(0:00–次日发放不定时) | **用户确认可接受**;均分策略使空窗概率最小化(余额平缓下降) | | 烧满 2M 的精确性 | 放弃该策略,改为过期风险驱动,结构性规避 | | 物理上限 | 窗口内快过期额度 > 2M 时每天最多洗 2M,极少数过期蒸发不可避(返还上限硬约束) | -| 触发连续性 | 依赖每天 23:00 成功;连续失败 ~8 天耗尽窗口缓冲(冲刺兜底),守护线程常驻风险低 | -| 风控 | 23:00 ± 抖动 + 15–40s 随机间隔 + 每天 3–5 次请求,接近真实行为 | +| 触发连续性 | 依赖每天 23:20 成功;连续失败 ~8 天耗尽窗口缓冲(冲刺兜底),守护线程常驻风险低 | +| 风控 | 23:20 ± 抖动 + 15–40s 随机间隔 + 每天 3–5 次请求,接近真实行为 | | 容器语料 | LiteLLM 容器内无 `/var/log` 系统日志 → 合成语料兜底(5000 行够用) | | 消耗任务自身扣费 | 结构性杜绝:plan ≤ 余额快照 − 缓冲,且每轮检查余额 | | 余额 API 滞后 | v5 恢复 v2(usage+balance−BUFFER):滞后收敛后守恒成立;滞后窗口由 BUFFER + 低余额加密同步覆盖;v4 冻结快照(balance−BUFFER)因误拦已回退 | @@ -345,7 +345,7 @@ litellm-config/ 1. 移植 `auto_consume.py` 的语料/执行逻辑到 `volc_consume.py`,请求层改为直连 Ark(env key) 2. `calculate_plan` 加两个 clamp + 停止条件;执行循环加 `daily_usage` INCR 与余额检查 -3. `volc_limit_sync.py` 守护线程加 23:00 调度钩子 + `consume:last_run` 去重 +3. `volc_limit_sync.py` 守护线程加 23:20 调度钩子 + `consume:last_run` 去重 4. `HARD_DAILY_LIMITS` 统一为 2M;`MONITOR_TARGETS`/`config.py` 退役 5. 用 `test_connection.py` 等价方式验证 Ark 连通 → 手动触发一次消耗任务(dry-run 模式验证 plan 计算与 clamp)→ 观察 Redis key 变化 6. 全量回归:守护线程限额逻辑不受影响(消耗线程独立 daemon) diff --git a/docs/volc-quota-notes.md b/docs/volc-quota-notes.md index 2d130fd..d1dcc10 100644 --- a/docs/volc-quota-notes.md +++ b/docs/volc-quota-notes.md @@ -20,7 +20,7 @@ ``` 每天任意时刻 真实用户 + 消耗任务 的消耗 = 当日采集量(火山实时统计,每模型单独计算) -每天 23:00 CST 消耗任务补齐缺口(可选) +每天 23:20 CST 消耗任务补齐缺口(可选) 次日 00:00 起 火山按昨日采集量发放资源包(不定时,可能延迟到上午) ``` @@ -162,7 +162,7 @@ v6 当前日志格式: | `daily_usage:{北京日期}:{model}:{hash}` | LiteLLM 成功回调 INCR + 消耗任务 INCR | 当日采集量,TTL 48h,北京 00:00 滚动 | | `limit:dynamic:volcengine:balance_at_sync:{model}` | 守护线程写 | 同步时点 API AvailableAmount 快照 | | `limit:dynamic:volcengine:usage_at_sync:{model}` | 守护线程写 | 同步时点 Redis 累计消耗快照 | -| `consume:last_run:{日期}` | 消耗任务 | 23:00 当日去重,TTL 48h | +| `consume:last_run:{日期}` | 消耗任务 | 23:20 当日去重,TTL 48h | ### 6.2 关键日志 @@ -395,7 +395,7 @@ reserve = 18输入 + 16声明 = 34,实际消耗 +167/+150(正文 ~16 + 思 2. 余额显示不动 ≠ 余额充足,是 API 滞后(Δ≈14 分钟,§4.2);v6 的 `balance_at_sync + usage_at_sync` 在滞后未收敛时 = B0 + L(虚高),「实时抵消」仅覆盖同步后窗口,L 不抵消(§7.5) 3. 判断「还能免费采多少」:保守用 `网页 B0 − 今日已采`;v6 公式在滞后收敛时等价,未收敛时偏高 L -4. 消耗任务 23:00 直连 Ark 绕过 LiteLLM 闸门(防自杀),其自身 clamp:`plan ≤ 2M − usage`、 +4. 消耗任务 23:20 直连 Ark 绕过 LiteLLM 闸门(防自杀),其自身 clamp:`plan ≤ 2M − usage`、 `plan ≤ 余额快照 − SMALL_BUFFER`,停止条件三闭环 5. 余额滞后不改变 B0 守恒;30 天过期蒸发是余额下跌的另一来源, 与消耗扣减区分(如 08-12 余额 1,558,307 → 596,869,其中约 50 万为滞后修正 + 过期) @@ -416,7 +416,7 @@ reserve = 18输入 + 16声明 = 34,实际消耗 +167/+150(正文 ~16 + 思 - [x] v6 实施(2026-08-12,§7.5;2026-08-13 生产验证) - [x] 2M 采集上限击穿修复(2026-08-12 20:53 实测 §7.6:reserve 改用 token_counter + max_tokens, 后置 soft_evict 兜底;23:18 重启后 PRE-BLOCK 生效) -- [ ] 消耗任务口径同步收紧(已改为 vls.SMALL_BUFFER,确认与闸门一致) +- [x] 消耗任务口径同步收紧(2026-08-13 代码确认:clamp2 用 `vls.SMALL_BUFFER`,与闸门同源一致) - [ ] 空窗期(0:00~返还到账)行为观察:v6 下 B0 自动收紧,确认无异常误杀 - [ ] 数据库泄漏:08-12 一天 deepseek-v4 消耗记录量级约 5 万条导致 Redis 内存膨胀;触发监控/清理机制 - [ ] v6 无静态 2M 兜底时的安全:08-13 幸由 B0(2,242,178) > 2M 静态墙兜底;若某日 B0 < 2M, @@ -424,6 +424,8 @@ reserve = 18输入 + 16声明 = 34,实际消耗 +167/+150(正文 ~16 + 思 - [x] reserve 精度验证(2026-08-13 短文本实证:估算 34 vs 实际 167 的根因 = 思考模型 reasoning tokens 不计入 reserve,已用 THINKING_RESERVE_FLOOR 兜底; token_counter cl100k_base 对中文偏高对输入侧仍是安全方向) -- [ ] consume clamp2 余额快照口径不同步:`plan ≤ balance − SMALL_BUFFER` 用拉包时刻余额快照 - (volc_consume.py:455),**不含 23:00→00:00 真实用户消耗**;clamp1 用共享 daily_usage 实时精准, - 但余额维度两方不同步。深夜真实消耗大时两方合计可超烧余额。建议运行时重取余额或扣减预估 +- [ ] consume clamp2 余额快照口径不同步(2026-08-13 最新思路下确认为**必改项**,方向待定): + `plan ≤ balance − SMALL_BUFFER` 用拉包时刻余额快照 (volc_consume.py:455),**不含 23:20→00:00 + 真实用户消耗**;clamp1 用共享 daily_usage 实时精准,但余额维度两方不同步。§4.2 已实测结算 + 滞后 Δ≈14 分钟 → 快照相对真实剩余虚高 L(随消费速度增长,实测 149,616 > SMALL_BUFFER 5 万), + 深夜真实消耗大时两方合计可超烧余额。方向二选一:运行时重取余额 / 按最近消费速率扣减 L 预估 diff --git a/volc_consume.py b/volc_consume.py index 32c3e2f..78bdebe 100644 --- a/volc_consume.py +++ b/volc_consume.py @@ -2,7 +2,7 @@ # 🚀 VolcConsume — 火山免费额度自动消耗任务 # ========================================== # 与 volc_limit_sync.py 合并方案的一部分(见 docs/merge-auto-consume.md): -# - 每日北京时间 23:00 由 volc_limit_sync 守护线程触发(可手动 --force) +# - 每日北京时间 23:20 由 volc_limit_sync 守护线程触发(可手动 --force) # - 洗掉 CONSUME_MONITOR_DAYS 天内过期的资源包额度(FIFO 下即队列头部), # 防止 30 天过期蒸发导致"启动资金"缩水(免费额度规则:消耗 X → 次日返还 X) # - 直连 Ark API(Bearer token,绕过 LiteLLM 限额,避免消耗任务自杀) @@ -12,7 +12,7 @@ # # 手动运行: # python volc_consume.py --dry-run # 只算计划不消耗 -# python volc_consume.py --force # 忽略 23:00 时间限制立即执行 +# python volc_consume.py --force # 忽略 23:20 时间限制立即执行 import os import sys import time @@ -471,7 +471,7 @@ def run_consume_model(model, source_groups, order, packages=None, dry_run=False) def _run_requests(model, plan, balance_snapshot, source_groups, order): """ - 循环发送消耗请求。速率:目标 1 小时内完成(23:00 触发,00:00 结算)。 + 循环发送消耗请求。速率:目标 1 小时内完成(23:20 触发,00:00 结算)。 """ burned = 0 fail_count = 0 @@ -481,7 +481,7 @@ def _run_requests(model, plan, balance_snapshot, source_groups, order): # 任务轻松(单次间隔 > MAX_SLEEP_BASE)→ 正常模式 15-40s 伪装,不赶时间 # 任务极重(间隔 < 2s)→ 极速模式 1-3s(本场景 plan≤2M 实际不会触发) # 其余 → 动态 ±5s - # 目标:23:00 触发、00:00 结算,1 小时内完成(plan=2M ≈ 47 次 × 27s ≈ 21 分钟) + # 目标:23:20 触发、00:00 结算,1 小时内完成(plan=2M ≈ 47 次 × 27s ≈ 21 分钟) est_per_req = (MIN_INPUT_CHARS + MAX_INPUT_CHARS) / 2 + 1000 est_requests = max(1, plan / est_per_req) interval = int(3600 / est_requests) @@ -594,10 +594,12 @@ def main(): force = "--force" in sys.argv if not force and not dry_run: - beijing_hour = (time.gmtime(time.time()).tm_hour + 8) % 24 - if beijing_hour != 23: + beijing_struct = time.gmtime(time.time() + 8 * 3600) + beijing_hour = beijing_struct.tm_hour + beijing_min = beijing_struct.tm_min + if beijing_hour != 23 or beijing_min < 20: vls.log( - f"⏰ 当前北京时间 {beijing_hour} 时,非 23:00 触发窗口。加 --force 强制执行", + f"⏰ 当前北京时间 {beijing_hour}:{beijing_min:02d},非 23:20 触发窗口。加 --force 强制执行", level="WARNING", ) return diff --git a/volc_limit_sync.py b/volc_limit_sync.py index 7ac28e4..333ac1b 100644 --- a/volc_limit_sync.py +++ b/volc_limit_sync.py @@ -1249,9 +1249,9 @@ def discover_monitored_models(): def _maybe_trigger_daily_consume(): """ - 🕰️ 每日北京时间 23:00 触发免费额度消耗任务(洗过期资源包)。 + 🕰️ 每日北京时间 23:20 触发免费额度消耗任务(洗过期资源包)。 - consume:last_run:{北京日期} 去重(SETNX,当天只触发一次) - - daemon 线程执行,不阻塞主循环;线程内 0-10 分钟随机抖动,避免固定 23:00 被风控识别 + - daemon 线程执行,不阻塞主循环;线程内 0-10 分钟随机抖动,避免固定 23:20 被风控识别 - 延迟 import volc_consume,避免模块级循环 import """ if not redis_client: @@ -1260,8 +1260,9 @@ def _maybe_trigger_daily_consume(): now = time.time() beijing_ts = now + (8 * 3600) beijing_hour = time.gmtime(beijing_ts).tm_hour - if beijing_hour != 23: - return + beijing_min = time.gmtime(beijing_ts).tm_min + if beijing_hour != 23 or beijing_min < 20: + return # 23:20 起才触发(北京时间) date_str = time.strftime("%Y-%m-%d", time.gmtime(beijing_ts)) lock_key = f"consume:last_run:{date_str}" if redis_client.set(lock_key, "1", nx=True, ex=172800) is None: