返回列表
🧠 阿头学 · 💬 讨论题

决策模型实战:阈值调优与概率校准决定评估成败

决策模型并非开箱即用的廉价替代品,其实际效能完全取决于业务约束驱动的阈值调优与概率校准,盲目套用默认参数将导致评估系统失效。
打开原文 ↗

2026-10-21 原文链接 ↗
阅读简报
双语对照
完整翻译
原文
讨论归档

核心观点

  • 默认阈值是性能陷阱:0.50的API默认阈值无视数据分布差异,直接套用必然引发严重漏报或误报,必须依据业务容忍度重新划定决策边界。
  • 成本与性能呈非线性替代关系:廉价决策模型经阈值优化后可逼近昂贵模型的性能,但突破性能瓶颈仍需支付数倍溢价,选型必须锚定帕累托前沿而非盲目追高。
  • 原始概率分数不可直接作为置信度:模型输出的概率值普遍存在系统性偏差,未经统计校准直接用于自动化路由将引发决策灾难,必须引入映射方法还原真实风险。
  • 传统机器学习技能是AI评估的压舱石:交叉验证与指标架构能力在AI时代价值重估,掌握这些工程化调参方法的团队能实现评估系统的规模化降本。

跟我们的关联

  • 对 Neta 意味着评估流水线必须从“跑分竞赛”转向“业务约束对齐”,下一步应在自动化测试中强制引入分层交叉验证与阈值寻优模块,彻底替代硬编码的截断逻辑。
  • 对 ATou 意味着模型选型不能仅看基座能力,下一步需建立成本-性能帕累托监控面板,按实际误报与漏报代价动态切换决策模型,实现Token预算的精准压降。
  • 对 Uota 意味着概率输出必须经过校准才能作为路由依据,下一步需部署统计校准服务,将失真分数转化为可计算的置信度预算,直接支撑分级干预架构。

讨论引子

  • 当业务场景对误报和漏报的容忍度极度不对称时,F1分数是否仍具备指导意义,应如何设计自定义代价函数来替代?
  • 在决策模型快速迭代的背景下,如何构建一套不依赖特定API版本、可自动重校准阈值与概率的MLOps流水线?
  • 面对仅百条样本的冷启动评估,如何平衡统计显著性要求与快速上线的业务压力,避免过拟合调参?

与 Antaripa Saha 合著。 决策模型最近异军突起,首先是 TypeSafe 的 Jev,接着是 Perplexity 的 Decider、Liquid 的 d1、Cloudflare 的 Clef 以及 OpenAI 的 Decisions API。这些模型承诺提供快速且廉价的分类。

对于自动化评估而言,决策模型是 LLM 评判器的一种极具吸引力的低成本替代方案。但它们是否是正确的选择,取决于你具体用例的成本、性能和延迟要求。

我们还注意到,人们在使用 0.50 的默认决策阈值时,并没有检查它是否适合他们的任务。这可能会错失大量性能。

在这篇文章中,我们将首先展示如何调整阈值以从决策模型中获得最佳结果。在调整好阈值后,我们将比较成本和性能,以帮助你评估哪种模型适合你的用例。最后,我们将探讨在决定是否采取行动时如何解释模型分数,以及校准如何使它们作为置信度估计更具实用性。

下方的图表为静态预览。为了获得最佳阅读体验,我建议在 hamel.dev 上阅读本文,你可以在这里探索交互式图表。

Read on hamel.dev

我们测试了什么

我们使用了来自公寓租赁代理的 100 条轨迹,即对话和操作的日志。任务是识别包含错误的轨迹,例如预订失败或遗漏了向人工的交接。我们在之前关于自动化评估的文章中使用了相同的数据集和广泛的故障查找任务。在这里,每个决策模型都会为每条轨迹返回一个故障概率。

我们测试了 Jev 1.13、Perplexity Decider 1.1 27B、Liquid d1 和 d1-ultra、Cloudflare Clef Flash 以及 OpenAI Decisions (GPT-6 Luna)。每个模型都收到了相同的评估标准。附录展示了提示词以及 Jev 使用的结构化版本。

我们从两个维度比较模型:

  • 性能:我们将每个模型的预测与人工审核并标记为通过或失败的轨迹进行比较。这衡量了模型在避免误报的同时捕获故障的能力。
  • 成本:我们根据 token 使用量和提供商的价格计算 API 成本。

为简洁起见,我们将延迟放在一边,因为它取决于本实验无法控制的因素。

理解决策阈值

我们为每条轨迹分配通过或失败两个标签之一。这是一个二元的通过/失败评估,我们推荐这种做法,因为它能鼓励制定清晰的标准,并且更容易与人类偏好保持一致。

每个决策模型都会返回一个 0 到 1 之间的分数,用于估计轨迹包含故障的可能性。API 使用 0.50 的默认决策阈值将这个分数转化为决策。分数达到或超过阈值的轨迹会被标记为故障。在不根据你的数据检查的情况下使用此默认值,可能会给你带来平庸的结果。

例如,一个模型可能会给每个失败项打 0.40 分,给每个通过项打 0.10 分。尽管这个模型在 0.30 的阈值下能完美分类轨迹,但 0.50 的默认阈值将什么也抓不到。

在实践中,失败和通过轨迹的分数会有重叠。降低阈值可以捕获更多故障,但也可能引发更多误报。选择阈值意味着要平衡:

  • 精确率:包含已知故障的被标记轨迹的比例。
  • 召回率:模型捕获的已知故障的比例。

正确的权衡取决于你的业务目标。例如,刑事审判对定罪设定了高标准,以避免冤枉无辜的人,这强调了精确率。癌症筛查可能强调召回率,接受更多误报以减少漏诊病例。在本次比较中,我们将使用 F1 分数(即两者的调和平均数)赋予精确率和召回率同等的权重。F1 分数范围从 0 到 1,越高越好。

