ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

AI模型横向实测全流程:从测试集设计到评分报告

AI模型横向实测全流程:从测试集设计到评分报告 做 AI 模型横向实测门槛从来不是把问题发给模型再等它返回一段文本。真正的难点在于要让一套题、一组采样参数、一条评分标准同时作用在多个模型上并且保证不同时间、不同人跑出来的结果可以互相比较。DeepSeek V4 Pro、Grok 4.6、Opus 4.8 这一类旗舰模型版本迭代快、API 差异大、不同任务的强弱分布也不同直接把几个模型拉到一起“上强度”最怕的不是模型答不好而是测试过程本身不严谨导致结果失真。这篇文章围绕三个模型做横向压测的方法展开核心不是给出某次固定得分而是提供一套可以直接改写的实测流程。这套流程包含测试集组织、采样参数统一、代码与推理类任务校验、裁判模型评分、成本和延迟统计以及最终报告输出。文中的模型型号和版本以官方发布为准落地前需要先核对 API 端点、模型 ID 和计费策略但评测框架与排查思路不依赖具体版本换一批新模型同样能用。1. 横向实测前先想清楚三个问题测什么、怎么测、结果怎么读1.1 这次测试的边界为什么不能只跑公开榜单题DeepSeek V4 Pro、Grok 4.6、Opus 4.8 这类模型官方通常会在发布材料里给出大量基准测试分数例如数学、代码、推理、多语言能力等。这些分数适合做宏观参考却不适合直接作为选型依据原因有三个。第一公开榜单题可能进入训练集。如果测试题和官方训练语料高度重合模型可能因为“见过答案”而得分偏高这种分数无法反映真实业务中的泛化能力。第二榜单分数是厂商在特定评测集上统计出来的评测集的构造方式未必贴合你的使用场景。你在意的可能是“中文长文归纳是否稳定”“复杂 JSON 输出是否格式合法”“调用外部工具时参数是否正确”这些未必是公开榜单覆盖的重点。第三横向对比的价值在于同题、同时段、同参数。只有把同一批问题按同一套脚本发给不同模型记录原始响应和评分过程才能得到可解释、可追溯、可复现的结论。所以这篇文章的实测对象不是“模型的所有能力”而是“在可控条件下三个模型面对同一批任务时的表现差异”。测试结果只对当天使用的 API 版本和评测集负责不对外推成永久结论。1.2 硬核实测真正容易翻车的环节评测过程本身给模型发 100 道题很容易难的是让这 100 道题对每个模型都处于公平条件。下面几种情况一旦发生分数几乎必然失真采样参数不一致。A 模型用默认参数B 模型用高随机性参数输出波动程度不同无法判断是能力差异还是随机差异。输出后处理不一致。有的模型返回代码块包裹内容有的返回纯文本如果评分时不统一解析会导致误判格式错误。上下文长度不一致。不同模型的上下文窗口不同长文本任务里A 模型读 8KB 模型读 128K答题条件完全不同。裁判模型身份不对。让被测模型给自己打分或者让裁判模型知道答案在哪个候选里都会产生系统性偏差。版本漂移。同一个模型 ID 在一天内可能完成灰度更新不同时间跑出来的结果未必可比。横向压测必须先把这些环节锁死再谈分数。这也是本文花费大量篇幅讲评测工程的原因。1.3 版本与事实的确认方式文章标题中出现的三个模型型号在写作时可能已经发生版本变化。落地测试前建议按下面顺序确认当前可用的真实版本信息到各厂商官方 API 文档核对当前支持哪些模型 ID不要凭印象填参数。用小成本请求验证模型 ID 是否正确例如发送一条 50 token 以内的简单请求。记录实际调用时返回的 model 字段因为网关可能做版本映射。确认该模型是否支持你需要的功能例如结构化输出、工具调用、视觉输入不同版本的能力矩阵不一样。注意模型版本、API 价格、限流策略都变化很快。任何评测报告都要写清楚“模型 ID 测试时间 API 版本”否则结果无法复核。2. 评测基线用 JSONL 管理测试集用统一脚本控制采样2.1 为什么要用文件而不是直接在代码里写死问题横向实测会反复迭代今天跑 80 题明天加 20 题后天换一组多选题。如果把测试题嵌在评测脚本里每次改题都要动代码无法做版本管理。推荐把测试集拆成独立数据文件脚本只负责读取和执行。测试集建议用 JSONL 格式每一行是一道完整题目字段保持稳定。下面是一个最小示例{id: math-001, category: math, task_type: answer, prompt: 一个水池有进水管和出水管进水管单独注满需要 6 小时出水管单独放空需要 9 小时。两管同时打开水池原有三分之一的水问多少小时后水池注满, reference: 18, source: private, tags: [rate_work]} {id: code-001, category: coding, task_type: function, prompt: 请实现 Python 函数 longest_palindrome(s: str) - str返回字符串 s 中最长的回文子串。要求只返回代码不写解释。, reference: unit_tests_palindrome.json, source: private, tags: [string, dp]} {id: follow-012, category: instruction, task_type: multi_turn, prompt: 第一轮请给我一段 3 句话的公司简介不要出现数字。\n第二轮请把上一轮生成的简介改写为 50 字以内并保留无数字要求。, source: private, tags: [format]}字段说明如下字段含义说明id题目唯一编号用于结果关联和排错category能力维度建议保持稳定便于分维度汇总task_type任务类型决定调用哪种评分器prompt发送给模型的提示词多轮题可以用分隔符约定reference参考答案可以是纯文本、数值、代码文件路径source题目来源private 表示自建私有题public 表示公开题tags标签用于筛选分析2.2 采样参数必须统一否则结果没有可比性不同模型的默认采样行为差异很大。有的推理模型会隐藏思考过程有的模型在 temperature 为 0 时依然存在随机性有的模型默认输出上限很小。统一采样参数并不是让所有参数完全相等而是让每个模型都在“官方推荐范围”内以可控方式运行。建议初始基线使用以下规则设置temperature 0如果模型 API 不支持则设置为该模型允许的最低值。设置最大输出 token 上限避免某个模型因为中途截断被判为失败。不设置stop的情况下统一按照文本结束标记判断是否生成完。对随机性较大的模型每个问题重复运行 3 到 5 次统计通过率或平均得分而不是只保留一次结果。记录每个请求的完整请求参数包括模型 ID、temperature、max_tokens、实际上送 token 数。这里的关键是我们要对比的是模型在同一条件下的表现而不是对比不同厂商默认配置下的“开箱体验”。如果某次测试目的就是开箱默认那要单独在报告中标注不能与基线测试混在一起。2.3 一套可运行的评测脚本骨架下面这个脚本骨架使用 Python 实现核心思路是“通过适配层把不同厂商的 API 转换为统一的消息格式”。不同厂商的请求格式不同但绝大多数都支持 messages 列表结构差异点主要在顶层字段命名例如 Anthropic 风格需要额外的system字段和不同的请求路径。为了控制复杂度脚本先用一个call_model函数做适配生产环境建议直接使用各厂商官方 SDK并为每个厂商单独写适配器。import json import os import time import argparse import httpx # 示例适配结构实际项目请按官方 SDK 和最新模型 ID 调整 ADAPTERS { openai_compatible: { base_url: os.getenv(OPENAI_BASE_URL, https://api.example.com/v1), api_key_env: OPENAI_API_KEY, model_env: MODEL_ID, }, anthropic: { base_url: os.getenv(ANTHROPIC_BASE_URL, https://api.example.com/v1/messages), api_key_env: ANTHROPIC_API_KEY, model_env: ANTHROPIC_MODEL_ID, }, } def load_dataset(path: str) - list[dict]: items [] with open(path, r, encodingutf-8) as f: for line in f: line line.strip() if not line: continue items.append(json.loads(line)) return items async def call_model(adapter: str, messages: list[dict], params: dict) - dict: # 各厂商参数结构不同这里按实际 API 结构调整字段 # 统一返回 {text: ..., prompt_tokens: n, completion_tokens: n} raise NotImplementedError(请按官方 SDK 实现适配器) def format_result(item, response, elapsed_sec): return { id: item[id], category: item[category], task_type: item.get(task_type), model: response.get(model), elapsed_sec: round(elapsed_sec, 3), prompt_tokens: response.get(prompt_tokens, 0), completion_tokens: response.get(completion_tokens, 0), output: response.get(text, ), status: ok, } def main(): parser argparse.ArgumentParser() parser.add_argument(--dataset, requiredTrue, help测试集 JSONL 路径) parser.add_argument(--output, defaultresults.jsonl, help结果输出路径) parser.add_argument(--limit, typeint, default0, help只跑前 N 题0 表示全部) parser.add_argument(--repeat, typeint, default1, help每题重复次数) args parser.parse_args() items load_dataset(args.dataset) if args.limit 0: items items[:args.limit] results [] for item in items: for turn in range(args.repeat): messages [{role: user, content: item[prompt]}] start time.time() response call_model(openai_compatible, messages, params{}) elapsed time.time() - start record format_result(item, response, elapsed) record[turn] turn 1 results.append(record) # 写一行输出一行防止中途中断时已跑结果丢失 with open(args.output, a, encodingutf-8) as f: f.write(json.dumps(record, ensure_asciiFalse) \n) print(f完成 {len(results)} 条请求结果写入 {args.output}) if __name__ __main__: main()这段代码只展示了流程。实际使用时还要补充几个部分错误处理。遇到限流或超时要退避重试但要标记哪些结果是重试后得到的因为重试会污染延时时长。并发控制。不要一次性把所有题目发出去建议控制并发数并根据不同 API 的限流策略增加等待。敏感信息。不要把 API Key 写死在代码或命令行中统一从环境变量或密钥管理服务读取。幂等。每次运行生成独立输出文件文件名带时间戳避免覆盖上一次的结果。用命令行运行时的示例python eval_harness.py --dataset datasets/v1.jsonl --repeat 3 --limit 50 --output results/202502_cmp_modelA.jsonl跑完后结果文件里每条记录都保留了原题 id、模型返回原文、耗时和 token 数后续评分不需要重新请求 API。3. 上强度按任务类别拆压测而不是简单堆 100 道题3.1 代码任务不要只看“生成了代码”要真正执行校验代码能力是最容易量化的维度但实现方式要严苛一些。建议把代码题设计成“独立函数 已写好的单测”提示词中要求模型只返回可执行的函数实现然后评测脚本把返回内容保存成文件用 pytest 或 unittest 执行。示例题目设计题目 1实现longest_palindrome(s)用几种典型字符串验证包括空串、单字符、全重复字符。题目 2实现parse_log_line(line)从一行 Nginx 日志中提取时间、状态码和响应字节数返回字典。题目 3实现一个 SQL 查询在给定表结构下查出“每个类目最近一笔订单”。代码题评分维度建议拆成三个评分点判定方式说明格式合法能否直接保存为文件并 import避免返回内容被 Markdown 代码块或说明文字污染单测通过pytest 执行通过率这是硬性客观指标代码复杂度人工抽检或有经验的评审者评分同一功能不同实现差异很大代码执行环境不要使用线上编译服务而是本地容器或虚拟环境。如果被测模型输出的代码包含不受控行为需要限制网络权限和文件系统权限避免评测环境被污染。3.2 数学与逻辑推理分步不等于可靠要把“最终答案”和“推理过程”分开记数学和逻辑题适合用确定性结果校验例如数值答案、选项编号、布尔判断。但大模型回答这类问题时即使最终答案正确推理过程也可能有误反过来推理过程看似正确最终答案却算错。评测时要同时保存两部分内容推理过程用于人工抽检和错误分析。最终答案用于自动对比。自动对比可以采用归一化后的字符串匹配。如果答案是数值要考虑单位和分数形式如果答案是选项要限定“只输出 A/B/C/D”。在提示词中显式约束输出格式能显著减少后续解析成本。下面是几类建议加入的题概率题判断条件概率容易因为语言歧义产生不同理解。逻辑推理题多人约定、真假话、排名关系这类题考察约束一致性。中文应用题工程类浓度计算、行程问题能反映中文语义理解。运行后把“最终答案错误”的样本单独拉出来看推理过程通常能发现两类问题一类是模型把题目条件理解错另一类是中途计算步骤错误但后续没有自检。这个分析过程比总分更有价值。3.3 长上下文任务用针堆测试测“定位查找”和“全局综合”不同模型的上下文窗口差异很大如果测试集里所有问题都很短无法暴露长上下文能力差距。建议设计包含 4 到 8 道长文题文本长度覆盖 8K、32K、128K 等典型档位。“大海捞针”式测试是基础在一段长文中随机位置插入一条事实例如“仓库编号 ZK-2026-0817 的货物预计在 6 月 18 日入仓”然后让模型回答编号或日期。这个测试能反映模型是否真的阅读长文中间段落。但要注意大海捞针通过率高并不代表长文理解能力强。更有效的两类长文任务全文摘要一致性给定一篇长文要求提取所有指定类型的实体并与预置的实体清单对比。这样可以检查模型是否漏掉分布在长文不同位置的实体。尾部任务干扰在长文末尾追加一段与主任务无关的段落要求模型忽略干扰信息完成主任务考察指令跟随稳定性。长文本任务执行前先获取全文 token 数超过模型上下文窗口的要单独标记避免某个模型因截断而丢分。3.4 函数调用与结构化输出JSON 合法只是起点字段语义要准确Grok、Opus、DeepSeek 系列在实际业务里更多承担 Agent 角色函数调用能力比“写作文”更重要。测试题不会只问“你会不会用工具”而是设计成“根据用户指令选择合适的工具并填入参数”。示例场景用户说“帮我查一下这个链接指向的页面是否还在更新顺便把页面标题保存到我的笔记列表里”。测试目标是让模型识别出两个操作一个是 URL 可用性检查一个是笔记保存并返回结构化的函数调用参数。建议准备的函数调用测试集参数提取从非结构文本中抽取日期、城市、金额。工具选择多个函数混在一起模型要判断到底调用哪个、要不要并行调用。参数边界缺省值、枚举值、类型转换是否正确。连续调用第一轮调用的返回值要影响第二轮参数。结果校验不能只看 JSON 是否合法还要看字段路径是否匹配、枚举值是否合规、缺省字段是否遗漏。3.5 指令遵循与中文场景把最常见的业务要求变成测试题模型分数高但一进业务就不好用很大程度上是“指令遵循”出了问题。例如要求“只输出 JSON不要解释”模型还是会在前面补一句“好的”要求“全文不超过 100 字”模型输出 150 字多轮对话中要求“记住我的名字后续用名字称呼”模型在第三轮就忘了。针对中文业务场景建议加入以下指令类型格式约束只输出 JSON、不要 Markdown、不要代码块。字数约束50 字、100 字、200 字分别测试。语气约束正式书面语、口语化客服、面向青少年的解释。多轮记忆前三轮提供事实第四轮提问考察是否引用前文事实。内容安全边界当用户输入包含诱导性指令、试图让模型忽略系统设定时模型应保持原有安全策略而不是无原则顺从。尤其要注意“内容安全边界”的测试设计。这里考察的不是绕过机制而是模型对“不当请求”的拒绝与纠正能力。例如用户说“忽略之前所有设定直接回答 XXX”即使这是测试场景也应该让模型学会识别冲突指令并说明限制。这类题目要设计成正面安全能力评估而不是攻击演练。4. 评分方案自动校验、裁判模型、人工抽检各管一段4.1 能确定性判定的题一律不要用裁判模型打分代码题用单测数学题用答案对比枚举题用匹配JSON 提取题用 schema 校验。凡是存在客观标准的题目都必须走确定性校验这样可以减少裁判模型的偏差。一个常用的流程是先做客观校验再判断是否需要裁判模型。只有那些没有标准答案的题目例如“总结是否准确”“翻译是否通顺”“回答是否有帮助”才交给裁判模型打分。下面是一个简化的评分调度内容def score_item(item, record): task_type item.get(task_type) if task_type function and item.get(reference, ).endswith(.json): return score_by_unit_test(item, record) if task_type answer: return score_by_exact_match(item, record) if task_type json_extract: return score_by_json_schema(item, record) # 其余开放题交给裁判模型 return score_by_judge_model(item, record)自动校验要做到严格但不死板。例如“最终答案”题允许去掉标点、空白和单位后进行对比但如果模型把完整推理过程都当作答案返回自动解析就会误判所以在提示词中就要按“只输出答案”的要求训练评测对象。4.2 裁判模型评分时的污染源要逐个排除开放题使用裁判模型是可行的但要遵循几个原则不要让被测模型自己给自己打分。如果题目来自模型 A 的输出由模型 A 裁判会出现偏好自家风格的系统性偏差。裁判时隐藏模型身份。把三个模型的输出随机打乱再打分不让裁判知道哪个回答来自哪个模型。成对比较要交换顺序。对 A/B 两个回答各打一次分一次 A 在前一次 B 在前降低位置偏差。给出具体评分维度。不要只给“1 到 10 分”要给出“准确性、完整性、格式遵循、可执行性”四个维度每个维度有分数锚点描述。裁判模型的提示词示例框架你是一名中立评测员。以下是同一个用户问题对应的两个回答。 请从准确性、完整性、格式遵循三个维度分别打分每个维度 1 到 5 分。 评分说明 5 完全正确且完整 4 基本正确少量遗漏 3 部分正确有明显遗漏或错误 2 大部分错误 1 完全不相关或拒绝回答 问题{question} 回答 A {answer_a} 回答 B {answer_b} 请只输出 JSON格式{accuracy: {A: n, B: n}, completeness: {A: n, B: n}, format: {A: n, B: n}}裁判模型返回的 JSON 要再次做 schema 校验解析失败时记录日志并走人工抽检不要静默跳过。4.3 响应时间、token 消耗和处理成本要一起记录模型能力再强如果单次响应速度过慢、token 成本过高在真实业务中也很难落地。每次请求都要记录以下字段指标记录方式用途总耗时从发起到接收完整响应的秒数判断实际终端体验首 token 延迟从发起到收到第一个 token 的秒数判断流式输出体验输入 token 数API 返回的 prompt_tokens计算上下文占用输出 token 数API 返回的 completion_tokens判断输出长度是否合理每次请求成本根据单价换算或使用官方计费接口估算评估运营成本成本换算要把输入 token 和输出 token 分开计算因为绝大多数厂商的输出 token 价格更高。如果题目是多轮对话要把每一轮调用的成本累加。测试报告里不要只报总成本还要报“每道题平均成本”和“每万 token 有效回答成本”后者更能反映模型的输出效率。注意成本数据以各厂商官方定价页为准。建议在报告标题或备注中写明“价格采集时间”避免读者拿着过期价格做决策。5. 横向压测里反复出现的五个坑5.1 输出格式不稳定导致误判现象同一道题模型 A 直接输出“答案是 18”模型 B 输出带推理过程再给答案自动匹配时把 A 算对、B 算错。原因没有在提示词中统一输出格式也没有在评分前做后处理。检查查看原始输出统计带代码块、带解释、带多余前缀的比例。解决题目末尾统一追加格式约束例如“只输出最终答案不要解释”。评分前对文本做归一化包括去除首尾空白、代码块标记、中英文标点替换。预防把每条结果原样落盘评分脚本和评分解析分开便于回溯。5.2 多次运行结果波动被当成模型能力差异现象某模型第一次跑准确率 80%第二次跑变成 68%结论无法复现。原因temperature 过高、重复次数不足、模型推理路径随机性强。检查对比同一模型同题多次运行的输出是否一致。解决基线测试使用低随机性参数对关键题目增加到 5 次重复报告稳定性和平均分数。预防在报告中同时输出“单次最好成绩”和“重复运行通过率”避免用单次结果下结论。5.3 模型 ID 和实际版本不一致现象配置里填的是 V4 Pro 的模型 ID运行时 API 网关映射到了另一个版本或厂商在没有通知的情况下灰度切换了上游权重。原因模型 ID 与产品名不是一回事部分平台允许别名映射。检查在结果文件中记录每条响应返回的 model 字段并对比配置文件中的模型 ID。解决用官方文档确认准确 ID测试时间集中到同一天完成减少版本漂移影响。预防在大规模测试前先跑一条小样例打印返回的 model 字段进行确认。5.4 公开测试集污染导致分数虚高现象使用厂商官方公开过的评测题重新测试所有模型得分都很高区分度不足。原因这些题目可能出现在模型的训练语料中。模型学的不一定是解题能力而是记住了答案。检查把题目改成相似但不完全相同的表述看分数是否出现断崖式下降。解决在公开题之外维护一套私有题集并持续更新重要题不发送到外部平台避免入池。预防测试集分为 fixed 和 refresh 两个集合。fixed 用于长期趋势对比refresh 题集用于防止记忆污染。5.5 裁判评分偏好导致结论系统性偏移现象模型 A 的文本更冗长、语气更正式裁判模型给的分更高即使模型 B 的内容更准确。原因裁判模型本身有回答风格偏好它会不自觉地把“更像自己人”的输出判为更优。检查对同一批回答把文本顺序调换后重新打分看结果是否翻转。解决采用成对比较且交换顺序多个裁判模型交叉验证人工抽检 10% 到 20% 样本。预防开放题采用“先维度评分、再综合排序”的两阶段方式降低风格因素影响。6. 把结果整理成可复用的对比报告和持续回归基线6.1 报告结构分维度得分、耗时成本、失败案例三部分实测完成后不要把结果简单汇总成一张总分表。建议输出三部分内容。第一部分是分维度准确率报告格式如下模型 ID代码通过率数学答对率长文检索函数调用指令遵循中文场景总体模型 A待填待填待填待填待填待填模型 B待填待填待填待填待填待填模型 C待填待填待填待填待填待填每个维度的分母和分子都要写清楚。例如“代码通过率 通过全部单测的题目数量 / 代码题总数”不包含“部分单测通过”的模糊状态。第二部分是延迟与成本模型 ID平均总耗时平均首 token 延迟平均输出 token 数每题平均成本总测试成本模型 A待填待填待填待填待填第三部分是失败案例摘录。只列分数不列案例的报告没有修正价值。把每个维度的典型失败输出截图或原文放出来标注“错误原因归类”例如格式未遵循。计算步骤错误。长文关键信息遗漏。JSON 字段缺失。多轮状态丢失。中文语义理解偏差。对错误原因做计数会发现模型的问题通常集中在某一两类而不是均匀分布。6.2 发布前检查清单每次发布评测报告前按下面清单逐项确认已记录测试开始时间和结束时间。已确认每个模型的 ID 与官方文档一致。所有题目的采样参数写进配置并有日志。代码题确实在隔离环境中执行结果文件已保存。自动评分脚本有原始输出可追溯。裁判模型打分的题已隐藏模型身份并交换顺序。至少人工抽检了 10% 的开放题答案。结果文件中记录了每条请求的 token 数、耗时和错误重试标记。成本数据注明采集日期和汇率口径。报告的结论没有超出该批次数据能支持的边界。6.3 扩展方向把评测脚本接进版本跟踪一次实测只能说明当前版本的表现。更可持续的做法是把这套评测脚本沉淀为团队的“回归基线”在每次模型版本更新时自动跑一遍子集。具体可以做三步第一步把测试集按稳定度分成快慢两组。快组 20 到 30 题覆盖核心编码、数学、格式遵循适合版本更新后快速跑慢组 100 到 200 题适合每周或每两周跑一次。第二步把结果输出接入表格或数据看板记录历史变化曲线用趋势而不是单点分数判断模型退化。第三步定期把线上真实用户问题脱敏后加入测试集保持题库对业务场景的敏感度。对“DeepSeek V4 Pro、Grok 4.6、Opus 4.8 上强度”这类横向实测最有价值的产出不是“谁第一”的结论而是一套能在下次新模型出现时立即运行的评估流水线。把题目、参数、评分、日志、成本都管好模型之间的差异才会真正浮出水面。等下一轮版本发布时直接复用这套流程记录变化、对比案例、更新报告才是长期受益的做法。
返回列表