同一个提示词在两个模型上结果差很远,原因通常落在两层:一层是请求参数没对齐,另一层才是模型本身的训练倾向和能力差异。判断顺序建议是先排除参数层,再做采样稳定性检查,最后才把差异归到模型上。因为参数不一致造成的输出变化,往往比模型差异更明显也更容易复现,先查这一层性价比最高。
判断顺序建议:先把两次请求体逐字段对齐(系统消息、温度、最大输出长度、流式开关),再记录返回体的结束原因与用量,然后用最低采样随机性重复调用同一模型,确认输出是否稳定。若参数与采样都固定,差异仍按任务类型分化,才更可能是模型本身差异。边界是:单次对比不能下结论,需要按任务类型各留几条样例并在相同条件下复现。
把两次调用里可调参数逐项摊开对比
先把两次调用当成两次实验,所有可调项写在一张表里,逐项标出它在请求体中的位置和实际取值。位置很重要,因为有些参数藏在 messages 数组里(system 提示词本身就是参数),有些在顶层,改错位置等于没对齐。
| 参数 | 请求体位置 | 本次取值 | 对齐动作 |
|---|---|---|---|
| 系统消息 | messages[0],role 为 system | 写清完整文本 | 两次调用用同一段文本,不要一边有一边没有 |
| 用户提示词 | messages 最后一条,role 为 user | 同一段原文 | 确认没有前端拼接、模板变量差异 |
| 温度 temperature | 顶层字段 | 如 0 / 0.7 / 1 | 先都设 0,再分别放开 |
| 最大输出长度 max_tokens | 顶层字段 | 如 512 / 4096 | 取两者中较小的值做基准 |
| 流式开关 stream | 顶层字段 | true / false | 排查阶段统一设 false,避免半截输出 |
| 其它采样参数 | 顶层,如 top_p、top_k、seed | 写清是否传入 | 不确定时先不传,或两边传同一组值 |
查看方式可以是把两次请求体各自存成文件再直接对比。通用骨架如下,把 payload 换成你实际发出的 JSON:
# 在调用处把完整请求体落盘,不要只打日志里的片段
cat > payload_a.json <<'EOF'
{
"model": "model-a",
"messages": [
{"role": "system", "content": "同一段系统消息"},
{"role": "user", "content": "同一段用户提示词"}
],
"temperature": 0,
"max_tokens": 1024,
"stream": false
}
EOF
# 换成 model-b 再存一份 payload_b.json,然后逐行对比
diff -u payload_a.json payload_b.json
如果请求体里键的顺序不稳定,可以先用文本工具排序键再对比,或者直接按上面表格逐项核对。差异一旦落到具体字段,就先改回一致再重跑,不要带着参数差异去讨论模型强弱。
记录每次调用的完整返回结构
输出变样有时不是正文变了,而是正文被截断、被过滤,或者流式拼接出了问题。排查时把完整返回体存下来,至少读三类字段:
choices[0].message.content -> 正文,真正要对比的内容
choices[0].finish_reason -> 结束原因,说明为什么停下来
usage.prompt_tokens -> 输入用量
usage.completion_tokens -> 输出用量
usage.total_tokens -> 合计用量
字段名在不同接入方式下会有差异,以 JellyToken 实际返回为准,但语义通常是这几种:stop 表示模型自然结束;length 表示撞到 max_tokens 被截断;content_filter 或类似值表示内容被安全策略拦截;tool_calls 表示模型选择去调工具而没有直接输出正文。两类调用的结束原因不同,输出自然看起来差很远。
用量字段的作用是确认两边跑的长度是否可比。如果一边 completion_tokens 明显偏小,另一边偏大,说明输出长度本身就不在同一量级,需要先调 max_tokens 或提示词约束长度,再谈内容质量。把这些字段和正文一起存进对比记录,后面回看时才不会只盯着一句话下结论。
用固定随机性设置做重复调用
要判断输出波动来自采样还是模型本身,先把采样随机性压到最低:temperature 设 0,top_p 设 1,不传 top_k,如果接口支持 seed 就固定一个值。然后同一模型、同一参数、同一提示词连续调用几次,每次把完整返回体单独存文件。
# 通用骨架,按实际接口替换地址与鉴权
for i in 1 2 3 4 5; do
curl -s "$ENDPOINT" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d @payload_fixed.json > "run_$i.json"
done
# 只看正文差异
diff -u run_1.json run_2.json
判读方式:如果同一模型下几次输出基本一致,那之前看到的巨大差异大概率来自采样参数或提示词被改动,而不是模型换了;如果参数已经压到最低、输出仍有肉眼可见的差别,说明这条链路存在非确定性,可能来自服务端版本变化、批处理差异或路由策略。此时换模型带来的差异就不能全算在模型头上。
让两个模型各跑三类任务样例
笼统说哪个模型更强没有可操作性,差异要落到任务类型上。建议至少准备三类样例,每类一条,两个模型在相同参数、相同采样设置下各跑一遍。
- 抽取样例:给一段含多个字段的文本,要求只输出 JSON,字段名固定。记录两边是否严格按字段名输出、有没有多余解释文字、有没有漏字段。事实可核性看抽出的值能否在原文中找到。
- 改写样例:给一段带明确事实的文本,要求同义改写且不新增信息。记录两边是否改变了数字、时间、主体,格式是否还保持段落结构。
- 问答样例:给一段材料加一个问题,要求只依据材料回答,材料没有就答不知道。记录两边是否编造了材料外内容,以及是否给出处片段。
每条样例都写清楚提示词全文、参数取值、两个模型的返回正文和结束原因。记录的重点是格式遵从和事实可核性这两列,而不是拿一句主观评价盖过去。
把确认下来的差异写进选择依据
排查做完后,把结论整理成一份按任务类型的模型选择记录。每条结论都要能追到实验条件,否则下次换环境又要重来。
| 任务类型 | 选用模型 | 实验条件 | 观察到的差异 | 备注 |
|---|---|---|---|---|
| 字段抽取 | 如 model-a | temperature=0、max_tokens=1024、stream=false、样例编号 S1 | JSON 格式是否稳定、字段是否齐全 | 是否受 system 提示词影响 |
| 文本改写 | 如 model-b | temperature=0、同一提示词、样例编号 S2 | 是否改动事实、篇幅是否失控 | 需要复跑次数 |
| 材料问答 | 待定 | temperature=0、附材料、样例编号 S3 | 是否编造材料外内容 | 结束原因是否被过滤 |
这份记录建议保留请求体副本、返回体副本和样例编号的对应关系。换模型时先看对应任务类型那一行,确认实验条件一致后再决定是否沿用。若某一条结论只在单次调用上成立,就标成待复现,不要直接当成选型依据。