Menu

Token 和上下文窗口:AI 如何读取文本

AI 模型以 token(文本的小块)为单位读取文本,并且一次只能容纳有限数量的 token,这个上限就是上下文窗口。了解两者的原理,以及聊天变长时该怎么办。

下面的每条提示词都可以编辑:改好后,在 ChatGPT、Claude 或其他 AI 应用中打开。

AI 语言模型读的不是字母或单词,而是 token:一小块一小块的文本,常常是一个完整的词,有时是词的一部分。而且它们一次只能接收有限数量的 token,这个上限叫作上下文窗口。这两个概念解释了为什么 AI 工具是这样定价的,为什么长聊天会开始忘记早先的指令,以及为什么模型可能数不清一个它刚刚拼写正确的单词里有几个字母。

AI 中的 token 是什么?

在模型看到你的提示词之前,一个叫作分词器(tokenizer)的程序会把文本切分成来自固定词表的片段,并把每个片段转换成一个数字。模型完全是在处理这些数字;它写回复的方式也一样,一次写一个 token,再由应用转换回文本。

词表是从大量文本中学来的,所以常用词往往是一个 token,较少见的词则被拆成几个常见的片段。空格通常附在后一个词的开头。一个英文句子大致会被这样切分:

Token ization isn 't magic .

这个切分只是示意。每个模型家族都有自己的分词器,同一个句子在不同分词器中切出来的片段数量可能不同。很多提供商都发布了分词工具或 token 计数 API,方便你查看真实的数量。

当模型接受图片、音频和文件时,这些内容也会被转换成 token,所以附上一张截图,也会用掉和你的文字同一份预算中的一部分。

一个词等于多少个 token?

对英文文本来说,一个广泛使用的经验法则是每个 token 大约四个字符,也就是大约四分之三个单词。按这个估算,1,000 个 token 大约是 750 个英文单词,一篇 3,000 词的文章大约是 4,000 个 token。

这个比例会随内容而变化:

  • 其他语言表达同样的意思往往需要更多的 token。分词器的训练数据中英文占比很高,所以英文单词能得到紧凑的 token,而日语、韩语、阿拉伯语、印地语等许多语言的文本会被切成更多片段。差距大小取决于分词器,在较新的分词器中已经缩小,但并没有消失。
  • 代码会在缩进、括号、运算符和长标识符上消耗 token,所以一个文件消耗的 token 往往比它的字数看上去要多。
  • 数字、URL 和不常见的字符串(比如 ID 和哈希值)往往会被拆成很多小 token。

token 为什么重要

成本。 API 按 token 收费,你发送的 token 和模型生成的 token 分别定价。在聊天中,每发一条新消息,整个对话都会被重新发送一遍,所以长对话中每条消息的成本比短对话更高。

上限。 模型有一个上下文窗口,容纳一次请求中读取和生成的全部内容,而且单个回复的长度常常还有单独的上限。在 API 中,你可以自己设置这个上限;使用 Anthropic 的 API 时则必须设置:max_tokens 是一个必填参数。

速度。 回复是一个 token 接一个 token 生成的,所以回复越长,完成所需的时间也按比例越长。

拼写类任务。 由于模型把 magic 这样的词看作一两个单位,而不是五个字母,那些依赖单个字符的任务,比如数字母、把单词倒过来写,或找出长度刚好为某个值的单词,对它来说比看上去更难。较新的模型在很多这类任务上表现更好,但当字符很重要时,要检查答案,或者让模型先把单词一个字母一个字母地写出来。

上下文窗口是什么?

上下文窗口是模型在单次请求中能够考虑的最大 token 数。可以把它想象成模型的工作记忆:模型写回复时能用到的一切,都必须同时装进这个窗口,包括:

  • 系统提示词,包括应用自己的指令和你的指令
  • 目前为止的对话,每一条用户消息和每一个回复
  • 附件、粘贴的文档,以及搜索或工具的结果
  • 正在写的回复

窗口之外的任何内容,对模型来说都不存在。它没有可以在后台查找资料的记忆。不同模型的上下文窗口大小差别很大,而且还在不断增长,所以请查阅你所用提供商的文档,而不要依赖某篇文章里的数字。

模型在两次请求之间也不会保留任何东西。聊天中看起来像记忆的东西,其实是应用每次都把整个对话重新发送一遍。聊天应用中的记忆功能也是同样的原理:应用保存关于你的笔记,或者搜索你之前的聊天,再把找到的内容插入新聊天的上下文中。

聊天变长时会发生什么

当对话超出上下文窗口时,总得舍弃一些东西。视应用而定,最早的消息会被丢弃,较早的部分会被替换成一份摘要,或者应用会告诉你这个聊天已经达到上限,应该开一个新的。无论哪种情况,开头的细节,包括你在第一条消息中给出的指令,都可能不再影响回答。

窗口被占满并不是唯一的问题。模型面前的材料越多,可能忽略的东西就越多;在长聊天中,早先的决定要和之后说过的所有内容争夺注意力。症状包括:回答忽略了你之前设定的约束,代码退回到你已经修复过的版本,或者模型重复提出你已经否决过的想法。

在上下文窗口内高效工作

