Token 与上下文
Token 与上下文 · 阅读后完成下方实验与检查题,再用「标记完成」记录进度。
学习目标
模型没有记忆。读完本课你能说清「记忆」是怎么伪造出来的,能估算一次请求要占用多少上下文,并写出长对话的裁剪策略。
问题场景
你先告诉助手「接下来所有代码都用 TypeScript」,聊了五十轮之后它开始输出裸 JS。你质问「我开头说过什么」,它答不上来。是模型变笨了吗?不——它根本没「听到」那句话:要么被你的裁剪策略裁掉了,要么上下文窗口已经被塞满了。
核心解释
模型是无状态的:每次调用都是一次全新请求,服务端不记得上次聊了什么。所谓「多轮对话」,是应用把历史消息数组每次都重新完整发送:
const messages = [
{ role: "system", content: "你是学习助手,回答用中文。" },
{ role: "user", content: "接下来所有代码都用 TypeScript" },
{ role: "assistant", content: "好的,之后示例都带类型标注。" },
{ role: "user", content: "写一个防抖函数" }, // 本轮新输入
];
// 这个数组随每次请求重新发给模型
发送的一切都按 token(词元) 计量——模型处理文本的最小单位,一个汉字通常对应一到两个 token,具体切分因分词器而异。三项共享同一个上限:
- 输入侧:system + 全部历史 + 本轮输入
- 输出侧:模型本次生成的回答
- 上限:上下文窗口,即单次请求允许的 token 总量(随模型不同而不同)
两个容易混淆的口径要分清:总额度是账户在计费、限速维度上累计消耗的 token,按周期结算;单次容量是上下文窗口,超了这条请求直接报错,跟余额无关。省前者靠少聊、压缩历史;省后者靠裁剪、摘要。报错时先分清是哪一类。
示例拆解
长对话的裁剪策略:system 永远保留,历史只留最近 N 条:
function buildContext(
system: string,
history: Array<{ role: "user" | "assistant"; content: string }>,
keep = 20
) {
return [
{ role: "system" as const, content: system },
...history.slice(-keep), // 从尾部取最近 keep 条
];
}
关键点:
- 从尾部取是因为最近的对话相关性最高;最早的约定最容易被裁掉——这正是开头场景的原因。重要约定要么写进 system,要么定期在新消息里复述。
- 裁剪只影响发给模型的内容;数据库里仍应存全量历史(A13 课讲持久化),两个层面别混为一谈。
- 本站的真实实现(
app/api/ai/chat/route.ts)就是查最近 20 条再反转顺序,与本例一致。 - 更完整的策略还会给旧对话做摘要、把超长正文替换成摘要链接,本课先掌握「留头留尾」。
验证别靠肉眼估:各提供方都有 token 计数工具,响应里也有 usage 用量字段,具体以所用提供方文档为准。本站把每次调用的 prompt_tokens / completion_tokens 记进调用记录,可以从记录反推每轮开销。
动手任务
- 估算一笔账:system 约 200 token、历史 20 条平均每条 300 token、预计输出 800 token——这次请求大约占多少?如果窗口上限是 8000,够不够?
- 把
buildContext的keep改成 4,用你自己的话描述用户会在什么场景下察觉「模型失忆」。 - 判断题:把历史裁到只剩最近 3 条,能降低计费成本吗?能降低单次超窗风险吗?分别回答并说明理由。
常见错误
- 以为模型记得开头的约定:它只记得这次发过去的。重要约定进 system 或定期复述,别指望它「记性」。
- 混淆两个口径:余额充足但窗口超限照样报错;窗口再大额度用完也照样失败。
- 裁剪把 system 一起裁掉:模型立刻丢失固定要求,回答风格漂移。system 永远置顶保留。
- 按字符数硬算 token:中英文、代码、标点的换算比例都不同,粗估可以,精确计数以提供方工具为准。
检查与复盘
自查清单:
- 能解释「多轮对话」的假记忆是怎么实现的
- 能区分总额度与单次容量,并说出各自的开销省法
- 能写出保留 system、截取最近 N 条的裁剪函数
参考资料
- OpenAI 文档:Text generation —— 上下文窗口与消息数组的官方说明
- openai/tiktoken —— OpenAI 官方分词器,直观感受一段文本被切成多少 token
- 下一课 A03 把这个消息数组真正发出去:在服务端完成一次受控的真实调用。
读完这一课了?
标记完成后,工作台会安排接下来的学习。