下方的曲线展示了每个模型如何对失败轨迹(橙色)和通过轨迹(蓝色)进行评分。曲线较高的部分意味着该类别的更多分数落在该范围内。达到或超过虚线阈值线的分数将被标记。使用切换按钮比较 0.50 与“为最佳 F1 优化”,后者会为每个模型选择一个阈值。

Explore the interactive chart on hamel.dev

OpenAI 在此任务上最好地分离了这两个类别,这也反映在其较高的 F1 分数上。

同样重要的是,不要盲目追求 F1 最大化,而不考虑分类器要发挥作用所需的最低要求。例如,如果此分类器用于自动警报系统,我会希望精确率和召回率都至少达到 80%,以便在捕获足够数量故障的同时尽量减少误报。

下方的图表展示了精确率和召回率在不同阈值下的变化情况。阴影区域标志着两者都至少达到 80% 的范围。

Explore the interactive chart on hamel.dev

Perplexity、Liquid d1-ultra 和 OpenAI Decisions 满足了我们的要求,但我们仍然需要在阴影区域内为每个模型选择一个决策阈值。在下一节中,我们将讨论如何调整这些阈值。

调整决策阈值

在这个例子中,我们将优化 F1 分数。对于我们的候选模型,我们将尽可能把调整限制在训练数据上同时满足 80% 最低要求的阈值内。我们让其他模型不受约束,以说明调整的效果。

有了标记好的轨迹和保存的模型分数,你就可以让编程代理来调整阈值。以下是一个示例提示词,你可以根据自己的用例进行修改:

Using my saved model scores and binary labels (1 means failure), tune a decision threshold to maximize F1 while requiring at least 80% precision and 80% recall. Use stratified five-fold cross-validation, choosing thresholds on the training examples and measuring performance on the remaining examples. If comparing models, use the same splits for each. Only select thresholds that meet both minimums on the training examples. If none qualifies, report that rather than relaxing either requirement. Report validation precision, recall, and F1, along with any splits where no threshold qualified or validation performance fell below my minimums. Compare with the default threshold of 0.50. Reuse the saved scores without making new model calls.

以下是为 F1 进行调整后每个模型性能的变化。我们将精确率-召回率图表中的三个候选模型组合在一起,并以灰色显示其他模型,以说明调整能带来多大帮助。

Explore the interactive chart on hamel.dev

我们的候选模型从 F1 调整中获得的收益不如 Liquid d1 和 Clef Flash 大,后两者未达到我们的最低要求。然而,即使是精确率和召回率的微小变化也会影响业务成本或用户体验,因此要有目的地选择权衡。在实践中,精确率和召回率不太可能同等重要(正如我们在本文中假设使用 F1 那样)。例如,F-beta 分数允许你在 beta 低于 1 时赋予精确率更高权重,或在 beta 高于 1 时赋予召回率更高权重。优化不同的指标可能会导致不同的阈值和结果。

性能与成本的对比

下方的图表比较了每个模型的 F1 分数(调整后)与成本,重点关注我们剩余的候选模型:

Explore the interactive chart on hamel.dev

虚线显示了剩余候选模型之间的帕累托前沿。对于任何给定的 F1 分数,它都能确定达到或超过该分数的最廉价候选模型。这张特定的图表看起来有些简单,因为只有三个选项,但它对于如何思考成本与性能的对比是一个有用的说明。

Perplexity 是这些候选模型中最便宜的,每 100 次评估运行花费 0.82 美分,F1 分数为 0.79。OpenAI Decisions 达到了 0.87 的 F1 分数,每 100 次评估运行花费 3.72 美分,成本大约是前者的 4.5 倍。

我们能信任这些概率吗?

除了通过/失败评估之外,你可能还想将决策模型的分数用作置信度估计。但是,你能从字面上理解这个数字吗?如果 0.80 的分数意味着 80% 的故障概率,那么大约 80% 具有相似分数的轨迹应该包含故障。

下方的图表展示了每个模型的概率校准:其预测概率与观察到的结果有多接近。每个点代表大约 20 条具有相似分数的轨迹。对角线上的点意味着预测概率与该组的故障率相匹配。在线上方,模型低估了故障风险。在线下方,模型高估了该风险。

如果预测概率与观察到的结果不匹配,我们可以使用另一个模型来调整它们。第二个面板展示了一种方法,即 Platt scaling,它通过拟合一条简单的曲线来进行调整。

Explore the interactive chart on hamel.dev

Perplexity、Liquid d1 和 Clef Flash 在校准后更接近对角线。例如,一组 Clef Flash 轨迹的平均预测故障概率约为 8%,但实际有 60% 包含故障。

Jev 和 Liquid d1-ultra 在这个数据集上已经校准得很好,几乎不需要调整。

Platt scaling 并不总是最合适的。例如,OpenAI 的概率在使用我们这版 Platt scaling 后变得稍微差了一些。你可以用以下提示词指导你的编程代理执行校准:

Using my saved failure probabilities and binary labels (1 means failure), test whether Platt scaling improves each model’s probabilities. Use stratified five-fold cross-validation with the same splits for every model. Fit the calibration mapping on each training split and apply it to the remaining traces. Fit on log-odds, clipping probabilities away from 0 and 1 first. Plot predicted probabilities against observed failure rates before and after calibration. Report Brier scores (lower is better), and explain which models improved or got worse. Reuse the saved scores without making new model calls.

提示词调整与评估器范围

我们在本文中保持了评估任务的宽泛性,以便更容易看出模型之间的差异。在实践中,我们建议为每种故障模式构建一个单独的评判器。在任务更窄、指导更具体的情况下,我们预计模型之间的性能差距会缩小。

