这次我们看一个轻量级开源 LLM Benchmark 工具,核心场景是把 OpenRouter 上的任意模型拉到同一套评测题里,批量跑完,最后输出一张可比对的报告。和本地跑权重最大的区别是:它不需要高配显卡,推理发生在模型提供方,你的笔记本只要能联网、有 API Key 就能跑。最值得关注的有三点:模型切换成本极低、评测集可以完全自定义、输出报告能导成 Markdown/JSON,方便接到自动化流程里。
这类工具的价值很直接:OpenRouter 把多家厂商的模型统一成兼容 OpenAI 的接口,你可以用同一个脚本快速切换模型,但每个模型在不同任务上的质量、速度、价格差异都很大。靠手工挨个试,既慢又不客观。一个轻量级 benchmark 工具,就是把“试模型”这个过程脚本化、可重复化、可审计化。下面我会先给核心能力速览,然后从环境准备、启动方式、功能测试、接口 API 和批量任务展开,最后给排查清单和最佳实践。如果你正在做 LLM 选型评估、Prompt 效果对比,或者要给 RAG/Agent 系统做回归测试,这篇可以直接照着用。
需要先说明一点:不同开源版本的实现细节会有差异。下面给出的目录结构和代码是我基于工程通用流程拆出来的最小可运行方案,你用实际项目时,要按下发的 README、配置文件以及本机路径做调整。这样能避免照搬后出现端口、模型名或字段对不上的问题。
1. 核心能力速览
| 能力项 | 说明 |
|---|---|
| 项目类型 | 轻量级 LLM Benchmark / 模型评测工具 |
| 核心目标 | 在 OpenRouter 上用同一套评测集对比任意模型 |
| 主要功能 | 多模型批量评测、自定义问题集、耗时统计、Token 统计、成本估算、Markdown/JSON 报告 |
| 显存需求 | 本工具作为 API 调用端,常规评测不直接占用显存;若同一环境还启动本地模型,显存由本地模型决定 |
| 支持平台 | Windows / Linux / macOS,有 Python 环境即可 |
| 启动方式 | 命令行运行评测脚本,或通过 FastAPI 暴露 HTTP 接口 |
| 接口 API | 支持,整体按 OpenAI 兼容接口调用 OpenRouter |
| 批量任务 | 支持,模型列表 + 评测集循环执行,可通过并发数控制速率 |
| 适合场景 | 模型选型、Prompt 对比、RAG/Agent 回归、成本与速度权衡 |
| 开源来源 | 开源社区通用方案,无特定厂商绑定;具体版本和仓库以你使用的项目为准 |
从能力表能看出来,这个工具的核心不是模型本身,而是“评测流程的统一”。它把不同模型的输入输出格式统一成一个入口,最终结果都落到同样的字段里。这样你比较模型时,比较的是模型在同一批问题上的表现,而不是不同调用方式带来的误差。
使用成本也非常低。普通笔记本跑一个 20 道题的对比评测,通常只需要几十秒到几分钟,瓶颈主要在模型提供方的响应速度,而不是本地计算资源。真正的成本来自 API 调用费用,所以工具设计上要把模型列表和评测集分开,方便你先跑小样本,再决定是否全量执行。
2. 适用场景与使用边界
这个工具最适用的场景是模型选型。比如你要给文档问答系统挑一个底座模型,候选有 GPT 系列、Claude、Llama 等。你可以准备 20 到 50 道业务真实问题,包含知识抽取、数学计算、JSON 输出、长文本总结等类型,然后让工具统一跑一遍,直接看每个模型的答案、耗时、Token 消耗和失败率。这比看各大榜单上的通用分数更有参考价值,因为评测题来自你的实际业务。
第二个适合的场景是 Prompt 比较。同一个模型,不同 Prompt 模板的效果差异经常很大。你可以在工具里把“模型”和“Prompt”都当作变量,跑几组对照实验,比如“用 CoT 提示词 vs 不用”“用角色设定 vs 用系统指令”。只要在评测集里额外加一列 prompt 字段,代码稍加改动就能支持。
第三个场景是 Agent 或 RAG 系统的回归测试。当你修改了检索策略、Prompt 模板或工具调用逻辑后,不能让整个系统变得不可用。你可以把这套 benchmark 工具接入 CI,每次更新后自动跑一遍核心问题集,看模型输出是否出现明显劣化。这个过程比肉眼观察日志要可靠得多。
不过也有一些场景不适合。如果你的目标是测量模型在真实生产环境里的并发上限,那应该在更高流量下去压测,而不是跑这种串行或低并发评测。如果评测集来自未脱敏的客户数据,也不适合直接上传到第三方 API。尤其是医疗、金融、法律等敏感领域,录入前必须先做脱敏、授权和合规确认。模型输出可能包含虚构内容,所有评测结果只能作为辅助参考,不能直接替代人工审核。
3. 环境准备与前置条件
先说结论:依赖很少,主流开发机都能跑。建议使用 Python 3.10 或更高版本,并准备一个干净的虚拟环境,避免 OpenRouter SDK、FastAPI、Pandas 等依赖和其他项目冲突。
操作系统方面,Windows 10/11、Ubuntu 20.04+、macOS 12+ 都可以。这个工具本质是网络请求 + 数据处理,所以没有显卡和 CUDA 的硬性要求。唯一的要求是运行环境能正常访问 OpenRouter API,且网络链路稳定。API 密钥需要提前在 OpenRouter 平台创建,创建后写入本地环境变量,不要硬编码进脚本或提交到 Git。
磁盘空间也不需要太多。一个纯脚本项目加 Python 依赖,总共占一两百 MB 已经很宽裕了。如果你还要把评测结果、日志、模型输出都存下来,按问题数和模型数递增,建议至少预留 1 GB 空间,避免长时间批量跑的时候磁盘被写满。
环境清单大致如下:
- Python 3.10+
- pip 或 conda
- OpenRouter API Key
- 项目需要的 Python 包:openai、pyyaml、pandas、fastapi、uvicorn、pydantic
在开始之前,先确认你的 API Key 是否有效。最直接的验证方法是写一个最简单的测试脚本,调用一次模型,返回正常后说明环境没问题。
4. 安装部署与启动方式
整个项目可以按下面的目录结构组织。这不是唯一方案,但能把配置、数据集、代码和输出分开,便于后续扩展。
bench_tool/ ├── config.yaml ├── datasets/ │ └── questions.csv ├── src/ │ ├── __init__.py │ ├── client.py │ ├── runner.py │ └── report.py ├── output/ └── requirements.txtrequirements.txt内容如下:
openai>=1.30.0 pyyaml>=6.0 pandas>=2.0 fastapi>=0.110.0 uvicorn>=0.27.0 pydantic>=2.0安装依赖:
cd bench_tool python -m venv venv # Linux/macOS source venv/bin/activate # Windows PowerShell .\venv\Scripts\Activate.ps1 pip install -r requirements.txt安装完成后,把 OpenRouter API Key 写入环境变量。Windows PowerShell 可以用:
$env:OPENROUTER_API_KEY="你的Key"Linux/macOS 可以用:
export OPENROUTER_API_KEY="你的Key"需要注意,这个 Key 是敏感凭证,不要让它在终端历史里留存太久。生产环境建议用密钥管理服务或.env文件,并在.gitignore中排除它。
config.yaml是核心配置,可以设置模型列表、评测集、并发数和输出路径:
models: - openai/gpt-4o-mini - anthropic/claude-3.5-sonnet - meta-llama/llama-3.3-70b-instruct dataset: ./datasets/questions.csv output: ./output/report.md temperature: 0 max_tokens: 1024 concurrency: 3 timeout: 120模型 ID 要按 OpenRouter 官方模型列表填写。不同时期的模型版本会有变化,以官方页面返回的 ID 为准。concurrency控制同时请求的数量,不建议一开始就调太高,容易被限流或触发超时。
评测集questions.csv可以包含这几列:
id,type,question,expected 1,logic,"A shop sells apples at 3 for 10 dollars. How many dollars do 9 apples cost?","30" 2,json,"Return a JSON object with fields name and age. Name is Alice, age is 25.","{\"name\":\"Alice\",\"age\":25}" 3,summary,"Summarize this paragraph in one sentence: ...","..."expected不是必填项,但如果你要自动计算准确率,就需要把标准答案或答案规则放进去。对于开放性问题,可以只记录模型输出,后续人工评测。
调用 OpenRouter 的客户端代码可以这样写:
from openai import OpenAI client = OpenAI( base_url="https://openrouter.ai/api/v1", api_key="YOUR_OPENROUTER_API_KEY", ) completion = client.chat.completions.create( model="openai/gpt-4o-mini", messages=[{"role": "user", "content": "1+1=?"}], temperature=0, ) print(completion.choices[0].message.content)这里使用base_url=https://openrouter.ai/api/v1和 OpenAI SDK,是因为 OpenRouter 提供 OpenAI 兼容接口。实际 endpoint 和参数以官方文档为准,但整体调用方式非常接近 OpenAI 原生接口,改造成本很小。
5. 功能测试与效果验证
部署完成后,不要急着一次跑全量模型,先用一个小规模测试确认链路是通的。下面按顺序给出几种测试。
5.1 基础调用测试
测试目的:确认 API Key、模型 ID、网络链路都正常。
操作步骤:
- 运行最简单的 Python 脚本,调用一个模型。
- 观察是否返回正常的回答文本。
- 检查是否得到
prompt_tokens和completion_tokens。
预期结果:脚本输出模型答案,没有报401、404或timeout。
常见失败原因:
- API Key 无效。
- 模型 ID 过期或拼写错误。
- 网络不可达。
如果这一步跑不通,后面所有功能都不用测。先解决环境问题。
5.2 单模型多问题测试
测试目的:确认评测集能被正确读取,并且模型能按问题依次回答。
使用代码:
import csv import time from openai import OpenAI client = OpenAI( base_url="https://openrouter.ai/api/v1", api_key="YOUR_OPENROUTER_API_KEY", ) def load_questions(path): with open(path, newline="", encoding="utf-8") as f: return list(csv.DictReader(f)) def ask_question(client, model, question): started = time.time() response = client.chat.completions.create( model=model, messages=[{"role": "user", "content": question}], temperature=0, max_tokens=1024, ) elapsed = time.time() - started return { "answer": response.choices[0].message.content, "elapsed": round(elapsed, 2), "prompt_tokens": response.usage.prompt_tokens, "completion_tokens": response.usage.completion_tokens, } questions = load_questions("./datasets/questions.csv") result = ask_question( client, "openai/gpt-4o-mini", questions[0]["question"], ) print(result)预期结果:返回结果包含answer、elapsed、prompt_tokens、completion_tokens四个字段。说明单模型调用链路没问题。
5.3 多模型批量测试
测试目的:验证同一个评测集可以在多个模型上运行,并能生成对比结果。
实现思路:读取配置中的模型列表,依次调用每个模型,把所有结果收集到 DataFrame 或 Python 列表里。建议使用AsyncOpenAI加信号量做并发,而不是严格串行。
一个带并发控制的异步版核心代码:
import asyncio from openai import AsyncOpenAI client = AsyncOpenAI( base_url="https://openrouter.ai/api/v1", api_key="YOUR_OPENROUTER_API_KEY", ) sem = asyncio.Semaphore(3) async def ask_one(model, question): async with sem: response = await client.chat.completions.create( model=model, messages=[{"role": "user", "content": question}], temperature=0, ) return response.choices[0].message.content这里Semaphore(3)表示最多同时 3 个请求。并发数过大时,请求会大量超时;并发数过小时,评测时间会拉长。建议从 2 到 5 开始试。
预期结果:所有模型都能返回答案,输出文件里能看到每个模型的耗时和 Token 消耗。如果某个模型返回错误,记录错误原因,不要中断整个流程。
5.4 JSON 输出稳定性测试
很多工程场景要求模型输出结构化 JSON,比如函数调用、数据抽取、Agent 工具参数等。这个测试专门验证模型的 JSON 输出能力。
测试形式:在问题里明确要求“只输出 JSON,不要解释”,然后用json.loads解析模型输出。
import json try: data = json.loads(result["answer"]) print("JSON parse success:", data) except Exception as exc: print("JSON parse failed:", exc)评测指标可以统计 JSON 解析成功率。如果某个模型连续多次解析失败,说明它的结构化输出能力在当前 Prompt 下不够稳定,可能需要额外加 few-shot 示例或换模型。
5.5 稳定性重复测试
同一个模型对同一个问题的回答,不会每次都完全一样,即使temperature=0,某些模型也可能存在随机性。稳定性测试的做法是:同一模型、同一问题重复 3 次,观察回答是否一致,或者关键字段是否一致。
测试代码可以复用上面的ask_question,在外面加一层循环,然后把多次输出保存下来。对于选择题或 JSON 抽取任务,可以计算“完全匹配率”;对于开放问答,则主要看内容有没有明显矛盾或幻觉。
这一项对生产落地很重要。如果你选中的模型在关键任务上每次答案都变,后续就难做断言。发现问题后,可以通过加大temperature=0、增加 system prompt、限定输出格式来缓解,但仍无法保证绝对稳定。
5.6 长文本与上下文测试
如果你的场景涉及长文档、代码仓库或长时间对话,需要测试模型在长输入下的表现。做法是在评测集里加入一个长度超过 4000 token 的文本,让模型做总结或抽取。
预期观察点:
- 是否返回令牌超限错误。
- 响应耗时是否显著增加。
- 总结内容是否忠实原文,有没有虚构。
- Token 消耗是否符合预期。
工具本身不设上下文长度限制,是否超限由模型决定。评测报告里记录每次请求的输入 Token 数,当接近模型上限时需要警惕质量下降。
6. 接口 API 与批量任务
除了命令行调用,这个轻量级工具还可以包一层 HTTP 接口,方便接入 CI 或内部平台。这里给出一个最小 FastAPI 服务壳,你需要根据实际项目调整路径和字段。
from fastapi import FastAPI from pydantic import BaseModel app = FastAPI() class BenchmarkRequest(BaseModel): models: list[str] dataset: str = "./datasets/questions.csv" concurrency: int = 3 @app.post("/benchmark") async def run_benchmark(req: BenchmarkRequest): # 这里触发评测任务 # 生产环境建议改成后台任务,并返回 task_id return { "status": "accepted", "models": req.models, "dataset": req.dataset, "concurrency": req.concurrency, }启动 API 服务:
uvicorn api_server:app --host 127.0.0.1 --port 8000调用方式:
curl -X POST "http://127.0.0.1:8000/benchmark" \ -H "Content-Type: application/json" \ -d '{ "models": ["openai/gpt-4o-mini"], "dataset": "./datasets/questions.csv", "concurrency": 3 }'接口返回之后,服务端应该在后台执行评测,并把结果写入output目录。这样调用方不需要一直保持连接,任务结束再去检查报告文件即可。
批量任务的设计重点是“可控”。建议支持这几个参数:
models:要评测的模型列表。dataset:评测集路径或上传文件。concurrency:并发请求数。max_questions:最多跑多少题,先做小规模验证。output_name:输出报告文件名的前缀。
有了这些参数,批量执行就变成了一个可编排流程。比如先跑 5 道题看成本,再跑 50 道题看详细结果,最后跑全部评测集做正式报告。每一步都有独立的任务记录,方便回滚和排查。
结果报告建议同时输出三种格式:Markdown 用于团队成员阅读,JSON 用于程序解析,CSV 用于表格筛选。核心字段至少包括模型名、题目 ID、问题类型、模型回答、标准答案、是否匹配、响应耗时、输入 Token、输出 Token、错误信息。
7. 资源占用与性能观察
这个项目最省心的一点是,它不需要 GPU,也不依赖模型权重。工具本身的资源占用主要集中在 Python 进程、网络连接和数据处理上。CPU 和内存占用通常都很低,除非你把评测集一次性全部读进内存,或者生成超大 DataFrame。
显存不是在本地消耗的。如果你只是用 OpenRouter API,那么推理资源由模型提供方承担,本机不需要任何显卡。若你在同一台机器上还部署了本地模型,那么本地模型的显存占用是另一个维度,不能算在这个 benchmark 工具头上。
性能瓶颈一般在三个地方:
第一是 API 并发限制。OpenRouter 和上游模型都可能有速率限制,并发数过高会报 429 或超时,导致评测任务失败。建议先用concurrency=2到concurrency=5测试,看看错误率是否明显上升。
第二是网络延迟。跨地域访问 API 时,延迟会直接影响总耗时。评测过程中的耗时统计要区分“网络往返时间”和“模型生成时间”,但常见模型返回里没有直接给出生成耗时,所以只能用总体响应耗时近似评估。
第三是输出 Token 长度。有些模型喜欢生成很长的回答,导致completion_tokens很高,成本和耗时都会增加。如果你的评测不关心长输出,可以在请求里设置max_tokens,比如 512 或 1024,避免无效输出拉高成本。
在评测过程中,建议把每一步的日志都打印出来,包括当前模型、当前题号、响应耗时、Tokens 和错误信息。这样既能定位卡住的请求,也能在后处理时知道哪条记录需要重试。
观察方式也比较简单:打开系统任务管理器,或者用htop看 CPU 和内存。如果内存占用在增加,多半是保存了太多模型输出,可以考虑边跑边写文件,而不是全部放在内存里最后一次性写。
整体流程可以按“小并发、分批跑、写日志、出报告”的顺序执行。先确认单条链路,再扩大批量,最后再做全量报告。
8. 常见问题与排查方法
下面是这套流程里最容易遇到的问题,按现象、原因、排查方式和解决方案整理。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 401 Unauthorized | API Key 无效或未正确设置 | 检查环境变量是否生效 | 重新设置 API Key,确认没有多余空格 |
| 404 Model Not Found | 模型 ID 拼写错误或已下线 | 对比 OpenRouter 官方模型列表 | 替换为官方返回的模型 ID |
| 429 Too Many Requests | 并发过高或触发速率限制 | 查看响应头中的限流信息 | 降低 concurrency,增加重试间隔 |
| Request Timeout | 网络不稳定或模型响应过慢 | 查看日志中的耗时统计 | 增大 timeout,改用更小的模型,或减少并发 |
| JSON 解析失败 | 模型输出带了额外文本 | 打印原始输出内容 | 在 Prompt 中强调只输出 JSON,或用结构化输出 |
| 输出长度超限 | 单次生成过长超出模型上限 | 查看 max_tokens 和 completion_tokens | 降低 max_tokens,或拆分成多个问题 |
| 某模型多次失败后中断流程 | 没有做异常捕获 | 检查日志是否有 Exception | 在循环里捕获异常,记录错误并继续 |
| 结果报告里缺少成本数据 | API 返回不包含 cost 字段 | 查看原始返回结构 | 根据官方定价表自行估算 |
一个比较实用的排查技巧是,先打印每次请求的响应状态码和原始返回体。很多问题直接用肉眼就能看出来,比如模型 ID 多了个空格,或者返回内容被截断。
另外,如果你发现评测结果不稳定,不要只换模型。先固定同一个模型,跑 3 次同样的评测,确认是模型本身的随机性还是配置问题。如果是随机性,需要调低 temperature,或者在报告中把“多次运行”的结果汇总成统计值,而不是只看单次输出。
9. 最佳实践与使用建议
第一次使用,不要直接跑全量。先选 1 个模型、5 道题,确认从配置到报告整个流程能跑通,再逐步增加模型数和题目数。这样能快速暴露配置问题,也更容易控制费用。
评测集的设计比工具本身更重要。建议把评测题按类型分成几组,例如:
- 事实问答,用来测知识覆盖度。
- 逻辑推理,用来测思维链能力。
- JSON 输出,用来测结构化能力。
- 长文本总结,用来测上下文化能力。
- 代码生成,用来测代码场景能力。
每组 5 到 10 道题,整体控制在 30 到 50 道。太多会把评测时间拉长,太少又说明不了问题。
成本控制方面,在正式跑大批量之前,先在配置里把max_questions限制为 5 到 10,观察每个模型的平均消耗和响应时间。如果某些模型输出过长,还可以设置max_tokens来限制。建议把评测模型按价格分层,先跑便宜的小模型确认数据链路,再跑性能更强的模型对比上限效果。
不要把所有评测结果都人工看一遍。工具里最好内置一个简单的评分函数,比如:
- 包含标准答案关键词:得 1 分。
- JSON 能解析且字段完整:得 1 分。
- 输出超时:计 0 分并记录。
对于开放题,可以用人工抽检 + 代码辅助统计,减少人工成本。
工程化方面,模型文件、输入素材、输出结果要分目录管理。所有 API Key 放环境变量,不写进代码。批量任务要加日志和失败重试,建议对 429 和超时做退避重试,比如等待 2 秒、5 秒、10 秒后重试,最多重试 3 次。
使用边界上,一定要避免把敏感数据未脱敏直接上传。OpenRouter 是第三方服务,调用前需要评估数据合规性。如果数据包含个人信息、商业机密或受版权保护的内容,请先脱敏或改用自部署模型。涉及人脸、声音、版权素材的场景,还需要获得相应授权。评测结果可能包含幻觉内容,发布或商用前必须人工复核。
最后,要把评测工具当作长期资产维护。模型版本更新后,重新跑一遍同一个评测集,看业务指标有没有变化;Prompt 调整后也重新跑一遍,确保改的是正向优化。这样工具的价值会随着时间越来越大。
10. 总结与下一步
这个项目最值得尝试的地方,是把“模型对比”从手工操作变成了可批量、可重复、可审计的工程流程。它最适合跑通的第一件事,是让 2 到 3 个模型在同一个评测集上跑出报告,确认报告的字段和分析方式能满足你的选型需求。
最容易被忽视的坑是评测集质量。如果问题集不适合你的业务,工具再轻量也看不明白模型差异。先拿真实业务问题来测试,再逐步补充边界场景。最容易踩的另一个坑是并发数调太高,导致大量 429 超时,最后拿到的数据全是失败记录。
后续可以扩展的方向有几个:接入更多评测数据集,比如 MMLU、GSM8K 等公开集,做标准能力对比;把工具接到 CI,在模型更新或 Prompt 变更后自动触发回归;或者给评测结果加可视化面板,按模型、按题型、按成本维度展示。只要把基础流程跑通,这些扩展都只是时间和需求问题。建议先收藏这篇文章,等真正开始做模型选型时,再照着步骤搭建一套自己的轻量级 LLM Benchmark 流程。