Odd_Chow

02 / CASE STUDY

agent-model-router模型调度器

让模型选择不再依赖隐式偏好
而成为可解释、可恢复的运行决策。

ACTIVEPYTHON 3.10+MITSTDLIB ONLY
VIEW SOURCE / GITHUB
ROUTING / DECISION PATHREADY
TASKexplicit task type + constraints
HARD FILTERScapability · quota · cooldown · deadline
UTILITY SCOREquality · cost · latency · failure risk
MODEL@PROVIDERselected with reasons
DECISION RECEIPTINSPECTABLE → RECOVERABLE
THE PROBLEM

Not just a dropdown.

模型选择不是界面偏好
它决定一个任务是否可做、何时完成、怎样失败
以及这个决定能否被复盘。

01 / DESIGN INTENT
当“自动选模型”无法说明理由时
成本、延迟和失败被藏进了黑箱。agent-model-router 把选择拆成可检查的约束
评分与执行状态。
PROJECT EVOLUTION

它不是
被一次设计出来的。

最初只是一个按角色和额度选模型的小工具。
真正的架构来自后续几轮失败
误判任务、错误冷却、量纲混乱、并发丢数据
以及名义上存在却没有真正执行的 fallback。

2026 / AUGUST

下面不是发布功能列表,而是每一轮实践如何改变下一轮判断。

v0.1–0.22026.08.19
ROLE CHAIN

先让“自动选择”跑起来。

第一版用 difficulty、urgency、固定 role chain、quota 与 cooldown 决定模型;随后加上 OpenAI-compatible proxy,让现有客户端无需改代码即可接入。这一步证明了路由可以进入真实调用链,但也把任务理解压缩得太简单。

WHAT BROKE

复杂任务被归纳成难度和紧急度。规则能命中熟悉表达,却无法稳定表达 coding、image、batch 等能力差异。

WHAT CHANGED

保留兼容层,但不再把 role chain 当成新架构的核心。

能自动选,不等于选得有依据。
v0.32026.08.20
TASK SYSTEM

模型之外,任务也需要生命周期。

项目从 router 变成“任务 + 模型 + 时间窗口”的调度器:加入 Task、状态机、JSON 持久化、defer、Executor 和最小看板。设计重点不是更多字段,而是让 task 只描述做什么与何时做,执行细节留给 Executor。

WHAT BROKE

代理层曾把所有 HTTP ≥ 400 都视为模型故障并进入 cooldown;400 参数错、401 凭据错也会错误惩罚模型。

WHAT CHANGED

把 request/auth、rate limit、server error 与 timeout 分类,只有真正的瞬态故障进入冷却或重试。

错误分类不是日志细节,它会改变下一次调度。
v0.4–0.52026.08.20
UTILITY

从固定角色链,转向可解释评分。

Utility 引入质量、成本、延迟、失败风险、额度压力和截止压力六个维度;Hard Constraints 先排除不可能的候选,breakdown 和 why 留下选择依据。Policy Compiler 再把“便宜一点”“尽快”等意图翻译成约束与权重。

WHAT BROKE

第一版直接把不同量纲乘权重后相加。balanced 模式下,成本绝对值甚至可能反压明显的质量优势。

WHAT CHANGED

增加候选集内 min-max 相对归一化,再应用权重;单候选则明确标注没有相对参照。

权重相等,不代表不同量纲的影响相等。
v0.6–0.6.22026.08.20–21
HARDENING

测试开始推翻“看起来没问题”。

StateStore 加入 SQLite,benchmark 开始对比 utility、role chain 和 round-robin。但最终审查与真实使用连续暴露了基础问题:多进程写入丢数据、并发 tick 竞态、quota 哨兵误判,以及纯文本模型被派去执行图片任务。

REPRODUCED

两进程各写 30 条任务,预期 60 条,实际只剩 31 条;合成 benchmark 也只能比较策略,不能证明生产并发能力。

WHAT CHANGED

改用 SQLite BEGIN IMMEDIATE 原子更新,补真实线程/进程压测,并公开 quota record_call 约 102 QPS 的已知瓶颈;image/vision 能力不匹配改为硬过滤。