我喜欢先从一个在任务上具有可接受性能的较大模型开始,然后尝试较小的模型,看看它们是否能以更低的成本匹配较大模型的性能。如果你有高质量的标签,提示词调整也会很有帮助。我在本文中讨论了如何编写一个好的评判器提示词。

将这些想法付诸实践

对于这个特定的数据集,OpenAI 以较高的成本实现了突破性的性能。Perplexity 和 Liquid d1-ultra 提供了成本更低、性能也更低的选项。我们观察到,模型性能在不同的任务和数据集之间差异很大,这意味着自己进行测量非常重要。

总结一下:

  • 一旦你对评判器的提示词感到满意,就为你的用例设定最低的精确率和召回率要求。排除那些无法满足这些要求的模型。
  • 在比较性能之前,调整每个模型的决策阈值。选择符合你业务目标的精确率-召回率权衡。要有目的性,不要仅仅因为本文使用了 F1 分数就去使用它!
  • 如果你想将分数用作置信度估计,请检查概率是否与观察到的结果相匹配。校准可能会有所帮助,但你需要衡量它是否真的带来了改善。
  • 根据你的工作负载权衡性能与成本和延迟的关系。

这个过程是迭代的。你可能会改进提示词,然后需要再次调整阈值或重新校准。在这篇文章中,我们重点关注阈值调整和校准,因为我们看到许多人在使用决策模型时直接开箱即用,而没有考虑这些因素。

你可能还想知道决策模型与 LLM 相比如何。OpenAI Decisions 使用了 GPT-6 Luna,它以最高的成本交付了我们最高的 F1 分数,这表明更难的评估任务可能值得为 LLM 支付更多费用。在更广泛的比较中,Randy Olson 发现决策模型可以在分类和路由方面以更低的成本匹敌 LLM,而 LLM 则在需要知识和推理的任务中脱颖而出。

本文讨论的想法都不是新事物。选择精确率-召回率权衡、调整阈值以及比较性能与成本,是评估任何分类器的一部分。要了解更多关于这些基础知识的内容,我推荐阅读 Kyle Gallatin 和 Chris Albon 编写的《Machine Learning with Python Cookbook, 2nd Edition》。

最后,如果你是一名数据科学家或机器学习工程师,想知道自己在 AI 时代的位置,这些技能你已经具备了。是时候“复仇”了 🙂。

P.S. 我会在我的时事通讯中分享此类文章以及我在构建 AI 产品方面的学习心得。要了解更多关于评估的内容,Shreya Shankar 和我合著了由 O’Reilly 出版的《AI Evals for AI Engineers》。

P.P.S. 这篇文章有很多脚注。你可以在 hamel.dev 上阅读它们。

Co-authored with Antaripa Saha.

与 Antaripa Saha 合著。

Decision models have exploded onto the scene recently, starting with TypeSafe’s Jev, followed by Perplexity’s Decider, Liquid’s d1, Cloudflare’s Clef, and OpenAI’s Decisions API. These models promise fast, inexpensive classification.

决策模型最近异军突起,首先是 TypeSafe 的 Jev,接着是 Perplexity 的 Decider、Liquid 的 d1、Cloudflare 的 Clef 以及 OpenAI 的 Decisions API。这些模型承诺提供快速且廉价的分类。

Decision models are an appealing low-cost alternative to LLM judges for automated evals. But whether they’re the right choice depends on the cost, performance, and latency requirements of your use case.

对于自动化评估而言,决策模型是 LLM 评判器的一种极具吸引力的低成本替代方案。但它们是否是正确的选择,取决于你具体用例的成本、性能和延迟要求。

We’ve also noticed people using the default decision threshold of 0.50 without checking whether it works well for their task. This can leave a lot of performance on the table.

我们还注意到,人们在使用 0.50 的默认决策阈值时,并没有检查它是否适合他们的任务。这可能会错失大量性能。

In this post, we’ll start by showing how to tune thresholds to get the best results from decision models. With those thresholds tuned, we’ll compare cost and performance to help you assess which model fits your use case. Finally, we’ll explore how to interpret model scores when deciding whether to act, and how calibration can make them more useful as confidence estimates.

在这篇文章中,我们将首先展示如何调整阈值以从决策模型中获得最佳结果。在调整好阈值后,我们将比较成本和性能,以帮助你评估哪种模型适合你的用例。最后,我们将探讨在决定是否采取行动时如何解释模型分数,以及校准如何使它们作为置信度估计更具实用性。

The charts below are static previews. For the best reading experience, I recommend reading this post on hamel.dev, where you can explore the interactive charts.

下方的图表为静态预览。为了获得最佳阅读体验,我建议在 hamel.dev 上阅读本文,你可以在这里探索交互式图表。

Read on hamel.dev

What we tested

我们测试了什么

We used 100 traces, or logs of conversations and actions, from an apartment leasing agent. The task is to identify traces containing an error, such as a failed booking or a missed handoff to a human. We used the same dataset and broad failure-finding task in our prior post on automated evals. Here, each decision model returns a failure probability for each trace.

我们使用了来自公寓租赁代理的 100 条轨迹,即对话和操作的日志。任务是识别包含错误的轨迹,例如预订失败或遗漏了向人工的交接。我们在之前关于自动化评估的文章中使用了相同的数据集和广泛的故障查找任务。在这里,每个决策模型都会为每条轨迹返回一个故障概率。

We tested Jev 1.13, Perplexity Decider 1.1 27B, Liquid d1 and d1-ultra, Cloudflare Clef Flash, and OpenAI Decisions (GPT-6 Luna). Each model received the same evaluation criteria. The appendix shows the prompt and the structured version used for Jev.

我们测试了 Jev 1.13、Perplexity Decider 1.1 27B、Liquid d1 和 d1-ultra、Cloudflare Clef Flash 以及 OpenAI Decisions (GPT-6 Luna)。每个模型都收到了相同的评估标准。附录展示了提示词以及 Jev 使用的结构化版本。

