把参数背后的机制讲透

调 LLM 的 API 时,一堆看起来眼熟的参数:temperaturetop_pfrequency_penaltyseedlogprobs……很多人照抄别人的数值,从来不知道为什么这么设。这篇想把这些参数背后的机制讲透,让你看一眼就知道某个场景该拧哪颗旋钮。

先建立一个直觉

每一步生成,模型在做同一件事:根据当前的输入和已生成的内容,给词表里每一个 token 算一个分数(logits)。这些分数经过 softmax 变成概率分布,然后从分布里采样一个 token 作为这一步的输出,接着往右滑一格重复。

举个例子,输入"今天天气真",模型输出的概率分布大致是:"好"占 42%,"差"占 12%,"不错"占 9%,"棒"占 8%,剩下的上千个词分掉最后的 29%。所有我们要讲的参数,本质上都是在改这个分布(让它更尖锐或更平滑)或者改选法(不是从全部候选里选,而是只从前几名里选)。理解了这一点,每个参数为什么存在、为什么互斥,就都清楚了。

temperature:调节随机性的主旋钮

temperature 是最常用也最被误解的参数。它做的事很简单:把 logits 除以一个温度值,然后再 softmax。温度小于 1 时,分布变得更尖锐,高概率的 token 更占优势,输出更确定、更保守;温度大于 1 时,分布被拉平,低概率的词也有机会露脸,输出更发散、更有创意。温度等于 0 是个特殊情况,理论上永远选概率最大的 token(实际上由于并行浮点计算的不确定性,结果并不能 100% 复现,要真可复现得配合 seed)。

至于具体该设多少,有一套直觉。代码生成、JSON 结构化抽取、数据处理这类任务,答案是"对的"或"错的",希望稳定,设在 0 到 0.3 之间;常规问答、客服类场景 0.4 到 0.7;写作、营销文案、起名 0.7 到 1.0;需要发散的诗歌、头脑风暴可以到 1.0–1.3;再往上就接近胡说八道了。

一个值得警惕的细节:OpenAI 的 o 系列推理模型(o1、o3、gpt-5-thinking 等)在设计上不允许调 temperature,只能是 1.0。这是因为推理模型的"思考链"是通过强化学习训练出来的决策路径,用采样技巧去扰动会让它脱轨,反而降低答对率。所以如果你看到 API 报错说 "temperature is not supported for this model",别纠结,那是在告诉你这个模型不玩这套。

top_p 与 top_k:更聪明的随机性控制

光靠温度有个遗憾——即使温度很低,一些概率极低的"奇怪词"仍有万一之机。nucleus sampling(也就是 top_p)解决了这个问题。它的做法是:把候选 token 按概率从高到低排序,累加,直到累计概率达到 p(比如 0.9)就停止,只从这些头部 token 里采样。这样长尾被动态地砍掉,既保留了多样性,又避免了离谱的词。

top_k 是更古老的思路,直接只看前 K 个候选,其他一律扔掉。OpenAI 的 API 没暴露 top_k,Anthropic 和本地推理引擎(llama.cpp、vLLM)都给。它的作用粗糙一些,但在本地部署微调模型时有用。

一条经常被官方反复强调的建议:temperature 和 top_p 不要同时调。它们都在修饰同一个分布,两个一起动会让效果难以预测。日常选一个就够:需要直觉简单就调 temperature,想要稳一点的多样性就把 temperature 设 1、再用 top_p=0.9 或 0.95 来卡住长尾。

max_tokens:成本和体验的闸门

max_tokens(新版 API 叫 max_output_tokens)限制单次请求能输出多少 token。这个参数看起来没什么好说的,但它实际上承担着三个任务。第一是防失控——模型啰嗦起来没个完,尤其是让它写列表或解释复杂概念时。第二是控成本——输出 token 的单价通常是输入的三到五倍,一次多说 500 个词的差距在高频调用里会显著。第三是控体验——流式输出让用户等太久不如直接截断让它重来。

一个容易被忽视的点是:它只限制输出,不限制总长度,输入加输出必须能装进模型的上下文窗口。另外对推理模型要留足余量,因为"思考过程"也算在输出里,有时 max_tokens 设 500 模型还没开始正式回答就没余额了。

