ARTICLE DETAIL

资讯详情

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

基于OpenRouter统一API的轻量级LLM基准测试工具实践

基于OpenRouter统一API的轻量级LLM基准测试工具实践 最近在做大模型选型时我一直在对比不同模型的回答质量。真正落地时才发现逐个厂商去注册、逐个 API 去写调用代码是一件非常琐碎的事情。更麻烦的是不同模型的入参格式、超时机制、返回结构各不相同评测结果很难在同一个尺度上横向比较。于是我用 OpenRouter 做了一个轻量级的开源基准测试工具也就是本文要讲的llm-bench。它可以让你通过一套统一 API将任意多个模型放进同一批测试问题里做对比最终输出结构化报告。无论你是刚接触 LLM 的开发者还是已经在做模型选型、Prompt 优化的后端工程师这篇文章都能直接落地使用。本文将围绕 LLM Benchmark 的核心概念、OpenRouter 的接入方式、评测指标设计、完整代码实现、常见报错排查以及工程化建议展开。你可以把这份内容当作一份完整的实操笔记也可以直接把它改造成自己团队的评测工具。1. 背景与核心概念1.1 什么是 LLM Benchmark简单来说LLM Benchmark 就是给大语言模型安排一场“考试”。考试的过程是准备一组标准问题让模型逐个回答然后根据参考答案和事先约定好的评分规则算出模型的得分。这和程序员日常接触的单元测试思路非常接近只是被测对象从函数变成了模型。从专业角度定义LLM Benchmark 通常包含三个要素。第一是数据集也就是“考卷”。数据集里每一条记录都应该包含问题文本、期望答案、所属分类等信息。第二是评估器也就是“阅卷规则”。评估器负责把模型的输出和期望结果进行比较并给出一个可量化的分数。第三是报告也就是“成绩单”。报告需要把每个模型在不同问题、不同类别上的表现聚合起来方便人工对比和分析。很多人容易把 Benchmark 和 Prompt 评测混淆。Benchmark 强调的是“固定条件下可重复的横向对比”重点在一致性而 Prompt 评测往往更关注具体方案的效果重点在迭代优化。但在实际工作中两者是互补的。你可以先用 Benchmark 筛出合适的模型再针对选定的模型做 Prompt 专项调优。1.2 为什么选择 OpenRouter 作为统一入口在接入了多个模型之后我发现 OpenRouter 非常适合作为 Benchmark 的统一出口。它本质上是一个 LLM API 聚合网关你只需要申请一个 API Key就可以通过同一个 OpenAI 兼容接口去调用市面上很多主流模型包括不同厂商的商用模型和开源模型。用 OpenRouter 做模型评测有几个明显的优势。第一调用方式统一。不管是哪家模型代码里只需要切换model参数即可不用为每个厂商单独适配 SDK。这大大降低了横向对比的代码成本。第二便于做“多模型同题对比”。我们可以把相同问题一次性发给多个模型然后把返回结果并排放在同一张表格里谁优谁劣一目了然。第三它天然支持按模型 ID 精确指定版本。如果你关心模型版本升级后的回归波动可以把旧版本模型和新版本模型放在同一个评测集中让它们接受完全相同的考验。当然使用任何第三方 API 服务时都要注意两点一是 API Key 属于敏感凭据绝不能硬编码在代码或配置文件中二是调用第三方模型会产生费用评测问题集越大成本越高需要做好预算和配额控制。1.3 工具设计目标我给自己定的工具名字叫llm-bench。它是轻量级的、配置驱动的命令行工具设计目标如下。支持通过 YAML 文件配置多个模型不需要改代码。支持通过 JSON 文件维护评测问题集。统一使用 OpenRouter 作为 API 入口。内置精确匹配、包含匹配、延迟统计、Token 统计等基础指标。一次运行结束后同时输出 JSON 原始结果和 Markdown 报告。这里我刻意没有引入大量外部依赖也没有做成 Web 服务因为对于大多数评测场景来说命令行工具已经足够了。轻量意味着更容易维护也更容易嵌入现有的 CI/CD 流程。2. 环境准备与项目结构2.1 环境要求在动手之前你需要准备以下环境。Python 3.9 及以上版本。可以访问 OpenRouter API 的网络环境。一个 OpenRouter 账号并创建好 API Key。需要安装 Python 依赖openai和PyYAML。本文示例以本机命令行执行为主操作系统使用 Linux 或 macOS 均可Windows 下命令略有差异但思路一致。Python 依赖的安装命令如下pip install openai pyyaml这里我不指定具体版本号因为openai库更新很快建议你安装时查看当前最新稳定版。示例代码使用 OpenAI SDK 的 Chat Completions 接口OpenRouter 对这一接口做了兼容所以代码里不需要引入 OpenRouter 专用的额外 SDK。2.2 获取 OpenRouter API Key打开 OpenRouter 官网完成注册登录后在个人后台的 Keys 页面可以创建一个 API Key。创建后复制保存因为部分页面不会再次展示完整 Key。获取到 API Key 后推荐用环境变量管理而不是写死在配置文件里。在终端中执行export OPENROUTER_API_KEYsk-or-v1-xxxxxxWindows PowerShell 用户执行$env:OPENROUTER_API_KEYsk-or-v1-xxxxxx在后面的示例代码里我会从OPENROUTER_API_KEY环境变量读取密钥。这样做的好处是不会因为误提交配置而泄露 Key也可以在同一台机器上灵活切换不同账号。2.3 项目目录结构整个工具采用模块化设计目录结构如下llm-bench/ ├── config.yaml ├── questions.json ├── requirements.txt ├── benchmark.py ├── evaluator.py ├── reporter.py └── reports/config.yaml模型列表、评测参数、输出目录配置。questions.json评测问题集。benchmark.py主脚本负责读取配置、调用模型、汇总结果。evaluator.py评估器负责计算匹配分数。reporter.py报告生成器负责输出 Markdown 报告。reports/存放运行结果和报告文件。由于是轻量级工具我没有把代码拆成包的形式而是用三个平级脚本文件。如果你需要扩展也可以很方便地改成src结构。3. 核心原理与设计思路3.1 Benchmark 的完整链路一个完整的 Benchmark 运行过程可以拆成下面五个阶段。加载配置读取config.yaml解析模型列表和运行参数。加载问题集读取questions.json拿到所有待测试题目。模型调用对每个模型、每道题目分别调用 OpenRouter API拿到模型回答。评估打分将模型回答与期望答案进行匹配计算各类指标。报告输出将评估结果写入 JSON 和 Markdown 文件。这个链路并不复杂但每一步都有细节需要处理。比如模型调用阶段的超时和重试策略评估阶段的归一化处理报告输出阶段的中文编码问题等。后面我会在代码中逐一处理。3.2 评估指标设计针对轻量级评测场景我设计了以下基础指标。指标说明适用场景exact_match模型回答与期望答案完全一致忽略大小写和首尾空格数值计算、固定格式问题contains_match期望答案是否出现在模型回答中开放问答、简述题latency单次请求的响应耗时模型响应速度对比prompt_tokens / completion_tokens输入和输出的 Token 数成本估算error请求失败时记录错误信息稳定性分析这些指标都很直观适合做第一轮粗筛。如果你需要更精细的语义评估可以引入LLM-as-a-Judge也就是用一个更强的大模型来给回答打分。本文会在扩展方向中给出思路但不展开完整实现因为这会增加成本和复杂度。3.3 模块划分我们按职责划分了三个脚本文件每个文件只做一件事。benchmark.py负责调度和串联不关心具体评分逻辑。evaluator.py负责评分不关心 API 调用。reporter.py负责展示不关心评测过程。这样做的好处是以后你想新增“关键词匹配”或“语义相似度”指标只需要改evaluator.py想改成 HTML 报告只需要改reporter.py想接入除 OpenRouter 之外的其他网关只需要改benchmark.py中的客户端初始化和请求逻辑。模块之间通过普通函数返回的字典传递数据没有复杂的类继承关系这对小工具来说是最务实的方案。4. 完整实战案例4.1 创建项目结构先创建一个项目目录并进入目录。mkdir llm-bench cd llm-bench接着创建reports子目录用于存放输出文件。mkdir reports后续的代码和配置文件都放在这个目录下。4.2 编写配置文件 config.yaml配置文件的作用是声明要评测哪些模型以及运行参数。下面是一个完整示例。# 文件路径config.yaml models: - id: openai/gpt-4o-mini label: gpt-4o-mini temperature: 0 max_tokens: 512 - id: anthropic/claude-3.5-sonnet label: claude-3.5-sonnet temperature: 0 max_tokens: 512 - id: meta-llama/llama-3.1-8b-instruct label: llama-3.1-8b temperature: 0 max_tokens: 512 dataset: questions.json output_dir: reports concurrency: 2 timeout: 60这里逐个解释关键配置项。models是一个列表每一项对应一个待评测模型。id是 OpenRouter 上的模型 ID格式通常为厂商/模型名。具体模型 ID 需要以 OpenRouter 官网 Models 页面为准如果你测试时发现某个 ID 报 404大概率是模型已下线或 ID 写法变了。label是展示名称会出现在最终报告里。你可以把它设置成更易读的中文名或版本备注。temperature设置为 0可以尽量保证结果确定性。对于评测场景来说稳定性比创造性更重要。max_tokens限制生成内容的最大长度避免模型在开放问题上无限输出从而控制成本和耗时。concurrency表示并发请求数量。并发提高可以缩短总耗时但过高的并发可能导致限流和请求失败。timeout表示单次请求的超时时间单位秒。4.3 编写问题集 questions.json问题集决定了评测覆盖的范围。真实项目中你应该从业务场景出发准备几十甚至几百道题目。这里为了演示我准备三道不同类别的题目。// 文件路径questions.json [ { id: qa-001, category: knowledge, question: 世界上最深的海洋部分是哪里, expected: 马里亚纳海沟 }, { id: qa-002, category: math, question: 一个长方形的长为 8 厘米宽为 5 厘米它的面积是多少平方厘米, expected: 40 }, { id: qa-003, category: code, question: Python 中反转一个列表可以调用哪个内建方法, expected: reverse } ]每个字段的含义如下。id是题目的唯一标识在报告里用于定位具体是哪道题。category是题目分类方便后续按类别汇总准确率。question是发送给模型的用户问题。expected是期望答案用于后续的匹配评分。在实际使用中你需要根据业务场景设计自己的问题集。比如做客服问答评测就把真实用户经常问的问题整理成集合做代码生成评测就准备带输入输出示例的编程题。4.4 编写主脚本 benchmark.py主脚本是整个工具的核心它负责读取配置、加载问题集、调用模型并把结果汇总。下面我们分模块编写。首先是导入依赖、定义全局常量和客户端初始化。OpenRouter 使用 OpenAI 兼容接口所以我们可以直接用OpenAI客户端把base_url指向 OpenRouter。# 文件路径benchmark.py import os import json import time import argparse import concurrent.futures from pathlib import Path import yaml from openai import OpenAI BASE_URL https://openrouter.ai/api/v1 _client None def get_client(): 获取 OpenRouter 客户端实例复用连接以提升性能。 global _client if _client is None: api_key os.environ.get(OPENROUTER_API_KEY) if not api_key: raise RuntimeError( 未检测到 OPENROUTER_API_KEY请先通过环境变量设置 API Key。 ) _client OpenAI( base_urlBASE_URL, api_keyapi_key, timeout60, ) return _client这里使用全局变量_client缓存客户端对象避免重复初始化连接。OpenRouter 官方建议在请求中附带HTTP-Referer和X-Title两个 Header用于标识网站信息和应用名称下面是单模型调用的函数。def call_model(model_cfg, question): 调用单个模型返回回答内容、耗时和 Token 使用情况。 client get_client() start time.time() try: response client.chat.completions.create( modelmodel_cfg[id], messages[ { role: system, content: 你是评测助手请用简洁准确的中文回答问题不要有多余解释。, }, {role: user, content: question[question]}, ], temperaturemodel_cfg.get(temperature, 0), max_tokensmodel_cfg.get(max_tokens, 512), extra_headers{ HTTP-Referer: https://your-site.example.com, X-Title: llm-bench, }, ) latency time.time() - start answer response.choices[0].message.content.strip() usage response.usage return { answer: answer, latency: round(latency, 3), prompt_tokens: usage.prompt_tokens if usage else None, completion_tokens: usage.completion_tokens if usage else None, total_tokens: usage.total_tokens if usage else None, error: None, } except Exception as exc: return { answer: , latency: round(time.time() - start, 3), prompt_tokens: None, completion_tokens: None, total_tokens: None, error: str(exc), }这里有两个细节需要注意。第一temperature和max_tokens从模型配置中读取这样可以为不同模型设置不同参数。第二call_model内部对异常进行了捕获并将错误信息写入error字段这样某个模型即使完全无法调用也不会中断整个评测流程。接下来是任务执行函数。由于我们需要对“每个模型 X 每道题”都调用一次可以把这个组合看作一个任务列表。def run_one(args): 执行单个评测任务返回包含模型和题目信息的完整结果。 model_cfg, question args result call_model(model_cfg, question) return { model_id: model_cfg[id], model_label: model_cfg[label], question_id: question[id], category: question[category], question: question[question], expected: question[expected], **result, }然后编写主函数。主函数按顺序做以下几件事解析命令行参数。加载 YAML 配置。加载 JSON 问题集。构造任务列表并用线程池并发执行。调用评估器逐条计算指标。把结果保存为 JSON。调用报告生成器输出 Markdown。def load_questions(path): 加载 JSON 格式的问题集。 with open(path, r, encodingutf-8) as f: return json.load(f) def save_json(data, path): 保存 JSON 文件确保目录存在且中文不乱码。 Path(path).parent.mkdir(parentsTrue, exist_okTrue) with open(path, w, encodingutf-8) as f: json.dump(data, f, ensure_asciiFalse, indent2) def main(): parser argparse.ArgumentParser(descriptionLightweight LLM Benchmark Tool) parser.add_argument(--config, defaultconfig.yaml, help配置文件路径) parser.add_argument(--dataset, defaultNone, help问题集路径默认从配置文件读取) parser.add_argument(--output-dir, defaultNone, help输出目录默认从配置文件读取) args parser.parse_args() with open(args.config, r, encodingutf-8) as f: cfg yaml.safe_load(f) dataset_path args.dataset or cfg[dataset] output_dir args.output_dir or cfg.get(output_dir, reports) models cfg[models] questions load_questions(dataset_path) # 构造模型 x 题目的全量任务 tasks [(model, q) for model in models for q in questions] # 并发执行 records [] with concurrent.futures.ThreadPoolExecutor( max_workerscfg.get(concurrency, 2) ) as executor: for result in executor.map(run_one, tasks): records.append(result) # 按问题 id 排序保证报告中的顺序稳定 records.sort(keylambda r: (r[model_label], r[question_id])) # 评估 from evaluator import evaluate_record for record in records: record[metrics] evaluate_record(record) # 保存原始结果 save_json(records, f{output_dir}/results.json) # 生成报告 from reporter import build_report build_report(records, f{output_dir}/report.md) print(f评测完成共 {len(records)} 条记录。) print(f结果文件{output_dir}/results.json) print(f报告文件{output_dir}/report.md) if __name__ __main__: main()主脚本的流程非常直观。你可能会注意到我在主函数内部才引入evaluator和reporter这是为了减少模块循环依赖也方便初学者按顺序阅读代码。4.5 编写评估器 evaluator.py评估器负责把模型回答与期望答案进行比较。这里需要先对文本做归一化处理否则大小写、空格、全角半角差异都会影响匹配结果。# 文件路径evaluator.py import re def normalize(text): 归一化文本转小写、去除空格和常见标点。 if not text: return text str(text).lower().strip() text re.sub(r[\s。、,.!?;:\\], , text) return text def exact_match(pred, expected): 精确匹配归一化后完全相等。 return normalize(pred) normalize(expected) def contains_match(pred, expected): 包含匹配期望答案出现在模型回答中。 expected_norm normalize(expected) if not expected_norm: return False return expected_norm in normalize(pred) def evaluate_record(record): 对单条记录计算评估指标。 pred record.get(answer, ) expected record.get(expected, ) if record.get(error): return { exact_match: False, contains_match: False, error: record[error], } return { exact_match: exact_match(pred, expected), contains_match: contains_match(pred, expected), error: None, }归一化函数normalize去掉了空白字符和常见中英文标点这样像“40”和“40”就可以被判定为匹配。需要注意这只是最基础的文本匹配方案它无法判断语义等价。比如模型回答“世界上最深的海沟是马里亚纳海沟”contains_match能命中但如果模型回答用了同义词“挑战者深渊”基础匹配就会漏判。这也是为什么后面要引入可选的 LLM-as-a-Judge。4.6 编写报告生成器 reporter.py报告生成器把评估结果转换成易读的 Markdown 表格。我们按模型维度聚合指标计算每个模型的总题数、精确匹配数、包含匹配数、准确率和平均延迟。# 文件路径reporter.py from collections import defaultdict from datetime import datetime def build_report(records, output_path): 根据评测记录生成 Markdown 报告。 # 按模型分组 groups defaultdict(list) for r in records: groups[r[model_label]].append(r) lines [] lines.append(# LLM Benchmark 报告) lines.append() lines.append(f生成时间{datetime.now().strftime(%Y-%m-%d %H:%M:%S)}) lines.append(f评测模型数{len(groups)}) lines.append(f总记录数{len(records)}) lines.append() # 总览表格 lines.append(## 一、模型总览) lines.append() lines.append(| 模型 | 题目数 | 精确匹配数 | 精确率 | 包含匹配数 | 包含率 | 平均延迟(s) | 错误数 |) lines.append(| --- | --- | --- | --- | --- | --- | --- | --- |) overview [] for label, items in groups.items(): total len(items) exact sum(1 for r in items if r.get(metrics, {}).get(exact_match)) contains sum(1 for r in items if r.get(metrics, {}).get(contains_match)) errors sum(1 for r in items if r.get(error)) latencies [r.get(latency, 0) for r in items if r.get(latency) is not None] avg_latency round(sum(latencies) / len(latencies), 3) if latencies else 0 overview.append((label, total, exact, contains, errors, avg_latency)) lines.append( f| {label} | {total} | {exact} | {exact / total:.1%} | f{contains} | {contains / total:.1%} | {avg_latency} | {errors} | ) lines.append() # 每题明细 lines.append(## 二、题目明细) lines.append() for label, items in groups.items(): lines.append(f### {label}) lines.append() lines.append(| 题目 ID | 分类 | 期望答案 | 实际回答 | 精确匹配 | 包含匹配 | 延迟(s) | 错误 |) lines.append(| --- | --- | --- | --- | --- | --- | --- | --- |) for r in items: metrics r.get(metrics, {}) answer (r.get(answer) or )[:80].replace(|, /) expected (r.get(expected) or )[:80].replace(|, /) lines.append( f| {r.get(question_id)} | {r.get(category)} | {expected} | f{answer} | {是 if metrics.get(exact_match) else 否} | f{是 if metrics.get(contains_match) else 否} | f{r.get(latency, 0)} | {r.get(error) or } | ) lines.append() lines.append(## 三、结论) lines.append() lines.append( 精确率表示模型回答与期望答案完全一致的比例适合数值和固定格式问题。) lines.append( 包含率表示期望答案是否出现在模型回答中适合开放性问题。) lines.append() report \n.join(lines) with open(output_path, w, encodingutf-8) as f: f.write(report) return report在生成 Markdown 时有两个容易踩坑的地方。第一模型回答可能包含换行符或竖线会破坏表格结构所以我对回答中的|做了替换并截断到 80 个字符。第二写文件时必须指定encodingutf-8否则 Windows 环境下打开文件可能出现中文乱码。4.7 运行与验证完成上述所有文件后在你的项目目录下执行python benchmark.py如果一切正常终端输出大致如下评测完成共 9 条记录。 结果文件reports/results.json 报告文件reports/report.md因为我们配置了 3 个模型、3 道题所以总记录数为 9。打开reports/report.md你会看到一个包含模型总览表格和题目明细的报告。每个模型的精确率、包含率、平均延迟都一目了然。如果你只想评测部分模型或部分题目可以临时修改配置文件或者在命令行覆盖参数。比如只跑一个模型python benchmark.py --config config-single.yaml这种参数化的设计在实际使用中非常方便因为你可以为不同场景准备多份配置而不需要改动任何代码。5. 常见问题与排查思路在实际运行过程中会遇到各类报错。下面我把最常见的几类问题整理成表格并给出详细排查思路。问题现象常见原因解决思路报错Missing OPENROUTER_API_KEY环境变量未设置检查终端是否执行了 export 命令报错 401 认证失败API Key 无效或已删除重新创建 Key确认没有多余空格报错 404 模型不存在模型 ID 错误或已下线去 OpenRouter Models 页面核对报错 429 请求限流并发过高或配额不足降低 concurrency稍后重试报错连接超时网络不稳定检查网络增大 timeout报告中文乱码文件编码不对统一使用 UTF-8 编码读写5.1 请求超时或网络不稳定OpenRouter 是一个远程 API 服务网络波动会直接影响评测结果。如果某些请求频繁超时建议先把concurrency调整为 1判断是不是并发导致的问题。如果是单请求都经常超时可以适当调大配置文件里的timeout值。另外评测大批量问题时建议分段执行每段之间留出间隔避免一次性发起过多请求。5.2 模型返回值不符合预期有时候请求成功但模型回答内容为空或者返回了一段奇怪的系统提示。这种情况通常是max_tokens设置过小模型还没来得及输出完整回答就被截断了。你可以把max_tokens调大并在报告中检查completion_tokens是否接近上限。如果确实接近上限说明截断是主因。5.3 评测结果不稳定如果你的temperature设置不为 0可能会发现同一模型同一道题多次运行结果不同。这是生成式模型的正常现象。为了保证 Benchmark 可复现建议在评测时固定temperature 0并在报告中记录每次请求的模型 ID、时间和参数。如果业务场景需要测试模型的创造性可以单独设置一组高temperature配置并和低temperature评测结果分开比较。6. 最佳实践与工程建议6.1 API Key 与敏感信息管理API Key 要始终通过环境变量或密钥管理服务注入程序。即使只是本地测试脚本也不要把它写进 YAML 或 JSON 配置文件。如果你要把项目提交到 Git 仓库建议在.gitignore中忽略所有可能包含密钥的文件。对于团队协作场景可以考虑使用类似 Vault 的密钥管理服务或者至少使用.env文件并严格限制该文件的访问权限。6.2 成本控制与并发调用 OpenRouter 模型是按 Token 计费的。评测问题集越大、问题越复杂费用越高。在开始大规模评测之前建议先小范围跑一遍估算平均每道题的 Token 消耗再乘以总题数得到大概费用。另外要关注concurrency的设置。并发过高不仅可能触发限流也可能在评测集很大的时候造成预算迅速消耗。稳妥的做法是先用小题目集验证流程再分批次跑完整评测并设置每日调用上限。6.3 评测集建设评测集是 Benchmark 的灵魂。一个有效的评测集应该有明确的业务导向不要只收集网上通用的百科题。建议按照真实使用场景组织分类比如客服场景下分为“咨询”“投诉”“售后”代码场景下分为“语法题”“算法题”“工程题”。每个分类下的题目数量要尽量均衡否则最终准确率会被大类别带偏。另外期望答案要经过人工审核避免出现模糊答案或错误答案。6.4 结果可复现性模型版本、参数、系统提示词都会影响评测结果。为了让报告具备长期对比价值你应该在报告中记录以下信息模型 ID、模型版本如果 OpenRouter 支持日期版本号、temperature、max_tokens、问题集文件版本、运行时间。目前llm-bench已经记录了模型 ID 和运行时间你可以继续扩展报告字段把评测配置也写进results.json。6.5 扩展方向这个工具目前只实现了基础匹配指标如果你有更复杂的需求可以考虑以下扩展方向。一是加入 LLM-as-a-Judge。用一个固定的强模型让它对每个回答按照 1 到 5 分进行打分并输出简短理由。这样可以解决同义表达无法匹配的问题但需要额外成本打分模型的选择也需要固定。二是加入分类汇总报表。按category维度计算各分类准确率这样能快速发现模型在哪个领域最弱。三是把工具包装成 Docker 容器或集成到 CI 流水线让每次模型更新后自动跑一遍回归评测。每一个扩展方向都建立在当前工具的数据结构之上所以不用担心现有代码会被推翻。模块化设计的价值就在于此。7. 总结这篇文章从零实现了一个轻量级开源 LLM Benchmark 工具核心是围绕 OpenRouter 统一 API 完成多模型、多题目的自动评测。我们通过config.yaml管理模型列表通过questions.json维护评测集通过benchmark.py完成并发调用通过evaluator.py计算精确匹配和包含匹配最后用reporter.py输出 Markdown 报告。整条链路都是配置驱动和模块化的你可以直接复制代码到本地运行也可以根据业务情况扩展新的指标和报告格式。如果你也正在做模型选型或 Prompt 优化建议先准备一份业务相关的评测集再把这套工具跑一遍用真实数据驱动决策。代码越简单越容易被团队采用也就越能在日常开发中持续发挥作用。
返回列表