你调过 temperature 吗。输出还是啰嗦,你把它从 0.7 降到 0.2,结果散文变成了更短的散文——格式该乱还是乱。于是你怀疑是不是该动 top-p,两个一起调,从此再也复现不出来。
先说结论:旋钮管的是「随机性」,管不了「正确性」,也管不了「格式」。
搞混这一点,是绝大多数调参内耗的根源。
模型每写下一个字之前,手里握着一张表。表上是所有候选字,每个字后面跟一个概率。
比如你让它续写「今天天气真」,表可能长这样:
好 42%、不错 18%、冷 9%、糟 5%……剩下几万个字分掉剩余的部分。
它接下来做的事是:从这张表里抽一个。不是挑最大的,是抽。
这就是随机性的来源。你所有的旋钮,动的都是这张表和这个抽法。
temperature(温度)可以理解成「拉平系数」。
温度低,表变陡。42% 那个被放大到 80%,尾巴上的被压到接近 0。抽来抽去都是同一个。
温度高,表变平。42% 降到 25%,原来 1% 的爬到 3%。冷门词有了出场机会。
打个比方。抽奖箱里本来 42 个红球、18 个蓝球、1 个黑球。降温相当于往里加红球、拿走黑球;升温相当于给每种球都补几个,让比例变均匀。
它改的是概率分布的形状,不是表上有哪些字。 温度再高,也不会凭空冒出一个原本不在表上的词。
top-p(也叫核采样,nucleus sampling)干的是另一件事。
先把候选按概率从高到低排队,一个个累加,加到超过 p 就停,后面的全部扔掉。
p = 0.9 的意思是:只保留累计概率前 90% 的那批候选,长尾直接删除。
区别在这里:
所以 top-p 更适合用来掐掉离谱的词。温度调高、top-p 收紧,能拿到「有变化但不发疯」的输出。
一次只动一个。 两个都动,你分不清是谁的功劳。默认做法:top-p 留在默认值别碰,只调 temperature。
这个最容易理解错。
max tokens 不是写作指令。你设成 500,模型不会规划出一篇刚好 500 token 的完整文章。它是一把闸刀:写到这个数,无论写没写完,当场断掉。
后果最狠的场景是 JSON。你让它吐一个结构化对象,写到一半闸刀落下,你拿到一段非法 JSON,解析直接抛错。
所以真实做法是两层:
调参最大的浪费是「改一次、看一眼、凭感觉」。这样永远收敛不了。
四步:
第 3 步是分水岭。三次差得离谱,是随机性问题,调温度有用。三次几乎一样但都不对,是指令问题,调温度白费力气。这两类毛病的解法完全不同,先分清再动手。
毛病一:啰嗦。
不是温度的锅。降温只会让它啰嗦得更稳定。
根因是提示词只说了「要什么」,没说「不要什么」和「多长」。模型的默认人格就是把话说满。
按住它靠三件事:给硬性长度、给禁止清单、给第一句。第三条最狠——首句定死,后面的调子就跟着定了。
毛病二:不稳定。今天给你 Markdown 列表,明天给你三段散文。
这也不是温度的锅。
温度调到 0 只是让抽样趋于确定,它变不出一个原本不存在的格式约束。提示词里没锁死格式,模型每次都在猜你要什么,猜得不一样太正常了。
解法是把格式变成机器能检查的东西:给一个完整示例,或者直接上 JSON Schema。参数层解决不了约束层的问题。
毛病三:瞎编。
模型没有「我不知道」这个默认动作。它的工作是把句子接下去,接不出来也得接。
降温同样不解决。低温只让它编得更笃定、更一致,反而更难被你看出来。
三件事按顺序做:
三条止血片段,直接粘到你现有提示词的末尾:
text【止血片段 1 · 治啰嗦】 硬性要求:正文不超过 300 字。 不要写的:开场寒暄、「总的来说」类总结段、对我问题的复述。 第一句必须是结论本身,不带任何铺垫。 【止血片段 2 · 治格式不稳】 只输出下面这个结构,不要输出任何解释文字: {"title": "字符串", "points": ["字符串", "字符串"], "risk": "字符串"} 字段没内容就填空字符串。不要删字段,不要加字段。 【止血片段 3 · 治瞎编】 只能使用我提供的资料回答。 资料没写的,输出「资料未覆盖」,不要推测,不要用常识补。 先分点列出你依据的原文句子,再给结论。顺序不能反。
调试节奏落到代码上,就是下面这个跑三遍的小脚本:
js// debug-runner.mjs —— 固定输入集,同一条跑三次,看波动
const ENDPOINT = "https://api.qianduandaren.com/v1/chat";
// 第 1 步:把输入固定下来,以后每次都跑这一组
const FIXED_INPUTS = [
"把 useEffect 依赖数组的坑讲给三年经验的前端",
"给「CSS 容器查询」写一条 60 字以内的公众号导语",
// ……凑够 5 到 8 条真实输入
];
// 第 2 步:一次只改这里的一个字段
const KNOBS = { temperature: 0.7, top_p: 1, max_tokens: 1024 };
async function runOnce(prompt) {
const res = await fetch(ENDPOINT, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
...KNOBS,
messages: [{ role: "user", content: prompt }],
}),
});
const data = await res.json();
return data.choices[0].message.content;
}
for (const input of FIXED_INPUTS) {
// 第 3 步:同一条跑三次
const runs = await Promise.all([1, 2, 3].map(() => runOnce(input)));
const lengths = runs.map((text) => text.length);
const spread = Math.max(...lengths) - Math.min(...lengths);
// 极差大 = 随机性问题,可以调温度
// 极差小但三条都不对 = 指令问题,回去改提示词
console.log(`[${input.slice(0, 12)}] 长度 ${lengths.join("/")} 极差 ${spread}`);
}旋钮改不了能力和事实。模型不会因为降温就更懂你的业务。
temperature 设成 0 也不等于每次输出完全一致。服务端的批处理和浮点计算顺序都会带来抖动。别把「确定性」建在这上面。要确定,就在外面做校验和重试。
越来越多的推理型模型不接受 temperature 和 top-p,或者接受了也没实际影响。碰到这种,别再找旋钮了。该做的是把控制力搬回提示词结构和结构化输出。
还有一条:面向用户的产品里,把温度做成用户可调的滑块通常是坏主意。用户不知道自己在调什么。给他们「严谨/平衡/发散」三个挡位,背后的参数你自己定。
挑一条你天天用的提示词,固定 5 条输入,同一条跑三次——差在格式就去补示例,差在措辞才轮到调温度。

扫码关注公众号,每周更新实用前端干货