stop:在某个位置切断

stop(Anthropic 叫 stop_sequences)接受一组字符串,模型生成到其中任何一个就停下。典型用法是在结构化输出里用分隔符切段,比如 stop: ['\n\n', '### '] 让它在空行或下一个标题处停止;或者在多轮对话里加 stop: ['Human:', 'User:'] 防止模型把下一轮的假对话也续写出来。要注意 stop 字符串本身不会出现在返回结果里,但模型是"看到它将要出现"才决定停,所以停在哪里得预设好。

frequency_penalty 与 presence_penalty:抑制重复的两种姿势

这两个参数长得像,作用很不同。frequency_penalty 会根据每个 token已经出现的次数扣分——出现一次扣一点,出现三次扣三点,所以它的作用是"别反复说同一个词"。presence_penalty 只看这个 token有没有出现过,不管次数,出现过就固定扣一次分,效果是"多引入些新词和新话题"。

什么时候开它们?写长文、做创意发散的时候,加个 0.3 到 0.5 的 frequency_penalty 能显著改善"绕口令"式的重复;做头脑风暴、想让模型多探索不同方向的时候用 presence_penalty。但代码任务千万别开——代码本来就有大量合理的重复(同一个变量名、同一种语法结构),惩罚会让它写出别扭的代码。还有一点:值不要开太大,超过 1 之后模型会被迫避开常用词,反而冒出奇怪组合。

seed:尽量可复现

seed 设成固定整数,配合同一 prompt、同一 temperature,模型的输出会尽量保持一致。这对测试、截图演示、A/B 对比至关重要。但"尽量"两个字要认真看——OpenAI 的 API 会返回一个 system_fingerprint 字段,只有它不变才说明底层没更新,模型版本每次小修都会打破可复现性,所以这个参数更适合短期的一致性需求,不要指望它帮你一年后复现今天的输出。Anthropic 没有原生 seed,这是家族差异。

response_format:让输出一定是合法 JSON

大概 LLM 应用里一半的痛苦都来自"返回的 JSON 不合法"。OpenAI 的 response_format 提供两档,普通 json_object 保证返回的是合法 JSON,strict 的 json_schema 模式连字段名、类型都严格按你给的 schema 来,100% 合法且符合结构。代价是 strict 模式不支持 JSON Schema 的一些高级特性(比如某些 allOf/oneOf 组合、正则 pattern),复杂 schema 有时得退回普通模式再用 zod 做兜底校验。

Anthropic 没有直接的 response_format,但提供了等价思路:通过 tool_use 让模型调用一个"返回结构化数据"的假工具,返回参数就是你要的 JSON。效果一样。

tools 与 tool_choice

工具调用本身主系列第 6 篇讲过了,这里补充一个常被忽略的参数:tool_choice。它控制模型对工具的使用态度。默认 auto 让模型自己判断是否需要调工具;none 禁用工具,强制纯文本回答;required 要求至少调一个工具(在 Router 场景很有用,比如用户问事实性问题就强制走搜索,不给它凭记忆回答的机会);指定具体工具则用 {type: 'function', function: {name: 'xxx'}} 强制走某个函数。required 是个被低估的工具,能把不确定性从"它会不会调"降到"它只是在决定调哪个"。

logprobs:让模型把"自信度"告诉你

开启 logprobs: true(再加上 top_logprobs: 5 这样的参数),模型返回每个输出 token 的对数概率,以及这一位置前 N 个候选的概率。对数概率接近 0 意味着模型相当确信(概率接近 1),非常负(比如 -5)意味着它在猜。

这东西有几种非常实用的用法。做分类任务时,让模型先输出单个 token 的标签(比如"正"或"负"),再读 logprobs 算出每个类别的真实概率——比让模型输出 0-100 分稳多了,因为数字本身对模型是个比较难的任务,但"下一个 token 该是哪个"才是它真正擅长的。做情感打分时,取"正"对应的 logprob,映射成置信度,就能量化地做阈值判断。做幻觉检测时,整句生成结束后平均 logprob 异常低的那些地方,基本就是模型瞎编的点。

唯一的遗憾是 Anthropic 的 API 不暴露 logprobs,这是它家一个长期的缺口,想用这套手艺目前只能走 OpenAI 或者本地部署。

