推荐系统很擅长计算点击,却不容易回答“这批结果是否新颖”“商品与用户意图是否真的相关”。CTR、CVR 可以从实时日志中直接聚合,新颖性、相关性等体验指标却依赖人工判断,反馈周期容易拉长到周级。得物的实践表明,引入大模型评测能力后,这类反馈可以缩短到小时级,并支撑单周百万级审计任务。
为什么体验指标长期处于量化黑盒
CTR 和 CVR 都有明确事件:用户是否点击、是否购买。只要埋点和归因链路可靠,就能持续计算。体验指标则有三类额外困难:
- 标准带有语境:同一件商品出现在“日常通勤”和“专业越野”场景中,相关性可能完全不同。
- 标签成本较高:人工审核需要阅读用户上下文、推荐理由和商品信息,难以覆盖大量实验流量。
- 结论难以复现:如果评审只给出“感觉不相关”,研发团队很难定位是召回、排序还是商品数据出了问题。
大模型的价值不是简单替代人工,而是把自然语言评审标准转化为可批量执行的评分任务。输入包括用户意图、候选商品和必要上下文;输出则应包含分数、证据与风险标签。这样,主观判断才能进入实验看板、回归流水线和问题归因流程。
一条可扩展的评测链路
面向百万级周审计量,评测平台不能只是一个循环调用模型的脚本。可以将链路拆成五层:
- 样本层:从线上日志、A/B 实验和专项审计任务中抽样,并对用户信息脱敏。
- 规则层:维护评价维度、评分锚点、少样本示例和版本号。
- 执行层:进行批量推理、限流、重试、超时控制和结果缓存。
- 校准层:使用人工标注集监控模型与专家的一致性,发现提示词或模型升级带来的漂移。
- 分析层:按场景、流量桶、召回源和实验版本聚合结果,而不是只展示一个全局平均分。
这里有两个关键设计。其一,模型输出必须结构化,否则后续统计会被措辞差异污染。其二,每个分数都要附带证据,例如指出推荐商品与用户意图冲突的属性。证据既方便抽检,也能帮助算法团队定位问题。
批量任务还需要稳定的幂等键,例如对“样本 ID、规则版本、模型版本”求哈希。相同输入命中缓存后不再重复推理,可以减少资源消耗;任务失败时也能从检查点恢复,而不必重跑整批数据。
可以这样实践:构建最小批量评测器
下面是一个可直接改造的示例。假设你使用兼容 OpenAI Chat Completions 的服务,并且该服务支持 JSON 输出模式。运行前安装依赖,并设置实际的接口地址、密钥和模型名:
python -m pip install openai
export LLM_BASE_URL="https://your-llm-gateway.example.com/v1"
export LLM_API_KEY="replace-me"
export LLM_MODEL="your-model-name"
将以下内容保存为 evaluate.py。示例使用两个并发线程处理样本;生产环境应根据网关限额调整 MAX_WORKERS,并增加集中式限流与持久化队列。
import json
import os
from concurrent.futures import ThreadPoolExecutor, as_completed
from openai import OpenAI
client = OpenAI(
base_url=os.environ["LLM_BASE_URL"],
api_key=os.environ["LLM_API_KEY"],
)
MODEL = os.environ["LLM_MODEL"]
MAX_WORKERS = 2
SAMPLES = [
{
"sample_id": "s-001",
"intent": "适合雨天城市通勤的防水鞋",
"item": "低帮防水徒步鞋,橡胶防滑外底,重量 380 克",
},
{
"sample_id": "s-002",
"intent": "夏季室内篮球鞋",
"item": "冬季加绒高帮休闲靴,厚重耐磨",
},
]
SYSTEM_PROMPT = """你是推荐结果审计员。根据用户意图评价候选商品。
相关性和新颖性均使用 1-5 分整数:
1 表示明显不满足,3 表示部分满足,5 表示高度满足。
只输出 JSON,字段必须为 relevance、novelty、evidence、risk_tags。
evidence 必须引用输入中的具体属性;risk_tags 是字符串数组。
不要根据输入之外的信息推断商品能力。"""
def evaluate(sample):
response = client.chat.completions.create(
model=MODEL,
temperature=0,
response_format={"type": "json_object"},
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{
"role": "user",
"content": json.dumps(sample, ensure_ascii=False),
},
],
)
result = json.loads(response.choices[0].message.content)
for field in ("relevance", "novelty"):
score = result.get(field)
if not isinstance(score, int) or not 1 <= score <= 5:
raise ValueError(f"invalid {field}: {score}")
return {"sample_id": sample["sample_id"], **result}
def main():
results = []
with ThreadPoolExecutor(max_workers=MAX_WORKERS) as pool:
futures = {pool.submit(evaluate, sample): sample for sample in SAMPLES}
for future in as_completed(futures):
sample = futures[future]
try:
results.append(future.result())
except Exception as exc:
results.append({
"sample_id": sample["sample_id"],
"error": str(exc),
})
print(json.dumps(results, ensure_ascii=False, indent=2))
if __name__ == "__main__":
main()
这个示例刻意保留了三个生产约束:temperature=0 用于降低随机波动;程序校验分数范围,避免格式正确但语义无效的响应进入统计;单条失败不会中断整个批次。真实平台还应记录提示词版本、模型版本、调用耗时、重试次数和 token 用量。
不要把模型评分直接当作真值
反馈从周级缩短到小时级,并不意味着质量问题已经自动解决。大模型评测至少存在以下边界:
- 偏差会规模化:评分规则中的歧义,会在百万级任务中被稳定复制。
- 模型升级会改变尺度:即使提示词不变,新模型也可能系统性地提高或降低分数。
- 输入缺失会制造幻觉:商品属性不完整时,模型可能使用常识补全不存在的能力。
- 平均分会掩盖局部事故:整体相关性上涨,不代表某个类目或用户群没有显著退化。
因此,应保留一组由专家维护的黄金样本,持续计算等级一致率、加权 Kappa 或 Spearman 相关系数;同时抽检高分、低分和模型不确定样本。新提示词或新模型先运行影子评测,通过校准后再替换线上基线。
上线前检查清单
一个可用的自动化评测平台,至少应满足这些条件:
- 评分标准包含清晰的 1 分、3 分、5 分锚点,并按业务场景维护版本。
- 输入已经脱敏,只提供完成判断所必需的用户与商品信息。
- 输出采用固定结构,并执行类型、范围和必填字段校验。
- 批量任务支持限流、重试、幂等、缓存和断点恢复。
- 看板能够下钻到场景、类目、召回源和实验桶。
- 模型结论经过人工黄金集校准,模型升级前执行影子对比。
- 资源成本按样本、模型和规则版本核算,避免用全量高成本模型处理所有任务。
自动化评测真正改变的不是多出一个模型分数,而是让原本模糊、缓慢的体验讨论进入可重复的工程闭环。模型负责扩大覆盖面,人工负责定义标准和校准边界,两者结合才能让小时级反馈既快又可信。