Day 13 / 共 68 天 · 阶段2 大模型基础

参数、成本与延迟:把模型开得又准又省又快

昨天(Day 12)学了流式输出,让答案"边想边冒字"。今天我们坐进驾驶室,认识几个关键"旋钮"——温度、top_p、max_tokens,再算清楚这趟车"花多少钱、跑多久"。学会这些,你才能为下一步(结构化输出、多模型路由)做出省钱又靠谱的选择。

📍 你在阶段 2「大模型基础」里的位置
D10 模型是什么 D11 调 API D12 流式输出 D13 参数/成本/延迟 D14 结构化输出 D15 多模型&路由
💡 今天用「打车出行」这套世界观兜住全场 把调一次大模型想成打一趟车temperature(温度)=司机的"驾驶风格旋钮",调低=老实按导航走(稳定、可复现),调高=爱抄小路搞创意(多样、但可能翻车);token 计费=出租车的计价表,按"字数里程"收钱,而且上车(你问的话)和下车(模型答的话)分两种单价延迟=这趟路花的时间,路越长(输出越多)、车越豪华(模型越大)就越慢;max_tokens=你上车前跟司机说的"最多花这么多,到点就停"。记住"旋钮调风格、计价表算钱、路程定时间",今天全通。
L01

三个最常用的旋钮总览

🤔 痛点同样一句问话,为什么别人调出来的答案稳定又便宜,你调出来忽长忽短、还贵?因为你没动那几个"旋钮",全用了默认值。
💡 本质(一句话)调用大模型时,除了消息内容,你还能传几个参数来控制"它怎么答、答多长"——就像打车前先跟司机说清"稳着开、最多跑多远"。

先混个脸熟,后面几讲逐个拆。最常用的三个:

参数管什么打车类比常见取值
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)   # 打印模型答案
注:model 这里选了 gpt-4o-mini 只是示例名,实际以你所用平台的模型清单为准。参数名(temperature/max_tokens)在主流平台基本通用。
L02

temperature:司机的驾驶风格旋钮

🤔 痛点为什么问它同一个问题,有时答得一模一样,有时每次都不同?很多人以为是 bug,其实是温度在起作用。
💡 本质(一句话)temperature 决定模型"下一个字有多敢乱选"——低温像老实司机严格按导航走,高温像爱抄近路的司机,路子野、惊喜多、也更容易出岔子。

回忆 Day 10:模型每一步都在"预测下一个 token",其实它心里对好几个候选词都有一个概率。温度就是在这个概率上做手脚:

  • temperature = 0(或很低):几乎总是选概率最高的那个词 → 答案稳定、可复现、适合"要标准答案"的活(分类、抽取、写代码)。
  • temperature 高(0.8~1.2):给低概率的词更多出场机会 → 答案多样、有创意,适合写文案、起名、头脑风暴。
同一个候选概率,被温度"拉平"或"拉尖" 低温 (0.2):只认最高的,稳 高温 (1.0):都有机会,野 柱子越高=越可能被选中。低温几乎只选"好",高温连"妙"都可能蹦出来。
图注:温度不改变谁最强,而是改变"弱选手有没有机会上场"。
📝 例子:同一问题、两种温度 问:给一家咖啡店起个名
temperature=0 → 每次都答"晨光咖啡"(稳定但普通,多跑几次也一样)。
temperature=1.0 → 这次"浮豆记",下次"拿铁与海",再下次"第七杯"(有惊喜,适合起名这种要创意的活)。
👶 一句话记住要"标准答案"就把温度调低(分类/抽取/代码),要"创意点子"就调高(文案/起名)。拿不准时,0.2~0.7 是安全区
L03

top_p 与其他常用参数

🤔 痛点文档里除了 temperature 还有 top_p、stop、penalty 一堆,是不是都要调?会不会互相打架?
💡 本质(一句话)它们都是"随机性 + 收尾"的细调旋钮;新手记住:temperature 和 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 当成"进阶玩家的备用旋钮",入门阶段忘掉它都行。

L04

token 与计费:那张"计价表"怎么读

