一、引言
调用 LLM API 看起来很简单------装一个 SDK,填几个字段,拿到结果。不少开发者第一次调用时的代码长这样:
python
from openai import OpenAI
client = OpenAI()
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "你好"}]
)
print(response.choices[0].message.content)
这段代码能跑,但 create() 方法实际接受几十个参数。这些参数控制着模型的行为------输出的随机性、长度、格式、是否可以调用工具、是否流式返回------而大多数开发者只接触到其中的三四个。
更复杂的是,当你从 OpenAI 切换到国内厂商(DeepSeek、通义千问、智谱 GLM 等)时,这些参数的行为会发生变化。有的参数不再支持,有的参数行为有微妙差异,有的则需要通过特殊的扩展字段传入。
这篇文章的路线图如下:
- 第二章:先看一次 Chat API 调用实际发出了什么 HTTP 请求------揭开 SDK 的黑箱
- 第三章:逐一介绍所有稳定参数的分类、语义和用法
- 第四章:区分 SDK 消费了哪些参数、哪些透传到 body
- 第五章:对比国内 7 家厂商在每个参数上的兼容性
- 第六章:分析参数组合的意图模式和常见程序类型
- 第七章:以项目中的自动续写功能为案例,串联各个参数
- 第八章:总结最佳实践
本文以 gpt-4o 对应的 OpenAI Chat Completions API 稳定版本(2024-08-06)为基准,不覆盖仍在实验阶段的参数。代码示例基于 OpenAI Python SDK(openai>=1.0)。本文基于 2025 年中的 API 版本编写,厂商 API 可能更新,请以各厂商最新文档为准。
二、客户端协议:一次 Chat API 调用到底发出了什么
2.1 HTTP 请求全貌
当你调用 client.chat.completions.create() 时,SDK 最终向服务端发送了一个 HTTP POST 请求。以下是一个完整请求的全貌:
http
POST /v1/chat/completions HTTP/1.1
Host: api.openai.com
Authorization: Bearer sk-proj-xxxxxxxxxxxxxxxx
Content-Type: application/json
User-Agent: OpenAI/Python 1.55.0
{
"model": "gpt-4o",
"messages": [
{"role": "system", "content": "你是一位助手。"},
{"role": "user", "content": "请用一句话介绍 Python"}
],
"max_tokens": 200,
"temperature": 1.0,
"top_p": 1.0,
"frequency_penalty": 0,
"presence_penalty": 0,
"stream": false,
"stop": null,
"seed": null,
"response_format": {"type": "text"},
"tools": null,
"tool_choice": "auto",
"parallel_tool_calls": true,
"logprobs": false,
"top_logprobs": null,
"n": 1,
"user": null,
"logit_bias": null
}
这就是 SDK 替你构造并发送出去的 JSON body。注意到即使你没有显式传参,SDK 也会用默认值填充所有字段。
2.2 参数的两类去向
从 SDK 到 HTTP 请求的过程中,参数的去向分为两类:
- 被 SDK 消费:不进入 body,而是被 SDK 用于构造请求的 header、URL、连接配置等
- 透传到 body:SDK 不做任何处理,直接序列化到 JSON body 中
例如,api_key 被 SDK 消费后放入 Authorization header,base_url 用于拼接 URL,http_client 用于配置连接池------这些参数永远不会出现在 body 中。而 model、messages、max_tokens 等则原样进入 body。
此外,SDK 提供了一个后门机制 extra_body,用于传入 body 中没有对应字段的自定义参数。这在调用国内厂商时非常有用,因为许多厂商在 OpenAI 标准之上增加了自己的字段。
三、参数详解
本章按功能分类逐一说明每个参数。每个参数包含:语义(它控制什么)、类型/范围/默认值、以及使用场景。
3.1 消息结构:messages
messages 是最核心的参数,它构成了与模型对话的全部上下文。它是一个对象数组,每种对象通过 role 字段区分角色:
text
messages: [
{ role: "system", content: "..." }, // 系统指令
{ role: "user", content: "..." }, // 用户输入
{ role: "assistant", content: "...", tool_calls?: [...] }, // 模型回复
{ role: "tool", content: "...", tool_call_id: "..." } // 工具调用结果
]
四种角色的语义:
| 角色 | 用途 | 说明 |
|---|---|---|
system |
设定模型的行为和风格 | 可选的顶层指令,影响整个对话的基调 |
user |
用户输入 | 问题、指令或需要模型处理的内容 |
assistant |
模型回复 | 在多轮对话中,需要将历史回复拼入 messages |
tool |
工具调用结果 | 配合 tool_calling 使用,回填函数执行结果 |
content 的两种形态:
简单的文本请求中,content 是一个字符串:
json
{"role": "user", "content": "请解释量子纠缠"}
在需要传入多模态内容时,content 是一个数组:
json
{
"role": "user",
"content": [
{"type": "text", "text": "这张图里有什么?"},
{"type": "image_url", "image_url": {"url": "https://..."}}
]
}
目前 content 数组支持的类型有 text 和 image_url 两种。国内厂商中,通义 Qwen、智谱 GLM、MiniMax 和 StepFun 支持图片输入,百度和 Moonshot 不支持。
关于 system 角色的说明:
在较新的 API 版本中(2024-08-06+),也可以在顶层使用 system 参数直接传入:
json
{"system": "你是一位助手", "messages": [...]}
这个方式与在 messages 中放入 system 角色效果相同,但部分国内厂商只支持 messages 中的 system 角色,不支持顶层 system 参数。
3.2 基础生成控制
这组参数控制模型"如何生成"输出------随机性多高、输出多长、是否重复相同的内容。
model
model 是最重要的一个参数,它决定了模型的能力边界------知识范围、推理能力、上下文窗口大小、对工具调用的支持程度等。
python
# OpenAI 系列
model="gpt-4o" # 最新旗舰模型
model="gpt-4o-mini" # 轻量版,性价比高
# 国内厂商
model="deepseek-chat" # DeepSeek 对话模型
model="qwen-max" # 通义千问
model="glm-4-plus" # 智谱 GLM
- 类型:
string - 必须: 是
max_tokens
控制模型输出的最大 token 数量(注意是输出 token,不是总 token)。
python
max_tokens=50 # 短回答,适合分类、提取
max_tokens=500 # 标准回答
max_tokens=4096 # 长文本输出
- 类型:
int - 范围: 1 到模型的最大输出 token 数
- 默认值: 由各厂商自行设定(OpenAI 为 4096)
- 重要: 当输出达到
max_tokens时,finish_reason为"length"------这不是错误,而是截断信号。程序应该检测这个信号并决定是否续写
temperature 与 top_p
这对参数配合使用,控制输出分布的随机性。
temperature 控制概率分布的"平滑度":
python
temperature=0 # 确定输出:每次调用返回概率最高的 token(argmax)
temperature=0.7 # 平衡:有一定随机性,但不会偏离太远
temperature=1.0 # 默认值:模型训练时使用的分布
temperature=1.5 # 高随机性:低概率词更容易被选中
temperature 的底层机制很简单:模型输出每个 token 时有一个概率分布,temperature 作为分母除一下 logits 再做 softmax。temperature=0 是 argmax(只选最高概率的 token),temperature 越大分布越平缓。
注意 :
temperature=0在数学上等价于贪心解码,但实际实现中因浮点精度和不同厂商的实现差异,不能 100% 保证每次调用的输出完全一致。
top_p(Nucleus Sampling / 核采样)是另一种随机性控制方式:
python
top_p=1.0 # 默认:考虑全部 token
top_p=0.9 # 只考虑累积概率前 90% 的 token
top_p=0.1 # 只考虑累积概率前 10% 的 token,非常集中
实际效果对比(相同 prompt,不同的组合):
text
temperature=0.0, top_p=1.0
"写一句关于春天的诗"
→ "春眠不觉晓,处处闻啼鸟" (每次相同)
temperature=0.8, top_p=1.0
"写一句关于春天的诗"
→ "春风又绿江南岸" 或 "杏花春雨江南" 或 "草长莺飞二月天" (随机变化)
frequency_penalty=-2.0 ← 鼓励重复
"写一句关于春天的诗"
→ "春天春天春天春天春天" (负值惩罚让模型反复使用已出现的词)
OpenAI 官方和国内厂商都建议:只调 temperature 或只调 top_p,不要同时调整二者。同时调整时,两者的交互效果难以预测。
国内厂商差异 :所有厂商都支持 temperature,Qwen 和 DeepSeek 行为与 OpenAI 一致。智谱 GLM 额外提供了 do_sample 参数,设为 false 时等同于 temperature=0 的贪心解码。
frequency_penalty 与 presence_penalty
这对参数配合使用,控制输出的重复程度。
两者的区别可以从命名中看出:
| 参数 | 惩罚的对象 | 效果 |
|---|---|---|
frequency_penalty |
同一个 token 出现的频次 | 换词不换话题 |
presence_penalty |
token 是否已经出现过 | 推动话题前进 |
实例效果:
text
# 不加惩罚:容易重复
frequency_penalty=0, presence_penalty=0
"写一首关于猫的诗"
→ "小猫小猫真可爱,小猫小猫喵喵叫,小猫小猫爱吃鱼..."
# 加频次惩罚:换词
frequency_penalty=2, presence_penalty=0
"写一首关于猫的诗"
→ "喵星人跳上窗台,尾巴轻摇,胡须颤动..."
# 加存在惩罚:换话题
frequency_penalty=0, presence_penalty=2
"先聊猫,再聊狗,再聊鱼"
→ 模型不会反复回到猫的话题,会主动推进到狗和鱼
- 类型:
float - 范围:
-2.0到2.0 - 默认值:
0 - 负值 → 鼓励重复(几乎没有使用场景)
最佳实践 :长文本生成设置 frequency_penalty=0.3~0.5 避免词汇重复;多轮对话设置 presence_penalty=0.3~0.5 避免死循环话题。
国内厂商差异:智谱 GLM-4 系列完整支持 frequency_penalty 和 presence_penalty,GLM-3 及以下版本不支持。七家厂商中完全支持两者的是 DeepSeek、通义 Qwen、智谱 GLM 和 StepFun。
stop
指定一个或多个字符串,当模型生成到这些字符串时停止:
python
stop=["\n", "结束"] # 遇到换行或"结束"就停止
stop="?" # 遇到问号就停止
- 类型:
string或array[string] - 最多支持 4 个停止词
- 默认值:
null(不设置停止词)
seed
指定随机数种子,配合 temperature=0 实现可复现的生成:
python
seed=42, temperature=0 # 每次调用返回相同结果
- 类型:
int - 默认值:
null(不指定种子) - 注意:确定性不是 100% 保证的。国内厂商中 DeepSeek、通义 Qwen、智谱 GLM 支持;Moonshot、MiniMax、百度不支持
logit_bias
直接修改某个 token 出现的概率:
python
logit_bias={
24939: -100, # 让 token ID 24939 几乎不可能出现
584: 20 # 让 token ID 584 更可能出现
}
- 类型:
dict(key 是 token ID,value 是偏置值) - 范围:
-100到100 - 极少使用的参数,需要先通过 tokenizer 查到 token ID
- 国内兼容性:只有 DeepSeek 完全支持,Qwen 部分版本支持,其余厂商均不支持
3.3 输出格式控制
response_format
控制模型输出的格式,有三种模式:
python
# 模式一:自由文本(默认)
response_format={"type": "text"}
# 模式二:JSON 对象
response_format={"type": "json_object"}
# 模式三:JSON Schema(严格校验)
response_format={
"type": "json_schema",
"json_schema": {
"name": "person",
"strict": true,
"schema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"}
},
"required": ["name", "age"],
"additionalProperties": false
}
}
}
三种模式的区别:
| 模式 | 保证的内容 | 适用场景 |
|---|---|---|
text |
无约束 | 通用对话、写作 |
json_object |
输出是合法的 JSON | 需要结构化但 schema 动态 |
json_schema |
输出符合预定义 Schema | 严格结构化输出、数据管道 |
json_object 模式的使用要求:必须在 messages 或 system prompt 中提示模型输出 JSON(如"请以 JSON 格式输出"),否则模型不会知道要输出什么字段。
json_schema 模式的优势 :strict: true 强制 Schema 校验,模型不会输出未定义的字段。
国内兼容性:
- json_object:DeepSeek、Qwen、智谱 GLM(部分)、MiniMax、StepFun 支持;Moonshot、百度不支持
- json_schema:DeepSeek 和 Qwen 支持较好,智谱不支持
3.4 工具调用(Tool Calling)
tools
定义一组可供模型调用的工具:
python
tools=[{
"type": "function",
"function": {
"name": "get_weather",
"description": "获取指定城市的天气信息",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "城市名"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
},
"required": ["city"]
}
}
}]
tool_choice
控制模型是否调用工具:
python
tool_choice="auto" # 模型自主决定是否调用(默认)
tool_choice="none" # 强制不调用
tool_choice="required" # 强制调用(必须至少调用一个)
tool_choice={"type": "function", "function": {"name": "get_weather"}} # 强制调用指定工具
parallel_tool_calls
是否允许模型在一次响应中调用多个工具:
python
parallel_tool_calls=true # 允许并行调用(默认)
parallel_tool_calls=false # 每次只调用一个工具
完整的数据流:
text
应用层 模型 工具
│ │ │
├─ 请求(含 tools 定义)──▶ │
│ │ │
│ ├─ 决定调用工具 │
│◀── tool_calls 响应 ─────┤ │
│ │ │
├─ 执行本地函数 ──────────┼──────────────────────▶│
│ │ │
├─ tool 结果回填到 messages▶ │
│ │ │
│ ├─ 基于工具结果继续回答 │
│◀── 最终响应 ────────────┤ │
│ │ │
国内兼容性 :DeepSeek、Qwen、智谱 GLM(有限制)、Moonshot、StepFun 支持工具调用;百度 ERNIE 不支持。注意智谱 GLM 的 tool_choice 只支持 "auto" 和具名 function,不支持 "required"。parallel_tool_calls 仅 DeepSeek 和 Qwen 支持。
3.5 流式输出
stream
当 stream=false(默认)时,SDK 发起一个普通 HTTP 请求,在完整的响应返回后才将结果交给应用层:
python
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "写一首短诗"}],
stream=False # 默认
)
# 等待完整响应
print(response.choices[0].message.content)
当 stream=true 时,SDK 发起一个 SSE(Server-Sent Events)流式请求,服务端逐 token 推送数据,SDK 逐 chunk 返回给应用层:
python
stream = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "写一首短诗"}],
stream=True
)
for chunk in stream: # 逐 token 到达
if chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="")
流式传输的 SSE 事件格式如下(每个 chunk 是一个独立事件):
text
data: {"id":"...","choices":[{"delta":{"content":"春"},"finish_reason":null}]}
data: {"id":"...","choices":[{"delta":{"content":"眠"},"finish_reason":null}]}
data: {"id":"...","choices":[{"delta":{"content":"不"},"finish_reason":null}]}
...
data: [DONE]
stream_options
从 2024-08-06 版本开始,可以通过 stream_options 在流式响应结束时附带 token 用量信息:
python
stream = client.chat.completions.create(
...,
stream=True,
stream_options={"include_usage": True}
)
最后一个 chunk 会包含 usage 字段,包含 prompt_tokens、completion_tokens 等统计。不是所有国内厂商的流式响应都支持 include_usage------DeepSeek 和 Qwen 支持,其余大多不支持。
3.6 推理模型参数
从 OpenAI o1/o3 系列开始,以及 DeepSeek R1 等国内推理模型,引入了推理模型专属的参数概念。
reasoning_effort(OpenAI o1/o3 系列)
控制模型在推理上投入多少计算量:
python
reasoning_effort="low" # 轻量推理
reasoning_effort="medium" # 默认
reasoning_effort="high" # 深度推理
reasoning_effort越高,模型花在"思考"上的 token 越多,答案质量可能更高,但响应时间更长- 该参数目前仅 OpenAI o1/o3 系列支持,国内厂商无对应参数
max_completion_tokens(替代 max_tokens)
在 o1/o3 推理模型中,推荐使用 max_completion_tokens 替代 max_tokens:
python
max_completion_tokens=4096 # 总的 completion token 预算(含 reasoning + 回答)
传统的 max_tokens 只控制输出 token 数量。但在推理模型中,输出包含两个部分:推理(reasoning)和回答(answer)。max_completion_tokens 的预算同时覆盖这两部分。如果推理过程消耗了大部分预算,回答部分可能很少甚至为空。
reasoning_content(国内推理模型特有字段)
DeepSeek R1 等国内推理模型的响应中包含一个标准 OpenAI 规范中没有的字段 reasoning_content:
python
response = client.chat.completions.create(model="deepseek-reasoner", ...)
message = response.choices[0].message
reasoning = getattr(message, "reasoning_content", None) # 思考过程
content = message.content # 最终回答
这个字段在同步响应的 message 对象上,也在流式响应的 chunk.choices[0].delta 上:
python
stream = client.chat.completions.create(model="deepseek-reasoner", ..., stream=True)
for chunk in stream:
delta = chunk.choices[0].delta
if getattr(delta, "reasoning_content", None):
print("[思考]", delta.reasoning_content) # 推理过程输出
if delta.content:
print(delta.content) # 回答内容输出
重要影响 :当 max_tokens 设置过小时,推理模型可能把所有 token 预算都消耗在 reasoning_content 上,导致 content 为空。程序逻辑不能假设 content 一定非空------需要检测 reasoning_content 是否有值来判断模型是否在思考,并相应调整 token 预算。
3.7 边缘参数
n
生成多个候选回答:
python
n=3 # 一次请求返回 3 个不同的候选回答
- 类型:
int(默认 1) - 注意:
n > 1时不能与stream=true同时使用(API 会报错) - 国内兼容性:DeepSeek 部分支持(只生效第一个候选),其余厂商基本不支持
logprobs 与 top_logprobs
返回每个输出 token 的对数概率值:
python
logprobs=True # 返回每个 token 的对数概率
top_logprobs=5 # 额外返回每个 token 位置的前 5 个候选概率
- 国内兼容性:DeepSeek 和 Qwen 支持,其余不支持
user
一个简单但实用的参数,用于标识请求的终端用户身份,方便在 OpenAI 侧进行用量监控和滥用检测:
python
user="user_abc123"
- 类型:
string - 不影响模型行为,仅用于标识
四、参数的去向:哪些被 SDK 消费,哪些透传
4.1 参数分流全景图
OpenAI Python SDK 处理参数的逻辑分为三层:
text
┌──────────────────────────────────────────────────────────────┐
│ OpenAI Python SDK │
├──────────────────────────────────────────────────────────────┤
│ │
│ 创建 Client 时消耗(永远不会进入 HTTP body) │
│ ───────────────────────────────────────── │
│ api_key → Authorization header │
│ base_url → 拼出 POST /v1/chat/completions │
│ organization → OpenAI-Organization header │
│ http_client → httpx 实例,控制连接池/超时/代理 │
│ default_headers → 附加到每个请求的 headers │
│ default_query → 附加到每个 URL 的 query params │
│ timeout → httpx.Timeout 配置 │
│ max_retries → 重试策略 │
│ │
│ 调用 create() 时处理(部分消费,部分透传) │
│ ──────────────────────────────── │
│ stream → 如果 true,SDK 切换到 SSE 流解析 │
│ 同时 body 中写入 "stream": true │
│ response_format → SDK 做 type 校验后原样写入 body │
│ extra_body → 直接 merge 进 body(扩展的后门) │
│ │
│ 透传到 body(SDK 不消费,直接 json 序列化) │
│ ────────────────────────────── │
│ model, messages, max_tokens, temperature, top_p │
│ frequency_penalty, presence_penalty, stop, seed │
│ logit_bias, n, logprobs, top_logprobs, user │
│ tools, tool_choice, parallel_tool_calls │
│ reasoning_effort, max_completion_tokens │
│ stream_options │
└──────────────────────────────────────────────────────────────┘
这个分层意味着:当你给 client.chat.completions.create() 传入参数时,不能假设所有参数都会进入 body。例如 api_key 和 http_client 一定不会------它们是用来配置 Client 行为的,不是模型行为。
4.2 extra_body:扩展参数的后门机制
extra_body 是 OpenAI SDK 提供的一个特殊参数,它的内容会直接合并(merge)进最终发送的 JSON body。这是调用国内厂商时最重要的机制,因为国内厂商的扩展字段在标准 SDK 中没有对应的参数名。
python
# 传入 DeepSeek 的 prefix 扩展字段
client.chat.completions.create(
model="deepseek-chat",
messages=[{"role": "user", "content": "从前有座山"}],
extra_body={"prefix": True} # 合并到 body: {... "prefix": true}
)
# 传入通义 Qwen 的 enable_search 扩展字段
client.chat.completions.create(
model="qwen-max",
messages=[{"role": "user", "content": "2025年诺贝尔奖"}],
extra_body={"enable_search": True}
)
如果不使用 extra_body,要传入这些扩展字段就需要直接操作 HTTP 请求,或者使用厂商提供的自定义 SDK。
五、国内厂商兼容性深度对比
5.1 兼容性矩阵
以下对比基于 OpenAI Chat Completions API 稳定版本(2024-08-06),覆盖 7 家国内厂商。✓=完全兼容,△=部分兼容/行为差异,✗=不支持。
| 参数 | GPT | DeepSeek | Qwen | 智谱GLM | Moonshot | StepFun | MiniMax | 百度ERNIE |
|---|---|---|---|---|---|---|---|---|
| model | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| messages: system | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | △ |
| messages: user/text | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| messages: user/image | ✓ | ✓ | ✗ | ✓ | ✗ | ✓ | ✓ | ✗ |
| messages: tool | ✓ | ✓ | ✓ | ✓ | ✓ | △ | ✓ | ✗ |
| max_tokens | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| temperature | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| top_p | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| frequency_penalty | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| presence_penalty | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| stop | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| seed | ✓ | ✓ | ✓ | ✓ | ✗ | ✓ | ✗ | ✗ |
| logit_bias | ✓ | ✓ | △ | ✗ | ✗ | ✗ | ✗ | ✗ |
| response_format/text | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| response_format/json | ✓ | ✓ | ✓ | △ | ✗ | ✓ | ✓ | ✗ |
| response_format/schema | ✓ | ✓ | ✓ | ✗ | ✗ | ✓ | ✓ | ✗ |
| stream | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| stream_options/usage | ✓ | ✓ | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ |
| tools + tool_choice | ✓ | ✓ | ✓ | △ | ✓ | ✓ | ✓ | ✗ |
| parallel_tool_calls | ✓ | ✓ | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ |
| logprobs + top_logprobs | ✓ | ✓ | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ |
| n | ✓ | △ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
| user | ✓ | ✓ | ✗ | ✓ | ✓ | ✗ | ✗ | ✗ |
5.2 典型差异案例分析
案例一:百度 ERNIE 的消息格式
百度 ERNIE 的 API 不是直接的 OpenAI 兼容接口。其消息格式与标准不同:
json
// OpenAI 标准
{"messages": [{"role": "user", "content": "你好"}]}
// 百度 ERNIE(非兼容模式)
{"messages": [{"role": "user", "content": "你好"}], "system": "..."}
在使用百度原生 API 格式时,ERNIE 的 system 指令需要通过顶层参数传入,且某些模型版本要求不同的消息角色名。而在 OpenAI 兼容模式下(通过百度千帆平台的 OpenAI 兼容接口),messages 数组中的 system 角色是受支持的。要使用标准的 OpenAI SDK 调用 ERNIE,需要通过一层协议转换中间件(如 LiteLLM、One API)。
案例二:智谱 GLM 的工具调用限制
智谱 GLM 支持基本的工具调用功能,但有以下限制:
tool_choice只支持"auto"和具名 function,不支持"required"- 复杂嵌套 JSON Schema 的工具参数可能解析失败
- 不支持
parallel_tool_calls
案例三:流式输出的 token usage 差异
在 OpenAI 的流式响应中,可以通过 stream_options={"include_usage": True} 在最后一个 chunk 获取 prompt_tokens 和 completion_tokens。DeepSeek 和 Qwen 支持此功能,但智谱 GLM、Moonshot、StepFun、MiniMax、百度均不支持。需要 token 统计时,这些厂商只能通过非流式请求获取。
5.3 国内厂商扩展字段
以下是各厂商在 OpenAI 标准 body 之外的专有请求字段:
| 厂商 | 扩展字段 | 类型 | 说明 |
|---|---|---|---|
| DeepSeek | prefix |
bool | 将最后一个 user 消息的 content 视为文本前缀而非指令(续写场景),需配合特定消息结构使用,通过 extra_body 传入 |
| 通义 Qwen | enable_search |
bool | 启用联网搜索增强回答 |
| 智谱 GLM | do_sample |
bool | 是否使用采样策略,设为 false 等同 temperature=0 |
| MiniMax | role_play |
bool | 角色扮演模式 |
| MiniMax | mask_sensitive_info |
bool | 敏感信息自动打码 |
这些扩展字段的标准传入方式是通过 extra_body:
python
response = client.chat.completions.create(
model="deepseek-chat",
messages=[...],
extra_body={"prefix": True} # DeepSeek 续写模式
)
六、参数组合与使用模式
6.1 参数的四个控制维度
所有参数组合,归根到底在四个维度上控制模型行为:
text
维度A:输出一致性 ←→ 创造性
控制每次调用结果的稳定程度
temperature=0 + seed → 确定输出
temperature=0.7 → 有一定随机性
temperature=1.2 + top_p=0.9 → 高创意
维度B:输出长度预算管理
控制一次请求能拿到多少内容
max_tokens=50 → 短回答
max_tokens=500 → 标准回答
max_tokens=4000 + 续写循环 → 长文本
维度C:话题聚焦 ←→ 话题探索
控制模型在内容上的粘性和跳跃
penalty 全 0 → 容易重复
frequency_penalty=0.5 → 换词不换话题
presence_penalty=0.6 → 主动推动话题
维度D:输出形式约束强度
控制返回内容的结构严格度
response_format="text" → 无约束
response_format="json_object" → 弱约束(只保证是 JSON)
response_format="json_schema" + strict → 严格约束
tool_choice="required" → 严格约束(曲线救国)
6.2 有意义与无意义的组合
理解每个维度的意图后,就能看出哪些组合是矛盾的:
| 组合 | 效果 | 评价 |
|---|---|---|
| temperature=0 + seed=42 | 确定输出 | ✅ 推荐,生产环境标配 |
| temperature=0 + top_p=0.1 | 极度确定 | ✅ 可用,但 top_p 多余 |
| temperature=0 + stream=true | 确定性流式 | ✅ 常见,不冲突 |
| temperature=1.5 + frequency_penalty=2 | 高创意但惩罚词 | ⚠️ 矛盾------temperature 在发散、penalty 在收敛,效果不可预测 |
| response_format=json_schema + temperature=1.5 | 严格结构 + 高创意 | ⚠️ 矛盾------大概率输出无效 JSON |
| max_tokens=50 + presence_penalty=2 | 短输出 + 推话题 | ⚠️ 浪费------token 预算不够说完一句话 |
| stream=true + n>1 | 流式 + 多候选 | ❌ API 报错 |
| tool_choice="required" + 无 tools | 强制调用 + 无工具 | ❌ API 报错 |
| reasoning_effort="high" + max_tokens=50 | 深度推理 + 短预算 | ❌ 必然截断,content 永远为空 |
6.3 实际程序中的常见模式
模式 A:纯管道
最常见的模式------temperature=0、无工具、短 max_tokens。
python
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "分类这条文本:..."}],
max_tokens=50,
temperature=0
)
- 用途:分类、提取、翻译、重写
- 程序结构:请求 → 解析 → 返回,无状态
- 风险:几乎无风险
模式 B:对话引擎
messages 持续增长,temperature=0.7~0.9,无 penalty。
- 用途:聊天机器人、客服
- 程序结构:维护 messages 历史列表,每次追加
- 风险:messages 无限增长 → token 成本 + 上下文窗口溢出
- 缓解:滑动窗口 / 摘要压缩历史
模式 C:结构化生成器
temperature=0 + response_format(json_schema) + max_tokens 充足。
- 用途:数据合成、结构化输出管道
- 程序结构:解析 JSON → 校验 schema → 失败重试
- 风险:strict 模式下仍可能输出无效内容
- 缓解:重试逻辑 + fallback
模式 D:长文生成器
temperature=0.8 + frequency_penalty=0.5 + 续写循环。
- 用途:写作、报告生成、故事
- 程序结构:检测 finish_reason → 拼 messages → 递归
- 风险:无限循环、推理模型 content 为空
- 缓解:max_continuations 限制 + reasoning_content 检测
模式 E:工具执行器
tools + tool_choice + 循环执行 + 结果回填。
- 用途:Agent 系统
- 程序结构:请求 → 解析 tool_calls → 执行 → 回填 → 再请求
- 风险:tool calling 循环死锁
- 缓解:max_tool_calls 限制
七、典型案例:自动续写中的参数协作
以项目 llmapi-example 中的自动续写功能为案例,展示多个参数如何协作。
7.1 续写场景的参数协作链
自动续写的核心流程,参数按顺序被触及:
text
① 发起请求
model="gpt-4o"
messages=[system, user]
max_tokens=200 ← 控制每次输出的 token 预算
② 检查响应
finish_reason="length" ← 模型输出被 max_tokens 截断
content="已生成的部分内容..."
③ 拼接历史(关键步骤)
messages.append({"role": "assistant", "content": content})
messages.append({"role": "user", "content": "继续"})
④ 再次请求(回到步骤①)
...
这个循环在 continuation.py 中实现。完整的参数协作链是:
text
max_tokens → 控制输出长度
↓
finish_reason → "length" 不等于出错,程序需要识别的信号
↓
messages → 必须正确拼入 assistant 内容,否则上下文丢失
↓
max_continuations → 防止无限循环的安全边界
7.2 推理模型的特殊适配
当使用 DeepSeek R1(deepseek-reasoner)等推理模型时,上述流程会出现一个陷阱:
python
# 陷阱:推理模型输出中可能只有 reasoning_content 没有 content
message = response.choices[0].message
reasoning = getattr(message, "reasoning_content", None) # "有值..."
content = message.content # ""(空!)
if not content and reasoning:
# 推理占满了所有 token,content 被挤掉了
# 不能拼入空 content → 会污染上下文
# 应该放大 max_tokens
max_tokens = min(max_tokens * 2, 4096)
continuation.py 中的实现(约第 51-65 行):
python
if finish_reason == "length" and continuation_count < cfg.max_continuations:
# 推理模式特殊处理:content 为空且 reasoning 有值
if reasoning_content and not content:
next_max_tokens = min(next_max_tokens * 2, 4096)
# 不拼入空的 assistant content
else:
messages.append({"role": "assistant", "content": content})
messages.append({"role": "user", "content": "继续"})
continuation_count += 1
7.3 同步 vs 流式续写
同步续写和流式续写的参数协作逻辑基本相同,但检查点的时机不同:
| 方面 | 同步模式 | 流式模式 |
|---|---|---|
| 检查 finish_reason | 在完整响应中一次性检查 | 在最后一个 chunk 中检查 |
| 拼接 content | 直接取 message.content | 逐 chunk 积累到完整 |
| 检测 reasoning_content | message 对象上直接 getattr | 逐 chunk 检查 delta.reasoning_content |
| 用户体验 | 等待完整响应后一次性输出 | 逐 token 展示,体验更好 |
流式模式中,程序需要实时积累内容的同时,对最后一个 chunk 进行截断判断。
八、总结与最佳实践
8.1 参数选择决策树
从程序实现的角度,选择参数组合的决策路径:
text
开始:我需要什么?
│
├─ 稳定输出 → temperature=0
│ ├─ 需要可复现 → + seed(如果厂商支持)
│ └─ 不需要可复现 → 不设 seed
│
├─ 长文本 → temperature=0.7~0.8
│ ├─ 需续写逻辑 → 实现 finish_reason 检测循环
│ │ ├─ 支持推理模式 → + reasoning_content 检测
│ │ └─ 非推理 → 直接续写
│ └─ 需控重复 → + frequency_penalty=0.3~0.5
│
├─ 对话 → temperature=0.7~0.9
│ └─ 需防话题循环 → + presence_penalty=0.3~0.5
│
├─ 结构化输出 → response_format 或 tools
│ ├─ 厂商支持 json_schema → response_format
│ └─ 厂商不支持 → 降级到 tool_choice="required"
│
└─ 需要工具 → tools + tool_choice + 循环执行
└─ 国内厂商 → 先确认工具调用树兼容性
8.2 跨厂商迁移检查清单
当从 OpenAI 迁移到国内厂商时,按优先级检查:
- model 映射是否准确 --- 不同厂商的模型能力差异巨大
- response_format 是否支持 --- json_schema 和 json_object 可能不可用
- seed 是否支持 --- 如果程序依赖可复现输出
- stream_options.include_usage --- 如果程序需要 token 统计
- tool calling 具体限制 --- parallel_tool_calls、tool_choice 支持程度
- 扩展字段 --- 是否需要通过 extra_body 传入厂商特有参数
- 消息格式 --- 百度 ERNIE 等可能需要适配器
8.3 推荐的错误处理策略
python
# 推荐的错误处理分层
try:
response = client.chat.completions.create(...)
except APITimeoutError:
# 超时重试(指数退避)
except AuthenticationError:
# API Key 无效,提示用户检查配置
except APIStatusError as e:
# 参数不支持、模型不存在等
# 判断 e.status_code:
# 400 → 参数错误,检查兼容性
# 401/403 → 认证问题
# 429 → 限流,退避重试
except APIError:
# 通用错误,记录日志
需要注意的几点:
finish_reason="length"不是错误,是截断信号- 空
content不一定是错误------推理模型的 content 可能为空但 reasoning_content 有值 - 重试时注意
max_continuations限制,避免无限循环