大模型回答问题时,内部到底发生了什么?

副标题:从一句“今天天气怎么样”开始,拆解一次回答生成和第二轮追问

写作日期:2026-07-13

大模型回答问题时的四个关键阶段:组装 prompt、切 token、逐个生成和缓存复用

一、使用 AI 时,你可能遇到过这些问题

每天使用 AI,最熟悉的动作就是在聊天框里输入一句话,然后点击发送。界面通常不会立刻摆出完整答案,而是先等待一小段时间,再把回答逐渐显示出来。有时只发一句话,很快就能看到开头;有时贴入一大段材料,要等更久才能看到第一个字。

再看一段最简单的两轮对话。假设应用已经取得北京的天气数据,这里只看回答怎样产生、两轮对话怎样接续。

用户:北京今天天气怎么样?
助手:今天北京天气晴!

用户:明天呢?

第二轮里,用户只写了“明天呢”,没有重复地点,也没有再说“天气”,AI 仍然能够沿着上一轮继续回答。它是真的把第一轮记住了吗?如果重新打开一个对话窗口,这份“记住”还在不在?

对话继续变长以后,还会出现另一种现象:新一轮明明只输入了几个字,回答却不一定比第一轮更快。前面的内容已经处理过,为什么历史越长,整轮计算仍可能越来越慢、越来越贵?

等待、逐步显示、接住追问和长对话变慢,看起来是四个独立现象,背后连着同一条计算链。

二、你在聊天框里只输入了一句话,模型实际收到了什么?

2.1 模型收到的,为什么比聊天框里看到的更多?

第一次问“北京今天天气怎么样?”时,聊天框里只有这一句话。应用真正发送给模型的内容通常还包括一段 system prompt,用来规定助手的身份、任务边界和回答方式。简化以后,这次请求可能是:

[system:你是一个天气助手。已知数据:北京今天晴,明天小雨。请用简洁中文回答]
[user:北京今天天气怎么样?]

模型本身不知道北京的实时天气,是应用先取得天气数据,再把数据和用户的问题一起放进请求。为了方便说明,这里把数据直接写进了 system prompt;真实应用也可能通过其他消息类型传入。

systemuser 也不是普通的排版标签。应用会按照模型配套的 chat template,把不同角色的消息转换成固定格式,其中可能包含角色标记、消息结束标记和助手开始标记。这些看不见的内容同样会进入模型。因此,即使用户输入完全相同,system prompt 或消息模板不同,模型实际收到的内容也可能不同。模型一次真正收到的这份完整输入,通常称为 prompt;聊天框里的一句话只是 prompt 的一部分。

聊天框里的一句话只是应用组装后完整 prompt 的一部分
2.2 文字为什么要先拆成 token?

模型执行的是数字运算,不能直接拿汉字、英文和标点做计算。程序会先用 tokenizer 把 prompt 切成一段段计算单位,这些单位叫 token

token 不固定等于一个汉字,也不固定等于一个完整单词。“北京今天天气怎么样?”可能被切成“北京”“今天”“天气”“怎么样”“?”,也可能采用更细的切法。实际结果由模型配套的 tokenizer 决定。

tokenizer 背后有一张 vocabulary,中文通常称为词表。可以把它理解成模型能够处理的 token 清单。换一个模型,这张清单和切分规则可能不同,同一句话得到的 token 数量也可能跟着变化。

每个 token 在词表里都有一个编号,叫 token ID。下面的数字只用于说明过程,不是真实模型中的编号:

token       token ID
北京        1024
今天        5321
天气        8765
怎么样      4210
?          31

这一步完成后,整段文字已经变成一串编号。输入有多少 token,会影响它占用多少上下文;采用按 token 计费的服务时,也会影响输入费用。

2.3 token ID 已经是数字,为什么还要继续转换?

token ID 只是词表里的行号,像一个目录编号。编号 1024 不表示“北京”的含义,也不表示它和编号 1025 对应的 token 更相似。模型还需要把这个编号转换成一组可以参加后续计算的数字。

模型内部有一张 embedding table。程序按照 token ID 找到对应的一行,取出一组有顺序的数字。这组数字叫 vector,中文通常称为向量;一个 token 得到的初始向量也叫 embedding

“北京”的 token ID 1024
→ 查 embedding table 的第 1024 行
→ [0.12, -0.47, 0.83, 0.05, ...]

