池预算与熔断 + 前台回复优先保 #90
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feature/82-budget-breaker"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #82
每池独立日预算上限(静态 config),超了即停;
reply池例外,永不停。四个 grill 决策
embedding/multimodal可能正在服务一次 reply 轮次,而 reply 不该因为别的池超预算而死掉 —— 让PoolExhausted冒泡等于让「用户说话它不应」通过侧门发生。这正是 AC 说的「可以降级」。BreakerState为什么是五态closed/open/protected(reply 超预算但不停)/unlimited(没配)/unpriced(配了上限没配定价,无从判断)。每一个都是部署者需要区分的不同处境:protected独立成状态是为了让「前台已经超预算了,只是我们没停它」可见 —— 渲染成closed等于把这件事藏起来。unpriced时 fail-open(不熔断)+ 显式标出:那是配置错误,而「因为你没填价格所以我把这个能力关了」是最糟的应对,部署者会以为功能坏了。熔断状态与花费一起进
/cost,不只写日志 —— 部署者来问「今天花了多少」的时刻,正是最该看见「某个能力已经停了」的时刻。自查与两轴 review 后修掉的
自查:第一个 commit 只给三条 proactive 入口加了闸,而我声称的「不留半截状态」在另外三个池上没兑现 ——
reflection的 cycle 会先跑起来、处理掉前几个 chat 才抛(而drain_delta是先删库提交再调 LLM,这是数据丢失级的);delegate会先mark_pull_attempted再失败;multimodal会逐张学到中途才停。三处都补了入口闸。规格轴抓到真 bug:
search_stickers工具内部那次 embed 没有保护。_run_tool_loop全文零except、反应式那条路是裸create_task—— embedding 熔断 + 模型调一次这个工具 = 整条回复静默消失,直接违反本片唯一不可省略的约束。既有那条「其余五池全熔断」测试摸不到它(脚本只发消息)。规格轴指出的无用 IO:reply 池的熔断读结论恒定(它永不 open),却发生在每一轮
complete()之前 —— 正踩在「不增加前台回复延迟」这条红线上。已短路。规格轴的建议采纳:
delegate熔断时让模型看不见那个工具(复用 issue #61 灰度开关那条通路),而不是「应声『我看看啊』再回一条『没能查出结果』」—— 后者要花两次 reply 池的钱去说一件本来就做不到的事。标准轴的工具痕迹清理:删掉
SEP_NEWLINE = chr(10)(我为绕开自己生成脚本的转义坑加的,而同 commit 里两处都在正常用"\n".join(...),自我反驳;还没加_前缀泄成了公共 API);修掉五个助手 docstring 的双倍行距;去掉runtime_loop.py里新加的 logger(core 一律不直接写日志,且熔断已由 guard 记过)。结构:
BudgetEntry挪到breaker.py(记账 vs 治理的边界);两处「池名打错」判定收敛到cost.unknown_pool_names;新增decide_pools让/cost与逐次检查共用同一套接线;补_STATE_LABEL的get_args穷举守卫。验证
1336 测试全绿。10 处变异逐条验过(reply 也熔断 / 边界 off-by-one / 没配预算当零 / 未配定价 fail-closed / 包装器闸摘掉 / 入口闸摘掉 / 召回不降级 / 贴纸检索不降级 / delegate 工具仍可见 / 熔断状态不进 /cost),各自只挂对应测试。
其中两处第一次没杀掉测试:包装器那道闸摘掉全绿(我的测试只走到入口那层,兜底层永不执行 —— 正是「上游已拦掉、兜底层测不到」的空转形状)、
/cost的熔断段摘掉也没被抓到。两条都已补强。另有一条降级测试第一版断言被自己污染(事件内容与用户消息用了同一个词),已改成不同词并加反向对照。