ADR 0051 — 已证明的能力维度;“用来做什么”是策展
背景
OpenRouter 的第一次真实同步给策展界面带来了 338 个模型。按厂商分组(Fase 12,带子组的 hub)让列表可以浏览,却没有回答面对它时真正提出的问题:这些中哪个适合我现在的需要?
针对线上 API 测得的两个发现,界定了这个问题。
第一:目录已经公布了界面没有显示的东西。 解析器读取 id、name、context_length、pricing 和 supported_parameters——却丢弃了 architecture。更糟的是,同步在模态能力上从不查询远端:
// sync-model-catalog.use-case.ts:202, antes
supportsVision: local?.supportsVision ?? false,
值来自已经保存的内容,而已经保存的内容诞生时就是 false。结果:338 个全是 supports_vision = false,包括那些 provider 整个页面都叫“vision”的模型。这一列生而为假、死而为假——不存在任何它能变成真的路径。
对照 2026-08-04 的目录,OpenRouter 声明:181 个模型接受图像输入,11 个产出图像,213 个接受 reasoning,25 个接受音频,0 个产出视频。
第二:想要过滤的东西有一半在任何目录中都不存在。 请求中包括“melhores IAs por tipo — documentar, imagem, vídeo, thinking, code”。图像和 thinking 是 provider 声明的。“最适合代码”和“适合文档”没有任何 provider 公布——没有字段,没有约定,除了模型名称什么都没有。而从名称推导能力,正是 ADR 0041 所禁止的:能力只在测试套件证明时才声明。
视频是极端情况:九个 provider 中零个模型。视频维度会是一个永远匹配不到任何东西的过滤器。
决策
两条轴都存在,彼此分开,各自住在它的事实所在的地方。
1. 能力维度——provider 证明的东西
models 中的三个新字段,由远端目录提供:
| 字段 | 在 OpenRouter 中的来源 |
|---|---|
supports_vision | architecture.input_modalities 包含 image |
generates_image | architecture.output_modalities 包含 image |
supports_reasoning | supported_parameters 包含 reasoning |
图像的输入和输出是不同的轴,而不是一条:读图表的模型和画图的模型解决的是不同的问题,把它们合并会把用户送到错误的模型那里。
同步开始从远端读取,以本地为回退,与 supportsToolCalling 的形式相同:
supportsVision: remoto.supportsVision ?? local?.supportsVision ?? false,
远端的 undefined 保留本地值——没有声明不等于声明没有。当 provider 沉默时,解析器省略这个字段,而不是发出 false;在那里声明 false,会在 provider 第一次改变目录格式时抹掉手工做的策展。
音频和视频被排除在外:音频是因为今天产品的任何部分都不使用它,视频是因为它不存在。有可过滤的东西时它们再进入。
2. 按用途的策展——团队发现的东西
五种用途的封闭词汇——codigo、documentacao、analise、imagem、conversa——按 workspace 标记,在 workspace_models.uses 列(text[])中。
它住在 workspace_models 而不是 models 中,理由与 ADR 0049 相同:它是运营者的意见。同一个模型在一个 workspace 中是“写代码的那个”,在另一个中是便宜的聊天模型,而付账的人才有权决定。没有全局轴,因为不存在全局的答案。
封闭词汇,而不是自由文本:一周之内界面上出现 code、coding、Code 和 código,就会产生一个什么都匹配不到的过滤器——比没有过滤器更糟。新用途进入类型并获得迁移,两侧都有编译期的穷尽性证明(与 llm-provider-names.ts 相同的机制)。
用 text[] 而不是 Postgres 的枚举,就像 Fase 8 的 delegations.area:新用途不应需要类型迁移。
3. 两条轴在 UI 和数据库中都不混合
- 能力徽章和用途徽章在目录行上有不同的色调,过滤 chip 由分隔线隔开。
- 标记用途不会在选择器中打开模型。
is_active列的 DEFAULT 是true,所以由用途标记产生的行以显式的isActive: false插入——没有这一点,对一个模型发表意见就会授权它花钱,违反 RN-043。 - 更改用途不会关闭已打开的东西:
is_active不在ON CONFLICT的SET中。 - 界面从不写“não lê imagem”。徽章只断言属实的东西,因为这里的
false意味着“provider 没有声明”。
后果
- 界面通过两条路径回答“哪个模型适合这个”:provider 证明的,以及团队在使用中发现的。
- 一个把列表清零的过滤器,开始与空目录可以区分——以前界面会对已经有凭据的人说“cadastre uma credencial”。
- 现有目录只有在下一次同步时才获得真实的维度:迁移以
false创建这些列,是同步从远端填充它们。不查询 provider 就不可能回填,而编造值就又是原来的缺陷。 - 不公布模态的八个 provider 继续是
false——诚实的,并且一旦其中任何一个开始声明就可以降级处理。 - “最适合代码”在这个产品中永远不会是能力。如果有一天某个 provider 公布了这样的字段,那也只是又一个意见——它的意见——不能替代团队的意见。
三种设计被舍弃,每一种的原因就是它会留下的后果:
- 从模型名称推断用途(
*-coder-*→ 代码):穿着数据外衣的猜测。它在擅长代码的通才上出错,在名字里有“coder”而团队觉得不好的模型上出错,而知道的人没办法纠正。 models中的全局用途:会恰好重复 ADR 0049 纠正的缺陷——一个 workspace 替邻居做决定。- 用途中的自由文本:一周之内就会把词汇碎片化,把过滤器变成文本搜索。
留待以后的事项
音频和视频的维度,当有可过滤的东西时:音频在 OpenRouter 中已经有 25 个模型,但今天产品的任何部分都不使用音频;视频在九个 provider 中为零。
按标记的用途排序目录——今天用途只用于过滤。词汇锁定后,排序只是一行;在没人标记过任何东西时,它会按空列表排序。
其他八个 provider 的维度。 今天只有 OpenRouter 公布模态;其余每一个都需要对照官方文档进行调查,就像 Fase 11 对各种怪癖所做的那样——禁止把一个的解析器继承到另一个上。