本文是 LLM API 基础系列第二篇。上一篇:LLM API 模型调用服务;下一篇:结构化输出与工具调用。
厂商参数支持核对于 2026-09-21。本文区分一般采样原理与具体服务行为,不把某家实现当成通用协议。
1. 三件容易混淆的事
| 要控制什么 | 常见参数或机制 | 不能误解为 |
|---|---|---|
| 生成的随机性 | temperature、top_p |
事实正确性的保证 |
| 单次生成的预算 | max_tokens、max_completion_tokens、max_output_tokens |
必须生成到这个长度 |
| 请求能容纳多少信息 | Context Window、输入裁剪、摘要、检索 | 永久保存全部聊天历史 |
调参会影响任务表现,但不能把参数当成万能质量旋钮。回答不符合要求时,先检查任务是否明确、证据是否充分、模型是否具备所需能力,再设计对照实验。
2. temperature 与 top_p:原理不等于接口承诺
2.1 temperature 控制采样分布的集中程度
在常见采样实现中,温度作用于 token 候选的分数:低温更偏向高概率候选,高温让分布更平缓。它改变的是生成策略,不是给模型增加知识。[1]
- 需要一致性时,可以从服务默认值或较低温度开始评测。
- 需要探索时,可以比较更高温度下的多样性与有效率。
temperature=0不等于 API 承诺逐字复现。服务版本、执行环境等因素仍可能影响结果;不能只凭一次差异就确定原因。[2]
0.2 可以是某次实验的候选值,不是“所有 Agent 的最佳默认值”。高温也不意味着无法输出合法 JSON,格式保证要看结构化输出机制,而不是只看温度。
2.2 top_p 限制候选概率质量
核采样通常按概率从高到低保留累计概率达到阈值的候选集合,再从中采样。top_p=1 表示不通过这个阈值缩减候选集,不表示均匀随机抽取整个词表。[1]
为了方便解释实验结果,一般一次主要调整一个采样参数。不是说同时设置两者一定错误,更不是说同时调整两者才会导致“不确定性”。
2.3 先确认参数是否真的生效
这是原文采样实验最需要修正的地方。 DeepSeek 在核对日的文档中明确规定:[3]
| DeepSeek 模式 | temperature |
top_p |
|---|---|---|
| 非思考模式 | 可配置 | 固定为 1.0,传入值被忽略 |
| 思考模式 | 不生效 | 有效范围为 0.95–1.0,低于 0.95 按 0.95 处理 |
原脚本关闭 thinking 后扫描 top_p=1.0/0.7/0.3/0.1,按当前接口约定并没有改变有效的 top_p。不同组出现不同答案,不能据此归因于 top_p。
原运行记录没有固定的底层模型版本和足够元数据,不能仅凭当前文档反推当时服务的所有行为;但它不应继续作为当前接口上的有效参数对照实验。
OpenAI 推理模型的参数支持也随模型变化。不要套用“所有推理模型都会忽略参数且不报错”的规则;有的组合会直接被拒绝。[4]
3. 如何做一个不误导自己的小实验
本节保留原文“给函数起名”的任务,只观察当前 DeepSeek 非思考模式下的温度差异。不是模型质量排行榜。
沿用第一篇 .env。以下脚本可独立运行,默认调用 15 次付费 API;先确认账户预算。
1 | import os |
观察结果时,把事实、假设和结论分开:
| 能直接观察到的事实 | 不能直接推出的结论 |
|---|---|
| 5 次采样里出现 2 个不同名称 | 候选答案总共只有 2 个 |
| 几组的去重数刚好相同 | 参数无效,或存在明确的温度“阈值” |
| 温度为 0 时仍有不同回答 | 一定是 MoE 路由或浮点误差造成的 |
| 某组答案种类更多 | 该组更准确、更适合生产 |
如果要做可信评测,应扩大样本、覆盖多个任务、重复运行,并记录模型标识、日期、提示词、实际参数、失败率和成本。具体样本量由目标差异和容许误差决定,30 次或 50 次也不是可信结论的自动通行证。
多样性指标可以辅助分析,但结构化任务还要看 schema 通过率和任务成功率。原文长篇运行日志及对后端机制的猜测不再作为教程结论保留。
4. 输出预算:上限不是实际花费
4.1 参数名称因端点而异
| 端点 | 常用预算字段 | 注意 |
|---|---|---|
| DeepSeek Chat Completions | max_tokens |
按 DeepSeek 的输出限制配置 |
| OpenAI Chat Completions | max_completion_tokens |
当前文档推荐字段;旧 max_tokens 不适用于所有模型 |
| Responses | max_output_tokens |
推理模型可能把推理 token 计入预算 |
这些字段都是上限,不是“要求模型必须写够”。计费通常依据实际处理的输入与输出 token,以及缓存等计费规则;提高上限不必然增加实际费用,也不会本身导致更容易截断。[1][4][5]
更准确的关系是:
- 在其他条件相同且请求被接受时,提高输出上限通常降低因该上限而截断的风险。
- 更大的上限允许产生更长输出,因此可能增加最坏情况下的成本和等待时间。
- 预算过小,推理模型甚至可能在产生可见答案前就用完额度。
- 给模型更大输出预算,不会自动扩大上下文窗口,也不会保证答案正确。
4.2 截断要与正常结束分开
| 接口 | 应检查的信号 | 处理方式 |
|---|---|---|
| Chat Completions | finish_reason == "length" |
检查输出额度及上下文限制 |
| Responses | status == "incomplete" 和 incomplete_details.reason |
按具体原因处理,例如 max_output_tokens |
不要一看到 incomplete 就假定都是预算不足;也不要只靠文本“看起来完整”判断成功。[1][4]
对于文章生成,可向用户展示明确标记为不完整的结果;对于工具参数、JSON 或代码修改,不要执行截断结果。应调整预算、缩小任务或重新生成完整结构。
5. Token 与 Context Window
Token 是模型处理文本或其他输入时使用的单位,不等于一个汉字、一个英文单词或一个字符。不同模型和输入类型的计数方式可能不同。[5]
做容量规划时,可以先使用下面这个教学模型:
1 | 输入估算 + 计划输出预算 + 安全余量 ≤ 上下文容量 |
还要单独满足模型的最大输入/最大输出限制;推理 token、多模态和缓存的具体核算依服务而定。缓存可能降低成本,但不等于这部分信息不占上下文。[4][5]
graph TD
A[整理输入并预留输出预算] --> B{容量是否足够}
B -->|是| C[发起请求]
B -->|否| D[筛选材料或压缩历史]
D --> E{关键内容能否保留}
E -->|能| A
E -->|仍放不下| F[拆分任务或报告超预算]
“历史太长会挤掉系统指令”不是所有 API 的自动行为。超窗后可能报错;只有应用或服务启用了某种截断策略时,内容才会被移除。例如 DeepSeek Responses 不支持 truncation,超窗会返回错误。[6]
5.1 计数是工程估算,不要承诺绝对精确
- 使用目标模型适用的 tokenizer 或厂商计数接口。
- 文本编码结果之外,还要考虑消息封装、工具定义等开销。
- 不要把
len(text)当 token 数,也不要把 OpenAI 的 tokenizer 当所有模型的通用精确计数器。 - 用实际响应的
usage校准估算,同时保留安全余量。[7]
服务返回的 usage 用来统计实际用量;请求前的计数用来规划预算。二者解决的是不同问题。
5.2 裁剪应保留完整的工具交互
下面只演示预算选择算法,不声称实现某个模型的精确计数。调用者必须提供适配目标端点的 estimate_tokens,其输入是完整消息列表。
历史按完整轮次组织:一轮包含用户请求、相关 assistant 输出,以及全部配对的工具调用与结果。先在应用中完成分组,不能直接把任意单条消息当成一轮。
1 | from collections.abc import Callable |
estimate_tokens 应把本次固定工具定义等额外开销也计入估算。这段代码不处理多模态,也不验证原历史的协议合法性。
它修正了按单条消息裁剪的风险:不能只留下 tool 结果,却删除对应的 assistant.tool_calls。对尚未完成的工具交互,不应当作可随意丢弃的历史轮次。[8]
长期运行时,可以结合摘要和检索,但摘要本身也可能丢信息。关键任务约束和工具证据应保留可追溯来源,不能靠“模型应该记得”维持状态。
6. 本篇小结
- 先检查服务是否支持参数,再做实验;传入成功不等于生效。
- 输出上限、实际输出、上下文容量是三个不同概念。
- 低温不保证完全确定;少量样本不足以解释模型后端机制。
- 裁剪历史时,保留任务约束和完整工具交互,不能只按字符数删消息。
下一篇:结构化输出与工具调用。