OpenOneRec 的 judge backend 可切换,但并不等价

背景

上一轮我已经确认,RecIF-Bench 不是一个完全脱离外部闭源模型的纯本地 benchmark。item_understandrec_reason 这两项语义任务都会走 LLM-as-Judge,而官方文档把默认复现路径指向了 Gemini

但还有一个更细的问题没有拆清:

如果公开代码已经暴露了 Gemini / DeepSeek / Claude 三种 backend,那么把 judge 从 Gemini 切到 DeepSeekClaude,能不能被视作“只是换了一个供应商,benchmark 口径基本不变”?

这一轮我直接去核 OpenOneRec/benchmarks/api 的源码、配置模板、任务配置和 evaluator,结论是否定的。

核心判断

我现在更倾向于把 benchmarks/api 理解成一个 provider wrapper,而不是 judge 标准化层。

这不是官方 README 的原话,而是我基于公开代码做出的判断。依据主要有四条。

第一,官方文档层的默认路径仍然明显是 Gemini-first

benchmark README 的 Quick Start 第 3 步直接写的是“填写你的 Gemini 配置”;item_understand/config.pyrec_reason/config.py 也都把 judge model 默认写成了 gemini。这说明在官方复现叙事里,另外两条 backend 并不是与 Gemini 同权的第一入口。

第二,DeepSeek 在官方代码里并不是“直连 DeepSeek 官方 API”,而是“走百度千帆的 DeepSeek”。

deepseek.py 的文件头和类注释都明确写了 through Baidu Qianfan platform。默认 base_url 也是 https://qianfan.baidubce.com/v2,而且客户端初始化时还会额外带一个 appid header。

这会直接改变我们对“支持 DeepSeek”的理解。更准确的说法不是“官方已经把 DeepSeek 官方 API 和 Gemini、Claude 并列接进来了”,而是:

官方暴露的是一条 Qianfan-DeepSeek 适配路径。

第三,Claude 这条路径当前甚至不是开箱即用的默认模板。

api/config/llm_config.json 里,claude.model_name 目前是空字符串;而 claude.py 的实现是直接把 self.config.get("model_name", "claude-sonnet-4-20250514") 赋给 self.model_name。这意味着如果用户照着模板只填 api_key,没有手工把 model_name 补上,代码并不会自动回退到示例里的 claude-sonnet-4-20250514,而是会把空字符串继续往下传。

换句话说,Claude backend 虽然已经挂在 MODEL_CLASSES 里,但默认配置模板并没有把它打磨成与 Gemini 同样顺手的“可直接照抄”路径。

第四,官方 evaluator 并没有做一层真正的 cross-backend 标准化。

无论是 item_understand/utils.py 里的 WIP 抽取与匹配,还是 rec_reason/utils.py 里的打分器,本质上都是:

  1. 把同一段中文 prompt 作为 user 消息发给不同 backend;
  2. 等模型返回文本;
  3. 用本地 json.loads 或简单正则去解析 JSON;
  4. 再把结果缓存到本地。

这里没有看到一层针对 Gemini / Qianfan-DeepSeek / Claude 的统一 response schema 约束,也没有看到公开的 cross-judge calibration 说明。因此“代码里可以切换 backend”不等于“切换后依然是完全同口径的 judge”。

更细一点看,缓存文件名还是直接按 model_name 来拼的。rec_reason 会写 llm_eval_{model_name}.jsonitem_understand 会写 wip_{model_name}.jsontest_gt_wip_{model_name}.parquet。所以如果 Claude 仍保持模板里的空 model_name,缓存名会退化成 llm_eval_.jsonwip_.json 这类形式。这再次说明它目前更像“代码里预留了插槽”,而不是“已经被官方打磨成同口径、同体验的现成 benchmark 入口”。

这会怎样改变我们对 RecIF-Bench 的表述

这一轮之后,Story Lab 对 RecIF-Bench judge 层的记录需要再细一格。

过去写“judge backend = Gemini / DeepSeek / Claude”已经不够了。更稳妥的记法应该至少包含:

  1. backend 名称;
  2. 实际 provider 路径;
  3. 具体 model name;
  4. 是否使用默认模板,还是做过手工补丁;
  5. 结果缓存和解析是否与其他 backend 完全一致。

尤其是 DeepSeek 这一项,现在更准确的标签应该写成 Qianfan-DeepSeek,而不是笼统地写成 DeepSeek

这也解释了为什么官方虽然开放了三种 adapter,我仍不建议把它简单概括成“RecIF-Bench 已经 provider-agnostic”。从代码现状看,它更像:

Gemini 是文档默认主线,Qianfan-DeepSeek 是代码级替换口,Claude 则仍需要额外手工补齐配置。

中文传播层的缺口

这一轮我顺手补做了中文检索,重点看中文社区是否已经有人讨论 Gemini / DeepSeek / Claude 三条 judge 路径之间的差异,或者至少有人提到 DeepSeek 实际走百度千帆这件事。

截至 2026-03-20,我仍然没有找到足够稳定、可长期回溯的高质量中文帖子来覆盖这一层。搜索结果还是更偏向 OpenOneRec 技术报告解读、OneRec 主线综述和泛化讨论,而不是 benchmark judge adapter 的实现细节。xhslink / 小红书 这一层也依旧没有沉淀出强来源。

这意味着 Story Lab 在这部分暂时还得继续依赖官方一手代码,而不是中文二手传播。

证据与来源

下一步

  • 如果后续 RecIF 数据门槛放松,优先实测 Gemini / Qianfan-DeepSeek / Claude 三条路径在 JSON 解析成功率和分数分布上的差异。
  • 继续追官方是否会把 Claude 模板补到可直接运行的状态,或在 README 中补充非 Gemini 路径的正式说明。
  • 继续检索中文高价值帖子和稳定 xhslink,尤其关注是否开始有人讨论 judge backend 差异而不是只复述技术报告。