真实模型的向量可能包含几千个数字。单独一维通常没有“地点”“天气”或“时间”这种可以直接命名的固定含义,整组数字共同参加后面的计算。这些数值来自模型训练,不是人工填写的解释字典。

模型还需要知道 token 的前后顺序。“猫吃鱼”和“鱼吃猫”包含相同的三个字,顺序一变,含义也跟着变化。模型会通过位置机制,让后续计算带上每个 token 所在的位置。

相同 token 改变位置顺序以后,句子的含义也会改变

到这里,输入转换可以收成一条链:

完整 prompt
→ tokenizer 切成 token
→ token 对应 token ID
→ token ID 查表得到 embedding
→ 位置机制让后续计算带上前后顺序
文字经过 tokenizer 变成 token、token ID 和 embedding vector

文字现在已经变成模型可以计算的数字,但模型还没有开始回答。下一步,它要先处理这份已经存在的完整输入,再从整个词表里选出第一个输出 token。

对业务使用来说,这一章最直接的含义是:你在聊天框里多贴一段材料,增加的不只是页面上的文字,也会增加模型需要处理的 token。它们会占用上下文,并可能增加等待时间和输入成本。

三、文字已经变成数字,模型怎样选出回答的第一个 token?

3.1 回答出现前,模型为什么要先读完整段输入?

用户点击发送时,system prompt、天气数据和“北京今天天气怎么样?”都已经存在。模型要先处理这些输入,才能开始回答。

当前生成式大模型的核心结构通常叫 Transformer。它由许多层计算组成,输入中的 token 会一层一层地经过这些计算。在同一层里,已经存在的多个 token 可以成批处理;但后一层仍然要等待前一层的结果。

这段“先处理完整输入、还没有生成回答”的阶段,通常叫 Prefill。经过多层计算以后,每个位置的数字表示都会带上各自能够看到的前文信息。最后一个输入位置能够看到完整 prompt,模型会利用它的计算结果,判断回答的第一个 token 应该是什么。

因此,聊天框迟迟没有出现第一个字,不一定是模型“没反应”。它可能正在处理 system prompt、历史对话和本轮新材料。输入越长,Prefill 通常要做的计算也越多。

3.2 问题最后的“怎么样”,怎样找到前面的“天气”?

位置机制只能告诉模型 token 的前后顺序,还不能直接告诉它哪些内容更值得参考。处理“怎么样”时,模型需要把它和前面的“北京”“今天”“天气”,以及 system prompt 里的天气数据联系起来。

Transformer 用来建立这种联系的核心机制叫 Attention,中文通常称为注意力机制。它会计算当前位置与自身及前面允许看到的各个位置之间的相关程度,再按照不同权重汇总信息。与当前问题关系更强的位置,通常会贡献更多;关系较弱的位置,也不一定完全被忽略。

如果继续往技术里面拆,Attention 通常会使用 QueryKeyValue,简称 Q、K、V。这里不用记具体公式,只需要先认识这三个名字;Key 和 Value 也会出现在缓存机制里。可以先把它们理解成:

Query:当前位置正在寻找什么
Key:各个可见位置提供什么匹配线索
Value:匹配以后,从相应位置取回什么信息

Q、K、V 实际上是模型计算出来的向量,用数字完成匹配和信息汇总。上面的三句话只是便于理解的类比。模型会综合多个位置并经过多层更新,整个过程比一次关键词搜索复杂得多。

当前 Query 与多个历史 Key 匹配,再按不同权重汇总历史 Value

即使关键信息离问题很远,Attention 也为模型提供了直接读取它的路径。不过,长材料中的信息仍可能被忽略或使用错误。因此,把任务目标、限制条件和关键数据写清楚、放在明确的位置,通常比只丢给模型一堆原始内容更有效。

3.3 模型怎样从整个词表里选出“今天”?

完整输入经过 Transformer 处理以后,模型会给词表里的每个候选 token 计算一个原始分数。这个分数叫 logit,所有候选的分数合在一起叫 logits

下面的数字只用于展示过程:

候选 token    logit
今天          4.2
北京          3.8
我            0.7
苹果         -0.4

logit 越高,表示这个 token 在当前位置越可能被选中,但它还不是概率。程序通常会用 softmax 把这些分数转换成一组总和为 100% 的概率,也就是 probability distribution,中文通常称为概率分布。