We compare models on two dimensions:

我们从两个维度比较模型:

Performance: We compare each model’s predictions with traces that people have reviewed and labeled as passing or failing. This measures how well the model catches failures while avoiding false alarms.

  • 性能:我们将每个模型的预测与人工审核并标记为通过或失败的轨迹进行比较。这衡量了模型在避免误报的同时捕获故障的能力。

Cost: We calculate API cost from token usage and the provider’s prices.

  • 成本:我们根据 token 使用量和提供商的价格计算 API 成本。

For brevity, we leave latency aside because it depends on factors outside this experiment’s control.

为简洁起见,我们将延迟放在一边,因为它取决于本实验无法控制的因素。

Understanding decision thresholds

理解决策阈值

We assign each trace one of two labels, pass or fail. This is a binary pass/fail eval, which we recommend because it encourages clear criteria and is easier to align with human preferences.

我们为每条轨迹分配通过或失败两个标签之一。这是一个二元的通过/失败评估,我们推荐这种做法,因为它能鼓励制定清晰的标准,并且更容易与人类偏好保持一致。

Each decision model returns a score from 0 to 1 that estimates how likely a trace is to contain a failure. The APIs turn this score into a decision using a default decision threshold of 0.50. Traces with scores at or above the threshold are flagged as failures. Using this default without checking it against your data can give you subpar results.

每个决策模型都会返回一个 0 到 1 之间的分数,用于估计轨迹包含故障的可能性。API 使用 0.50 的默认决策阈值将这个分数转化为决策。分数达到或超过阈值的轨迹会被标记为故障。在不根据你的数据检查的情况下使用此默认值,可能会给你带来平庸的结果。

For example, a model could assign every failure a score of 0.40 and every passing trace a score of 0.10. Even though this model classifies traces perfectly at a threshold of 0.30, the default threshold of 0.50 would catch nothing.

例如,一个模型可能会给每个失败项打 0.40 分,给每个通过项打 0.10 分。尽管这个模型在 0.30 的阈值下能完美分类轨迹,但 0.50 的默认阈值将什么也抓不到。

In practice, scores for failures and passing traces will overlap. Lowering the threshold catches more failures but can also raise more false alarms. Choosing a threshold means balancing:

在实践中,失败和通过轨迹的分数会有重叠。降低阈值可以捕获更多故障,但也可能引发更多误报。选择阈值意味着要平衡:

Precision: The share of flagged traces that contain a known failure.

  • 精确率:包含已知故障的被标记轨迹的比例。

Recall: The share of known failures that the model catches.

  • 召回率:模型捕获的已知故障的比例。

The right tradeoff depends on your business goals. For example, criminal trials set a high bar for conviction to avoid convicting innocent people, emphasizing precision. Cancer screening may emphasize recall, accepting more false alarms to miss fewer cases. For this comparison, we’ll give precision and recall equal weight using the F1 score, their harmonic mean. F1 ranges from 0 to 1, and higher is better.

正确的权衡取决于你的业务目标。例如,刑事审判对定罪设定了高标准,以避免冤枉无辜的人,这强调了精确率。癌症筛查可能强调召回率,接受更多误报以减少漏诊病例。在本次比较中,我们将使用 F1 分数(即两者的调和平均数)赋予精确率和召回率同等的权重。F1 分数范围从 0 到 1,越高越好。

The curves below show how each model scores failures (orange) and passing traces (blue). Taller parts of a curve mean more scores from that class fall in that range. Scores at or above the dashed threshold line are flagged. Use the toggle to compare 0.50 with Optimized for best F1, which chooses a threshold for each model.

下方的曲线展示了每个模型如何对失败轨迹(橙色)和通过轨迹(蓝色)进行评分。曲线较高的部分意味着该类别的更多分数落在该范围内。达到或超过虚线阈值线的分数将被标记。使用切换按钮比较 0.50 与“为最佳 F1 优化”,后者会为每个模型选择一个阈值。

Explore the interactive chart on hamel.dev

OpenAI separates the two classes best on this task, which is also reflected in its higher F1 score.

OpenAI 在此任务上最好地分离了这两个类别,这也反映在其较高的 F1 分数上。

It’s also important not to blindly maximize F1 without thinking about your minimum requirements for the classifier to be useful. For example, if this classifier is used for an automated alerting system, I would want a minimum of 80% on both precision and recall so that I’m minimizing false alarms while catching a sufficient number of failures.

同样重要的是,不要盲目追求 F1 最大化,而不考虑分类器要发挥作用所需的最低要求。例如,如果此分类器用于自动警报系统,我会希望精确率和召回率都至少达到 80%,以便在捕获足够数量故障的同时尽量减少误报。

The chart below shows how precision and recall change at different thresholds. The shaded region marks where both reach at least 80%.

下方的图表展示了精确率和召回率在不同阈值下的变化情况。阴影区域标志着两者都至少达到 80% 的范围。

Explore the interactive chart on hamel.dev

Perplexity, Liquid d1-ultra, and OpenAI Decisions meet our requirements, but we still need to choose a decision threshold within the shaded region for each model. In the next section, we’ll discuss how to tune those thresholds.

Perplexity、Liquid d1-ultra 和 OpenAI Decisions 满足了我们的要求,但我们仍然需要在阴影区域内为每个模型选择一个决策阈值。在下一节中,我们将讨论如何调整这些阈值。

Tuning the decision threshold

调整决策阈值

For this example, we’ll optimize F1. For our candidates, we’ll restrict tuning to thresholds that meet both 80% minimums on the training data whenever possible. We leave the other models unconstrained to illustrate the effect of tuning.

