有些领域的验证很便宜:生成一段代码,跑测试就知道对不对。另一些领域的验证很贵:要判断一个改动是否有效,得真的训练一遍、评测一轮,成本以小时和算力计。

后者的链路里,瓶颈从来不是"想不出办法",而是能验证几次。有一类做法针对的就是这个瓶颈:用一个便宜的训练好的筛选器预测哪些改动值得试,把昂贵的验证集中在最有希望的候选项上。我在 4sapi(https://4sapi.com)上设计需要真实评测的 agent 循环时,这个两级结构是最直接的效率来源。

一、验证为什么是稀缺资源

先明确这个瓶颈的性质。

在一个"提出改动 → 验证改动"的循环里,两侧的成本形态完全不同。提出改动的边际成本趋近于零——模型可以源源不断地生成候选项,而且生成更多候选项几乎不增加什么负担。验证改动的边际成本是固定的且很高——每个候选项都要跑一遍完整流程才能得到结论。

两者的不对称决定了循环的形态:候选项过剩而验证配额稀缺。此时任何"多提几个想法"的优化都不会改善整体速度,因为瓶颈在后面。真正有效的方向是提高每个验证名额的命中率——让被验证的候选项更可能是有价值的。 这个判断有一个可以直接用于自检的推论:如果提高生成速度没有让整体进展变快,说明瓶颈在验证侧。实践中不少团队会先优化生成(换更强的模型、写更好的提示词、并行生成更多候选),在这些动作没有带来端到端加速时,往往得到"模型还不够强"的结论——而真实原因是生成的已经不是瓶颈。识别这一点只需要一次简单的测量:把生成环节的耗时与验证环节的耗时分别统计,看总时间花在哪一侧。

这也解释了为什么"验证效率"这类问题长期被低估:它不在生成侧,而现有的注意力大多集中在生成侧。

二、两级结构:便宜地筛选、昂贵地验证

解法是把循环拆成两级。

候选项池(模型生成的改动提案,数量多、成本低)
      |
      v
[第一级:筛选器] 预测每个提案是否可能带来改进
      |              (便宜、快,允许判断粗略)
      v
筛选后的少量候选项
      |
      v
[第二级:真实验证] 训练 + 评测,得到确定结论
      |              (昂贵、慢,结论可靠)
      v
采纳 / 拒绝,并回流作为筛选器的训练信号

两级的分工很清楚:第一级追求排序质量,第二级追求结论可靠。第一级不需要准确判断"能提升多少",只需要把明显无效的排在后面——这是一个比"精确预测"容易得多的任务,也正是它能够便宜的原因。

三、为什么通用前沿模型做筛选器收益有限

一个自然的想法是:直接用最强的通用模型做筛选器。研究给出的结论是收益有限。

原因可以从筛选任务的形态推出来。筛选需要的是在特定领域里判断"这个改动方向对不对"的能力,而这种判断依赖该领域的经验——哪些改动通常有效、哪些模式是陷阱、什么情况下看起来的改进其实是噪声。

通用模型在这些领域细节上往往不如领域专家的直觉,而它的优势(广泛知识、复杂推理)在"快速二选一"这类任务上发挥不出来。更具体地说,筛选任务的输出空间很小(值得试 / 不值得试),因此模型的大量能力被浪费在无关维度上;而它缺的恰好是那一点点领域经验。

这与本报告此前讨论过的另一个结论同构:在判定类任务上,专门化的中等规模模型常与最强通用模型同级,而成本低得多。

四、专门化 critic 的定位

由此引出专门化的"想法级评审模型"(idea-level critic):它的唯一职责是预测一个提议的改动是否会比当前方案更好。

三个特征值得注意。

第一,它的预测对象是"相对改进"而不是"绝对质量"。 判断"这个方案好不好"是开放问题;判断"这个改动会不会比现状更好"要具体得多,也更容易拿到训练信号。

第二,它的输出是一个排序分而不是最终决策。 决策由阈值(验证配额)决定,筛选器只负责排序。这样即使预测绝对值不校准,只要排序对,筛选就是有效的。

第三,它的价值随验证成本上升而上升。 如果验证很便宜,筛选器的收益有限;验证越贵,把验证名额用在对的地方收益越大。这条决定了适用边界。

五、两段式训练:SFT 起手、GRPO 提升预测准度

研究给出的训练方式是两段:先用高质量的评审文本做监督微调,再用强化学习进一步提升预测准确性。

第一段的原料值得注意:高质量评审由更强的模型合成。也就是说,先让强模型解释"为什么这个改动好或不好",用这些解释作为训练数据,把判断能力从大模型转移到小模型上——这是蒸馏的一种具体应用。

第二段用强化学习优化的是预测准确性,而不是解释质量。这里的信号来自真实结果:改动验证之后知道它到底有没有改进,这个结论就是"筛选器当时判断得对不对"的标签。

两段的分工可以这样理解:第一段学会"怎么分析一个改动",第二段学会"怎么把分析收敛成一个准确的预测"。前者是能力,后者是校准。这也解释了为什么两段都需要——只做第一段的模型能写出合理分析,但预测未必准;只做第二段则缺少分析基础,样本效率会很低。

六、筛选器的评估要点

筛选器的评测与普通模型的评测不一样,因为它不直接产出最终结论。评测要围绕它在链路里的作用来设计。

评估项 含义 为什么重要
排序质量 有效改动是否排在前面 直接决定验证名额的利用效率
顶部的精确率 排在最前的那些有多少真的有效 验证配额的命中率
校准性 预测分数与实际改进幅度的关系 用于设阈值,不要求精确
覆盖率 可用于筛选的改动类型范围 决定它能否承担主要筛选职责

第一行最关键:筛选器的价值来自排序,而不来自预测绝对值。一个把改进幅度系统性高估的筛选器,只要排序正确,仍然是好筛选器。反过来,一个预测值很准但排序混乱的筛选器几乎没有用。

第二行决定收益上限:如果排在最前的十项里有九项无效,那么验证配额仍然被浪费,筛选没有起作用。 第四行则决定了这套结构能否长期承担主要筛选职责。如果筛选器只在少数改动类型上有效(例如只擅长调超参数、不擅长改结构),那么在其他类型上它无法替代人,链路会退化成"部分筛选 + 部分人工",整体复杂度上升而不一定有净收益。接入前先用一批历史改动做离线评估,看能不能覆盖主要类型,比上线后发现覆盖不足再补救省事得多。

七、筛选阈值怎么定

阈值决定"多少候选项进入验证",它的定法取决于验证成本与验证名额。

设 Cv = 单次验证成本,Q = 可用验证名额
筛选收益 = 被验证项中的有效数 × 单次改进收益 - Q × Cv

阈值越低 -> 进入验证的项越多 -> 验证名额被摊薄
阈值越高 -> 可能漏掉边缘有效的改动

实践中的定法是从排序分布入手:先看筛选器给候选池打出的分数分布,把阈值设在分数出现明显断层的附近——断层以上是筛选器比较有把握的项。这个做法比拍一个百分比稳健,因为它依赖分布形态而不是先验。

需要定期复核阈值:当验证成本变化(例如训练提速)或候选池构成变化(例如开始探索新方向)时,最优阈值会移动。

八、接入示例:两级验证循环

下面这段脚本实现两级结构:筛选器排序、阈值截断、真实验证,并把验证结论回流作为筛选器的训练样本。

import os, json, time
from collections import defaultdict
from openai import OpenAI

client = OpenAI(
    api_key=os.getenv("FOURSAPI_API_KEY"),
    base_url="https://4sapi.com/v1",
)

CRITIC = os.getenv("MODEL_CRITIC", "critic-specialized")
QUOTA = int(os.getenv("VERIFY_QUOTA", "3"))          # 每轮可用验证名额
LOG = os.getenv("FEEDBACK_LOG", "./critic_feedback.jsonl")
_stats = defaultdict(int)


def score_idea(idea, current_best):
    """筛选器:预测该改动是否可能优于当前方案,返回 0-1 的排序分。"""
    r = client.chat.completions.create(
        model=CRITIC, temperature=0,
        messages=[
            {"role": "system",
             "content": "判断候选改动的长期收益是否可能高于当前方案。"
                        "只输出 JSON:{\"score\": 0-1, \"risk\": \"low|mid|high\"}"},
            {"role": "user", "content": json.dumps(
                {"current": current_best, "candidate": idea}, ensure_ascii=False)},
        ],
    )
    try:
        obj = json.loads((r.choices[0].message.content or "{}").strip())
        return float(obj.get("score", 0.0)), obj.get("risk", "mid")
    except Exception:
        return 0.0, "unknown"


def verify(idea):
    """真实验证:接入实际训练与评测。这里以占位实现说明接口。"""
    t0 = time.perf_counter()
    # 真实场景:提交训练任务 -> 等待 -> 评测 -> 返回指标
    improved = False
    delta = 0.0
    return {"improved": improved, "delta": delta,
            "cost_seconds": round(time.perf_counter() - t0, 2)}


def round_once(candidates, current_best, threshold=0.5):
    """一轮:筛选 -> 截断 -> 验证 -> 回流。"""
    scored = []
    for c in candidates:
        s, risk = score_idea(c, current_best)
        scored.append({"idea": c, "score": s, "risk": risk})
    scored.sort(key=lambda x: -x["score"])

    picked = [x for x in scored if x["score"] >= threshold][:QUOTA]
    results = []
    for x in picked:
        v = verify(x["idea"])
        x.update(v)
        results.append(x)
        _stats["verified"] += 1
        _stats["improved"] += int(v["improved"])
        with open(LOG, "a", encoding="utf-8") as f:
            f.write(json.dumps({"idea": x["idea"], "pred_score": x["score"],
                                "risk": x["risk"], "improved": v["improved"],
                                "delta": v["delta"]}, ensure_ascii=False) + "\n")
    _stats["rounds"] += 1
    return {"picked": len(picked), "total": len(candidates), "results": results}


def report():
    v = _stats["verified"] or 1
    print(f"轮数={_stats['rounds']} 已验证={_stats['verified']} "
          f"命中率={_stats['improved'] / v:.1%}")


if __name__ == "__main__":
    demo_best = {"lr": 3e-4, "batch": 64}
    demo_cands = [{"lr": 1e-4, "batch": 64}, {"lr": 3e-4, "batch": 128},
                  {"lr": 1e-3, "batch": 32}, {"lr": 2e-4, "batch": 64}]
    print(json.dumps(round_once(demo_cands, demo_best), ensure_ascii=False)[:400])
    report()

三个设计点:score_idea 让筛选器同时输出风险等级——高风险项即使分数高也可以延后验证,这是对筛选器不确定性的处理;round_once 用阈值加配额双重截断,阈值保证质量、配额保证成本可控;写入 FEEDBACK_LOG 的样本包含预测分与真实结论,这正是第二段训练(用真实结果校准预测)所需要的素材。

九、检查项

  1. 链路里是否存在"生成便宜、验证昂贵"的不对称;
  2. 筛选器是否与生成器分离,而不是让生成器自己判断自己的提案;
  3. 筛选器的评估是否以排序质量为主而不是预测绝对值;
  4. 阈值是否依据分数分布设定,并随验证成本与候选池变化复核;
  5. 是否记录了预测分与真实结论,用于持续校准;
  6. 验证名额是否被硬性限制,避免筛选形同虚设。

第六项是这套结构能否生效的前提:如果验证名额没有上界,筛选就没有意义——所有候选项最终都会被验证,筛选只是延迟了开销。

十、成本与风险提示

结论

在需要真实训练与评测才能验证的领域,瓶颈在验证而非生成——候选项过剩而验证名额稀缺。两级结构针对的就是这个不对称:用专门训练的筛选器预测哪些改动值得试(追求排序质量、允许判断粗略),把昂贵验证集中在最有希望的候选项上。四条实践里最值得记住的是评估口径——筛选器的价值来自排序,而不是预测绝对值,因此评估应当围绕"顶部的精确率"设计,而不是要求它精确预测改进幅度。我在 4sapi.com 上把这类循环从"逐个验证"改成两级筛选之后,同等算力下的有效探索次数明显上升,而最直接的改善其实来自那条硬性约束:先给验证名额设上界。

这套结构最容易被执行偏的地方是最后一环:验证名额若无上界,所有候选项最终都会被验证,筛选只剩延迟开销而没有筛选作用。

欢迎在评论区聊聊各自的验证效率做法,以及筛选阈值是怎么定的。