
很多业务场景要的其实不是一段话。
客服系统收到一条消息,要的是"转账单组还是技术组";审核系统拿到一张图,要的是"过还是不过";工单系统要的是一个严重度评分。答案就是一个选项、一个概率、一个分数,程序拿到就走自己的逻辑,没有人去读它。
但过去的通行做法是:把整段文本发给前沿大模型,让它"按 JSON 格式输出",然后祈祷它这次不要多写一句解释、不要漏一个括号。格式错了就重试,字段类型不对再修一遍——用过的人都知道这条路有多烦。
最近一个月,这个局面被一个叫 Jev 的闭源模型和它的开源对手 Laya 彻底搅动了。更有意思的是,这股浪潮几乎立刻冲进了 .NET 生态:先是 Laya 的两个独立移植 NLaya 和 layar 前后脚出现,紧接着又出现了两条思路截然相反的 .NET 原生实现路线——用 26B 扩散大模型"单步读取"的 TensorSharp,和用小编码器加决策头的 Sezika;与此同时,社区还补上了客户端这一环——SystemOneSharp,一个把所有兼容服务统一起来的类型化 HTTP 客户端。而自托管 Agent 运行时 OpenClaw.NET 的 PR #246 与紧随其后的 PR #255,则演示了把这套东西真正接进生产需要过多少道关卡——以及它最终如何收敛成一个纯 .NET 的技术栈。
这篇文章把这条链路从头拆到尾:判断引擎到底是什么、它解决了什么问题,以及在 .NET 里你有哪四条引擎路线、一个客户端可以走,最后看一份把它开上生产路的真实样本。