基准可以帮助比较,但不能替代真实并发与故障复现。
v1.0–1.1.22026.08.21–25
OWNERSHIP

最后补上的
是“谁真正拥有任务”。

项目更名为 agent-model-router,明确它不只是时间调度器。随后一次接管审查指出:多 scheduler claim 不原子、running 没有 lease/heartbeat、旧 worker 可以迟到回写,而降级矩阵当时只记录 action 字符串,并没有真正执行 fallback。

WHAT BROKE

进程崩溃会留下永久 running;重复 worker 可能执行同一任务;“fallback”只是记录了一个词,不能证明模型真的切换。

WHAT CHANGED

加入 SQLite CAS claim、worker/attempt ownership、lease、heartbeat、stale recovery 与 owner-safe completion。只有 Executor.prepare_fallback() 真正成功,任务才重新排队;否则 fail closed。

恢复能力不是重试按钮,而是一套可证明的所有权协议。
EXAMPLE INTERACTION

A decision you can inspect.

这是结构真实但完全虚构的示例决策记录。选择下方的硬约束,查看它如何改变候选与理由。

HARD CONSTRAINT

先让不可能的候选退出,再讨论谁更合适。

EXAMPLE DECISION RECORDSELECTED
TASK TYPE
coding
SELECTED
model-a@provider-x
FALLBACK
model-b@provider-y
CONSTRAINT
quota_available
  • capable for coding
  • within quota
  • healthy candidate
FAILED DIRECTIONS

有些方向
必须承认走错了。

这些不是边角 bug,而是曾经成立、后来被证据推翻的设计假设。

“有 fallback 字段,就算实现了降级。”

早期矩阵只把 action_taken 写进 last_error。执行器没有准备下一个候选,任务却可能被描述成已经 fallback。

后来:prepare_fallback() 成功才重排;否则 fail closed。

“SQLite 单次 update 有事务,所以多进程安全。”

真正的问题跨越 load 与 save:两个进程都从旧快照出发,后写入者覆盖先写入者。

后来:整个读—改—写放进 BEGIN IMMEDIATE 原子事务。

“合成 benchmark 足以证明生产能力。”

它只能在同一套假设下比较三种策略,无法证明真实锁竞争、写吞吐或 provider 表现。

后来:分开标注合成策略对比与真实并发压测,并公开已知瓶颈。

“未知 task type 就让所有模型参与。”

image 缺配置时退化为 ALL_TIERS,成本和延迟评分可以把纯文本模型推到图片任务前面。

后来:能力缺失返回 0.0,宁可无候选,也不假装能做。
CURRENT ARCHITECTURE

现在的架构
是这些修正叠出来的。

库只负责可解释选择与可恢复任务语义。任务理解、provider 传输与凭据仍属于接入层。

01 / TASK调用方显式提供 task type、优先级、deadline 与不透明 payload。
02 / FILTER能力、额度、cooldown、deadline 与健康红线先排除不可行候选。
03 / SCORE剩余候选按六维 Utility 相对归一化并留下 breakdown / why。
04 / CLAIMSQLite CAS claim 建立 worker / attempt / lease 所有权。
05 / RECOVERheartbeat 保活;过期 lease 恢复;旧 owner 的迟到结果被拒绝。
THE BOUNDARY

Executor 执行真实 provider 调用。fallback 也必须由 Executor 真正准备;调度器不解析私有 payload,也不会凭一个状态字符串假装完成了切换。

SCOPE / BOUNDARY

What stays outside.

清晰的边界让路由库可以专注于决策与调度,而不会假装替每个接入系统完成它自己的职责。

TASK UNDERSTANDING

库不让 LLM 猜测任务类型;由调用方显式传入,或由接入层负责推断。

PROVIDER TRANSPORT

不实现 provider SDK、凭据或网络传输;这些留给具体的执行与接入层。

GUARANTEES

不承诺“永远最优”的模型选择;它提供可检查的依据、约束与失败处理语义。

CURRENT STATE / ACTIVE

一次路由决策
应当留下可以复盘的痕迹。

← 回到首页