系统可以直接选择概率最高的 token,也可以按照概率分布抽取一个,这种方式叫 sampling,中文通常称为采样。如果系统使用 sampling,常见参数 temperature 会调整概率的集中程度:较低时,结果通常更稳定;较高时,低分候选获得的机会增加。

logits 经过 softmax 变成概率分布,再选择或采样下一个 token

假设这一次选中了“今天”,服务才产生了回答的第一个 token。从请求开始到服务产生或返回第一个输出 token 的时间,常用 Time to First Token 衡量,简称 TTFT,中文可以理解为首 token 延迟。Prefill 是其中的重要部分;用户实际看到第一个字的等待,还可能受到网络传输、排队、服务调度和界面缓冲的影响。对业务使用来说,输入材料越长,首个输出前通常要处理的内容越多。

从发送请求到界面显示首个 token,网络、排队、Prefill 和界面缓冲都会影响等待

到这里,第一次生成可以收成一条更完整的链:

完整 prompt
→ Prefill 处理已有输入
→ Attention 汇总相关信息
→ 输出层生成 logits
→ softmax 转成概率分布
→ 选择第一个输出 token

四、已经选出“今天”,为什么还不能一次得到整句话?

4.1 选出“今天”以后,模型下一步做什么?

模型刚刚选出了回答的第一个 token——“今天”。要继续回答,它会把“今天”接到原来的 prompt 后面,再根据这段变长的内容预测下一个 token。

为了方便理解,可以把过程简化成:

完整 prompt
→ 预测“今天”

完整 prompt + “今天”
→ 预测“北京”

完整 prompt + “今天北京”
→ 预测“天气”

真实的 token 不一定正好对应一个完整词,但生成顺序不变:每次选出一个 token,把它加入已有前文,再预测下一个。这种方式叫自回归生成,推理服务通常把逐 token 生成的阶段叫 Decode。读者只需要记住,每一步都要等上一步选定。

模型不能提前确定后面所有 token,因为第二个 token 依赖第一个 token 实际选中了什么,第三个又依赖前两个。服务器可以同时处理多个用户的请求,但一条回答内部仍然要沿着已经生成的前文逐步向后。

完整 prompt 先经过 Prefill,回答再通过 Decode 逐 token 生成
4.2 回答一点点显示,是模型在“打字”吗?

模型没有在键盘上打字。它在不断选出新的 token,服务再通过 streaming,也就是流式输出,把新产生的内容陆续发送给应用。应用一边接收、一边显示,聊天框里的回答就会一点点出现。

模型实际生成的是 token,界面显示的是文字,两者并不总是一一对应。一个 token 可能包含多个汉字,也可能只是一个词的一部分;应用还可能把几个 token 暂存后一起刷新。因此,用户看到的一小段文字,不一定刚好对应模型的一次生成。

模型逐个生成 token,服务流式传输,应用缓冲后再显示成界面文字

第三章的 TTFT 关注第一个 token 前的等待;回答开始以后,服务常用“每秒输出多少 token”描述后续速度。开始显示得快,不等于整段回答结束得快。评估使用体验时,需要同时看首个 token 的等待和后续输出速度。

4.3 每生成一个 token,都要把前面全部重算一遍吗?

如果每次预测下一个 token,都重新计算完整 prompt 和已经生成的回答,大部分工作会不断重复:

生成第 1 个 token:计算完整 prompt
生成第 2 个 token:重新计算 prompt + token 1
生成第 3 个 token:重新计算 prompt + token 1 + token 2

系统会保留前面已经算出的中间结果,下一步主要计算新位置,所以不用从 prompt 开头重新计算。这项机制叫 KV Cache。它的名称来自 Attention 中保存的 Key 和 Value,也就是 K 和 V。

KV Cache 保存模型内部的中间计算结果,供当前回答的后续步骤复用。缓存减少了重复计算,前后依赖仍然存在:下一个 token 要等当前 token 选定以后才能生成。

没有 KV Cache 会重复计算完整前缀;有 KV Cache 时,当前 Q 读取完整 K/V,当前 K/V 再追加进缓存

在同一次回答的 Decode 过程中,KV Cache 会随着新 token 不断增长。它主要服务于当前这次回答,不代表模型已经跨对话记住了用户。第一轮回答结束以后,再问“明天呢”已经是一个新的请求。

