前两篇分别解决了"怎么连上"和"用哪种模式"。请求能发出去了,下一个问题就是参数:那几个数字到底填多少?这一篇把最常打交道的几个参数一个个掰开讲,最后给一份按场景查的配置清单和一份"出问题了先看哪里"的排查清单。
一、先说个结论:参数只决定"挑哪个词",不决定"懂了什么"
新手遇到"模型答得不对",第一反应往往是去调 temperature。调了一圈发现没什么用,然后陷入困惑。
原因在于,这几个采样参数的作用范围其实很窄:它们只影响模型在已经算出的候选词里怎么挑,不改变模型对问题的理解。 模型把"帮我总结这段话"理解成了"帮我翻译这段话",这是理解层面的偏差,你把 temperature 从 0.9 调到 0.1 也不会变好——它只会在"翻译"这个错误方向上挑得更有把握一点。
所以有个判断可以拿来用:
- 答案内容跑偏了 → 九成是输入的问题,去看 Prompt 怎么写,别碰参数;
- 答案方向对,但每次不一样 / 车轱辘话 / 突然断掉 → 这才是参数该管的事。
把这条界线划清楚,能省掉大量无效调参的时间。
二、temperature:随机性的总开关
它到底在做什么
模型每生成一个词,其实是对词表里的几万个候选算出各自的概率,然后从中挑一个。temperature 就作用在这个概率分布上:把它"压尖"或者"摊平"。
- 调低(比如 0.1):分布变尖,最高概率那个词的占比被放大,模型更倾向于每次都选"最保险"的那个词。表现就是输出稳定、保守、可预测。
- 调高(比如 1.2):分布变平,原本概率很低的词也有了被选中的机会。表现就是输出多样、有意外,但同时也更容易说出不靠谱的话。
一个很直观的类比:temperature 调低像是让一个人照着标准答案答题,调高像是让他自由发挥。答标准答案不会出错,但也没什么灵气;自由发挥可能有金句,也可能跑偏。
一个必须知道的坑:调到 0 也不等于完全确定
很多人以为 temperature = 0 就是"每次都返回一模一样的结果",于是拿它来做需要幂等的场景。实测下来,同一个问题连问十次,答案大概率一致,但偶尔就是会差一点——可能是个别措辞不同,也可能是更实质的差异。
原因不神秘:temperature = 0 只是让采样尽量取概率最高的那个词,但概率值本身是两个数相加算出来的,浮点数相加的顺序一变,末位就可能不一样;再加上服务端会做批量推理、并发调度,不同请求落在不同的批次里,这些都会带来细微扰动。最后这个扰动经过几十次连乘,就可能放大成肉眼可见的差异。
所以结论是:别把 temperature = 0 当成"结果可复现"的保证。如果你的业务真的需要确定性(比如要做结果比对、回归测试),那就要从设计上接受"允许一定波动",而不是指望一个参数解决。
推荐区间
这是实测下来比较稳妥的取值,可以直接当起点:
- 分类、信息抽取、写代码:
0 ~ 0.3。这类任务要的是准确和规范,不需要创造力,越低越好。 - 翻译、摘要、改写:
0.3 ~ 0.5。需要一点灵活度让句子通顺,但不能乱编。 - 日常对话、客服、文案:
0.7 ~ 1.0。这是模型出厂默认值所在的区间,也是"自然但不失控"的范围。 - 创意写作、头脑风暴:
1.0 ~ 1.2。要的就是多样性,同一个问题给十个不同方向的答案。 - 超过 1.3:开始明显不靠谱,会出现逻辑断裂、事实胡编。除非你明确在做"越离谱越好"的实验,否则不建议。
三、top_p:换一种方式限制"能挑哪些词"
top_p 和 temperature 看起来在做同一件事——都是让输出更随机或更稳定,但它们的机制不同。
top_p 的做法是:把候选词按概率从高到低排好,然后从头开始累加,累加到刚好超过 top_p 这个值为止,只保留这批词,后面的全部扔掉。
举个例子,top_p = 0.9 意味着"只在累计概率达到 90% 的那批词里挑"。如果前面的词很集中,可能五个词就够 90% 了;如果很分散,可能要两百个词。它限制的是候选范围,而不是直接改变概率分布。
和 temperature 的分工:
temperature决定分布的形状(尖还是平);top_p决定从分布里截掉多长的尾巴(那些极低概率的词)。
官方建议:别同时调
因为两者都在控制随机性,同时调会互相打架,你会搞不清到底是哪个在起作用。固定一个,调另一个。
实践中更常用的做法是:只动 temperature,top_p 保持默认。理由是 temperature 的直觉更线性、更好理解,而 top_p 的效果会随问题类型变化——同样一个 0.9,在词集中的问题里可能只保留几个词,在开放的创作任务里可能保留上百个,行为不稳定,不好预测。
如果遇到"车轱辘话、总是重复同一个句式"这类问题,可以先试试把 top_p 降到 0.8 左右,它砍掉的是长尾,往往能压掉那些莫名其妙的重复选项。
四、max_tokens:回答被腰斩的元凶
这个是三个参数里最容易出事、但最容易被忽略的一个。
先明确它限制的是"输出"
max_tokens 管的是模型这次能生成多少 token,和你输入了多长的内容没关系。这个区分很重要——有人以为它限制的是总长度,于是输入了一段很长的文档,把 max_tokens 设得很小,结果模型还没开始好好回答就被掐断了。
顺带一个实际影响:很多厂商在计费和额度上会按 max_tokens 做预扣。也就是说,哪怕你这次实际只用了 50 个 token,但你写了 max_tokens = 8000,对方可能会先按 8000 占你的额度。所以把它设得过大,不只是浪费,还可能在高并发时报"额度不足"而你又觉得莫名其妙。
怎么判断"是不是被截断了"
这是这篇文章里最实用的一条:看 finish_reason。
非流式的响应里带这个字段:
{
"choices": [
{
"finish_reason": "length",
"message": {
"role": "assistant", "content": "西湖位于浙江省杭州市,是中国著名的……" }
}
]
}
finish_reason 的取值含义:
stop:模型正常说完了,这是你想要的;length:碰到max_tokens上限被截断了,答案是残缺的;content_filter:被内容安全策略拦了;- 其他取值:以你使用的模型文档为准,遇到不认识的值不要假设它是正常的。
流式模式下,这个字段会在最后一个数据块里给出。所以如果你的业务会把回答存下来,落库前判断一下 finish_reason——看到 length 就知道这条记录是半截的,要么标记成异常,要么重试。
很多人排查"回答怎么突然断了",翻半天代码,其实答案就在这个字段里躺着。
设多少合适
原则是:按业务预估的最长输出,再加一点缓冲,而不是直接拉满。
- 短回复类(分类、意图识别、是否判断):
100 ~ 300足够; - 常规问答、摘要:
800 ~ 2000; - 长文生成、代码生成:
3000 ~ 8000,具体看你用的模型上限。
如果业务上确实可能出现超长输出,稳妥的做法不是把 max_tokens 一次拉满,而是检测到 finish_reason = length 之后,带着已有内容让模型接着写。这样既不会浪费预扣额度,又能覆盖正常场景。
五、两个惩罚项:能治重复,也可能把话说坏
frequency_penalty 和 presence_penalty 是用来抑制重复的:
frequency_penalty:按某个词已经出现过的次数扣分,出现越多扣越狠;presence_penalty:只要某个词出现过就扣分,不看次数。
对付"这个问题很重要,很重要,很重要"这种复读机症状,它们是有用的。取值范围一般在 -2 ~ 2,正值抑制重复,负值鼓励重复。
但代价要清楚:它是无差别打击的。模型不知道哪些词是废话、哪些词是必须重复的。写代码时变量名反复出现、技术文档里专业术语本就该高频出现、中文里"的""了"这类虚词天然就多——这些都可能被一起压掉,结果就是话说得别扭、代码变量名莫名其妙变了。
所以用法建议是:默认不设,或者设一个很小的值(0.1 ~ 0.3)。 只有当确认问题就是重复,且降 top_p 没用时,再考虑动它,并且别设大。
六、stop:让模型提前收尾
stop 参数传一个字符串数组,模型生成的文本里一旦出现其中任何一个字符串,就立刻停下。
它有两个用途:
- 省钱:如果你只需要回答的第一段,可以设
stop = ["\n\n"],模型一换段就停,后面的 token 就不用付了; - 控格式:比如要求模型按
答案:xxx的格式输出,可以设stop = ["\n"],防止它自由发挥加一堆解释。
坑在于:stop 的匹配是字符串包含,不是语义匹配。如果那个字符串在正文里提前出现了(比如你设 stop = ["END"],而模型正文里正好提到了 "END"),流会被提前掐断,你会得到一个莫名其妙的短回答。所以用 stop 时,最好选那些在正文里几乎不可能自然出现的标记。
七、seed:能不能复现
部分厂商提供了 seed 参数,语义是"固定随机种子"。听起来像是终于能复现了,但要有合理的预期:
同一个 seed,只能让结果更容易重现,不能保证跨时间、跨版本、跨调用完全一致。
原因是前面提到过的浮点累加和批量推理扰动,这些是服务端实现层面的东西,不是一个种子能锁住的。而且模型一旦升级,同样的 seed 出来的一定是不同结果。
所以 seed 的正确用法是:在做 A/B 对比、或者写回归测试时,用它来减少噪声,让两次对比的差异更可能来自你改的东西(比如 Prompt),而不是来自随机性。别把它当成数据库事务那样的保证。
八、照抄这套配置起步
把这些收成一份可以直接拿来用的起点配置:
String body = """
{
"model": "qwen3.8-max",
"temperature": 0.3,
"top_p": 0.8,
"max_tokens": 2000,
"messages": [
{
"role": "user", "content": "把下面这段话翻译成英文:"}
]
}
""";
按场景改 temperature 和 max_tokens,其它保持不动:
- 分类 / 抽取 / 代码:
temperature 0.1、max_tokens 1000 - 翻译 / 摘要:
temperature 0.4、max_tokens 2000 - 对话 / 客服:
temperature 0.8、max_tokens 2000 - 创意 / 头脑风暴:
temperature 1.1、max_tokens 3000
top_p 固定 0.8 先别动,等确认是长尾在捣乱再单独调。
九、出问题了先看哪里
这份清单大致按出现频率从高到低排列,从上往下排查通常最快:
- 答非所问、理解错意图 → 不是参数问题。去改 Prompt,把要求写具体。
- 答案每次都不一样 → 这是
temperature的正常表现,调到 0.1 附近会稳定很多;但要接受它做不到完全一致。 - 回答突然断在半句 → 九成是
max_tokens太小,看finish_reason是不是length。 - 翻来覆去说同一句话 → 先把
top_p降到 0.8,还不行再给frequency_penalty设 0.2。 - 输出格式每次都变,写好的解析老失败 → 这是格式稳定性问题,靠调参数解决不了,要用结构化输出,这个系列后面会专门讲一篇。
- 报额度不足,但明明没发几个请求 → 检查是不是
max_tokens设得过大,预扣把额度占了。
下一篇
参数调完,正常情况下模型已经能稳定干活了。但前几篇里那些散着写的超时、重试、异常处理,还堆在同一个 main 方法里。下一篇把这些收拢起来,写成一个能真正放进项目里用的客户端——包括超时怎么分层设、重试在什么情况下是安全的、流断了怎么分类处理。
本文是「用 Java 接入大模型」系列第三篇。前两篇分别讲了把流式输出跑通、流式与非流式怎么选,建议按顺序看。文中参数取值基于通义千问的 OpenAI 兼容接口,具体范围和默认值以你使用的模型文档为准。