参数、成本与延迟:把模型开得又准又省又快
昨天(Day 12)学了流式输出,让答案"边想边冒字"。今天我们坐进驾驶室,认识几个关键"旋钮"——温度、top_p、max_tokens,再算清楚这趟车"花多少钱、跑多久"。学会这些,你才能为下一步(结构化输出、多模型路由)做出省钱又靠谱的选择。
三个最常用的旋钮总览
先混个脸熟,后面几讲逐个拆。最常用的三个:
| 参数 | 管什么 | 打车类比 | 常见取值 |
|---|---|---|---|
temperature | 答案的随机性/创意度 | 司机驾驶风格 | 0 ~ 1(甚至到 2) |
top_p | 另一种控制随机性的方式 | 只从"最靠谱的几条路"里挑 | 0 ~ 1 |
max_tokens | 答案最多多长(花的上限) | 车费/里程上限 | 按需,如 512 |
它们都是在调用时以参数形式传进去的。token 这个词后面 L04 会详细讲,现在你只要知道它约等于"字数单位"。
# 用 openai 风格的 SDK 举例(Claude、国产模型接口大同小异)
from openai import OpenAI
client = OpenAI() # 会自动读环境变量里的 API key
resp = client.chat.completions.create(
model="gpt-4o-mini", # 选一个便宜的小模型
messages=[{"role": "user", "content": "用一句话解释什么是大模型"}],
temperature=0.2, # 旋钮1:低温=稳定、少发挥
top_p=1.0, # 旋钮2:先保持默认(见 L03)
max_tokens=100, # 旋钮3:最多答 100 token 就停
)
print(resp.choices[0].message.content) # 打印模型答案
gpt-4o-mini 只是示例名,实际以你所用平台的模型清单为准。参数名(temperature/max_tokens)在主流平台基本通用。temperature:司机的驾驶风格旋钮
回忆 Day 10:模型每一步都在"预测下一个 token",其实它心里对好几个候选词都有一个概率。温度就是在这个概率上做手脚:
- temperature = 0(或很低):几乎总是选概率最高的那个词 → 答案稳定、可复现、适合"要标准答案"的活(分类、抽取、写代码)。
- temperature 高(0.8~1.2):给低概率的词更多出场机会 → 答案多样、有创意,适合写文案、起名、头脑风暴。
给一家咖啡店起个名temperature=0 → 每次都答"晨光咖啡"(稳定但普通,多跑几次也一样)。temperature=1.0 → 这次"浮豆记",下次"拿铁与海",再下次"第七杯"(有惊喜,适合起名这种要创意的活)。
top_p 与其他常用参数
- top_p(核采样):另一种控随机性的方式。
top_p=0.9表示"只在累计概率前 90% 的候选词里挑,剩下 10% 的冷门词直接不考虑"——相当于司机只从最靠谱的几条路里选,不去乱试偏门小路。一般 temperature 和 top_p 只调一个。 - max_tokens:答案最长多少 token(到点就停)。既防止它啰嗦,也是花钱的硬上限(见 L04)。
- stop(停止词):遇到指定字符串就立刻停。比如设
stop=["\n\n"],模型写完一段就收手。 - frequency_penalty / presence_penalty:让模型别老重复同一个词/话题(值越大越"换着说")。写长文防车轱辘话时有用,日常可不管。
👶 小白:那我到底调 temperature 还是 top_p?
👨🏫 老师:绝大多数情况只调 temperature 就够了,top_p 保持默认 1.0。它俩都是控随机性的,同时猛调容易互相干扰、结果难预测。把 top_p 当成"进阶玩家的备用旋钮",入门阶段忘掉它都行。
token 与计费:那张"计价表"怎么读
token 是什么?模型不是按"一个汉字/一个单词"数数,而是按它自己的切分单位 token。粗略记:英文 1 个 token ≈ 4 个字符 ≈ 0.75 个单词;中文大约 1 个字 ≈ 1~2 个 token。一次调用的花费大致是:
每次响应里通常都带用量统计,学会读它你就能随时算钱:
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "用一句话解释 token"}],
)
u = resp.usage # 这次调用的用量对象
print("输入 token:", u.prompt_tokens) # 你问的部分
print("输出 token:", u.completion_tokens) # 它答的部分
print("合计 token:", u.total_tokens)
# 假设单价:输入 ¥0.001/千token,输出 ¥0.004/千token(示意,以实际为准)
cost = u.prompt_tokens/1000*0.001 + u.completion_tokens/1000*0.004
print(f"本次约花 ¥{cost:.5f}")
→ 同样问一句话,第 10 轮的输入费是第 1 轮的 100 倍。这就是为什么 Day 18「上下文工程」要教你压缩历史——它直接省钱。
延迟从哪来:这趟车为什么慢
- 首 token 延迟(TTFT, Time To First Token):从你发请求到冒出第一个字的时间。受网络、模型排队、输入长短影响。
- 生成速度:之后每秒能吐多少 token。输出越长,总时间越长;大模型比小模型慢。
这就解释了 Day 12 为什么要做流式输出:它没让模型变快,但让用户"第一个字"很快就看到,感觉快多了——相当于司机边开边跟你聊,而不是到终点才一次性说话。
| 想更快,怎么做 | 原理(打车类比) |
|---|---|
| 选更小的模型 | 换轻便的车,跑得快 |
| 让输出更短(收紧 prompt、设 max_tokens) | 路程短,早到 |
| 开流式输出 | 边开边聊,不用等到终点 |
| 缩短输入(少堆历史) | 行李少,起步快 |
省钱又省时的实战技巧
- 贵活便宜活分流:简单任务(分类、格式化)用小模型,难任务(复杂推理)才上大模型——这就是 Day 15「模型路由」要展开的。
- 精简输入:别把用不到的历史、整篇文档一股脑塞进去。输入直接决定输入费。
- 设 max_tokens 上限:给输出封顶,既防啰嗦又防"失控烧钱"。
- 缓存重复问题:同样的输入直接返回上次结果,不再调模型(0 成本、0 延迟)。
- 低温更省重试:需要稳定结果的活用低温,减少"答歪了要重来"的浪费。
# 一个极简"结果缓存":同样的问题不再花钱调模型
cache = {} # 用字典当缓存:问题 -> 答案
def ask(question):
if question in cache: # 命中缓存
return cache[question] # 直接返回,0 成本 0 延迟
resp = client.chat.completions.create(
model="gpt-4o-mini", # 简单活用小模型
messages=[{"role": "user", "content": question}],
temperature=0.2, # 要稳定,低温
max_tokens=200, # 封顶,防烧钱
)
answer = resp.choices[0].message.content
cache[question] = answer # 记下来,下次直接用
return answer
print(ask("1+1=?")) # 第一次:真的调模型
print(ask("1+1=?")) # 第二次:命中缓存,秒回、免费
👶 小白:缓存听起来太简单了,真项目也这么干?
👨🏫 老师:真项目里缓存会更讲究(比如用 Redis、给缓存设过期时间、对相近问题也能命中),但核心思想就是这么朴素:跑过的路记下来别重复跑。到 Day 56「成本治理 & 缓存」我们会把它做成正经一套。
今日小结 + 动手 10 分钟
🧠 今天你应该能回答
- temperature 高低分别适合什么活?(低=标准答案,高=创意)
- temperature 和 top_p 为什么别一起猛调?(都是控随机性,会互相干扰)
- 大模型怎么计费?输入和输出为什么分开算?(按 token,输出通常更贵)
- 为什么 Agent 会"越聊越贵"?(历史越堆越长,输入费涨)
- 延迟由哪两段组成,怎么让它更快?(TTFT + 生成速度;小模型/短输出/流式/短输入)
- 省钱四招是什么?(分流、精简输入、设上限、缓存)
✋ 动手(约 10 分钟)
目标:亲手感受"温度"和"计费"。把下面代码存成 day13.py 跑一跑(需要你已配好 API key,见 Day 11)。
from openai import OpenAI
client = OpenAI()
# 实验1:同一问题,跑 3 次高温,看答案是不是每次不同
print("=== 高温(创意) ===")
for i in range(3):
r = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "给一家咖啡店起个名,只回名字"}],
temperature=1.0, max_tokens=20,
)
print(i, r.choices[0].message.content.strip())
# 实验2:低温跑 2 次,看是不是几乎一样
print("=== 低温(稳定) ===")
for i in range(2):
r = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "1 到 5 的中文数字,用逗号分隔"}],
temperature=0, max_tokens=20,
)
print(i, r.choices[0].message.content.strip())
# 实验3:读用量、算钱
print("输入token:", r.usage.prompt_tokens, "输出token:", r.usage.completion_tokens)