对业务使用来说,流式输出可以让用户更早看到回答,后续 token 仍需继续生成。在其他条件相近、主要按可见输出 token 计费时,更简洁的回答通常会缩短输出阶段并减少相应费用。部分推理模型还会产生不可见的推理 token,最终延迟和费用仍要看具体服务的计费方式。

五、第二轮只问“明天呢”,AI 为什么还能接得上?

5.1 AI 是真的把上一轮记在脑子里了吗?

第一轮回答结束后,用户只输入了“明天呢”。这句话里没有“北京”,也没有“天气”,单独拿出来几乎无法判断在问什么。

常见聊天应用会在发送第二轮请求时,把前面的对话历史一起放回 prompt。简化以后,模型这次收到的内容是:

[system:你是天气助手。北京今天晴,明天小雨]
[user:北京今天天气怎么样?]
[assistant:今天北京天气晴]
[user:明天呢?]

对模型来说,“明天呢”前面已经有地点、主题和上一轮回答。Attention 可以读取这些历史位置,于是模型知道用户仍在问北京天气,并回答“明天北京有小雨”。

这种连续感首先来自应用重新提交了历史消息。重新打开一个不带旧历史的对话窗口,只问“明天呢”,模型通常就失去了判断依据。部分产品会在新对话中自动加入用户偏好或历史摘要,这仍是应用额外提供的信息。

5.2 历史重新提交以后,模型就可以少算这部分吗?

应用把旧对话重新放进 prompt,解决的是“模型这次能看到什么”。服务端能否少做重复计算,还要看缓存是否命中。这是两个不同环节。

如果每一轮都重新处理整段历史,长对话会反复执行大量相同的 Prefill。服务端可以保留一段已经完成的计算;新请求到来时,再从开头寻找完全相同的 token 前缀。这种跨请求复用通常叫 Prompt Cache,有些系统也叫 Prefix Cache

可以把过程简化成:

从开头匹配完全相同的部分
→ 命中的部分直接复用缓存
→ 新增或没有命中的部分继续处理

“历史被重新提交”不代表“历史已经命中缓存”。前缀必须完全相同,而且缓存仍然存在。开头改动一个 token,后面很长一段前缀都可能无法继续复用。第一轮回答在上一轮属于输出,到了第二轮则会成为输入;它实际能命中多少缓存,仍取决于具体服务。

历史重新提交让模型看见前文,Prompt Cache 还需要从开头匹配相同 token 前缀

Prompt Cache 复用的是中间计算结果。命中以后,模型仍要处理“明天呢”,再生成新的回答。它不会因为缓存里出现过“今天北京天气晴”,就把旧答案直接复制一遍。

5.3 对话历史、缓存和长期记忆,分别解决什么问题?

这三个概念很容易在聊天界面里混在一起,可以先记住它们各自解决的问题:

对话历史:让本轮请求带上前面说过什么
Prompt Cache:减少相同前缀的重复计算
长期记忆:跨会话保存用户信息,需要时重新放进 prompt
对话历史解决看得见前文,Prompt Cache 解决少做重复计算,长期记忆解决跨会话使用

长期记忆通常位于模型之外。应用可以把用户偏好、项目背景或重要事实存进数据库,需要时再检索并加入新的 prompt。对于这类保存在模型外的用户长期记忆,模型本轮能够使用的,是应用重新提供给它的部分。

因此,“新建对话后还记得我”主要是产品和应用层能力。业务系统如果需要可靠地保存客户偏好、任务状态或审批结论,应当明确设计保存、更新、检索和删除机制,不能只依赖聊天窗口曾经出现过这些内容。

对业务使用来说,多轮对话能接住追问,依赖历史是否被带回;重复前缀能否省计算,依赖缓存是否命中;跨会话能否继续使用旧信息,依赖长期记忆是否被保存并重新注入。三个环节分开以后,就不会再把“看得见历史”“算得更少”和“真正记住用户”当成同一件事。

六、缓存命中越来越高,为什么长对话仍可能越来越慢、越来越贵?

6.1 缓存命中率高,省掉的究竟是什么?