stream:流式返回

流式不只是为了"让用户看着字蹦出来"有仪式感,它还影响成本控制和服务设计。开启 stream: true 后响应以 SSE 推送,一段段来。有两个细节容易忽略:一是默认流式响应不包含 usage 统计,需要加 stream_options: { include_usage: true } 才能在最后一帧拿到 token 数;二是用户在前端关闭浏览器或点了取消按钮,你得在后端同步中止到模型的请求(AbortController 配合 fetch 的 signal),不然模型还在继续生成,钱照扣。

n:一次取多个候选

n: 3 让模型一次生成三个不同的候选回答。在"让用户从几个版本里挑一个最合适的"这类产品里很好用——图生文、标题生成、邮件模板等场景都有这种需求。还有一种更隐蔽的用法叫 self-consistency:对同一个推理题跑 n=5 次,然后取多数一致的那个答案,复杂数学题能涨 10 个百分点。成本计算要注意,输入 token 只算一次,但输出 token 每份单独算,n=5 且每次 500 输出等于向服务商交了 2500 个输出 token 的钱。

user:多租户场景的礼貌动作

传一个 user 字符串给 OpenAI,可以是你的 user id 的哈希(别传真名邮箱)。服务商用它来做滥用检测和限流分配。不传的话一旦你的应用有个别用户触发了可疑行为,整个 API key 都可能被限,传了就能精准追踪到具体账户。多租户应用永远应该传。

推理模型的特殊口味

o1/o3/gpt-5-thinking 以及 Claude 的 extended thinking 模式有几个自己的参数,也砍掉了一些通用参数。新的 reasoning_effort 接 low/medium/high,控制它"想多久",high 准确率明显更高但延迟和费用都涨。Claude 的 thinking.budget_tokens 类似,给思考预算一个上限。与此同时 temperature、top_p、logprobs、frequency_penalty 等都在推理模型上不生效或固定为默认值。

这些差异的背后逻辑是:推理模型的决策链路是强化学习蒸馏出来的,你用采样技巧去扰动只会让它变笨,所以 API 干脆不让你调。适应这套新范式比抱怨它好——推理模型的默认行为已经调得相当好,你要做的是改 prompt 和控制"让它想多久",而不是去改 logits。

各家差异速查

写跨厂家代码时要记得:temperaturetop_p 是所有厂家都支持的基础盘;top_k 只在 Anthropic、Gemini、本地引擎上有;frequency_penaltypresence_penalty 基本是 OpenAI 家族的东西,Anthropic 不给;seed 目前主要 OpenAI 正式支持,Anthropic 还是 beta;logprobs 上面说了,OpenAI 独占;stop 所有家都有但参数名不同(OpenAI 叫 stop,Anthropic 叫 stop_sequences)。

一个值得思考的哲学差异:Anthropic 提供的参数明显少,OpenAI 多。这背后的产品观是"Anthropic 更倾向让你通过 prompt 解决问题,而不是调旋钮"。这并不是说少就不好,实际使用中你会发现 80% 的效果差异来自 prompt 而不是参数。

一个可以直接抄的调参顺序

实际工作中,遇到输出不理想,按这个顺序排查通常最快。先改 prompt——能靠把需求说清楚解决的,永远别先调参。然后把 temperature 按任务类型定个档(代码 0.2、对话 0.7、创意 1.0)。接着设好 max_tokens 防失控。如果要结构化输出就直接上 response_format。重复问题出现再考虑 frequency_penalty,需要可复现再加 seed。其他参数维持默认。

最后一条经验:参数调整能带来的效果提升有天花板,大部分场景在 5% 到 20% 之间。真正能把效果翻倍的永远是 prompt 的精心打磨、上下文的组织、以及选对模型。别把时间全花在玩参数上。

参考资料

版权声明: 如无特别声明,本文版权归 sshipanoo 所有,转载请注明本文链接。

(采用 CC BY-NC-SA 4.0 许可协议进行授权)

本文标题:番外 2:API 参数全解——从 temperature 到 logprobs

本文链接:https://www.sshipanoo.com/blog/ai/ai-for-frontend/番外02-API参数详解/

本文最后一次更新为 天前,文章中的某些内容可能已过时!