temperature、max_tokens 和 config, 模型参数应该放在哪里
同一段 Prompt,在开发环境里回答得不错,到了线上却突然变长、变慢、变贵,甚至换了模型。很多团队第一反应是继续调 temperature。
这通常调错了方向。
模型参数真正难的地方,不是背住一长串字段,而是知道每个参数属于哪一层,谁可以修改它,修改后怎样被追踪。没有这层边界,配置会慢慢散落到请求处理、业务函数和环境变量里,最后没有人知道某一次结果为什么不一样。
本文会建立一套参数分层方法,并通过三个小实验理解随机性、输出预算和运行时覆盖。示例中的模型名仅用于说明调用形式,实际运行请替换为自己可访问的服务。
参数不是一堆旋钮,而是三类责任
连接参数决定请求怎样抵达服务端。生成参数决定模型怎样生成。运行时参数则描述本次调用怎样被追踪、限制和归类。
把 timeout 当成生成参数,或把用户可以控制的模型名直接塞进请求参数,都会带来稳定性和成本问题。
实验一, temperature 改变的是采样,不是事实性保证
低温度适合抽取、分类和固定格式输出。较高温度适合需要多个不同候选的创意任务。
from langchain.chat_models import init_chat_model
extractor = init_chat_model(
"openai:gpt-5.5",
temperature=0,
max_tokens=300,
)
writer = init_chat_model(
"openai:gpt-5.5",
temperature=0.9,
max_tokens=800,
)
print(extractor.invoke("提取这句话中的城市和日期,北京团队将在周五开会。").content)
print(writer.invoke("为一款安静的机械键盘写三个不同风格的短标题。").content)
不要把温度当作「回答质量」按钮。低温度让采样更稳定,不会让错误知识变正确。高温度也不等于更有创意,它只是增加候选变化。
更重要的是,temperature=0 也不承诺绝对一致。模型版本、服务端实现、系统更新和并行请求都可能改变输出。业务如果要求严格一致,应把关键判断交给规则、Schema 校验或数据库约束。
实验二, max_tokens 是输出预算,不是上下文总长度
模型的上下文窗口要同时容纳系统提示、对话历史、检索内容、用户输入和模型输出。max_tokens 通常约束的是最后一项。
short_answer = init_chat_model(
"openai:gpt-5.5",
max_tokens=80,
)
response = short_answer.invoke("解释向量数据库的工作原理,并举一个业务例子。")
print(response.content)
如果输出被截断,不能机械把 max_tokens 调大。需要检查三个问题。
- 任务是否可以拆成提纲、分段生成和汇总。
- 输入上下文是否塞入了不相关的历史或检索片段。
- 业务究竟需要一段长文本,还是几个可验证字段。
长输入加长输出,往往同时提高延迟和成本。对于报告类任务,先让模型生成结构,再逐段填充,通常比一次生成全部内容更可控。
实验三, 初始化默认值与运行时 config 不能混用
初始化参数表达长期稳定的默认策略。config 表达一轮调用的上下文、追踪信息和经过明确允许的临时变化。
from langchain.chat_models import init_chat_model
model = init_chat_model(
"openai:gpt-5.5",
temperature=0.2,
timeout=30,
max_retries=3,
configurable_fields=("model", "temperature", "max_tokens"),
)
result = model.invoke(
"将下面这段说明压缩为三条要点。",
config={
"run_name": "policy_summary",
"tags": ["summary", "production"],
"metadata": {"request_id": "req_123"},
"configurable": {
"temperature": 0.4,
"max_tokens": 300,
},
},
)
这里有一条重要边界。只有被 configurable_fields 显式允许的字段,才应当由运行时覆盖。否则,一个普通用户请求可能意外切换到高成本模型,或者绕开稳定性设置。
结构化输出的可靠性,不能只靠低温度
提取姓名、日期、金额、分类标签时,最常见的错误是要求模型「以 JSON 返回」,然后用字符串或正则去解析。
更稳妥的方式是先定义数据契约,再选择模型支持的结构化输出策略。
from pydantic import BaseModel, Field
class Invoice(BaseModel):
vendor: str
amount: float = Field(ge=0)
currency: str
structured_model = model.with_structured_output(Invoice)
invoice = structured_model.invoke("发票来自 Acme,金额 1280.50 美元。")
print(invoice.model_dump())
不同服务商可能使用原生结构化输出或工具调用策略。它们的限制和失败形态不同。上线前要针对真实样本测试缺字段、类型错误、额外字段和模型拒答,而不是只测试一次成功样例。
四个生产参数,不应该留给默认值
| 参数 | 建议做法 | 原因 |
|---|---|---|
timeout | 显式设置 | 防止慢请求长期占用连接 |
max_retries | 设置有限预算 | 网络错误、429、5xx 可恢复,401 不可恢复 |
max_concurrency | 与服务商配额一起测试 | 防止批量任务把错误率推高 |
metadata | 记录请求和业务场景标识 | 便于追踪成本、延迟和故障 |
重试也需要克制。401 通常是凭证或权限问题,404 通常是模型名、Endpoint 或路径错误,这两类问题反复重试只会浪费时间。429 和部分 5xx 才适合有限次指数退避。
把调参变成实验,不要靠感觉
为每个关键任务准备一组固定输入,记录模型、参数、输出是否通过、延迟和 token 用量。一次只改一个变量。
任务, 发票信息抽取
成功标准, Schema 校验通过且金额正确
对照项, temperature 0 与 0.5
记录项, 通过率、延迟、输入输出 token、失败类型
这会让参数讨论从「我觉得 0.7 比较好」变成可复查的工程决策。模型可以变,实验和业务标准不应该丢。
还应把实验结果和发布配置分开保存。实验允许快速试不同温度和模型,生产配置则应经过评审,并限制哪些字段允许请求覆盖。否则一次临时调试很容易变成无人知晓的线上默认值。
延伸阅读
更多推荐

所有评论(0)