在这个例子中,我们将优化 F1 分数。对于我们的候选模型,我们将尽可能把调整限制在训练数据上同时满足 80% 最低要求的阈值内。我们让其他模型不受约束,以说明调整的效果。

With labeled traces and saved model scores, you can ask a coding agent to tune the thresholds. Below is an example prompt you can modify for your use case:

有了标记好的轨迹和保存的模型分数,你就可以让编程代理来调整阈值。以下是一个示例提示词,你可以根据自己的用例进行修改:

Using my saved model scores and binary labels (1 means failure), tune a decision threshold to maximize F1 while requiring at least 80% precision and 80% recall. Use stratified five-fold cross-validation, choosing thresholds on the training examples and measuring performance on the remaining examples. If comparing models, use the same splits for each. Only select thresholds that meet both minimums on the training examples. If none qualifies, report that rather than relaxing either requirement. Report validation precision, recall, and F1, along with any splits where no threshold qualified or validation performance fell below my minimums. Compare with the default threshold of 0.50. Reuse the saved scores without making new model calls.

Using my saved model scores and binary labels (1 means failure), tune a decision threshold to maximize F1 while requiring at least 80% precision and 80% recall. Use stratified five-fold cross-validation, choosing thresholds on the training examples and measuring performance on the remaining examples. If comparing models, use the same splits for each. Only select thresholds that meet both minimums on the training examples. If none qualifies, report that rather than relaxing either requirement. Report validation precision, recall, and F1, along with any splits where no threshold qualified or validation performance fell below my minimums. Compare with the default threshold of 0.50. Reuse the saved scores without making new model calls.

Here’s how tuning for F1 changed each model’s performance. We group the three candidates from the precision-recall chart together and show the other models in gray to illustrate how much tuning can help.

以下是为 F1 进行调整后每个模型性能的变化。我们将精确率-召回率图表中的三个候选模型组合在一起,并以灰色显示其他模型,以说明调整能带来多大帮助。

Explore the interactive chart on hamel.dev

Our candidate models gained less from F1 tuning than Liquid d1 and Clef Flash, which fell below our minimums. However, even a small shift in precision and recall can affect business costs or the user experience, so choose the tradeoff deliberately. In practice, it’s unlikely that precision and recall are equally important (as we assumed in this post with F1). For example, the F-beta score lets you give precision more weight with beta below 1, or recall more weight with beta above 1. Optimizing a different metric could lead to different thresholds and results.

我们的候选模型从 F1 调整中获得的收益不如 Liquid d1 和 Clef Flash 大,后两者未达到我们的最低要求。然而,即使是精确率和召回率的微小变化也会影响业务成本或用户体验,因此要有目的地选择权衡。在实践中,精确率和召回率不太可能同等重要(正如我们在本文中假设使用 F1 那样)。例如,F-beta 分数允许你在 beta 低于 1 时赋予精确率更高权重,或在 beta 高于 1 时赋予召回率更高权重。优化不同的指标可能会导致不同的阈值和结果。

Performance versus cost

性能与成本的对比

The chart below compares each model’s F1 (after tuning) against cost, focused on our remaining candidates:

下方的图表比较了每个模型的 F1 分数(调整后)与成本,重点关注我们剩余的候选模型:

Explore the interactive chart on hamel.dev

The dashed line shows the Pareto frontier among our remaining candidates. For any given F1 score, it identifies the cheapest candidate that meets or exceeds that score. This particular chart is a bit silly as there are only three options, but it’s a useful illustration for how to think about cost vs. performance.

虚线显示了剩余候选模型之间的帕累托前沿。对于任何给定的 F1 分数,它都能确定达到或超过该分数的最廉价候选模型。这张特定的图表看起来有些简单,因为只有三个选项,但它对于如何思考成本与性能的对比是一个有用的说明。

Perplexity was the cheapest of these candidates at 0.82 cents per 100 eval runs, with an F1 of 0.79. OpenAI Decisions reached an F1 of 0.87 at 3.72 cents per 100 eval runs, about 4.5 times the cost.

Perplexity 是这些候选模型中最便宜的,每 100 次评估运行花费 0.82 美分,F1 分数为 0.79。OpenAI Decisions 达到了 0.87 的 F1 分数,每 100 次评估运行花费 3.72 美分,成本大约是前者的 4.5 倍。

Can we trust the probabilities?

我们能信任这些概率吗?

Beyond pass/fail evals, you may want to use a decision model’s score as a confidence estimate. But can you take that number literally? For a score of 0.80 to mean an 80% chance of failure, roughly 80% of traces with similar scores should contain failures.

除了通过/失败评估之外,你可能还想将决策模型的分数用作置信度估计。但是,你能从字面上理解这个数字吗?如果 0.80 的分数意味着 80% 的故障概率,那么大约 80% 具有相似分数的轨迹应该包含故障。

The chart below shows each model’s probability calibration: how closely its predicted probabilities match observed outcomes. Each point represents about 20 traces with similar scores. A point on the diagonal means the predicted probability matches the failure rate in that group. Above the line, the model underestimates the risk of failure. Below it, the model overestimates that risk.

下方的图表展示了每个模型的概率校准:其预测概率与观察到的结果有多接近。每个点代表大约 20 条具有相似分数的轨迹。对角线上的点意味着预测概率与该组的故障率相匹配。在线上方,模型低估了故障风险。在线下方,模型高估了该风险。

If the predicted probabilities don’t match the observed outcomes, we can adjust them using another model. The second panel shows one method, Platt scaling, which fits a simple curve to make that adjustment.

如果预测概率与观察到的结果不匹配,我们可以使用另一个模型来调整它们。第二个面板展示了一种方法,即 Platt scaling,它通过拟合一条简单的曲线来进行调整。

Explore the interactive chart on hamel.dev