每个任务开一个新聊天。 一个有清晰第一条消息的新聊天,通常胜过一个已经在好几个话题之间漂移的长聊天。

把摘要带到新聊天里。 当一个聊天里已经积累了有用的决定时,让模型写一份可以粘贴到新聊天中的摘要,然后在新聊天里继续。根据你的工作填写下面的空白。

在新聊天中继续
Fill in
这个聊天越来越长了。写一份摘要,让我可以粘贴到新聊天中继续工作。包括:目标、我们做出的决定及原因、数据库结构的当前版本,以及尚未解决的问题。摘要不超过 300 字,数据库结构本身不计入字数。
Try it
Example replyReplies vary between models and runs.

目标: 为一个小型图书馆应用设计数据库,使用 SQLite 存储。

决定:

  • 借阅单独建一张表,以便保留完整的借阅历史。
  • 一本书没有 returned_at 为空的借阅记录时,就算作可借。

当前结构:

CREATE TABLE books (id INTEGER PRIMARY KEY, title TEXT NOT NULL, author TEXT NOT NULL);
CREATE TABLE members (id INTEGER PRIMARY KEY, name TEXT NOT NULL, email TEXT UNIQUE);
CREATE TABLE loans (
  id INTEGER PRIMARY KEY,
  book_id INTEGER NOT NULL REFERENCES books(id),
  member_id INTEGER NOT NULL REFERENCES members(id),
  loaned_at TEXT NOT NULL,
  returned_at TEXT
);

尚未解决的问题:

  • 需要收取逾期费吗?
  • 图书馆是否要记录同一本书的多个副本?

只粘贴任务需要的内容。 粘贴有 bug 的函数和调用它的代码,而不是整个代码仓库;粘贴合同中相关的那一节,而不是全部四十页。材料越少,可能被忽略的东西就越少。

把问题放在长材料之后,并重申要点。 当你粘贴一份长文档时,把问题放在最后,也就是文档之后,并在那里重复关键的约束。这样,指令就紧挨着模型开始写回复的位置。

把一条消息放不下的文档拆开发送。 有些应用会限制单条消息能粘贴的内容长度,即使模型的窗口还能容纳更多。分几部分发送文档可以绕过消息长度限制,但绕不过上下文窗口:每一部分仍然都计入其中。一开始就告诉模型,在你发完所有内容之前不要回应。

Fill in
我要分 3 部分把一份长文档发给你。每收到一部分,只回复“已收到第 N 部分,共 3 部分”,不要说别的。在我提出问题之前,不要总结,也不要回答任何内容。 第 1 部分,共 3 部分: """ [在这里粘贴第 1 部分] """
Try it
Example replyReplies vary between models and runs.

已收到第 1 部分,共 3 部分

在要紧时计算 token 数。 如果你在用 API 开发,发送之前先计数。OpenAI 的开源库 tiktoken 可以用 OpenAI 的分词器对文本进行分词,Anthropic 的 API 也有一个计算 token 数的端点。用一家提供商的分词器得到的数字,对另一家来说只是估算。

import tiktoken

enc = tiktoken.get_encoding("o200k_base")  # one of OpenAI's tokenizers
tokens = enc.encode("Tokenization isn't magic.")
print(len(tokens))
print([enc.decode([t]) for t in tokens])

当你围绕模型构建应用时,决定哪些内容放进窗口、以什么顺序放,本身就成了一个设计问题。上下文工程讲的就是这个,提示词链则展示了如何把一项大任务拆分成多个步骤,让每一步都能轻松放进窗口。想了解单条消息在整个输入中处于什么位置,请看提示词是什么。

常见问题

AI 中的 token 是什么?

Token 是语言模型读取和生成文本的单位。它可以是一个完整的常用词、一个长词的一部分、一个标点符号或一段空白。在模型看到你的提示词之前,分词器会把它切分成 token,并把每个 token 转换成一个数字;模型生成回复时,也是一次生成一个 token。

1,000 个 token 大约是多少个英文单词?

对英文来说,一个常用的经验法则是每个 token 大约四个字符,换算下来,1,000 个 token 大约是 750 个英文单词。实际数字取决于模型的分词器和具体文本。代码、数字以及许多英语以外的语言,表达同样多的内容会用掉更多的 token。

上下文窗口是什么?

上下文窗口是模型在一次请求中能够考虑的最大 token 数。它必须装下系统提示词、目前为止的对话、所有附件或工具结果,以及模型正在写的回复。窗口之外的任何内容,对模型来说都不存在。

聊天超出上下文窗口时会发生什么?

应用必须腾出空间。视应用而定,它会丢弃最早的消息、用一份摘要替换它们,或者告诉你对话已经达到上限。无论哪种方式,聊天早期的指令和细节都可能不再影响回答,这就是为什么长聊天有时看起来会忘事。

ChatGPT 或 Claude 会记住我之前的聊天吗?

模型本身不会。每个回复都是根据那一次请求的上下文窗口中的内容生成的。有些应用有记忆功能,会保存关于你的笔记,或者搜索你过去的聊天,再把找到的内容加进新的聊天;项目也可以包含共享的文件和指令。但这些都是应用把文本放进上下文,而不是模型自己记住了。

Coddy 编程语言插图

和 Coddy 一起学编程

开始