🤔 痛点调模型到底怎么收费?为什么两次都问"一句话",账单还不一样?做 Agent 会不会一不小心烧掉一大笔?
💡 本质(一句话)大模型按 token 计费(token≈"字数里程"),而且你问的(输入)和它答的(输出)是两种单价——就像打车上车费和路程费分开算,输出通常更贵。

token 是什么?模型不是按"一个汉字/一个单词"数数,而是按它自己的切分单位 token。粗略记:英文 1 个 token ≈ 4 个字符 ≈ 0.75 个单词;中文大约 1 个字 ≈ 1~2 个 token。一次调用的花费大致是:

一次调用 = 上车费(输入) + 路程费(输出) 输入 token 你的 system+历史+提问 × 输入单价 + 输出 token 模型生成的答案 × 输出单价(通常更贵) = 这次的账单
图注:输入越长(历史堆积、贴大段文档)越贵;输出单价常是输入的 3~4 倍。

每次响应里通常都带用量统计,学会读它你就能随时算钱:

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}")
📝 例子:为什么 Agent 会"越聊越贵" 第 1 轮你问一句(输入 20 token)。第 10 轮时,为了让模型记得前文,你把前 9 轮的对话全塞进输入(输入可能涨到 2000 token)。
→ 同样问一句话,第 10 轮的输入费是第 1 轮的 100 倍。这就是为什么 Day 18「上下文工程」要教你压缩历史——它直接省钱。
具体单价各家、各模型都不同,且会变化,务必以你所用平台的官方定价页为准;这里的数字只用于演示怎么算。
L05

延迟从哪来:这趟车为什么慢

🤔 痛点用户点了按钮,转圈好几秒才出字,体验很差。慢在哪?是网络问题还是模型问题?
💡 本质(一句话)延迟主要由两段组成:等第一个字出现的时间(TTFT) + 后面逐字吐出来的时间;输出越长、模型越大,就越慢——就像路越远、车越重,到得越晚。
  • 首 token 延迟(TTFT, Time To First Token):从你发请求到冒出第一个字的时间。受网络、模型排队、输入长短影响。
  • 生成速度:之后每秒能吐多少 token。输出越长,总时间越长;大模型比小模型慢。

这就解释了 Day 12 为什么要做流式输出:它没让模型变快,但让用户"第一个字"很快就看到,感觉快多了——相当于司机边开边跟你聊,而不是到终点才一次性说话。

想更快,怎么做原理(打车类比)
选更小的模型换轻便的车,跑得快
让输出更短(收紧 prompt、设 max_tokens)路程短,早到
开流式输出边开边聊,不用等到终点
缩短输入(少堆历史)行李少,起步快
L06

省钱又省时的实战技巧

🤔 痛点做 demo 时随便调,一上量账单和延迟就爆炸。有没有一套"默认就该这么干"的省法?
💡 本质(一句话)省钱省时的核心口诀:能用小车别用豪车、能少带行李就少带、能设上限就设上限、跑过的路记下来别重复跑
  1. 贵活便宜活分流:简单任务(分类、格式化)用小模型,难任务(复杂推理)才上大模型——这就是 Day 15「模型路由」要展开的。
  2. 精简输入:别把用不到的历史、整篇文档一股脑塞进去。输入直接决定输入费。
  3. 设 max_tokens 上限:给输出封顶,既防啰嗦又防"失控烧钱"。
  4. 缓存重复问题:同样的输入直接返回上次结果,不再调模型(0 成本、0 延迟)。
  5. 低温更省重试:需要稳定结果的活用低温,减少"答歪了要重来"的浪费。
# 一个极简"结果缓存":同样的问题不再花钱调模型
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「成本治理 & 缓存」我们会把它做成正经一套。

L07

今日小结 + 动手 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)
明日预告 · Day 14 结构化输出:今天我们让模型答得又快又省,但答案还是"一段自由的话",程序很难接住。明天教你让模型稳定吐出 JSON——用 json mode / response_format + pydantic 校验,让下游代码能可靠地拿到"姓名、金额、类别"这些字段。这是从"聊天玩具"迈向"能接进系统的工程"的关键一步。
← Day 12 流式输出 Day 14 · 结构化输出 →