Perplexity, Liquid d1, and Clef Flash move closer to the diagonal after calibration. For example, one group of Clef Flash traces had an average predicted failure probability of about 8%, but 60% contained failures.

Perplexity、Liquid d1 和 Clef Flash 在校准后更接近对角线。例如,一组 Clef Flash 轨迹的平均预测故障概率约为 8%,但实际有 60% 包含故障。

Jev and Liquid d1-ultra were already well calibrated on this dataset and needed little adjustment.

Jev 和 Liquid d1-ultra 在这个数据集上已经校准得很好,几乎不需要调整。

Platt scaling isn’t always the best fit. For example, OpenAI’s probabilities got slightly worse with the version of Platt scaling we used. You can direct your coding agent to perform calibration with the following prompt:

Platt scaling 并不总是最合适的。例如,OpenAI 的概率在使用我们这版 Platt scaling 后变得稍微差了一些。你可以用以下提示词指导你的编程代理执行校准:

Using my saved failure probabilities and binary labels (1 means failure), test whether Platt scaling improves each model’s probabilities. Use stratified five-fold cross-validation with the same splits for every model. Fit the calibration mapping on each training split and apply it to the remaining traces. Fit on log-odds, clipping probabilities away from 0 and 1 first. Plot predicted probabilities against observed failure rates before and after calibration. Report Brier scores (lower is better), and explain which models improved or got worse. Reuse the saved scores without making new model calls.

Using my saved failure probabilities and binary labels (1 means failure), test whether Platt scaling improves each model’s probabilities. Use stratified five-fold cross-validation with the same splits for every model. Fit the calibration mapping on each training split and apply it to the remaining traces. Fit on log-odds, clipping probabilities away from 0 and 1 first. Plot predicted probabilities against observed failure rates before and after calibration. Report Brier scores (lower is better), and explain which models improved or got worse. Reuse the saved scores without making new model calls.

Prompt tuning and evaluator scope

提示词调整与评估器范围

We kept the eval task in this post broad to make the differences between models easier to see. In practice, we recommend building a separate judge for each failure mode. With a narrower task and more specific guidance, we expect the performance gaps between models to shrink.

我们在本文中保持了评估任务的宽泛性,以便更容易看出模型之间的差异。在实践中,我们建议为每种故障模式构建一个单独的评判器。在任务更窄、指导更具体的情况下,我们预计模型之间的性能差距会缩小。

I like to start with a larger model that has acceptable performance on a task and then try smaller models to see if they can match the larger model’s performance for less cost. Prompt tuning can also be helpful if you have high-quality labels. I discuss how to craft a good judge prompt in this post.

我喜欢先从一个在任务上具有可接受性能的较大模型开始,然后尝试较小的模型,看看它们是否能以更低的成本匹配较大模型的性能。如果你有高质量的标签,提示词调整也会很有帮助。我在本文中讨论了如何编写一个好的评判器提示词。

Putting these ideas into practice

将这些想法付诸实践

For this particular dataset, OpenAI had breakout performance at a higher cost. Perplexity and Liquid d1-ultra offer lower-cost options with lower performance. We have observed that model performance varies substantially across tasks and datasets, which means it’s important to measure things yourself.

对于这个特定的数据集,OpenAI 以较高的成本实现了突破性的性能。Perplexity 和 Liquid d1-ultra 提供了成本更低、性能也更低的选项。我们观察到,模型性能在不同的任务和数据集之间差异很大,这意味着自己进行测量非常重要。

To recap:

总结一下:

Once you’re satisfied with your judge’s prompt, set minimum precision and recall requirements for your use case. Rule out models that cannot meet these.

  • 一旦你对评判器的提示词感到满意,就为你的用例设定最低的精确率和召回率要求。排除那些无法满足这些要求的模型。

Tune each model’s decision threshold before comparing performance. Choose a precision-recall tradeoff that fits your business goals. Be intentional, don’t just use F1 score because this post did!

  • 在比较性能之前,调整每个模型的决策阈值。选择符合你业务目标的精确率-召回率权衡。要有目的性,不要仅仅因为本文使用了 F1 分数就去使用它!

If you want to use scores as confidence estimates, check whether the probabilities match observed outcomes. Calibration can help, but you need to measure whether it improves them.

  • 如果你想将分数用作置信度估计,请检查概率是否与观察到的结果相匹配。校准可能会有所帮助,但你需要衡量它是否真的带来了改善。

Weigh performance against cost and latency for your workload.

  • 根据你的工作负载权衡性能与成本和延迟的关系。

This process is iterative. You may improve the prompt, then need to tune the threshold again or revisit calibration. In this post, we focused on threshold tuning and calibration because we’ve seen many people use decision models off the shelf without considering these factors.

这个过程是迭代的。你可能会改进提示词,然后需要再次调整阈值或重新校准。在这篇文章中,我们重点关注阈值调整和校准,因为我们看到许多人在使用决策模型时直接开箱即用,而没有考虑这些因素。

You might also be wondering how decision models stack up to LLMs. OpenAI Decisions uses GPT-6 Luna, which delivered our highest F1 at the highest cost, suggesting that harder eval tasks may justify paying more for an LLM. In a broader comparison, Randy Olson found that decision models could match LLMs on classification and routing at lower cost, while LLMs pulled ahead on tasks requiring knowledge and reasoning.

你可能还想知道决策模型与 LLM 相比如何。OpenAI Decisions 使用了 GPT-6 Luna,它以最高的成本交付了我们最高的 F1 分数,这表明更难的评估任务可能值得为 LLM 支付更多费用。在更广泛的比较中,Randy Olson 发现决策模型可以在分类和路由方面以更低的成本匹敌 LLM,而 LLM 则在需要知识和推理的任务中脱颖而出。

