大多数团队把「模型调用成功」当作终点。Veral 的设计把它当作起点:一次请求要留下证据,证据要变成可重复评测,评测要形成带版本的能力判断,判断再经过 shadow、canary 和门禁,才有资格改变下一次请求的去向。
这篇只讲一个核心观点:评测与路由平台真正的产品单位,不是一次模型调用,而是一次有证据、可追溯、可渐进发布、可回滚的策略变化。
前面的系统施工图讲了十个模块怎么切,数据规格书把 Trace、样本、标签和训练数据拆到字段级,Inspect AI 实操跑通了最小评测管道,评测平台产品化划清了平台与业务用户的责任。这篇站得更高一点:把这些零件收回同一张生产架构图,解释它们为什么必须长成一个闭环。
先声明证据档位:文中关于 Veral 边界和目标状态的陈述来自项目已批准的设计规格与 ADR;关于 LiteLLM、Langfuse、Inspect AI 和 OpenTelemetry 的职责来自文末官方文档;「真正的产品单位是策略变化」是我的设计判断,不是行业标准。
先装四个杠杆:证据、版本、隔离、发布
如果只记住四个词,记这四个:
| 杠杆 | 它解决的问题 | 拧错之后会发生什么 |
|---|---|---|
| 证据 | 为什么这次选了模型 A?它到底答得怎样? | 路由变成黑箱,失败只能靠猜 |
| 版本 | 这次分数对应哪版模型、Prompt、数据集和评分器? | 新旧结果混算,趋势看似连续、实际不可比 |
| 隔离 | 离线评测或观测故障时,在线请求能否继续? | 队列、数据库或 Langfuse 抖动直接拖垮业务 |
| 发布 | 一份更高的离线分数,凭什么直接接管生产流量? | 评测偏差被放大成线上事故 |
这四个杠杆共同构成一个判断公式:
证据回答「凭什么改」,版本回答「改的是哪一版」,隔离回答「哪里坏了不连坐」,发布回答「怎么安全地让变化发生」。
全貌:两条不同速度的链,闭合成一个回路
Veral 有两条链。
在线链追求稳定和低延迟,只做一件事:读取已经发布的不可变策略快照,给出完整 RoutingDecision,再让 LiteLLM 执行模型调用和 fallback。
离线链追求证据质量:从 Langfuse Trace 里挖候选样本,审核并冻结数据集,交给 Inspect AI 评测,形成能力标签、Gate 和策略候选,再逐级发布。
flowchart TB
subgraph ONLINE["在线:只消费已发布策略"]
direction LR
APP["业务应用<br>经 Caddy 进入"] --> ROUTE["routing-runtime<br>进程内策略快照"]
ROUTE --> LITE["LiteLLM Proxy"]
LITE --> MODEL["外部 API 或 vLLM"]
end
subgraph OFFLINE["离线:制造下一版证据"]
direction LR
TRACE["Langfuse Trace"] --> DATA["审核与冻结数据集"]
DATA --> EVAL["eval-runtime<br>Inspect AI"]
EVAL --> LABEL["能力标签与 Gate"]
LABEL --> RELEASE["不可变策略版本<br>shadow → canary → active"]
end
LITE -. "异步证据" .-> TRACE
EVAL -. "统一模型调用" .-> LITE
RELEASE -. "发布快照" .-> ROUTE
图里最重要的不是箭头多,而是箭头的方向:在线只消费离线已经验收的结果,离线不能反向卡住在线请求。
如果 control-api、RabbitMQ、eval-runtime 或 Langfuse 暂时不可用,已经加载策略的 routing-runtime 仍应继续服务;策略刷新失败则继续使用内存与磁盘中的 last-known-good。这里的隔离不是性能优化,而是故障边界。
一次请求如何走完两圈
把同一条请求跟到底,它会完成两次循环。
第一圈:在线执行,留下可解释证据
- 业务应用从 Caddy 进入 OpenAI-compatible 接口。
- routing-runtime 用请求摘要与策略快照运行纯决策函数,输出选中模型、候选列表、理由、策略版本和 fallback 链。
- LiteLLM 负责真正的 Provider 调用、统一协议和执行级 fallback;Veral 不重写网关。
- 路由字段和模型执行字段异步进入 Langfuse。内容只按脱敏与采样策略保存,fallback、负反馈、shadow 分歧等高价值请求可以强制完整采样。
第一圈结束时,用户已经拿到回答;离线链路是否健康,不影响这次调用完成。
第二圈:离线评测,把证据变成策略
- Trace 经过规则挖掘进入候选池,补齐 Oracle 或 Rubric,审核后冻结成不可变数据集版本。
- 用户提交的评测不是一句「测一下模型」,而是模型版本、Prompt Pack、数据集版本、评分器版本组成的版本四元组。
- control-api 在业务事务中写入运行状态与 Outbox 事件,RabbitMQ 至少一次投递;eval-runtime 用租约、heartbeat、fencing token 和幂等结果抵抗重复投递与 Worker 崩溃。
- Inspect AI 执行评测,但模型访问仍经过 LiteLLM;样本级结果写回 MySQL,
.eval日志与大对象进入 MinIO。 - 只有成功运行的结果能派生能力标签。标签带样本量、区间、数据集/评分器/运行来源和 fresh/stale 状态,用户不能手工把模型改成「擅长代码」。
- 策略候选先离线回放,再 shadow 观察新旧决策分歧,接着 canary 小流量验证质量、成本、延迟与 fallback,达标才转 active。
第二圈结束时,新的策略快照回到 routing-runtime。下一次请求会消费这次请求留下的集体经验。
数据放在哪里,决定系统能不能解释自己
「都存一份」不是可追溯,「每类数据只有一个明确真源」才是。
| 系统 | 唯一职责 | 明确不承担 |
|---|---|---|
MySQL veral_* | 业务元数据、状态机、版本引用、审计 | Trace 大对象、临时缓存 |
| Langfuse | Trace、Observation、Session、在线 Score | 路由策略和业务配置真源 |
| MinIO | 样本内容、环境快照、.eval 日志、导出产物 | 可变业务状态 |
| RabbitMQ | 任务与领域事件投递 | 最终业务状态 |
| Redis | 策略缓存、锁、短期进度 | 不可恢复的唯一数据 |
| LiteLLM | 模型调用执行与网关统计 | 模型能力标签真源 |
这张表解决了两个常见误区。
第一,消息成功不等于业务成功。运行状态必须先进入 MySQL,再通过 Outbox 投递;消费者重复收到事件,也只能幂等地得到同一个结果。
第二,缓存命中不等于策略存在。routing-runtime 的热路径可以只读内存,但策略的发布历史、来源证据和回滚关系仍由控制面持有;磁盘 last-known-good 是恢复手段,不是治理真源。
为什么不能直接把三个开源项目粘起来
LiteLLM、Langfuse 和 Inspect AI 已经覆盖了大量通用能力。Veral 的价值不是再写一遍,而是补上三者之间没有共同所有者的业务闭环。
| 开源底座 | 它已经做好什么 | Veral 必须持有什么 |
|---|---|---|
| LiteLLM Proxy | 统一模型协议、Provider 执行、网关能力 | 业务路由决策、模型治理、策略发布 |
| Langfuse | LLM Trace 与 Observation 可观测性 | 路由字段、采样规则、Trace 挖掘与项目权限投影 |
| Inspect AI | Task、Solver、Scorer、评测执行与日志 | 场景产品化、版本引用、运行编排、结果入库 |
边界规则很简单:第三方 SDK 只存在于 Adapter;领域模型、跨服务契约和前端 API 只看 Veral 自己的 DTO。这样底座升级时,策略状态机、数据血缘和业务权限不需要一起改写。
生产化不是多起几个副本,而是每条变化都有退路
Veral 首期生产基线仍是 Docker Compose,故障域只承诺单台宿主机内的进程与容器,不伪装成跨机房高可用。在这个边界内,Caddy 是唯一入口,无状态服务与 LiteLLM 可以运行双副本,eval-runtime 以两个竞争消费 Worker 执行任务;状态组件依靠持久卷、健康检查、自动重启、逻辑备份和恢复演练获得可恢复性。
策略发布则用状态机把「谨慎」写进系统:
flowchart LR
DRAFT["draft"] --> OFFLINE["offline verified"]
OFFLINE --> SHADOW["shadow"]
SHADOW --> CANARY["canary"]
CANARY --> ACTIVE["active"]
ACTIVE --> ARCHIVED["archived"]
SHADOW -->|越界| ROLLED["rolled back"]
CANARY -->|越界| ROLLED
ACTIVE -->|kill switch| ROLLED
ROLLED --> LKG["last-known-good"]
LKG --> ROUTE["routing-runtime"]
这里有一个容易混淆的点:副本解决的是进程退出,回滚解决的是错误仍在正常运行。容器全部 healthy,不代表新策略没有把代码任务路由到便宜但不会写代码的模型。前者靠编排恢复,后者必须靠评测、指标门禁和旧策略快照恢复。
理想全貌不等于当前完成状态
本文画的是 Veral 已批准的目标架构,不是把路线图全部涂成绿色。
当前已经有工程治理、四个部署单元、评测目录与运行、Inspect AI、MinIO 产物、LiteLLM/Langfuse 集成及工作台等经过不同层级验证的基础。接下来的关键产品切片仍是把「策略候选 → 发布/激活 → routing-runtime 决策 → 可回滚证据」跑成真实纵向闭环。
shadow、canary、自动回滚、双副本摘除、租约恢复、DLQ、备份恢复演练和完整生产观测属于理想生产形态的验收项。它们写进架构,是为了约束后续实现方向;在验收器给出证据前,不应被描述为已经上线。
这也是架构图应有的诚实:它既要告诉你终点长什么样,也要让你看见哪段桥还没有验收。
带得走的检查表
判断一套「评测 + 路由」系统是不是闭环,不妨只问八个问题:
- 每次路由决定能否说出策略版本、候选、理由和 fallback?
- Trace 能否经过脱敏、审核和冻结,变成可重复的评测样本?
- 一次分数能否追溯到模型、Prompt、数据集和评分器版本?
- 能力标签是否只能由评测流水线写入,而不是人工编辑?
- control-api、队列、Langfuse 故障时,已加载策略的在线请求能否继续?
- 新策略是否必须经过离线验证、shadow 和 canary 才能拿到全部流量?
- 越界时能否自动或人工回到 last-known-good,并留下部署证据?
- 每类数据是否有唯一真源,消息与缓存是否都可以重建?
八问都能用运行证据回答,平台才不只是画出了一个圆,而是真的闭合了它。
诚实的提醒
本文没有亲手复现任何性能、可用性或模型质量数字,也没有把项目目标状态冒充生产现状。官方资料只用于核对开源底座的职责边界;Veral 特有的服务划分、状态机与数据所有权来自项目批准设计。
最低成本的验证实验是:使用 mock 模型发布一个最小策略,然后发起一次 model: veral/auto 的 OpenAI-compatible 请求。沿同一条链核对 routing decision、LiteLLM 响应、Langfuse Trace、评测 Run、能力标签和 deployment 记录,确认 request、run 与 policy 标识能串起来;随后让新策略触发门禁越界,观察是否回到 last-known-good。这个实验不证明模型聪明,但能证明闭环不是 PPT。
参考来源
工程实践与官方文档
- 公司级大模型路由与评测系统施工图
- 路由评测数据规格书
- Inspect AI 从零到一实操
- 评测平台产品化
- LiteLLM Proxy 官方文档
- Langfuse Observability 官方文档
- Inspect AI 官方文档
- OpenTelemetry Generative AI semantic conventions
arXiv 论文
本篇讨论工程边界与发布闭环,没有用论文替代项目规格或运行证据,因此不列与主线无直接关系的论文。