2026 年 9 月 15 日到 9 月 24 日,九天里发生了三件事:TypeSafe AI 发布闭源决策模型 Jev;Convai Innovations 开源了对标的 Laya;1Panel 把 Laya 封装成 Laya Server 上架应用商店。
三件事单独看都是新闻,串起来看是一条完整的产业信号:"判断"正在从"生成"里被剥离出来,成为一层独立的基础设施。
Jev 出自 TypeSafe AI,创始人 Diogo Almeida 是前 OpenAI 研究员、RLHF 的共同发明者之一。他反复问的一个问题是:模型在对话上超越人类已经好几年了,那么自动化到底在哪里?
他自己的回答是:我们一直用最贵、最慢、最不确定的工具,去干最廉价、最需要确定性的活。
Jev 的名字取自 Daniel Kahneman 的双系统理论——System 1 是快速、直觉的判断,System 2 是缓慢、审慎的推理。Chat 模型和推理模型都在追 System 2(一步一步想),Jev 做的是 System 1:像一个熟练的客服扫一眼工单就知道该转给谁,不解释、不展开、不写理由,只给结论和把握程度。
调用形态被压缩成两个概念:
模型一次前向传播把整批问题全部算完,返回带概率的答案。没有 token 逐个采样,没有 JSON 需要修复。它的全部表达能力由三种原语构成:
原语 | 作用 | 返回 | 典型用法 |
|---|---|---|---|
choice | 从离散选项里挑一个 | 选中的选项 + 概率分布 + 置信度 | 这个工单归 billing / technical / sales? |
score | 在固定等级上打分 | 一个数值 + 分布 + 置信度 | 客户愤怒程度 0–2 打几分? |
noul | 是非判断 | 校准后的 P(true) | 这条消息是否要求退款? |
这里有两个关键设计值得展开。
关键一:答案空间在调用之前就锁死了。 传统做法是在输出之后做校验;Jev 的做法是程序先声明"我要问什么、允许答什么",模型只负责在约束内填概率。它不可能返回错误的类型或畸形结构——这是构造性保证,不是训练出来的好习惯。但它依然可以判错:它保证的是"答案合法",不是"答案正确"。
关键二:校准过的置信度才是判断能进生产的前提。 Jev 和 Laya 都用 RLCD(Reinforcement Learning for Calibrated Decisions)训练,奖励函数是严格 proper scoring rule——只有当模型诚实报告自己的概率时,期望奖励才最大。于是代码可以写成:
if conf >= 0.85:
route_automatically(dept) # 高置信度:自动执行
else:
escalate_to_human(dept) # 低置信度:转人工,或升级到大模型"自动化处理大多数 + 阈值兜底少数",才是决策模型真正的产品形态。
代价是:Jev 闭源,只能调 API,数据要出网。这个缺口存在了三天。
Laya 由 Convai Innovations 于 9 月 18 日发布,Apache 2.0 许可,权重、代码、训练数据全部公开,接口设计几乎一比一对应 Jev。它发布了三个 checkpoint:
Checkpoint | 基座 | 参数量 | 上下文 | 说明 |
|---|---|---|---|---|
laya | ModernBERT-large | 421M | 512 tokens | 英文基础版 |
laya-multilingual | mmBERT-base | 322M | 1024 tokens | 100+ 语言,中文场景用这个 |
laya-typed-decisions | ModernBERT-large | 421M | 512 tokens | 在客服、发票、安全事件、Agent trace 四个工作流上微调 |
但必须泼一盆冷水,Laya 的 model card 自己写得很直白:零样本 typed-decisions 准确率只有 0.362,连"永远猜最多那一类"的多数类基线(0.461)都不如;逐题型重拟合温度参数后,平均 ECE 才能从 0.466 降到 0.081。项目方的原话是:"Laya is a fast base to specialise, not a zero-shot decision engine."——它是一个可快速特化的底座,不是开箱即用的零样本决策引擎。
还有一条来自第三方测算的规律必须记住:成本优势的上限 = 1 / 弃权率。 无论单次判断多便宜,只要有 30% 的判断要回退给大模型,整体最多省 3.3 倍。自托管只省掉了单价,不省掉弃权。
对 .NET 开发者来说,最直接的上车方式是移植版。有意思的是,Laya 发布没多久,.NET 社区就冒出了两个互相独立的移植:NLaya(TorchSharp + ONNX Runtime)和 layar(ONNX Runtime + TorchSharp)。两个都号称与 Python 原版逐位对齐,三个 checkpoint 全部支持,但设计取向不太一样——NLaya 走"生态全家桶"路线,layar 走"精简内核 + 双后端对打"路线。先看 NLaya。
职责 | 组件 |
|---|---|
分词 | Microsoft.ML.Tokenizers(BpeTokenizer,直接读 checkpoint 的 tokenizer.json) |
张量运算 | System.Numerics.Tensors |
原生推理 | TorchSharp,直接加载 model.safetensors |
ONNX 推理 | Microsoft.ML.OnnxRuntime,跑官方导出的 encoder.onnx + head.onnx |
后端只负责把一批 token id 变成 logits(ILayaBackend.Run),分词、prompt 布局、校准、解码全部在核心库里,所以两个后端给出的答案完全一致。
using NLaya;
using NLaya.TorchSharp;
await using var agent = await Laya.LoadAsync(
"convaiinnovations/laya-multilingual", o => o.UseTorchSharp());
var questions = new Questions()
.Choice("department", "Which team should handle `body`?",
("billing", "invoices, payments, refunds"),
("technical", "bugs and outages"),
("sales", "pricing"))
.Score("urgency", "How urgent is this?",
"not urgent", "somewhat urgent", "very urgent")
.Noul("refund_requested", "Does the sender ask for money back?");
var result = agent.Predict(
new { body = "I was charged twice for invoice 4411. Please refund me today." },
questions,
new PredictOptions { MaxLen = 8192 });
Console.WriteLine(result.Choice("department").Choice);NLaya 不替你下载模型,用 Hugging Face CLI 拉到标准缓存目录即可;缺模型时报错信息会直接告诉你该跑哪条 hf download 命令。
这是 NLaya 最" .NET 味儿"的能力——用 C# 类型描述答案,拿回来的就是这个类型。每个属性对应一个问题:枚举是 choice,bool 是 noul,带 [Range] 的整数是 score;[Description] 就是 instructions。
public enum Team
{
[Description("Payments, invoices and refunds")] Billing,
[Description("Bugs and outages")] Support,
Other,
}
public sealed record Triage(
[property: Description("Which team should handle `body`?")] Team Team,
[property: Description("How urgent is this?"), Range(1, 3)] int Urgency,
bool NeedsHuman);
[JsonSourceGenerationOptions(UseStringEnumConverter = true)]
[JsonSerializable(typeof(Triage))]
internal partial class AppJson : JsonSerializerContext;
Triage t = agent.Decide(email, AppJson.Default.Triage); // AOT 安全配合源生成的 JsonSerializerContext,整条链路在 Native AOT 下可发布、可裁剪——四个核心包都标记了 IsAotCompatible。
NLaya 不是玩具移植,它把 Python 库的工程化能力几乎搬全了:
PredictBatch 共享前向传播,PredictStreamAsync 对接数据库游标、CSV、消息队列,内存有界;MicroBatchingPredictor 把并发请求攒几毫秒合成一个 batch,这是 GPU 吞吐的正确打开方式;AddLaya / AddLayaRouter 注册单例,tracing 和 metrics 走标准 ActivitySource / Meter,OpenTelemetry 零配置接入;UseLayaGuardrail 在 LLM 看到用户输入之前先做护栏筛查,LayaRouterChatClient 用一道 choice 题给请求选模型,还能把判断能力作为工具挂进 Agent 循环;mlContext.Transforms.Laya(...) 直接在 IDataView 上追加答案列,CSV 进、分好类的表出。最后一个场景特别值得展开:用 Laya 给大模型当门卫和路由。每个用户请求先过一道几十毫秒的判断——该不该拦、该给哪个模型答——只有真正需要生成和推理的请求才触达大模型。这正是 Laya 内置 router / guard / moderation 三个 preset 的设计意图,也是 1Panel AI 网关用 Laya Server 替代 Embedding 相似度路由的同款思路:路由规则用自然语言写成 criteria,改一行字就能调整路由行为,不需要标注样本、重建向量、重新部署。
layar(nullean 出品)是另一个独立移植,气质和 NLaya 很不一样:它不做生态集成,而是把内核做到极简,然后认真回答一个工程问题——ONNX Runtime 和 TorchSharp 到底哪个快?
它的设计里有几个点很见功力:
Layar.Tokenization 直接读 Hugging Face 的 tokenizer.json(byte-level GPT-2 式 + Metaspace/SentencePiece 式都支持),不依赖任何外部分词包,运行时零 Python;Layar.Onnx 和 Layar.TorchSharp 都实现 IDecisionBackend,可以在同一批请求上互相对打基准;TorchSharp 后端加载的是 torch.jit.trace 导出的 TorchScript 模块,而不是手工移植的网络结构;IQuestionSchema<T> 把固定问题集和答案绑回 record 的方式合在一起,PredictAsync 直接返回 TriageResult,不用字符串 key 去查字典;Shortlist,精确对齐 Python 的 shortlist.py,连 NaN 和零向量的边界行为都对齐)、按需下载 checkpoint 的 CheckpointCache(库版本和模型版本用两套独立 tag 发布,互不惊动)。最硬的是它的验证方式:两个后端都与 Python 原版的输出逐位对齐(置信度一致到 1e-6),而且是提交进仓库的回归测试,不是一次性的手工核对。 BenchmarkDotNet 实测(Apple M2、CPU、多语言 checkpoint、一次 3 题批量请求):
后端 | 均值 | 相对 | 分配内存 |
|---|---|---|---|
TorchSharp | 70.9 ms | 0.48x | 85.3 KB |
ONNX Runtime | 148.7 ms | 1.00x | 82.6 KB |
TorchSharp 在这里快约 2.1 倍——作者还坦白交代了 ONNX 侧的 SessionOptions 调优没能补上差距。两个后端还各自带一个 Native AOT 冒烟测试,本地已验证 AOT 二进制能加载真实 checkpoint 并给出正确预测。
你的需求 | 选谁 |
|---|---|
要 DI、OpenTelemetry、ML.NET、Extensions.AI 护栏/路由等生态集成 | NLaya,全家桶开箱即用 |
想要极简内核、自己控制依赖、或在两个后端间做性能对比 | layar,接口更小,基准数据现成 |
重度 AOT 场景 | 两者都已验证 AOT 兼容 |
不想引入外部分词依赖 | layar(分词器从零自研) |
一个赛道出现两个高质量移植不是坏事——它们的 parity 测试互为交叉验证,等于给"这个判断引擎在 .NET 里到底靠不靠谱"上了双保险。
NLaya 和 layar 是"移植",而 TensorSharp 和 Sezika 代表的是"原生实现"——它们共享同一套 Choice / Score / Boolean 契约,但引擎几乎是反着走的。
先看上游发生了什么。9 月 22 日,vLLM 合并了 PR #57250,把 Google 的文本扩散模型 DiffusionGemma 改造成了一台结构化读取引擎:扩散模型生成文本时在一块叫 canvas 的区域里反复去噪,vLLM 把这个机制反过来用——canvas 缩到 16 或 32 宽,预填一份答案模板(如 "billing": "?"),只在答案位置留 mask,然后只跑一步去噪,直接读空位上各候选词的 logprobs。全程没有逐 token 生成,自然没有格式错误和重试。
TensorSharp(纯 .NET 的本地 LLM 推理引擎)第二天就把这套机制搬进了 .NET,实现了 Jev 兼容的 POST /v1/systemone,底层跑 26B-A4B 的 DiffusionGemma,并且做了两件原型没有的事:
项目自带的红绿灯例子设计得很巧:state 文本只说"车辆正在接近路口",一个字没提灯,但请求里嵌了一张绿灯照片——模型回答绿灯、不用停车,答案只能来自像素。更妙的是文档自带反事实实验:同一个请求去掉图,模型会很有把握地答"红灯,必须停车"。
安全细节也到位:服务器没加载视觉塔时收到带图请求返回 503 明确拒绝;远程图片 URL 和本地文件路径一律拒收,免得推理服务器被当成免费代理。
IoTSharp 团队的 Sezika 走的是完全相反的方向:既然只要一个判断,为什么要用 26B 的生成模型?
它的底座是 mmBERT-base 级别的多语言编码器(正是 Laya multilingual 的同款基座),上面接类型化决策头。输入进来编码器过一遍,决策头直接给每个候选打分——没有 canvas,没有去噪,连"一步"都不需要。
这个项目最吸引人的是它的纯粹程度:整个推理栈都是 C# 写的——embedding、attention、RoPE、归一化、MLP、决策头,CPU 上有标量 FP32 / SIMD FP32 / W8A32 量化三条路径,不碰 Python、PyTorch、ONNX Runtime。GPU 路径的做法在 .NET 圈很少见:构建期用 ILGPU 把 kernel 编译成 PTX,运行时 Native AOT 程序通过静态绑定直接调 CUDA Driver,绕开了与 AOT 不兼容的运行时代码生成,cuBLAS、cuDNN 一概不用。
性能数据给得很克制:Core Ultra 9 185H + RTX 4070 Laptop、CUDA FP32 后端,1 个问题热路径约 40 ms,32 个约 1.23 秒。而且作者把限定条件写得很全:只预热一次、采样五次,明说"五次采样不足以证明稳定的尾延迟";W8A32 量化目前比 SIMD 还慢,照实写;所有输出标记为 uncalibrated,没校准就是没校准。这种"丑话说在前面"的文档风格,在人人吹 benchmark 的年代挺少见。
两边的请求骨架(model / state / questions / criteria)几乎同构,但有几个语义差异比字段差异更值得注意:
confidence 和 concentration 不是一个东西。 TensorSharp 的 confidence 是条件分布的最大概率;Sezika 的 concentration 是归一化熵算出的集中度。同一个请求两边跑出来的数没有可比性,别混用。samples: "auto",条件熵超阈值自动触发),给平均值和多次读取间的标准误;Sezika 是确定性的,一次前向一个答案,但把"信不信得过"做成协议的一部分——低于浓度门槛标记 abstained,由调用方决定怎么办。一个靠"多想几遍",一个靠"明说不敢答"。场景 | 建议 |
|---|---|
想要 Laya 完整生态:Router、校准、ML.NET、Extensions.AI | NLaya,移植保真度最高,DI/AOT 全套支持 |
想要极简内核、自研分词器、双后端性能对比 | layar,ONNX 与 TorchSharp 任选,逐位对齐 Python |
高频低延迟的本地判断:意图识别、路由、RAG 重排 | Sezika,毫秒级,AOT 编译嵌进进程即可 |
需要大模型的世界知识和语义理解 | TensorSharp Jev |
答案在图里:质检、票据、红绿灯这类视觉判断 | TensorSharp Jev,目前只有它支持图片 |
业务流程里需要显式拒答和校准状态 | Sezika,协议自带 abstained / calibration |
不确定时希望模型自己多算几遍 | TensorSharp Jev 的 samples: "auto" |
引擎在别处(云端 Jev / Laya Server),只要一个顺手的调用方 | SystemOneSharp,一个客户端通吃所有兼容服务 |
其实四者并不冲突。接口契约几乎一样,完全可以分层用:Sezika、NLaya 或 layar 放前面做高频路由和初筛,拿不准的、需要看图或者需要常识的,再丢给 TensorSharp Jev 或大模型。小引擎守门口,大引擎做终审。
前面四个项目都是"引擎"——模型在你这边跑。但还有一种同样常见的形态:引擎在别处(TypeSafe 的云端 Jev、1Panel 的 Laya Server、自己部署的 TensorSharp /v1/systemone),你的程序只是调用方。这层需求由 SystemOneSharp 补上:一个 .NET 10 的 System One API 类型化客户端,零运行时依赖,同一个 POST /v1/systemone 请求,云端 Jev 和本地 Laya 服务器通吃。
它的存在本身就是"接口契约统一"这条主线最好的注脚:因为大家都讲同一套协议,一个客户端就够了。
using SystemOneSharp;
using var http = new HttpClient();
ISystemOneClient client = new SystemOneClient(http, new SystemOneOptions
{
ApiKey = Environment.GetEnvironmentVariable("TYPESAFE_API_KEY")
});
var request = new SystemOneRequestBuilder()
.WithState("Please refund my duplicate charge")
.AddChoice("team", "Which team should handle this?", choice => choice
.Option("billing", "Payments and refunds")
.Option("support", "Product issues"))
.AddScore("urgency", "How urgent is this?", score => score
.Level("routine").Level("soon").Level("critical"))
.AddNoul("refund", "Does the customer ask for a refund?")
.Build();
var response = await client.DecideAsync(request);
var team = response.GetChoice("team");
var refundProbability = response.GetNoul("refund").Noul;切到本地 Laya Server 只需把 BaseUri 指向 http://127.0.0.1:8000、按需省略 ApiKey——改一行配置就换了供应商。
客户端库的分量不在 Demo,在边角处理,SystemOneSharp 这几点做得挺扎实:
Retry-After,失败时抛出区分传输层、API 层、协议层的类型化异常;DecideAsync 走名为 SystemOneSharp 的 ActivitySource,采用 OpenTelemetry GenAI 属性命名(模型、各类型问题数、重试次数、token 用量、结果),且明确承诺不记录 state、instructions、criteria、答案和 API Key;WithState 可传 JsonNode / JsonElement / 任意 C# 对象,配合 JsonSerializerContext 的 JsonTypeInfo<T> 可完全避开反射;routing)收进 AdditionalProperties 原样保留,不做强行校验——这正是"客户端不该比协议更聪明"的正确姿势。另外还有三个可选包对接微软 AI 栈:SystemOneSharp.Extensions.AI(把 ChatMessage 对话规范地投影成 System One state)、SystemOneSharp.Extensions.AI.Evaluation(一次请求产出多个 MEAI 评估指标)、SystemOneSharp.AgentFramework(给 Microsoft Agent Framework 提供 LoopEvaluator 和函数调用中间件)。
要说明一点:它是社区维护的非官方客户端,与 TypeSafe 和 Laya 官方都没有隶属关系。
前面讲的都是引擎和客户端。真正的问题来了:把概率模型的输出接进一个正在跑业务的 Agent 系统,需要多少工程护栏? 自托管 Agent 运行时 OpenClaw.NET 的 PR #246(贡献者 Telli,《feat: add hosted Jev and local Laya decision routing》)给出了一份教科书级的答案——它把托管 Jev 和本地 Laya 同时接进了回合路由层,而其工程形态远比"接一个 API"复杂。
OpenClaw.NET 原本就有一套 ONNX 小模型做的动态回合路由:每个用户回合被分类到 T0–T3 四个成本层级,路由结果投影到模型档案、工具过滤和推理档位上。这套基线的文档诚实得近乎自贬——Macro-F1 只有约 0.65,并配了一张严苛的上线门禁表(标注集 Macro-F1 ≥ 0.90、路由附加延迟 p95 ≤ +80ms、任何 Sev1 事故立即回滚)。换言之,在 PR #246 之前,路由的骨架与门禁文化已经在了,缺的只是一个更好的"判断器官"——ONNX 小模型是第一代,Jev/Laya 是第二代候选。
PR 为 Jev 设计了版本化 rubric openclaw-tiers-v1,一次请求并行问三道题:tier(T0–T3/abstain 的 Choice)、high_risk(Noul)、requires_tools(Noul)——正是 Jev"一次前向并行多题"能力的最小生产形态。送往云端的 state 只含当前请求和至多四条最近消息,脱敏管道先行。
可靠性设计的密度令人印象深刻,值得逐条列出:
abstain、不确定、未知层级、模型版本不符、凭据缺失、HTTP 错误、超时——全部收敛到同一个动作:保留基线;DowngradeMinConfidence 0.95 对 MinConfidence 0.80,外加前两名概率间距 ≥ 0.15);本地路径回答的是"数据不能出厂"的同一个问题。Laya 服务被做成独立 Python 进程(PyTorch 刻意挡在 .NET 网关和 NativeAOT 二进制之外),只听 127.0.0.1,同一时刻只处理一条推理;模型资产钉死 Hugging Face 修订号、生成 SHA-256 清单、强制离线模式。最重的投入在校准:运营者必须先用自有标注数据按检查点/题型/选项数分桶拟合温度参数,把校准产物的 SHA-256 写进配置,active 模式强制要求响应中的校准 ID 与配置匹配——不达标就永远停在影子模式。这直接回应了 Laya 出厂 ECE 高达 0.466 的已知短板。
PR 的发货默认值是 disabled;启用后的第一站是 shadow 模式——基线决策原样返回,但系统在有界期限内等待一次真实评估,把基线层级、提案层级、概率、置信度、延迟与估算成本追加进元数据日志(刻意不记录对话文本和凭据)。配套 Python 评估脚本计算准确率、混淆矩阵、ECE 与风险-覆盖曲线;无标注时报告明确不做任何准确率断言。回滚是一行配置加重启。
这套设计的立场可以概括成一句话:决策模型做提案,工程约束做裁决。 每次决策无论采纳与否都可事后重放——"模型当时为什么这么想"是事实,不是猜测。
同样需要如实记录边界:PR 只覆盖回合路由——Agent 系统里频率最高、爆炸半径最小的决策点;技能选择、记忆重排、心跳分诊被列为未来工作。先在最安全的焊点上建好"决策模型 + 护栏 + 日志 + 校准"的全套规程,再向高风险焊点推广——这个顺序本身,就是给所有想把判断引擎接入业务的团队最有价值的示范。
PR #246 留下的唯一遗憾是:本地路径的核心部件还是 Python——tools/laya_service 是个独立的 Python 进程,评估脚本也是 python3 scripts/evaluate-jev-routing.py。当时的理由是"把 PyTorch 刻意挡在 .NET 网关和 NativeAOT 二进制之外",但 NLaya 的成熟让这道隔离墙失去了存在的必要。
紧随其后的 PR #255(贡献者 geffzhang,《Laya to dotnet》)干的就是这件事:把 tools/laya_service 整体迁移为 .NET 10 + NLaya 的工具链。PR 里的迁移计划覆盖了八个任务:CLI 脚手架、安全的 loopback 协议与服务行为、模型下载与清单校验、运行时集成、评估/校准/报告的逐项对齐、CI 与解决方案接线、Python 清理、文档更新。落地后的对比非常直观:
# 之前(PR #246):Python 评估脚本
python3 scripts/evaluate-jev-routing.py ./jev-decisions.snapshot.jsonl --output /tmp/jev-report.json
# 之后(PR #255):同一条命令,纯 .NET
dotnet run --project tools/laya_service -c Release -- report \
./jev-decisions.snapshot.jsonl --output /tmp/jev-report.json测试入口也同步从 python3 -m unittest 换成了 dotnet test。细节上的较真程度延续了 PR #246 的风格:校准拟合时拒绝非数值的原始概率、强制答案概率为 [0,1] 有限值、概率和允许 ±0.002 的舍入漂移并在缩放前重新归一化;CLI 把 checkpoint_not_installed、requested_device_unavailable 等启动失败映射为明确的退出码和 stderr 输出,不让运维者猜。
这一步的意义超出"少装一个 Python":NLaya 从社区移植变成了被生产级 Agent 运行时选中的正式底座,而整条决策路由链路——引擎、服务、校准、评估、报告——第一次完整收敛到了纯 .NET 技术栈内,Native AOT 发布、单一工具链构建、统一的可观测性全都顺理成章。回头看,这正是 PR #246 把决策客户端抽象成 OpenClaw.Routing.Decisions 时就铺好的路:契约对了,引擎换心只是配置问题。
判断引擎这类东西,接入业务的门槛从来不在 API 好不好调,而在你敢不敢信它。无论你选哪条路线,这五件事绕不开:
conf >= 0.85 这种阈值没有意义。对照上一章 OpenClaw.NET 的做法会发现,这五条清单在它的 PR 里全部硬化成了配置和代码:校准对应"校准 ID 强制匹配",兜底对应"一切异常收敛到保留基线"。清单是态度,护栏是实现,两者都要。
最后记住那条最重要的数学规律:成本优势的上限 = 1 / 弃权率。 真正决定 ROI 的,从来不是"一次判断多少钱",而是"你自己的数据能把弃权率压到多少"。
模型给出的概率只是概率,没校准过就不是正确率。这几个项目至少在这点上都很清醒——先把丑话写进协议里,再谈怎么用。判断合法不等于判断正确,动不动手,永远由调用方决定。
参考资料
文中性能数据均来自各项目公开文档,测试条件以原文为准。