None of the ideas discussed in this post are new. Choosing a precision-recall tradeoff, tuning thresholds and comparing performance vs. cost are part of evaluating any classifier. To learn more about these fundamentals, I recommend Machine Learning with Python Cookbook, 2nd Edition by Kyle Gallatin and Chris Albon.

本文讨论的想法都不是新事物。选择精确率-召回率权衡、调整阈值以及比较性能与成本,是评估任何分类器的一部分。要了解更多关于这些基础知识的内容,我推荐阅读 Kyle Gallatin 和 Chris Albon 编写的《Machine Learning with Python Cookbook, 2nd Edition》。

Finally, if you’re a data scientist or ML engineer wondering where you belong in the AI age, these are skills you already have. It’s time to get revenge 🙂.

最后,如果你是一名数据科学家或机器学习工程师,想知道自己在 AI 时代的位置,这些技能你已经具备了。是时候“复仇”了 🙂。

P.S. I share articles like this and what I’m learning about building AI products in my newsletter. To learn more about evals, Shreya Shankar and I co-authored AI Evals for AI Engineers, published by O’Reilly.

P.S. 我会在我的时事通讯中分享此类文章以及我在构建 AI 产品方面的学习心得。要了解更多关于评估的内容,Shreya Shankar 和我合著了由 O’Reilly 出版的《AI Evals for AI Engineers》。

P.P.S. This post has lots of footnotes. You can read them on hamel.dev.

P.P.S. 这篇文章有很多脚注。你可以在 hamel.dev 上阅读它们。

