「自动化任务不许烧钱」这句话,写进制度很容易,落进系统里非常难。难的不是省钱本身,而是省钱的动作会静默失败——工具返回 success: true,任务照跑,账单照涨,或者反过来:你以为切到了本地免费模型,实际上整批任务从那天起再没成功运行过。
下面四个坑,是我们把近 30 条定时自动化从「全走线上付费模型」改成「轻活走本地 27B、重活走团队模型」的过程中,一个个撞出来的。每条都有可复现的判据,不是经验之谈。
给自动化任务设置模型,工具的参数名是 驼峰 modelId。写成 model 或 model_id 会怎样?
什么都不报。返回体是成功,库里纹丝不动。
这是最难查的一类故障:调用方拿到 success,认为改完了;被调用方根本没读那个字段,认为无事发生。中间没有任何一处会喊一声。我们第一次批量切换时就踩在这里——13 条任务的返回值全绿,实际一条都没改成,直到第二天发现账单没降才回头查。
取值也有格式要求:自定义模型必须带前缀,拼上模型清单里的 id,例如 custom-local:bonsai-27b。少写前缀不是报错,是另一个静默忽略。
更麻烦的是,这个工具自己的 view / list 返回体里根本不包含模型字段。也就是说,你没法用同一个工具来自证清白。
唯一可信的判据是绕过工具、只读打开数据库直接看:
import sqlite3
con = sqlite3.connect("file:workbuddy.db?mode=ro", uri=True)
for name, mid in con.execute(
"SELECT name, model_id FROM automations "
"WHERE deleted_at IS NULL ORDER BY model_id"):
print(mid, name)
注意两点:?mode=ro 是硬要求,查配置绝不开写权限;deleted_at IS NULL 也必须带上,库里留着大量历史条目,不筛会把已删任务算进分母。
今天这次核对的结果是:有效自动化任务 29 条,其中 8 条走本地 27B,21 条走线上团队模型。这个比例本身就是个信号——如果按「轻活一律本地」的口径,21 条里有多少其实只是巡检、同步、汇总,值得逐条过一遍。数量少不代表可以放过,模型路由是一笔每天都在结算的账。
模型路由不只是成本问题,它是单点故障。我们经历过一次:某次模型配置变更出错,直接打瘫了 31 条任务运行——不是跑错,是根本没起来。
| 现象 | 表面原因 | 真实后果 |
|---|---|---|
| 定时班次全部没启动 | 配置项不合法,调度侧拒绝拉起 | 3 天内网站零更新、零扫描、零内容产出 |
| 没有任何告警 | 没启动 ≠ 运行失败,不算「运行异常」 | 只能靠「产物没变」反推,滞后 1~2 天 |
| 恢复后状态错乱 | 错过的班次不会补跑 | 内容断更 5 天,收录节奏全乱 |
教训很具体:「任务有没有跑」必须由产物判断,不能由任务状态判断。我们现在每天开班第一件事不是看任务列表,而是比对三个物证——站点版本文件里的构建时间、sitemap 条目数、最新一篇文章的 slug。这三样只要有一天没动,就先按「没跑」处理,再去查原因。
顺带一个容易误判的细节:部署脚本会先写版本文件、再调部署命令。编译失败时版本号已经前进了,看起来像发布成功。所以「版本号变了」也不能当成功判据,得用带缓存穿透参数的线上实测。
把轻活切到本地 27B(局域网 :8091 的 OpenAI 兼容端点)能省下全部推理费,但调用方式和线上 API 不完全一样,三处必须显式约定:
enable_thinking: true)。这类推理模型默认可能不吐思考过程,直接给出质量很差的短答。max_tokens 不能给小。我们实测设到 800 以下时,思考过程会把额度吃光,正文被截断成空——表现是「调用成功、返回空字符串」,又是一次静默失败。<think> 段。思考标签会连同正文一起返回,不清洗直接拿去发文章、发邮件,就是把内心独白发给客户。还有一个部署侧的坑:模型清单文件是进程启动时读入内存的。新加的模型 id 写进清单后,正在运行的服务不会自动重载,界面上也就看不到——看起来像「加了没生效」,其实是要重启。
我们最后定下来的分级很简单,一条判据:这一步错了,代价有多大?
| 任务性质 | 路由 | 判据 |
|---|---|---|
| 巡检、健康检查、日志汇总、日报、同步、简单判断、短文本 | 本地 27B | 错了重跑一次就行,无外部副作用 |
| 长文生产、批量翻译、经营决策、对外触达文案、竞品分析 | 线上团队模型 | 错了会发出去、会被搜索引擎收录、会影响判断 |
| 任何形式的凭据处理、付款判断、客户回复定稿 | 线上团队模型 | 错了不可逆 |
反过来说,最容易犯的错是把重活也塞给本地模型图省钱。27B 写一篇有数据密度的长文,结构会散、数字会对不上,最后人工返工的成本远高于那点推理费。省钱省在该省的地方,不是省在所有地方。
我们收件箱里躺着两封通知:9 月 21 日 10:46、9 月 22 日 10:41,个人账户可用额度两次耗尽。额度见底那一刻,所有依赖个人计费通道的自动化同时失效——不是降级,是停摆。
这件事直接改写了我们的选型原则:自动化只能跑在「有人买单且不会突然见底」的通道上,即本地自建端点(零边际成本)和团队账号绑定的模型。任何挂在个人额度上的调用,都是给整条产线埋了一颗定时炸弹。
还有个容易混淆的地方:会员在期 ≠ 额度在账。我们看到过会员续费到 10 月底的通知,同期额度通知却是已耗尽。这两件事在计费系统里是分开的,别用前者安慰自己。
modelId,改完立刻只读查库复验——不看工具返回。max_tokens 下限、<think> 清洗,三件事写进同一个函数,不许各写各的。这套东西做完,自动化才真正变成「资产」而不是「账单」。省下来的钱不多,但知道钱花在哪、知道任务到底跑没跑,这两件事值钱得多。
先只读查库看 model_id 字段真实值,不要看工具返回体(它不含该字段)。如果库里没变,八成是参数名写成了 model 或 model_id,工具会静默忽略并照样返回成功。
最常见是 max_tokens 给小了,思考过程把额度吃光,正文被截断。把上限提到 800 以上通常立刻恢复。其次是没打开思考开关,导致输出质量极差而非为空。
看产物,不看状态。版本文件里的构建时间、sitemap 条目数、最新内容的 slug/时间戳,三样只要有一天没动,就按没跑处理。任务「没启动」不会产生运行失败记录,只会安静地什么都不做。
巡检、汇总、同步这类有明确判据的任务不会。但长文生产、对外文案、经营判断会明显下降,27B 在数据密度和结构一致性上撑不住,返工成本高于推理费。分级标准是「错了代价多大」,不是「能不能省」。
因为额度会突然见底,而见底的那一刻整条产线同时停摆,且不产生告警。自动化必须跑在零边际成本的本地端点或团队账号通道上;会员在期也不等于额度在账。