| Invalid Date
Words 0Read Time≈ 1 min
type
Post
status
Published
date
Oct 4, 2026
slug
summary
本地记账 165/100 还被拦,提示我加一个我已经加过的参数——一次额度事故的复盘。
tags
Write
额度闸门
MCP 限额
幂等重跑
DSH 排障现场
category
DSH
icon
password
url

一、提示我加一个我已经加过的参数

那天晚上我想读一次课表,命令跑下去,屏幕上回来的是一行我完全没预料到的字:本地额度保护已触发,本次未发起调用,要强制发起请加 --force-quota。
问题是我已经加了。
我把命令重新看了一遍,参数确实在。又跑一次,同样的提示,同样的建议。那一刻的感觉很怪——不是被拒绝,是被一个自己写的开关拒绝,而它还在教我加那个开关。这不是额度问题,这是逻辑问题。
后来我把这条命令拆开看,才发现那个参数从写下的第一天起就没生效过。它一直是个装饰。
这篇写的是这件事,以及它逼我想清楚的一套东西:给任何按次计费的接口做本地闸门,到底该管什么、不该管什么,以及为什么「逃生口」这种东西必须真的能逃生。

二、额度是怎么被烧掉的

先说烧掉的过程,因为它比结果更有信息量。
那天我写了一段读课表的逻辑,思路很直白:要看今天和明天的安排,那就今天读一次、明天读一次。写出来就是一个按日期循环。跑起来没问题,数据也拿到了,我甚至觉得这段代码挺干净。
但接口那边不是这么算的。它按工具分别计数,每读一次算一次,一天一百次封顶。我那个循环,每跑一轮就是一次调用,而我在调试的时候反复跑了很多轮。等我想起来去看用量,那个读课表的工具已经记到了 165。
一百的额度,用到了一百六十五。超出去的部分不是接口拦的,是我自己没在看。
这里有个很容易被忽略的细节:接口的计数和我的计数是两套。我以为我在读「今天和明天」,接口看到的是「调用了两次」。我以为调试不算数,接口一视同仁。这种认知差不会报错,它只是安静地累积,直到某天晚上你要用的时候,发现闸门落下来了。

三、本地记账,比远端报错早一步

事故之后我做的第一件事,是在调用层加一本自己的账。
逻辑很简单:每次调用之前,先在本地记一笔,然后看这本账。远端的一百次是硬的,改不了;但本地这本账可以让我在撞墙之前就知道墙在哪。
账本解决的是「知道」,还需要解决「反应」。我定的是两条线:用到七成,发一次提醒,同一天同一个工具只提醒一次,避免自己把自己吵烦;用到九成,直接拒绝发起。
为什么是九成而不是十成?因为在额度耗尽的那一刻被拒绝,和提前留一点余量,是两种完全不同的处境。前者是静默失败——你以为调用成功了,其实什么都没发生,等到第二天看数据才发现空了一块。后者至少还留了十次给真正紧急的事。
还有一条是给「跑飞」准备的。同一个工具,十分钟内超过十二次,直接拒。这条抓的不是额度,是循环。人不会在十分钟里手动点十二次,只有代码会。额度线是慢的,突发线是快的,两条线管的是两种病。

四、逃生口必须真的能逃生

回到开头那个参数。
我当初加它,是因为我知道闸门会有误判的时候:也许我确实需要多读一次,也许本地账本记错了,也许就是有急事。所以我留了一个手动绕过的开关,还把它写进了文档,写进了配置顶部的提示,写进了知识库的置顶章节。
然后它从来没生效过。
根因有两层。表层是判断写坏了——两个分支写成了同一句话,等于没判断。更深的一层是,那个开关变量压根没往底层传:几个包装函数各管各的,谁都没把它交给真正发起请求的那一层。所以除了一个查用量的子命令,其他所有入口上,这个开关都是空转。
这件事最坏的地方不是它不生效,是它让我以为我试过了。被闸门拦住的时候,我的第一反应是「加参数」,而参数加上了还是被拦,我会怀疑额度、怀疑网络、怀疑接口,唯独不会怀疑那个参数本身。一个从不生效的绕过开关,比没有开关更危险——它把「被拦住」伪装成「已经强制过了」。
修复本身不复杂:让包装函数把开关透传下去,把两个分支合并成一句,同时保证原来的调用方式不变——简报那条链路不能因为这次改动坏掉。改完我做了两个方向的验证:不加强制的路径,行为跟以前一模一样,照旧被拦;加强制的路径,真的发出去了,并且收到了远端返回的「今日限额已达」。
两个方向都对上了,我才敢说这个开关存在。

五、省额度是设计出来的,不是省出来的

闸门是兜底,真正让额度够用的是调用结构本身。
我后来把每天的调用重新排了一遍:早晚两次简报,各读一次课表区间、各读一次跑步记录,加起来四次;白天调整计划的会话,控制在五次以内。合计不超过十次,只占日额度的一成。
关键在「一次拿区间」这个约束。要看今天和明天,就读一次区间,而不是读两次单日。这个约束我写成了铁律,因为它是那次事故的直接反面——按日循环看起来更自然,但它把一次语义调用拆成了 N 次物理调用,而计费是按物理调用算的。
同样的道理适用于所有按次计费的接口:你脑子里的「一次查询」,和账单上的「一次调用」,往往不是一回事。把这两者对齐,是省额度里最值钱的一步。

六、可迁移的几条判据

这套东西不只对跑步数据接口有用。任何按次计费、有配额上限的集成,都可以照这个思路来:
  • 按工具分别记账。 不要做一个总数。不同工具的额度是独立的,混在一起记,你永远不知道是哪一项在逼近上限。
  • 本地记账要早于远端报错。 远端只会在撞墙时告诉你,本地账本应该在撞墙前告诉你。预警线设在七成,拒绝线设在九成,中间那段余量留给意外。
  • 加一条突发线。 慢速的额度线和快速的频率线,抓的是两种不同的故障。十分钟十二次这种阈值,专门抓代码跑飞。
  • 逃生口必须实测过再写进文档。 声称存在而实际不生效的绕过开关,会把「被拦住」误报成「试过了」。文档里的每一个开关,都应该有一次真实生效的记录。
  • 两分支写成一模一样的代码,静态检查抓不到。 这种残缺的重构只有真跑一次才会暴露。凡是判断,都要有一个能走到另一边的用例。
  • 把语义上的「一次」和计费上的「一次」对齐。 能一次拿区间的,就不要循环拿单点。这是省额度里最根本的一条,也是最容易被写代码的直觉带偏的一条。
那天晚上我最终没能读到课表,只能翻出之前存下的版本。这件事本身没什么,但它让我把「额度」从一个数字,变成了一套需要设计的结构。闸门不是限制,是让你在撞墙之前就知道墙在哪。
Loading...
Catalog