Co-authored with Antaripa Saha. Decision models have exploded onto the scene recently, starting with TypeSafe’s Jev, followed by Perplexity’s Decider, Liquid’s d1, Cloudflare’s Clef, and OpenAI’s Decisions API. These models promise fast, inexpensive classification. Decision models are an appealing low-cost alternative to LLM judges for automated evals. But whether they’re the right choice depends on the cost, performance, and latency requirements of your use case. We’ve also noticed people using the default decision threshold of 0.50 without checking whether it works well for their task. This can leave a lot of performance on the table. In this post, we’ll start by showing how to tune thresholds to get the best results from decision models. With those thresholds tuned, we’ll compare cost and performance to help you assess which model fits your use case. Finally, we’ll explore how to interpret model scores when deciding whether to act, and how calibration can make them more useful as confidence estimates. The charts below are static previews. For the best reading experience, I recommend reading this post on hamel.dev, where you can explore the interactive charts. Read on hamel.dev What we tested We used 100 traces, or logs of conversations and actions, from an apartment leasing agent. The task is to identify traces containing an error, such as a failed booking or a missed handoff to a human. We used the same dataset and broad failure-finding task in our prior post on automated evals. Here, each decision model returns a failure probability for each trace. We tested Jev 1.13, Perplexity Decider 1.1 27B, Liquid d1 and d1-ultra, Cloudflare Clef Flash, and OpenAI Decisions (GPT-6 Luna). Each model received the same evaluation criteria. The appendix shows the prompt and the structured version used for Jev. We compare models on two dimensions: Performance: We compare each model’s predictions with traces that people have reviewed and labeled as passing or failing. This measures how well the model catches failures while avoiding false alarms. Cost: We calculate API cost from token usage and the provider’s prices. For brevity, we leave latency aside because it depends on factors outside this experiment’s control. Understanding decision thresholds We assign each trace one of two labels, pass or fail. This is a binary pass/fail eval, which we recommend because it encourages clear criteria and is easier to align with human preferences. Each decision model returns a score from 0 to 1 that estimates how likely a trace is to contain a failure. The APIs turn this score into a decision using a default decision threshold of 0.50. Traces with scores at or above the threshold are flagged as failures. Using this default without checking it against your data can give you subpar results. For example, a model could assign every failure a score of 0.40 and every passing trace a score of 0.10. Even though this model classifies traces perfectly at a threshold of 0.30, the default threshold of 0.50 would catch nothing. In practice, scores for failures and passing traces will overlap. Lowering the threshold catches more failures but can also raise more false alarms. Choosing a threshold means balancing: Precision: The share of flagged traces that contain a known failure. Recall: The share of known failures that the model catches. The right tradeoff depends on your business goals. For example, criminal trials set a high bar for conviction to avoid convicting innocent people, emphasizing precision. Cancer screening may emphasize recall, accepting more false alarms to miss fewer cases. For this comparison, we’ll give precision and recall equal weight using the F1 score, their harmonic mean. F1 ranges from 0 to 1, and higher is better. The curves below show how each model scores failures (orange) and passing traces (blue). Taller parts of a curve mean more scores from that class fall in that range. Scores at or above the dashed threshold line are flagged. Use the toggle to compare 0.50 with Optimized for best F1, which chooses a threshold for each model. Explore the interactive chart on hamel.dev OpenAI separates the two classes best on this task, which is also reflected in its higher F1 score. It’s also important not to blindly maximize F1 without thinking about your minimum requirements for the classifier to be useful. For example, if this classifier is used for an automated alerting system, I would want a minimum of 80% on both precision and recall so that I’m minimizing false alarms while catching a sufficient number of failures. The chart below shows how precision and recall change at different thresholds. The shaded region marks where both reach at least 80%. Explore the interactive chart on hamel.dev Perplexity, Liquid d1-ultra, and OpenAI Decisions meet our requirements, but we still need to choose a decision threshold within the shaded region for each model. In the next section, we’ll discuss how to tune those thresholds. Tuning the decision threshold For this example, we’ll optimize F1. For our candidates, we’ll restrict tuning to thresholds that meet both 80% minimums on the training data whenever possible. We leave the other models unconstrained to illustrate the effect of tuning. With labeled traces and saved model scores, you can ask a coding agent to tune the thresholds. Below is an example prompt you can modify for your use case: Using my saved model scores and binary labels (1 means failure), tune a decision threshold to maximize F1 while requiring at least 80% precision and 80% recall. Use stratified five-fold cross-validation, choosing thresholds on the training examples and measuring performance on the remaining examples. If comparing models, use the same splits for each. Only select thresholds that meet both minimums on the training examples. If none qualifies, report that rather than relaxing either requirement. Report validation precision, recall, and F1, along with any splits where no threshold qualified or validation performance fell below my minimums. Compare with the default threshold of 0.50. Reuse the saved scores without making new model calls. Here’s how tuning for F1 changed each model’s performance. We group the three candidates from the precision-recall chart together and show the other models in gray to illustrate how much tuning can help. Explore the interactive chart on hamel.dev Our candidate models gained less from F1 tuning than Liquid d1 and Clef Flash, which fell below our minimums. However, even a small shift in precision and recall can affect business costs or the user experience, so choose the tradeoff deliberately. In practice, it’s unlikely that precision and recall are equally important (as we assumed in this post with F1). For example, the F-beta score lets you give precision more weight with beta below 1, or recall more weight with beta above 1. Optimizing a different metric could lead to different thresholds and results. Performance versus cost The chart below compares each model’s F1 (after tuning) against cost, focused on our remaining candidates: Explore the interactive chart on hamel.dev The dashed line shows the Pareto frontier among our remaining candidates. For any given F1 score, it identifies the cheapest candidate that meets or exceeds that score. This particular chart is a bit silly as there are only three options, but it’s a useful illustration for how to think about cost vs. performance. Perplexity was the cheapest of these candidates at 0.82 cents per 100 eval runs, with an F1 of 0.79. OpenAI Decisions reached an F1 of 0.87 at 3.72 cents per 100 eval runs, about 4.5 times the cost. Can we trust the probabilities? Beyond pass/fail evals, you may want to use a decision model’s score as a confidence estimate. But can you take that number literally? For a score of 0.80 to mean an 80% chance of failure, roughly 80% of traces with similar scores should contain failures. The chart below shows each model’s probability calibration: how closely its predicted probabilities match observed outcomes. Each point represents about 20 traces with similar scores. A point on the diagonal means the predicted probability matches the failure rate in that group. Above the line, the model underestimates the risk of failure. Below it, the model overestimates that risk. If the predicted probabilities don’t match the observed outcomes, we can adjust them using another model. The second panel shows one method, Platt scaling, which fits a simple curve to make that adjustment. Explore the interactive chart on hamel.dev Perplexity, Liquid d1, and Clef Flash move closer to the diagonal after calibration. For example, one group of Clef Flash traces had an average predicted failure probability of about 8%, but 60% contained failures. Jev and Liquid d1-ultra were already well calibrated on this dataset and needed little adjustment. Platt scaling isn’t always the best fit. For example, OpenAI’s probabilities got slightly worse with the version of Platt scaling we used. You can direct your coding agent to perform calibration with the following prompt: Using my saved failure probabilities and binary labels (1 means failure), test whether Platt scaling improves each model’s probabilities. Use stratified five-fold cross-validation with the same splits for every model. Fit the calibration mapping on each training split and apply it to the remaining traces. Fit on log-odds, clipping probabilities away from 0 and 1 first. Plot predicted probabilities against observed failure rates before and after calibration. Report Brier scores (lower is better), and explain which models improved or got worse. Reuse the saved scores without making new model calls. Prompt tuning and evaluator scope We kept the eval task in this post broad to make the differences between models easier to see. In practice, we recommend building a separate judge for each failure mode. With a narrower task and more specific guidance, we expect the performance gaps between models to shrink. I like to start with a larger model that has acceptable performance on a task and then try smaller models to see if they can match the larger model’s performance for less cost. Prompt tuning can also be helpful if you have high-quality labels. I discuss how to craft a good judge prompt in this post. Putting these ideas into practice For this particular dataset, OpenAI had breakout performance at a higher cost. Perplexity and Liquid d1-ultra offer lower-cost options with lower performance. We have observed that model performance varies substantially across tasks and datasets, which means it’s important to measure things yourself. To recap: Once you’re satisfied with your judge’s prompt, set minimum precision and recall requirements for your use case. Rule out models that cannot meet these. Tune each model’s decision threshold before comparing performance. Choose a precision-recall tradeoff that fits your business goals. Be intentional, don’t just use F1 score because this post did! If you want to use scores as confidence estimates, check whether the probabilities match observed outcomes. Calibration can help, but you need to measure whether it improves them. Weigh performance against cost and latency for your workload. This process is iterative. You may improve the prompt, then need to tune the threshold again or revisit calibration. In this post, we focused on threshold tuning and calibration because we’ve seen many people use decision models off the shelf without considering these factors. You might also be wondering how decision models stack up to LLMs. OpenAI Decisions uses GPT-6 Luna, which delivered our highest F1 at the highest cost, suggesting that harder eval tasks may justify paying more for an LLM. In a broader comparison, Randy Olson found that decision models could match LLMs on classification and routing at lower cost, while LLMs pulled ahead on tasks requiring knowledge and reasoning. None of the ideas discussed in this post are new. Choosing a precision-recall tradeoff, tuning thresholds and comparing performance vs. cost are part of evaluating any classifier. To learn more about these fundamentals, I recommend Machine Learning with Python Cookbook, 2nd Edition by Kyle Gallatin and Chris Albon. Finally, if you’re a data scientist or ML engineer wondering where you belong in the AI age, these are skills you already have. It’s time to get revenge 🙂. P.S. I share articles like this and what I’m learning about building AI products in my newsletter. To learn more about evals, Shreya Shankar and I co-authored AI Evals for AI Engineers, published by O’Reilly. P.P.S. This post has lots of footnotes. You can read them on hamel.dev.

📋 讨论归档

讨论进行中…