把 DeepSeek V4 Pro、Grok 4.6、Opus 4.8 放到同一套评测流程里对比,比单独看一张模型榜单要难得多。难在接口协议不一致、限流策略不同、模型版本随时会变化,更难在评测集很容易被训练语料污染,导致得分虚高。所以这篇文章不打算只给“谁最强”这种无法复现的结论,而是整理出一套能直接落地的硬核横向对比方案:从账号准备、评测集设计、批量请求脚本、结果打分到常见错误排查,一条线走完。你可以把文中的占位地址和模型名替换成自己账号下有权限的真实模型,跑出一份可复现的评测报告。
先回答三个会影响评测路线的问题:这三款模型有没有本地权重,能不能本地部署?答案取决于各自官方发布策略,我在这里不假定它们一定都提供开源权重,也不假定都只能走官方 API。在动手前第一件事必须是查官方模型卡和仓库,确认授权、部署方式和计费规则。评测需不需要很强的 GPU?如果全程走 API,普通开发机即可,不需要本地 GPU,只要服务器到各 API 的网络路径稳定;如果想本地跑开源权重,就需要按量化位宽和上下文长度重新评估显存。评测到底在测什么?真正要测的是边界推理、长文本保持、结构化输出、批量稳定性和成本可控性,而不是把几个模型接到同一个聊天窗里问一两个主观题。下面这份方案,核心目的是让每个结论都有日志、有版本、有可回放步骤。
1. 评测目标与模型接入前核对项
先给这次硬核对测定个边界。DeepSeek V4 Pro 来自 DeepSeek 系列,Grok 4.6 来自 xAI 系列,Opus 4.8 来自 Anthropic 的 Claude 系列。它们在接口形态、上下文窗口、计费逻辑、数据留存政策上都可能不同,所以评测的第一步不是写 Prompt,而是把“被测对象”的具体版本固定下来。很多人评测模型时只写一个模型名,不写日期和版本快照,结果半个月后模型更新,旧数据全部失效。这个问题要避免。
接入前建议用一份清单确认:模型真实名称是什么,API 请求里填的model字段是否与文档完全一致;服务商是否支持 OpenAI Chat Completions 风格接口,还是走 Anthropic Messages 风格接口;是否支持seed、temperature、max_tokens、response_format这些控制参数;单次请求的上下文窗口上限是多少 token;价格是按输入 Token、输出 Token 分别计费,还是按固定请求包计费;是否有并发数限制和每分钟请求数限制;API Key 的作用域是否只允许创建评测专用 Key。
把这些核对项整理成一个配置表,每次跑分都带上。下表列出三个被测对象需要核对的通用属性,真实值要以官方文档为准。
| 核对项 | DeepSeek V4 Pro | Grok 4.6 | Opus 4.8 |
|---|---|---|---|
| 官方渠道 | DeepSeek 官方技术文档 | xAI 官方技术文档 | Anthropic 官方技术文档 |
| 接口风格 | 需要确认是否兼容 OpenAI 格式 | 需要确认是否兼容 OpenAI 格式 | Anthropic Messages API 或兼容层 |
| 本地权重 | 以官方开源策略为准 | 一般以托管 API 为主 | 以官方发布策略为准 |
| 控制参数 | 需确认 temperature/seed 等 | 需确认 temperature/seed 等 | 需确认 temperature/max_tokens 等 |
| 评测建议 | 先做 5 条连通性测试 | 先做 5 条连通性测试 | 先做 5 条连通性测试 |
这个表不填死参数是对的。因为任何模型版本更新、服务区域不同,都会导致字段差异。真正负责的做法是保留每次请求的请求体和响应体,让结果可以回溯。
2. 环境准备与评测资源配置
评测脚本建议放在独立目录,使用干净的 Python 虚拟环境。这一步可以避免本机其他项目的依赖版本冲突,也能让结果文件集中在一个目录里,方便后续核对。你需要准备的东西包括八到十六核的 CPU 开发机或普通云服务器、Python 3.9 以上环境、稳定的公网访问能力、一个用于保存评测输出结果的目录。如果使用 API,不需要 GPU;如果后续要测开源权重,才需要考虑带独显的 Linux 服务器。
安装依赖时最小化即可。不建议一上来就装几十个 Python 库,依赖越少,复现越容易。这里只需要requests和标准库就能完成批量调用;如果你希望做更复杂的重试、限速和结构化统计,可以再用pandas或polars,但不是必须。
mkdir -p llm-hardset && cd llm-hardset python3 -m venv .venv source .venv/bin/activate pip install --upgrade pip requests评测 API Key 不要写进脚本,也不要写进配置文件再推到 Git 仓库。最稳妥的方式是放在本地环境变量里,并在脚本启动时强制检查。每个服务商给 Key 的命名方式不同,统一使用DEEPSEEK_API_KEY、GROK_API_KEY、OPUS_API_KEY三个环境变量作为占位,实际名称按官方文档调整。命令行可以这样声明:
export DEEPSEEK_API_KEY="sk-你的key" export GROK_API_KEY="xai-你的key" export OPUS_API_KEY="sk-ant-你的key"如果你担心某个 Key 是否误提交到 Git,可以在仓库根目录创建.gitignore并加入.env、*.log、results/。本地有多个模型账号时,建议把 API Key 权限限制到“只允许调用模型”,不要给拥有账单、删除资源等高权限的 Key。评测过程中最好同时开启月度费用提醒,避免某个评测集因为并发重试导致成本飙升。
3. 评测集设计:怎样才算“上强度”
所谓上强度,不是把 Prompt 写得刁钻,而是让模型在多个考察维度上都接受量化评分。这里给出一个适合三模型横向对比的评测集骨架。推荐每个维度至少二十条已准备好标准答案或可自动校验的题目,长文本维度可以做到五到十条,但文本长度必须超过模型上下文窗口的一半才有区分度。下表列出六个核心评测组。
| 评测组 | 考察点 | 建议数量 | 打分方式 |
|---|---|---|---|
| 多步推理 | 数学、逻辑链条是否完整 | 20 | 正则匹配 + 人工复核 |
| 代码生成 | 函数实现、多文件调用、边界条件 | 20 | 单元测试自动运行 |
| 结构化输出 | JSON Schema 是否严格匹配 | 20 | jsonschema自动校验 |
| 工具调用 | 函数选择、参数类型、空值处理 | 15 | JSON 字段匹配 |
| 长文本保持 | 在长文本中定位关键约束 | 10 | 关键语句正则匹配 |
| 对抗性干扰 | 已知信息缺失时是否编造 | 15 | 人工评分 + LLM Judge |
题面要尽量自己构造,直接复制公开 Benchmark 的题目容易导致分数虚高。比如多步推理维度,可以设计“若干人在不同时间进入和离开电梯,求最后电梯里有多少人”的变体;代码生成维度,可以设计一个带文件读写和异常处理的 Python 函数,要求返回指定 JSON;结构化输出维度,通过response_format或 Prompt 要求返回 JSON,再用 JSON Schema 校验必填字段。长文本维度可以准备一份模拟业务日志,在中间和末尾插入互相矛盾的时间字段,看模型能否发现不一致。
对抗性干扰是最容易被忽略的。很多模型在遇到“数据是否真实存在”的问题时会强行补充一个看起来合理但不存在的答案。正确测试方式是直接问“某公司 2024 年第四季度的分产品营收是多少”,但评测材料里并不提供这一数据;模型应当明确说信息不足,而不是编数字。所有题面在运行前都要检查是否包含个人隐私、未授权文档、商业秘密等内容,涉及版权材料时要做脱敏处理。
4. 批量请求与评测运行脚本
评测脚本要解决的问题很简单:读入一组任务,对三个模型分别发起请求,记录每次请求的输入、输出、耗时、状态码和异常信息。这里给出一个通用实现。由于不同服务商接口有差异,使用配置文件区分每个模型各自的endpoint和payload_template;你只要把占位地址替换成官方地址,把响应体解析规则按实际接口调整即可。
先创建config.json,内容是对三个模型的统一接入配置。不同模型的endpoint不同,并不代表代码有两套逻辑,而是把差异收敛到配置层。
{ "output_dir": "results", "models": [ { "name": "deepseek-v4-pro", "api_key_env": "DEEPSEEK_API_KEY", "endpoint": "https://example.com/v1/chat/completions", "payload_template": { "model": "deepseek-v4-pro", "temperature": 0.2, "max_tokens": 1024, "messages": [] } }, { "name": "grok-4.6", "api_key_env": "GROK_API_KEY", "endpoint": "https://example.com/v1/chat/completions", "payload_template": { "model": "grok-4.6", "temperature": 0.2, "max_tokens": 1024, "messages": [] } }, { "name": "opus-4.8", "api_key_env": "OPUS_API_KEY", "endpoint": "https://example.com/v1/messages", "payload_template": { "model": "opus-4.8", "temperature": 0.2, "max_tokens": 1024, "messages": [] } } ] }注意一点:这里三个配置的endpoint都用了example.com占位,直接跑必然会失败。你必须替换成服务商文档里的真实请求地址;同时请求体字段也需要与真实接口匹配。配置中的messages数组会在运行时被注入新的用户消息。对于 Anthropic Messages API,模型名、消息结构、输出限制字段与 OpenAI 格式不完全一样,所以最好在启动前先用一条请求做连通性测试。
接着在tasks.jsonl中存题目,每行一个 JSON 对象:
{"task_id": "logic_001", "group": "logic", "prompt": "一个房间里有红、蓝、白三种颜色的球。红球比蓝球多 2 个,白球是红球的两倍,三种球加起来是 22 个。请列出方程并求出每种球的数量。"}然后是核心的 Python 脚本。这个脚本会把不同模型的响应体解析交给一个extract_text函数处理,兼容 OpenAI 风格和 Anthropic 风格的常见返回格式。脚本不对模型做主观评分,只输出原始 JSONL 记录,方便后续做离线打分。
import json import os import time import argparse import requests def load_jsonl(path): tasks = [] with open(path, "r", encoding="utf-8") as f: for line in f: line = line.strip() if line: tasks.append(json.loads(line)) return tasks def extract_text(data): if isinstance(data, dict): if "choices" in data: return data["choices"][0]["message"].get("content", "") if "content" in data and isinstance(data["content"], list): parts = [ block.get("text", "") for block in data["content"] if block.get("type") == "text" ] return "".join(parts).strip() if "content" in data and isinstance(data["content"], str): return data["content"] return "" def call_model(cfg, prompt, timeout=180, max_retries=3): api_key = os.environ.get(cfg["api_key_env"]) if not api_key: raise RuntimeError(f"missing env {cfg['api_key_env']}") headers = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json", } payload = json.loads(json.dumps(cfg["payload_template"])) payload["messages"] = [{"role": "user", "content": prompt}] last_error = None for attempt in range(max_retries): start = time.time() try: resp = requests.post( cfg["endpoint"], headers=headers, json=payload, timeout=timeout ) elapsed = round((time.time() - start) * 1000, 2) if resp.status_code == 429: retry_after = resp.headers.get("Retry-After") wait = float(retry_after) if retry_after else 2 ** attempt time.sleep(wait) continue if resp.status_code >= 500: time.sleep(2 ** attempt) continue if resp.status_code >= 400: return { "model": cfg["name"], "status": resp.status_code, "error": resp.text[:500], "latency_ms": elapsed, } data = resp.json() return { "model": cfg["name"], "status": resp.status_code, "text": extract_text(data), "latency_ms": elapsed, "raw": data, } except requests.exceptions.Timeout: last_error = f"timeout after {timeout}s" except requests.exceptions.RequestException as exc: last_error = str(exc) finally: pass return { "model": cfg["name"], "status": -1, "error": last_error or "exhausted retries", "latency_ms": -1, } def main(): parser = argparse.ArgumentParser() parser.add_argument("--config", required=True) parser.add_argument("--tasks", required=True) parser.add_argument("--output", required=True) parser.add_argument("--repeat", type=int, default=1) args = parser.parse_args() config = json.load(open(args.config, "r", encoding="utf-8")) tasks = load_jsonl(args.tasks) os.makedirs(os.path.dirname(args.output), exist_ok=True) with open(args.output, "w", encoding="utf-8") as out: for task in tasks: for model_cfg in config["models"]: for rep in range(args.repeat): record = call_model(model_cfg, task["prompt"]) record["task_id"] = task["task_id"] record["group"] = task.get("group", "general") record["repeat"] = rep out.write(json.dumps(record, ensure_ascii=False) + "\n") out.flush() print( f"{model_cfg['name']} | {task['task_id']} | " f"rep {rep} | status {record['status']} | " f"{record.get('latency_ms', -1)}ms" ) if __name__ == "__main__": main()这个脚本可以满足最基本的多模型批量请求。执行时只需要把配置文件、题目文件和输出文件传进去:
python runner.py \ --config config.json \ --tasks tasks.jsonl \ --output results/run_2025.jsonl \ --repeat 3第一次运行建议只挑三个任务,把--repeat设为 1,先确认接口、Key、输出解析都通了,再跑全量。避免一次循环几百个请求后才发现某个模型的响应格式没有正确解析。
5. 效果评测:不只是看答得对不对
批量请求跑完后,得到的是原始 JSONL 文件,里面包含每个模型的输出文本、状态码和耗时。接下来要做的不是拍脑袋打分,而是先把数据拆成两个部分:可自动判定的结果和需要人工复核的结果。可自动判定的包括代码生成是否通过单元测试、结构化输出是否满足 JSON Schema、数学题的最终数字是否匹配;这些可以写一个离线评分脚本批量算。需要人工或 LLM Judge 的包括逻辑解释是否完整、长文本是否虽然结果正确但没有发现矛盾、对抗性题是否在信息不足时选择拒绝作答。
为了减少随机性影响,同一道题建议至少跑三次,最终取中位数或者通过率。模型输出取每次调用的结果,不把多个重复请求合并成一次。如果某一次请求因为超时或限流失败,这一条不应该直接计为回答错误,而应该单独标为“调用失败”,在总成功率里单独统计。很多横评测错把超时当作答案错误,最后拉低某个 API 的分数,这不公平。
结构化输出可以用json.loads做基本解析,再用jsonschema校验。先看模型是否返回了合法 JSON,再看字段是否完整、类型是否正确。代码类任务则是把生成的代码写入临时文件,由测试脚本执行pytest或单测样例,跑通才算得分。这样能避免“看起来对但运行报错”的情况。推理题因为答案存在多种表达方式,建议先做答案归一化,再与标准答案比对;只有这样才能让结果可重复。
在产出报告时,建议把下面这些字段保存下来:模型名、模型版本快照、请求时间、任务类型、Prompt 原文、输出原文、延迟、返回状态、是否重试、最终是否通过。一个规范化结果表可以设计成这样:
| 字段 | 说明 |
|---|---|
| model | 实际请求中的模型名 |
| task_id | 评测任务编号 |
| group | 评测组 |
| pass | 是否通过规则判定 |
| latency_ms | 完整请求耗时 |
| finish_reason | 模型返回的结束原因 |
| retry_count | 重试次数 |
| error | 失败时的错误信息 |
这个表能帮助你快速定位“某模型在哪类任务上失败最多、失败是因为拒答还是超时”。如果某个模型在长文本维度频繁返回截断,说明max_tokens或上下文管理可能有问题,而不是单条 Prompt 的问题。
6. 显存、延迟与资源占用的观察方法
如果你走的完全是 API 评测,资源占用观察的重点不是 GPU,而是网络延迟、失败请求比例和日志占用的磁盘空间。测延迟时,尽量让三个模型的请求在同一时间段、同一台机器上运行,避免不同网络路径带来的误差。网络波动大时,需要把延迟按秒级分布画出来,不要只取平均值,因为少数超时会严重拉高平均耗时,掩盖正常请求的表现。
如果你想本地部署 DeepSeek V4 Pro 或其他有开源权重的版本,评测前就要确认 GPU 显存是否足够。显存估算可以先按模型参数量粗略判断:半精度权重大约是每十亿参数需要 2GB 显存,再加上 KV Cache、激活值和推理框架开销,实际占用要按程序运行时读取的显存为准。不要相信任何“固定 7GB 就能跑”的说法,这类数字只适用于特定模型版本、量化位宽和上下文长度。
观察显存最直接的方法是在 Linux 服务器上单独开一个终端,持续输出显存变化:
watch -n 1 nvidia-smi如果评测脚本是在容器里运行,还可以用nvidia-smi dmon -d 1查看更细粒度的显存读写、温度、功耗数据。本地跑批量任务时要留意,尽量把 batch size 调成 1,先确认单请求显存峰值,再逐步增大 batch,否则容易直接触发 CUDA Out of Memory。显存不足时优先考虑降低输入输出长度、切换轻量化量化格式、减少同时推理的序列数,而不是一开始就换大显存显卡。
另一个容易被忽略的性能指标是“输出生成时间”。如果同一个模型在同一套题上多次运行,耗时方差比较大,说明供应链端负载不稳定或网络路径不稳定。这时候需要从日志里按请求时间和耗时画散点图,而不是简单归因于模型本身。
7. 批量任务与接口服务稳定性建议
需要把评测脚本接入正式业务,或者做更大规模批量评测时,稳定性比单次推理质量更重要。一个基本准则是:不要用无界队列直接打满全部请求,先用小批量探测出服务商实际的限流水位,再把并发步长放大。每次启动批量前可以从 1、2、4、8 的并发数开始观察,出现 429 限流就降低并发并保存断点。
评测任务最好是可分片、可续跑。上面的脚本按 JSONL 逐行输出,已经具备日志断点的雏形。发生故障时,不需要重新跑全部任务,只需根据task_id和model去重,把缺少的结果重新补跑即可。更严格的做法是给每个任务分配唯一 ID,记录请求发送前的状态,并在回调返回后更新状态;这样即使进程中途被杀掉,也能根据状态表恢复。
批量任务还要处理好重试的幂等风险。一个请求发出去后可能已经到达模型服务端,但响应在传输中丢失,客户端只能超时重试。对个人评测影响不大,多花一点历史费用而已;但对接生产系统时,一定要使用服务商提供的请求 ID 或幂等键,或者保存上游返回的标识,避免重复生成、重复计费。如果 API Key 被多个任务共享,建议在请求头中标记不同的调用方名称,方便事后按调用方拆分日志。
批量评测过程中,还建议把结果按任务类型分别保存,而不是所有内容混在一个 CSV 里。可以按results/logic/、results/code/、results/long_context/建立子目录,每个子目录里有对应的 JSONL 原始响应和 CSV 统计文件。后续要写复盘报告时,只用读取对应维度,不需要从巨大的混合日志里改筛选逻辑。
results/ ├── logic/ │ ├── deepseek-v4-pro.jsonl │ ├── grok-4.6.jsonl │ └── opus-4.8.jsonl ├── code/ ├── long_context/ └── run_2025_summary.csv8. 常见问题与排查方法
批量评测中常见的问题不只是普通的代码报错。结合三模型横评场景,把最容易出现的问题整理成下表。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 请求返回 401 | API Key 不存在、过期或格式错误 | 检查环境变量是否加载 | 重新生成 Key,确认环境变量名 |
| 请求返回 403 | Key 没有模型调用权限 | 在服务商控制台查看权限范围 | 给 Key 添加对应模型权限 |
| 请求返回 404 | endpoint 或模型名不对 | 对比官方文档与请求日志 | 替换真实 endpoint/model |
| 请求返回 429 | 触发限流或并发超额 | 查看响应头和日志中的 Retry-After | 降低并发、增加退避时长 |
| 响应超时 | 网络路径不稳或模型生成过长 | 查看 latency_ms 与错误原因 | 延长 timeout,改用流式输出 |
| 输出被截断 | max_tokens 设置不足 | 检查 finish_reason 是否为 length | 提高 max_tokens,或拆分任务 |
| JSON 解析失败 | 模型返回了 Markdown 或多余文字 | 查看原始输出 | 使用 response_format,或后处理剥离代码块 |
| 长文本结果不一致 | 提示词约束被深埋在长文中 | 查看是否出现上下文丢失 | 优化长文本组织,重要指令放在开头和结尾 |
| 批量任务卡住 | 并发过高或异常没有被捕获 | 查看进度打印和日志文件 | 给单请求加超时和最大重试次数 |
| CUDA out of memory | 本地部署时显存不足 | 使用 nvidia-smi 观察显存峰值 | 降低 batch、降低序列长度或更换量化格式 |
遇到这些异常时,最优先看的不是模型效果,而是原始请求日志。日志里要包含发送时间、请求地址、请求体大小、返回状态码、响应体片段和耗时。保存日志是排查问题的第一前提,如果你连请求是否成功发出都无法确认,后面的模型效果讨论就没有意义。
9. 评测合规与数据使用边界
做模型横评,特别是使用 API 批量调用时,必须注意几个合规边界。输入评测集不能包含任何未经授权的个人隐私、商业敏感文档、受版权保护的全文内容。如果测试场景确实需要长文本,建议使用自己生成的模拟数据、公开授权语料或脱敏后的虚构业务记录,而不是直接把企业内部文档灌进模型。任何企业数据进入第三方模型服务前,都应该先确认数据是否允许离开内网环境。
评测输出也可能有版权和数据留存问题。不同服务商对 API 请求数据的存储、训练使用政策有差异,如果企业没有签署专门的数据保护协议,默认情况下不要用核心业务数据做 Prompt 测试。模型的回答也不一定准确,公开评测结论时要注明模型版本、评测时间、评测集来源和运行环境,不要把某一个时间点的结果包装成永久结论。
涉及模型回答中的合规问题,还要留意脚本是否会生成恶意代码、钓鱼文案、绕过安全限制的内容。评测集如果偏对抗与安全,需要限定在测试环境内运行,并确保输出不会用于实际攻击场景。所有内容必须符合使用场景的公序良俗和法律法规,不要用模型能力去制造虚假信息、破解系统或侵犯他人肖像与知识产权。准备发布的案例,必须经过脱敏和人工复核。
另一个容易被忽视的问题是日志中的 API Key 泄露。JSONL 原始响应里可能包含请求头中的令牌信息,如果你把完整请求体原样写入日志文件,请确保日志目录被严格保护,不要提交到公开 Git 仓库。输出 CSV 报告时,只保留“请求时间、模型名、任务、结果”等必要字段,不要把 Authorization Header 和完整原始响应体导出到公开文件里。
10. 总结与下一步
这份评测方案能帮你得到一份可用的大模型横评记录,而不是只有一句结论。你会获得原始请求日志、自动评分结果、耗时统计和错误排查记录。接下来值得继续扩展的方向有几个:把评测集接入 CI,在模型版本更新后自动触发回归测试;增加 Agent 类任务,让模型不只回答问题,还要完成多步工具调用;用统一 Prompt 模板做金标测试集,当新模型发布时快速对比历史数据。最值得先跑通的是小规模三项检查:接口连通性、JSON 输出稳定性和单条 Prompt 的耗时。只要这三项能稳定跑通,再往上叠加任务和并发才安全。评测不是一次性的攻城战,关键是建好一条能持续对比的流水线,让后面的版本更迭都能在同一个尺度上被看见。