假设一段对话已经有 1000 个 token,这一轮只新增 50 个。如果前面的内容保持不变,而且缓存仍然存在,大部分历史就可能直接复用。历史增长到 10000 个 token、本轮仍只新增 50 个时,能够复用的比例看起来会更高。

这解释了为什么长对话很容易出现很高的 cache hit ratio,中文通常称为缓存命中率。这里用常见的 token 口径理解:它表示本轮输入中有多大比例命中了缓存;具体产品仍要以它公开的指标定义为准。

它没有回答这轮请求一共要花多少时间、占多少显存,也没有说明输出每个新 token 时还要读取多少历史。因此,“缓存命中率更高”和“这一轮一定更快、更便宜”不是同一句话。

用户实际支付的费用还取决于服务商的计费规则。缓存输入通常会获得折扣,但未必免费;对话变长以后,即使命中比例上升,参与计费的缓存 token 绝对数量仍可能增加,本轮新增输入和输出也可能继续计费。判断账单时,需要同时看各类 token 的绝对数量和单价。

6.2 历史没有重新算,为什么新回答仍可能变慢?

Prompt Cache 省掉了相同历史的重复计算,但历史并没有消失。模型处理本轮新增内容、生成回答时,Attention 仍可能需要读取前面已经缓存的 K/V。

可以把差别理解成:缓存让模型不用重新整理整本会议记录,但回答新问题时,仍要带着并读取这本越来越厚的记录。对话从 1000 个 token 增长到 10000 个 token 后,新 token 能够访问的历史更长,KV Cache 占用的显存也更大。

Decode 阶段同样如此。回答每生成一个新 token,都要基于已有上下文继续预测;历史越长,每一步面对的上下文就可能越大。缓存避免的是重复 Prefill,不会把长历史的 Attention 和存储成本变成零。

所以,长对话可能同时出现两件事:大部分旧输入成功命中缓存,单轮总延迟和资源消耗仍然上升。这并不矛盾,因为一个衡量复用比例,另一个反映完整请求的总成本。

缓存命中率上升时,重复 Prefill 下降,但历史读取、显存占用和参与计费的缓存 token 绝对量仍可能上升
6.3 上下文窗口很大,为什么不等于可以把所有材料都塞进去?

一次推理中,模型能够处理的输入 token 与生成 token 的总容量叫 Context Window,中文通常称为上下文窗口;具体服务还可能单独限制最大输出 token 数。Prompt Cache 回答的则是“输入中有多少相同前缀可以少做重复计算”。

Context Window:限制一次推理中输入与输出的 token 总量
Prompt Cache:减少相同前缀的重复 Prefill

命中缓存的历史仍然属于当前上下文,仍会占用窗口。超过窗口以后,应用还是需要删除不再相关的旧消息、把长历史压缩成摘要,或者只检索当前问题真正需要的材料。缓存不能替模型扩大上下文容量。

即使某个模型支持很大的上下文窗口,也不意味着把窗口塞满一定更好。无关材料、过期结论和相互冲突的要求,可能让模型更难找到当前真正重要的信息;实际费用也要看服务商对普通输入、缓存输入和输出 token 的计费方式。

6.4 对业务使用来说,应该记住什么?

更实用的做法是让稳定、会重复使用的说明尽量保持在开头,把真正相关的历史和材料带进本轮,同时及时删掉或总结已经失去价值的内容。这样既有利于复用稳定前缀,也能减少无关历史对当前任务的干扰。评估成本时先看普通输入、缓存输入和输出 token 的绝对数量,再结合命中率与单价判断。

回到最开始的天气对话,现在可以把整条链串起来:

应用组装完整 prompt
→ 文字被切成 token,再转换成模型可以计算的向量
→ Prefill 处理已经存在的输入
→ 模型选出第一个 token,再通过 Decode 逐个生成后续 token
→ KV Cache 减少同一次回答里的重复计算
→ 下一轮由应用重新提交对话历史
→ Prompt Cache 尝试复用相同的前缀

AI 能接住“明天呢”,首先因为应用把上一轮重新带了回来;回答一点点出现,是因为模型正在逐 token 生成;长对话即使命中大量缓存仍可能变慢,是因为历史没有从上下文中消失。

模型选择只是其中一项。系统还要设计 prompt、历史、摘要和长期记忆,并分别衡量等待时间与 token 成本。先沿这条链定位,再判断是不是模型的问题。

参考来源