Qwen Thinking 模型进行 CEval 评测时 content=null 的问题定位与解决(小白暑期实习日常实录~)
1. 背景
在昇腾环境中,我使用 AISBench 通过 OpenAI-compatible vLLM API,对 Qwen3.6-35B-A3B 模型进行能力评测。
在完成 BoolQ 和 GSM8K 后,mentor 希望进一步评价模型在中文金融、经济和法律专业知识方面的表现,因此从 CEval 中选择了六个相关子任务:
ceval_all_sets = [
'college_economics',
'business_administration',
'accountant',
'tax_accountant',
'law',
'legal_professional',
]
六科验证集共计 233 道单项选择题,评价指标为 Accuracy。
2. 实验环境
操作系统:Linux
Python:3.10
环境名称:py310
评测框架:AISBench Benchmark
模型服务:vLLM OpenAI-compatible API
服务地址:http://127.0.0.1:8006/v1/chat/completions
模型:qwen3.6-35b-a3b
任务类型:0-shot 单项选择题
3. CEval 数据集配置
自定义数据集配置文件:
ais_bench/benchmark/configs/datasets/ceval/
ceval_finance_compliance_strict.py
核心 prompt:
prompt=(
f'以下是中国关于{_ch_name}考试的单项选择题,请选出正确答案。\n'
f'你必须只输出一行,格式为:answer:A、answer:B、answer:C 或 answer:D。\n'
f'不要解释,不要输出思考过程,不要输出其他任何内容。\n\n'
f'{{question}}\n'
f'A. {{A}}\n'
f'B. {{B}}\n'
f'C. {{C}}\n'
f'D. {{D}}\n'
f'答案:'
)
答案后处理:
ceval_eval_cfg = dict(
evaluator=dict(type=AccEvaluator),
pred_postprocessor=dict(
type=last_option_postprocess,
options='ABCD'
)
)
4. 问题一:六个 CEval 任务全部 code 1
第一次运行:
ais_bench \
--models vllm_api_general_chat \
--datasets ceval_finance_compliance_gen_0_shot_noncot_chat_prompt \
--summarizer example
AISBench 能识别六个任务,但六个 infer 任务全部失败:
OpenICLInfer[...] failed with code 1
summary 中所有结果均为:
-
日志中显示六个子任务在推理阶段和评估阶段均失败。
根因
进一步打开子任务 .out 日志,定位到:
CEvalDataset is not in the ais_bench::load_dataset registry
虽然类文件存在:
ais_bench/benchmark/datasets/ceval.py
但模块初始化时未导入该类,因此注册装饰器没有被执行。
解决方法
修改:
ais_bench/benchmark/datasets/__init__.py
加入:
from .ceval import CEvalDataset
随后验证:
python - <<'PY'
from ais_bench.benchmark.datasets import CEvalDataset
print("CEvalDataset import ok")
PY
输出:
CEvalDataset import ok
至此解决数据集注册失败问题。
5. 问题二:任务能跑完,但 Accuracy 只有 0%~3.64%
修复注册问题后,AISBench 能正常生成 summary,但结果严重异常:
college_economics 3.64
business_administration 3.03
accountant 0.00
tax_accountant 0.00
law 0.00
legal_professional 0.00
对于四选一任务,随机猜测通常也会接近 25%,因此 0%~3% 显然不是正常模型能力结果。
检查 prediction 文件
读取 prediction JSON 后发现:
ceval-college_economics:55 条中 53 条为空
ceval-business_administration:33 条中 32 条为空
ceval-accountant:49 条中 49 条为空
ceval-tax_accountant:49 条中 49 条为空
ceval-law:24 条中 24 条为空
ceval-legal_professional:23 条中 23 条为空
典型样本:
gold: B
prediction: ''
因此初始低分实际是大量空预测造成的,而不是模型专业知识只有 0 分。
6. 问题三:Received 满了、Failed 为 0,为什么 prediction 仍为空
AISBench 日志可能显示:
Post: 49
Received: 49
Failed: 0
但同时又出现:
Request ... has no output
这里容易产生误解。
Received=49 只能说明 HTTP 层收到了 49 个响应,并不代表每个响应中都存在有效的最终答案。
检查原始 API 响应后发现:
{
"message": {
"content": null,
"reasoning": "模型的思考过程"
},
"finish_reason": "length"
}
AISBench 当前主要读取:
message["content"]
由于 content=null,最终 prediction 被记录为:
''
完整问题链路为:
CEval 题目发送到模型
↓
模型进入 reasoning/thinking
↓
reasoning 消耗全部 max_tokens
↓
finish_reason=length
↓
最终 content 尚未生成
↓
AISBench 无法取得答案
↓
prediction=''
↓
Accuracy 接近 0
该问题及 content=null、reasoning 非空、finish_reason=length 的对应关系在原始记录中已有明确体现。
7. 为什么 prompt 中的 /no_think 没有解决
最初 prompt 中已经写入:
不要解释,不要输出思考过程。/no_think
但模型仍然返回 reasoning。
这说明 /no_think 只是文本提示,不一定被当前模型服务使用的 chat template 稳定识别。
因此:
在 prompt 中要求模型不要思考
和:
从模型请求参数层关闭 thinking
不是同一件事。
真正有效的控制参数是:
"chat_template_kwargs": {
"enable_thinking": false
}
8. 先用 curl 做最小验证
测试请求:
curl http://127.0.0.1:8006/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "qwen3.6-35b-a3b",
"messages": [
{
"role": "user",
"content": "CEval题目……"
}
],
"temperature": 0.0,
"max_tokens": 16,
"chat_template_kwargs": {
"enable_thinking": false
}
}'
返回:
{
"content": "answer:B",
"reasoning": null,
"finish_reason": "stop"
}
说明:
- 模型服务正常。
- 题目 prompt 正常。
enable_thinking=false可以可靠关闭 reasoning。- 关闭 thinking 后,16 个输出 token 已足够完成选择题回答。
9. 检查 AISBench 是否支持传递 chat_template_kwargs
不能仅凭 curl 成功就直接修改配置,还需要确认 AISBench 是否会将参数原样发送到 API。
检查 VLLMCustomAPIChat 源码后发现,其流程为:
generation_kwargs = self.generation_kwargs.copy()
generation_kwargs.update({"max_tokens": max_out_len})
generation_kwargs.update({"model": self.model})
response = self.client.request(cache_data, generation_kwargs)
同时,AISBench 会将:
HUMAN → user
BOT → assistant
SYSTEM → system
因此 prediction 文件中的 HUMAN/prompt 只是 AISBench 内部格式,并不是最终发送给 OpenAI-compatible API 的错误格式。
客户端构造请求时又会将参数直接并入请求体,因此可以把:
chat_template_kwargs
写入 generation_kwargs。
10. 创建 CEval 专用 no-thinking 模型配置
为避免影响 BoolQ、GSM8K 等已有任务,没有直接修改通用配置,而是创建:
vllm_api_ceval_no_think.py
核心内容:
models = [
dict(
attr="service",
type=VLLMCustomAPIChat,
abbr="qwen36-35b-a3b-ceval-no-think",
model="qwen3.6-35b-a3b",
request_rate=0.2,
retry=10,
host_ip="127.0.0.1",
host_port=8006,
max_out_len=512,
batch_size=1,
generation_kwargs=dict(
temperature=0.0,
top_k=10,
top_p=0.95,
seed=None,
repetition_penalty=1.03,
chat_template_kwargs=dict(
enable_thinking=False,
),
),
pred_postprocessor=dict(
type=extract_non_reasoning_content
),
)
]
这里使用独立 abbr,避免 no-thinking 结果与旧结果混淆。该配置的实际创建过程和最终内容已有完整记录。
11. 不直接全量:采用四级验证流程
为了避免再次浪费全量运行时间,采用以下验证流程:
单条 curl
↓
20 条直连 API
↓
20 条 AISBench
↓
六科全量
20 条直连 API 结果
tested: 20
request errors: 0
empty content: 0
non-empty reasoning: 0
finish_reason=length: 0
strict format: 20
correct: 17
accuracy: 85.0%
20 条 AISBench 结果
total: 20
empty prediction count: 0
valid answer format: 20
correct: 17
manual accuracy: 85.0
两次结果完全一致,证明:
- API 参数正确
- AISBench 参数传递正确
- prediction 保存正确
- 后处理正确
- summary 正确
12. 六科全量 no-thinking 结果
最终运行目录:
outputs/default/20260716_151051
结果如下:
| 科目 | Accuracy |
|---|---|
| 大学经济学 | 74.55% |
| 工商管理 | 75.76% |
| 注册会计师 | 83.67% |
| 税务师 | 75.51% |
| 法学 | 83.33% |
| 法律职业资格 | 82.61% |
总体:
total: 233
correct: 183
overall accuracy: 78.54
empty prediction count: 0
invalid format count: 0
初始结果与修复后结果对比:
| 阶段 | 结果 |
|---|---|
| 初始错误评测 | 0%~3.64%,大量空预测 |
| 修复后有效评测 | 总体 78.54%,空预测 0 |
13. Thinking 是否一定提高 Accuracy
为了验证 thinking 是否能够提高专业选择题成绩,又建立了:
vllm_api_ceval_thinking.py
参数:
max_out_len=2048
chat_template_kwargs=dict(
enable_thinking=True,
)
大学经济学前 20 条结果:
| 模式 | 正确数 | Accuracy |
|---|---|---|
| no-thinking | 17/20 | 85% |
| thinking | 17/20 | 85% |
逐题比较:
same answer: 17
different answer: 3
thinking fixed: 1
thinking broke: 1
both wrong but different: 1
这说明 thinking 并不是单向提升:
- 它可以将部分错误答案改对;
- 也可能把原本正确的答案改错;
- 还会明显增加推理耗时和 token 消耗。
20 条样本不足以判断总体收益,因此需要在同一批 233 条上做完整配对实验。
14. 后台运行问题:nohup 后找不到 ais_bench
最初使用:
nohup bash -c '
ais_bench ...
' > "$LOG" 2>&1 &
出现:
ais_bench: command not found
Exit 127
原因是新建的非交互 bash 没有正确继承当前 conda 环境的 PATH。
解决方法:
AIS_BENCH_BIN=$(command -v ais_bench)
nohup "$AIS_BENCH_BIN" \
--models vllm_api_ceval_thinking \
--datasets ceval_finance_compliance_thinking \
--summarizer example \
> "$LOG" 2>&1 &
使用绝对路径后,后台任务成功运行。
15. 日志监控问题:为什么一直显示 Post 20
启动全量任务后,监控窗口仍显示:
Post: 20
Received: 20
Failed: 0
最初误以为全量任务仍然只跑了 20 条。
实际原因是:
RUN=$(ls -td outputs/default/* | head -1)
是在旧任务期间赋值的。新任务启动后,已有 shell 变量不会自动更新,因此监控的仍是旧的 20 条目录。
解决方法是明确指定新目录:
RUN=/home/liluchi/benchmark/outputs/default/20260716_162709
或者从当前运行记录中读取 PID 和日志:
read PID LOG < ceval_thinking_current_run.txt
tail -f "$LOG"
同时通过 PID 区分:
旧 20 条任务 PID:826604
新全量任务 PID:834416
16. 经验总结
经验一:Failed=0 不代表 prediction 有效
必须同时检查:
Received
Failed
has no output
empty prediction
invalid format
经验二:summary 不能单独作为结果依据
正式结果至少要同时检查:
summary
prediction JSON
空预测数量
手动后处理准确率
经验三:文本提示不等于模型配置
/no_think
不一定可靠。
对于支持 thinking 的模型,应优先使用:
chat_template_kwargs=dict(
enable_thinking=False
)
经验四:先小样本闭环,再跑全量
推荐流程:
1 条 → 20 条直连 → 20 条框架 → 全量
经验五:模型评测问题不一定是模型能力问题
本次从 0%~3.64% 修复到 78.54%,模型权重没有发生变化。
变化来自:
- 数据集注册修复
- API 返回字段分析
- thinking 参数控制
- 输出格式约束
- 后处理和空预测复核
因此,模型评测首先是一项工程链路验证工作,其次才是模型能力比较。
更多推荐
所有评论(0)