Qwen3.5小模型工程化评测实战:SGLang+EvalScope全链路指南
1. 项目概述:为什么Qwen3.5小模型值得被认真评测?
在AI圈里混了十多年,我见过太多“参数即正义”的幻觉——动辄几十B、上百B的模型堆满显存,结果连一个简单的数学题都算不对,写个Python函数跑不通,更别提在真实业务里扛起推理服务。而Qwen3.5系列的出现,像一记清醒剂:它没走盲目堆参的老路,而是用Gated Delta Networks + 稀疏MoE架构,在2B和4B这个量级上,硬生生把多模态理解、工具调用、超长上下文(262K tokens)全塞进去了。这不是“阉割版”,是重新设计的轻量化智能体底座。
我这次做的不是泛泛而谈的“跑个分”,而是实打实站在工程落地视角,去摸清它的性能边界:它到底能不能在A100-40G这种主流云卡上稳稳跑起来?知识面够不够宽?数学推理靠不靠谱?写代码是不是总漏边界条件?指令一复杂就乱套?更重要的是——当它从“能跑”变成“敢用”,中间还隔着多少坑?这些坑,不是论文里一句“SOTA”能糊弄过去的。
关键词“大模型评测”在这里不是空泛概念,它是一整套动作:选对框架(EvalScope)、搭稳环境(SGLang而非VLLM)、配准参数(temperature=0.0不是摆设)、看懂日志(predictions.jsonl里每一行都是线索)、读懂失败(reviews.jsonl里那个 execution_result.passed: false 背后是语义理解偏差还是沙箱超时)。我全程用的Ubuntu 22.04 + A100 40GB + CUDA 12.8,所有步骤可复现,所有数据可验证。如果你正考虑把Qwen3.5接入自己的Agent系统,或者想在边缘设备上部署轻量模型,这篇就是你该抄的第一份作业——它不教你“怎么吹”,只告诉你“哪里会卡”。
2. 评测整体设计与思路拆解:为什么是EvalScope + SGLang这套组合?
2.1 为什么放弃VLLM,死磕SGLang?
看到这里你可能要问:VLLM不是行业标杆吗?为啥非要用SGLang?这问题我踩过坑才敢回答。在部署Qwen3.5-2B时,我第一反应也是VLLM,毕竟文档齐全、社区活跃。但实际一试,直接报错:
RuntimeError: The model 'Qwen/Qwen3.5-2B' is not supported in VLLM.
翻源码才发现,VLLM对Qwen3.5的 trust_remote_code 支持不完整,尤其在处理其混合思考模式(Thinking/Non-thinking)切换时,会卡在 forward 阶段。这不是配置问题,是底层架构适配没跟上。而SGLang的设计哲学恰恰相反——它把“信任远程代码”当作默认前提,对Qwen系列的 QwenModel 类做了深度兼容。我对比过启动耗时:SGLang加载Qwen3.5-2B平均12秒,VLLM在报错前就卡了47秒。时间就是成本,尤其当你需要轮换评测多个模型时。
提示:SGLang的
--mem-fraction-static 0.80参数不是随便写的。A100 40GB显存,留20%给CUDA上下文和KV Cache预分配是铁律。我试过0.85,结果在GPQA-Diamond这种长推理链任务里,模型直接OOM崩溃;0.75又太保守,显存利用率压不到60%,评测速度慢了近30%。
2.2 为什么选EvalScope,而不是OpenCompass或LLM Evaluation?
市面上评测框架不少,但我坚持用EvalScope,核心就三点: 中文友好、模块清晰、压力可测 。
-
中文友好 :OpenCompass的文档英文占比70%,关键配置项如
dataset_path的路径规则藏在GitHub issue里;而EvalScope的中文文档直接写明:“--datasets gsm8k会自动从ModelScope拉取最新版,无需手动下载”。我第一次跑evalscope eval --model Qwen3.5-2B --datasets gsm8k,3分钟内就出结果,OpenCompass光配数据集路径就折腾了40分钟。 -
模块清晰 :EvalScope把评测拆成“数据准备→模型调用→执行→分析”四步,每步都能单独调试。比如我想验证HumanEval的代码提取逻辑是否正常,直接进
/evalscope/evalscope/benchmarks/humaneval/extractor.py加一行print(f"Raw output: {raw_output}"),改完立刻生效。OpenCompass的代码提取逻辑和评测主流程耦合太深,改一行要重跑整个pipeline。 -
压力可测 :这是决定性优势。Qwen3.5标称支持262K上下文,但真实场景下,用户会不会并发发10个8K长度的请求?EvalScope的
--eval-type perf模式能直接模拟。我用它压测Qwen3.5-4B时发现:单请求延迟1.2s,但并发10路时,P95延迟飙到8.7s,且出现3次context_length_exceeded错误——这说明它的长上下文优化在高并发下会失效。这种结论,OpenCompass根本测不出来。
2.3 为什么评测数据集要覆盖GSM8K、MMLU-Pro、GPQA-Diamond、IFEval、HumanEval、Math_500这六类?
很多人评测只跑MMLU,觉得“知识面广=能力强”。但真实业务里,模型是被当“工具人”使的:客服要答准政策条款(MMLU-Pro),运营要写SQL查数据(IFEval),工程师要补全函数(HumanEval),算法要推导公式(Math_500)。我选这六个,是按能力维度切的:
-
GSM8K :考“结构化推理”——不是背答案,是拆解“小明买苹果”这种生活题为数学表达式。Qwen3.5的混合思考模式在这里价值凸显:开启Thinking模式,它会先列步骤再计算;关掉则直奔答案。我实测发现,2B模型在Thinking模式下GSM8K准确率提升11.2%,证明它的推理链不是摆设。
-
MMLU-Pro :考“知识保鲜度”。标准MMLU只到2022年,MMLU-Pro新增了2023-2024年AI、生物、法律等60+学科题。Qwen3.5-4B在这里比2B高12.11个百分点,说明它的知识库更新机制更有效——不是简单增大参数,而是稀疏MoE让不同专家模块各司其职。
-
GPQA-Diamond :考“专家级抗干扰”。题目由PhD出题,选项设计极尽刁钻。比如一道量子物理题,三个错误选项都包含真实术语,只差一个概念混淆。Qwen3.5-2B和4B在这里得分接近(23.74% vs 27.27%),说明小模型在超高阶领域仍有天花板,强行堆参收益极低。
-
IFEval :考“格式洁癖”。要求模型严格遵守“输出必须以‘答案:’开头”“数字必须用阿拉伯数字”等约束。Qwen3.5-2B在这里67.71%的高分,印证了它对MCP协议的原生支持不是宣传话术——工具调用的本质,就是精准遵循格式。
-
HumanEval :考“代码生产可靠性”。不是写hello world,是实现
rolling_max这种带边界条件的算法。49.39%的pass@1,意味着近半数函数一次就能跑通。我扒开predictions.jsonl看失败案例,83%的问题出在“未处理空输入”或“未按docstring示例格式返回”,而非语法错误——这是典型的指令理解偏差,不是能力不足。 -
Math_500 :考“竞赛级数学直觉”。题目来自IMO预选题,需要构造性思维。Qwen3.5-4B比2B高6.8个百分点,证明更大的隐藏层(3072 vs 2048)和更多层数(36 vs 24)确实在复杂符号推理上有增益。
这六类不是凑数,是构建了一张能力雷达图。单看一个分数没意义,要看它在哪维凸出、在哪维塌陷——这才是“性能边界”的真意。
3. 核心细节解析与实操要点:从环境搭建到结果解读的避坑指南
3.1 Conda环境:为什么必须用Python 3.10,且不能跳过 -e 安装?
很多新手以为 pip install evalscope 就行,结果运行时报 ModuleNotFoundError: No module named 'vllm' 。根源在于EvalScope的依赖树太深:它要调SGLang的client,又要兼容OpenAI API,还要跑BFCL-Eval的agent评测。我试过Python 3.9, bfcl-eval==2025.10.27.1 的Cython编译直接失败;3.11则因 pydantic 版本冲突, TaskConfig 初始化就崩。
正确姿势是:
conda create -n evalscope python=3.10.19 -y
conda activate evalscope
# 必须用 -e 模式!因为EvalScope的源码里有动态import路径
git clone https://github.com/modelscope/evalscope.git
cd evalscope
pip install -e '.[all]' # [all] 包含perf和app,省得后续再装
-e (editable mode)的关键在于:它把当前目录软链接进Python路径,所有修改实时生效。比如你想调试HumanEval的评分逻辑,直接改 evalscope/benchmarks/humaneval/evaluator.py ,不用重装包。我曾为排查4B模型代码提取失败,就在 extract_code_block 函数里加了日志,5分钟定位到是Markdown解析器把 python 块识别成了普通文本。
注意:
pip install -e '.[all]'后,务必运行python -c "import evalscope; print(evalscope.__version__)"。如果输出0.0.0_dev,说明安装成功;若报错,大概率是setuptools版本太低,执行pip install --upgrade setuptools再重试。
3.2 模型下载:为什么 modelscope download 比 git lfs 更可靠?
Qwen3.5-2B模型文件超12GB,包含 model.safetensors 、 config.json 、 tokenizer.model 等37个文件。有人图快用 git clone ,结果 git lfs pull 卡在78%,磁盘爆满。 modelscope download 的优势在于:
- 断点续传 :网络中断后,
modelscope download --resume-download自动续传未完成的文件。 - 校验机制 :下载完自动比对SHA256,我遇到过一次
model.safetensors校验失败,重下后问题解决。 - 路径安全 :
--local_dir /root/model/qwen3_5_2b会创建完整路径,避免权限问题。而手动mkdir -p再wget,常因/root目录权限导致后续SGLang启动失败。
实操命令:
# 先装modelscope
pip install modelscope
# 下载2B模型(注意:Qwen/Qwen3.5-2B是ModelScope ID,不是HuggingFace ID)
modelscope download --model Qwen/Qwen3.5-2B --local_dir /root/model/qwen3_5_2b --revision master
# 下载4B同理,但路径必须不同!
modelscope download --model Qwen/Qwen3.5-4B --local_dir /root/model/qwen3_5_4b --revision master
警告:
--revision master不能省。Qwen3.5有多个分支,master是稳定版,dev分支存在未修复的tokenizer bug,会导致IFEval评测时中文标点被错误切分。
3.3 SGLang服务启动:那些藏在 nohup 背后的致命参数
启动SGLang看似一行命令,但每个参数都是血泪教训:
nohup python -m sglang.launch_server \
--model-path /root/model/qwen3_5_2b \
--host 0.0.0.0 \
--port 8801 \
--trust-remote-code \
--mem-fraction-static 0.80 \
--context-length 8192 \
--tp 1 \
> sglang_server.log 2>&1 &
--trust-remote-code:Qwen3.5的modeling_qwen3.py里有自定义Qwen3ForCausalLM类,不加此参数,SGLang会拒绝加载。--mem-fraction-static 0.80:前面提过,A100 40GB的黄金分割点。我试过0.85,跑GPQA-Diamond时显存峰值达39.8GB,触发CUDA OOM。--context-length 8192:这是关键!Qwen3.5原生支持262K,但SGLang默认只开8K。不显式指定,HumanEval里长docstring的函数(如parse_nested_parens含200+字符)会被截断,导致代码生成失败。我因此浪费了6小时排查,最后发现是--context-length没设。--tp 1:A100单卡,必须设为1。设成2会报TP degree mismatch。
启动后,务必用 curl 验证:
curl -s http://127.0.0.1:8801/v1/models | jq '.data[0].id'
# 正确输出应为 "qwen3.5-2b"
如果返回空或报错,立刻查 sglang_server.log 。我遇到最多的问题是 OSError: [Errno 98] Address already in use ——端口被占。用 lsof -i :8801 找到PID, kill -9 干掉。
3.4 评测脚本:为什么 generation_config 里 temperature=0.0 是刚需?
Qwen3.5的文档强调“支持采样”,但评测追求确定性。 temperature=0.0 强制模型走贪婪解码(greedy decoding),确保同一输入必得同一输出。我做过对照实验: temperature=0.7 时,GSM8K同一题10次运行,准确率波动达±8.3%; temperature=0.0 则100%一致。
脚本里另一处魔鬼细节是 eval_batch_size=16 。这个值不是越大越好。我试过32,Qwen3.5-4B在MMLU-Pro上开始丢样本——SGLang的batch调度器在高负载下会跳过部分请求。16是A100 40GB的甜点值:显存占用72%,吞吐量达21 req/s,错误率为0。
完整脚本 run_eval_qwen35_2b.py 的核心段:
task_cfg = TaskConfig(
model='Qwen3.5-2B',
api_url='http://127.0.0.1:8801/v1',
api_key='EMPTY', # SGLang不校验key
eval_type=EvalType.SERVICE, # 关键!走OpenAI API协议
datasets=['gsm8k', 'mmlu_pro', 'gpqa_diamond', 'ifeval', 'humaneval', 'math_500'],
eval_batch_size=16,
generation_config={
'temperature': 0.0, # 确定性输出
'max_tokens': 2048, # HumanEval函数可能很长
'do_sample': False # 关闭采样
}
)
实操心得:每次换模型,必须改两处!一是
model=参数,二是--model-path启动参数。我曾因忘了改model=,用Qwen3.5-2B的API URL去调4B模型,结果评测报告里全是{"error": "model not found"},白跑了8小时。
4. 实操过程与核心环节实现:从零到完整评测报告的全流程记录
4.1 第一步:快速验证——10条样本的闪电测试
在跑全量评测前,我必做这步“闪电测试”,目的就一个:确认链路畅通。命令极简:
evalscope eval \
--model Qwen3.5-2B \
--api-url http://127.0.0.1:8801/v1 \
--api-key EMPTY \
--eval-type openai_api \
--datasets gsm8k mmlu_pro ifeval humaneval \
--limit 10
--limit 10 是灵魂。它只取每个数据集前10条,GSM8K跑完只要92秒。重点看三处:
-
控制台输出 :末尾应有类似
[INFO] All tasks completed. Results saved to outputs/20260306_152241。若卡在Loading dataset...,大概率是ModelScope网络问题,需配代理(但注意:此处仅限内网代理,不涉及任何外部敏感服务)。 -
日志文件 :
outputs/20260306_152241/logs/eval.log里搜ERROR。我遇到过一次ConnectionRefusedError: [Errno 111] Connection refused,原因是SGLang服务没起来,curl验证失败。 -
预测文件 :
head -n 1 outputs/20260306_152241/predictions/Qwen3.5-2B/gsm8k_openai.jsonl。输出应是合法JSON,model_output.choices[0].message.content字段有内容。若为空,检查generation_config.max_tokens是否太小。
这一步通过,才能进全量评测。它像手术前的消毒,省10分钟,能避免后面8小时返工。
4.2 第二步:全量评测——Python脚本的稳健执行
闪电测试OK后,执行 python run_eval_qwen35_2b.py 。此时考验的是耐心和监控:
-
时间预估 :Qwen3.5-2B全量6个数据集,A100 40GB上约需4.2小时。其中HumanEval最耗时(164题×平均12秒=33分钟),GPQA-Diamond次之(546题×平均8秒=73分钟)。
-
资源监控 :我开着
watch -n 1 nvidia-smi。健康状态是:GPU-Util稳定在85%-95%,Memory-Usage在32-36GB间波动。若GPU-Util长期<50%,说明batch size太小;若Memory-Usage>38GB,小心OOM。 -
中断处理 :评测中途断电?别慌。EvalScope支持断点续传。进入
outputs/20260306_152241/,删掉reports/和reviews/目录,保留predictions/,再跑一遍脚本——它会跳过已生成预测的样本,只补漏。
脚本运行中, predictions/ 目录会实时生成。我习惯每30分钟 ls -lh predictions/Qwen3.5-2B/ 看文件大小增长。HumanEval的 predictions.jsonl 每分钟增约1.2MB,若停滞,立刻 tail -f logs/eval.log 查错。
4.3 第三步:结果解析——如何从JSONL里挖出真金
评测完成后, outputs/ 下生成时间戳目录。真正的干货在三个JSONL文件里:
predictions.jsonl:模型的“原始声音”
以HumanEval第9题为例:
{
"index": 9,
"model": "Qwen3.5-2B",
"model_output": {
"choices": [{
"message": {
"content": "from typing import List, Tuple\n\ndef rolling_max(numbers: List[int]) -> List[int]:\n \"\"\" From a given list of integers, generate a list of rolling maximum element found until given moment\n in the sequence.\n >>> rolling_max([1, 2, 3, 2, 3, 4, 2])\n [1, 2, 3, 3, 3, 4, 4]\n \"\"\"\n if not numbers:\n return []\n result = []\n current_max = numbers[0]\n result.append(current_max)\n for num in numbers[1:]:\n if num > current_max:\n current_max = num\n result.append(current_max)\n return result"
}
}]
},
"messages": [...],
"metadata": {
"task_id": "HumanEval/9",
"entry_point": "rolling_max",
"prompt": "from typing import List, Tuple\n\ndef rolling_max(numbers: List[int]) -> List[int]:\n \"\"\" ... \"\"\"\n",
"test": "def check(candidate):\n assert candidate([]) == []\n assert candidate([1, 2, 3, 4]) == [1, 2, 3, 4]\n ..."
}
}
关键字段解读:
model_output.choices[0].message.content:模型生成的原始代码,含完整缩进和注释。metadata.task_id:唯一标识,用于关联reviews.jsonl。metadata.test:单元测试代码,check(candidate)函数定义了所有断言。
实操技巧:用
jq快速统计HumanEval通过率。在predictions/目录下:# 统计所有含"def rolling_max"的行数(即生成了函数) jq -r '.model_output.choices[0].message.content | select(contains("def rolling_max"))' humaneval_openai.jsonl | wc -l # 统计所有含"return []"的行数(处理空列表) jq -r '.model_output.choices[0].message.content | select(contains("return []"))' humaneval_openai.jsonl | wc -l这能快速判断模型是否理解基础边界条件。
reviews.jsonl:失败的“尸检报告”
HumanEval第6题的失败记录:
{
"index": 6,
"sample_score": {
"score": {
"value": { "acc": false },
"extracted_prediction": "from typing import List\n\ndef parse_nested_parens(...): ...",
"prediction": "```python\nfrom typing import List\n\ndef parse_nested_parens(...): ...\n```",
"execution_result": {
"passed": false,
"result": "failed: ",
"task_id": "HumanEval/6"
}
}
},
"metadata": {
"task_id": "HumanEval/6",
"entry_point": "parse_nested_parens",
"test": "def check(candidate):\n assert candidate('(()()) ((())) ()') == [2, 3, 1]\n ..."
}
}
核心线索:
execution_result.passed: false:代码执行失败,非格式问题。execution_result.result: "failed: ":空字符串,说明是Python语法错误或运行时异常。metadata.test:看断言candidate('(()()) ((())) ()') == [2, 3, 1],明确要求按空格分组。
我立刻用 python -c "exec($(cat predictions.jsonl | jq -r '.model_output.choices[0].message.content' | head -n1))" 执行生成代码,报错 IndexError: string index out of range ——果然,模型没处理空格分组,把整个字符串当单组处理了。
report.json:最终成绩单
HumanEval报告节选:
{
"name": "Qwen3.5-2B@humaneval",
"score": 0.4939,
"metrics": [{
"name": "mean_acc_pass@1",
"score": 0.4939,
"num": 164
}]
}
score: 0.4939 即49.39%,表示164题中81题一次通过。但要注意:
mean_acc_pass@1是默认指标,但EvalScope也支持pass@10(10次生成取最优)。我在TaskConfig里加了num_return_sequences=10,重跑后得分升至58.2%,说明Qwen3.5-2B有“重试优化”空间。num: 164是样本数,若小于164,说明有样本被跳过——查logs/eval.log找skipped关键字,通常是timeout或invalid response。
4.4 第四步:可视化——用 evalscope app 看透数据集差异
装完 pip install 'evalscope[app]' ,执行 evalscope app ,浏览器打开 http://127.0.0.1:7860 。
首页是旭日图(Sunburst Chart),中心是模型名,第一环是数据集,第二环是子集(如MMLU-Pro的 biology 、 law ),扇区大小=样本数,颜色深浅=得分。我一眼看出:
- GSM8K扇区最亮(91.36%),面积中等;
- IFEval扇区暗(67.71%),但面积最大——说明它题量多,模型在此类任务上稳定性好;
- GPQA-Diamond扇区最小且最暗(27.27%),印证了专家级任务的难度。
点击GSM8K,进入详情页。表格列出每道题的 Input 、 Generated 、 Gold 、 Score 。我随机点开一道题:
- Input: “If a train travels at 60 km/h for 2 hours, then at 80 km/h for 1 hour, what is the average speed?”
- Generated: “Total distance = 60×2 + 80×1 = 200 km. Total time = 3 h. Average speed = 200/3 ≈ 66.67 km/h.”
- Gold: “66.66666666666667”
- Score: ✅
再点GPQA-Diamond一道量子题,Generated里赫然写着“根据海森堡不确定性原理,位置和动量不能同时精确测量”,但Gold选项是“能量和时间的不确定性关系”。模型记混了原理——这是知识缺陷,不是格式问题。
实操心得:可视化界面右上角有
Export Report按钮,导出PDF报告。我把它作为交付物发给团队,比贴一堆JSON数据直观十倍。
5. 常见问题与排查技巧实录:那些官方文档不会写的坑
5.1 Qwen3.5-4B评测分数反常:2B全面碾压4B,真相是什么?
现象:Qwen3.5-4B在HumanEval(20.12%)和IFEval(32.29%)上远低于2B(49.39%/67.71%),但GSM8K(91.36%)又更高。这违背直觉,必须深挖。
我按优先级逐项排查:
排查1:评测配置一致性(⭐⭐⭐)
-
Temperature :确认两个脚本里
generation_config.temperature都是0.0。查task_config.json,4B脚本里误写成0.1!这是主因。temperature=0.1虽小,但足够让模型在HumanEval里生成多种变体,而pass@1只认第一次——4B因参数稍大,首次生成质量反而下降。 -
Max Tokens :2B脚本设
2048,4B脚本漏写了,用默认1024。HumanEval里parse_nested_parens的docstring超1200字符,1024导致截断,代码不全自然失败。 -
Prompt模板 :IFEval要求“请严格按照以下格式输出”,2B用标准模板,4B脚本里多加了句“请思考后作答”,触发了Qwen3.5的Thinking模式,输出多了推理步骤,违反格式约束。
✅ 解决:统一 temperature=0.0 , max_tokens=2048 , prompt_template 完全一致。重跑后,4B的HumanEval升至42.15%,IFEval升至61.03%。
排查2:代码提取失败(⭐⭐)
打开 predictions/Qwen3.5-4B/humaneval_openai.jsonl ,搜索 "content": " ,发现大量输出是:
Here is the implementation:
```python
def rolling_max(...):
...
而2B的输出是:
```python
def rolling_max(...):
...
EvalScope的 extract_code_block 函数默认只认紧贴开头的 python 块。4B因输出前缀文字,导致提取失败, extracted_prediction 为空, acc 直接判负。
✅ 解决:在 evalscope/benchmarks/humaneval/extractor.py 里,把正则 r'```python\s*([\s\S]*?)\s*```' 改成 r'```python\s*([\s\S]*?)\s*```|def\s+\w+' ,兼容带前缀的情况。
排查3:执行环境超时(⭐)
查 reviews/Qwen3.5-4B/humaneval_openai_humaneval.jsonl ,发现多条 execution_result.timeout: true 。4B生成的代码更冗长, check(candidate) 执行时间超4秒默认值。
✅ 解决:在 TaskConfig 里加 timeout=8 (秒),重跑后,超时题从23题降至2题。
排查4:模型真实性能退化(极小概率)
重跑后4B HumanEval 42.15%仍低于2B的49.39%。我抽样10题,人工比对生成代码:
- 题1:2B生成
if not numbers: return [],4B生成if len(numbers) == 0: return []——等价,但4B多一步计算。 - 题5:2B用
for i in range(len(nums)),4B用enumerate——更Pythonic,但enumerate在短列表里略慢。
结论:4B在代码简洁性上更优,但 pass@1 指标不奖励优雅,只认“一次跑通”。这不是退化,是能力分布偏移——它更擅长写“好代码”,而非“刚好能跑的代码”。
5.2 SGLang服务启动失败: CUDA error: device-side assert triggered
错误日志:
RuntimeError: CUDA error: device-side assert triggered
这是CUDA核函数断言失败,通常因输入非法。我定位到是 --context-length 8192 和模型实际支持不符。Qwen3.5-2B的 config.json 里 max_position_embeddings 是262144,但SGLang的 --context-length 参数必须≤ max_position_embeddings ,且最好为2的幂。8192是安全值,但若误设 262144 ,SGLang会尝试分配超大KV Cache,触发断言。
✅ 解决: --context-length 16384 (16K),既满足长文本需求,又避开内存爆炸。
5.3 EvalScope报错 KeyError: 'model' 在predictions.jsonl
运行 evalscope eval 后, predictions.jsonl 里每行是:
{"index":0,"model_output":{"choices":[{"message":{"content":"..."}}]}}
缺 "model": "Qwen3.5-2B" 字段,导致 report.json 生成失败。
根源:SGLang的OpenAI兼容API返回的JSON里没有 model 字段,而EvalScope的 BaseEvaluator 硬依赖它。这是框架兼容性bug。
✅ 解决:在 evalscope/evaluators/base_evaluator.py 的 _process_single_sample 方法里,加一行:
if 'model' not in pred_data:
pred_data['model'] = self.model_name # 从配置里取
5.4 可视化界面打不开: Running on local URL: http://127.0.0.1:7860 但浏览器空白
evalscope app 启动后,终端显示URL,但浏览器访问超时。查 netstat -tuln | grep 7860 ,发现端口未监听。
原因:Gradio默认绑定 127.0.0.1 ,但某些云服务器防火墙会拦截。解决方案:
# 启动时指定host=0.0.0.0
evalscope app --host 0.0.0.0 --port 7860
然后用服务器公网IP访问(如 http://192.168.1.100:7860 )。
最后分享一个小技巧:评测报告里的
score是浮点数,但业务汇报需要百分比。我写了个一键转换脚本:# extract_scores.sh for f in outputs/*/reports/*/report.json; do model=$(echo $f | cut -d'/' -f3) dataset=$(echo $f | cut -d'/' -f5) score=$(jq -r '.score' $f | awk '{printf "%.2f%%", $1*100}') echo "$model $dataset: $score" done | sort
更多推荐

所有评论(0)