哈喽大家好,我是欧呦,这篇文章将为大家分享大模型的底层运行原理——不推数学公式,直接用大白话把平时经常听到的 Token、注意力机制(Attention)和 KV Cache 讲明白。
很多朋友在用 Claude Code、Codex 或网页版 AI 的时候,经常会遇到几个让人困惑的现象:
为什么在同一个聊天窗口里聊得越久,AI 就越容易忘掉最开始定好的要求、改代码的质量也越来越差?为什么前面几篇教程里总在强调「要用 Skill 按需加载」「要用子 Agent 隔离上下文」「千万别随便改动系统提示词的开头」?
其实这些现象和使用技巧,背后全都由大模型的两个底层机制决定:一个是注意力机制,另一个是 KV Cache(键值缓存)。把这两个概念的直觉搞懂,你以后不管是用 AI Agent 写代码,还是自己设计 AI 产品,心里都会有一本明白账。
大模型是怎么看懂文字并回答你的?
在聊注意力机制和缓存之前,我们先花两分钟,把「你在输入框里发出一句话,到大模型吐出回答」这中间最基础的三件事理清楚。
1. 模型眼里没有整句话,只有切碎的词块(Token)
当我们把一段中文、英文或者代码发给大模型时,模型并不是像人眼一样直接认字的,它会先把这段话切成一小块一小块的碎片,每一小块就叫一个 Token(词块)。
一个 Token 可能是一个常见的英文单词、半个长单词,或者一两个汉字。模型读入内容、计算关系、输出回答,全都是以 Token 为最小单位来进行的;平时我们说的「上下文窗口有 200K」「这轮消耗了多少额度」,指的也是 Token 的数量。
2. 大模型本身是「没有记忆」的(无状态)
平时我们在聊天窗口里跟 AI 一来一回聊了十几个回合,感觉它好像一直记着我们刚才说过的话。但实际上,大模型本身是完全「无状态(Stateless)」的——它每回答完你一句话,脑子就会立刻清空,根本不记得上一秒发生过什么。
那为什么第 10 轮对话时,它还能接上你第 1 轮提的需求?
原因在于你手里的客户端软件(比如网页聊天框、Claude Code 或 Codex)在背后替你做了打包:当你发送第 10 轮的那句话时,客户端会把第 1 轮到第 9 轮你们聊过的所有对话、AI 读过的所有文件内容、工具返回的全部日志,再加上你刚写的第 10 句话,拼成一份长长的完整文本,从头到尾重新发给大模型。
所以,对话轮次越多,每次发给模型的文本包就越庞大。
3. 回答不是一口气想好的,而是「一个字一个字往下接龙」
很多人以为大模型收到问题后,是在肚子里先把整篇回答想好,再像打字机一样展示出来。其实不是。
大模型生成回答的唯一方式,叫做单字接龙(学术上叫自回归生成)。它每次只做一件事情:看完前面所有的 Token,预测「下一个最应该接什么 Token」。
比如你问它「法国的首都是哪里」,它看完问题后先算一次,猜出第 1 个字是「巴」;接着它把「巴」字拼到原问题的末尾,变成「法国的首都是哪里 巴」,再拿这整串内容去猜下一个字是「黎」。像这样每猜出一个词块,就把它追加到末尾充当新的前文,一步步循环往后接,直到拼出代表结束的标记为止。
什么是「注意力机制」?为什么上下文越长 AI 越容易忘事?
既然大模型只做「看前文、猜下一个字」的接龙,那当它面对前面成千上万个词块时,它是怎么搞清楚上下文里的指代关系和重点的?靠的就是大名鼎鼎的 注意力机制(Self-Attention,自注意力机制)。
1. 用一句话看懂注意力机制在干什么
我们来看这样一句话:
- 「小狗跑得气喘吁吁也没追上那只猫,因为它实在太累了。」
人类一眼就能看出来,这句话里的「它」指的是前面跑得气喘吁吁的小狗,而不是猫。但对于从左往右读文章的大模型来说,「它」和「累」跟前面的「小狗」中间隔了好几个词,模型怎么知道到底是谁累了呢?
当模型从左往右读到「它」和后面的「太累了」时,注意力机制就会像一束聚光灯一样,带着当前词的线索回头去扫视前面的每一个词块,计算当前词和前面每个词之间的关联紧密度。模型一比对,发现前面「跑得气喘吁吁」的「小狗」关联分最高,而跟「没」「那只」这些词几乎没关系——于是模型就把绝大部分注意力集中在「小狗」身上,把「小狗」的含义吸取过来。
2. 三张小纸条讲明白 Query、Key 和 Value
如果你去翻看任何讲大模型底层的资料,一定会看到 Q(Query)、K(Key)、V(Value) 这三个字母。不用被名字吓到,它们其实就是每个词块随身携带的「三张小纸条」,流程非常像我们在资料室里找档案:
- 查询条(Query · 我想找什么线索):当前正在处理的这个词块,举着一张写明自己需求的查询条。比如模型读到「它」或「累」时举着查询条:「我想在前面找一个刚刚发出了剧烈奔跑动作的动物主语。」
- 特征牌(Key · 我身上有什么标签):排在前面的每一个词块,胸前都挂着一块结合前文语境算出来的特征牌。比如前面的「小狗」挂着特征牌:「我是名词、是动物、是这句里跑得气喘吁吁的主语。」
- 信息包(Value · 我实际能提供什么内容):每个词块手里提着一个融合了前文语境、装有自己真实语义的信息包。
当模型要理解当前词块时,就会拿它的 查询条(Query),去跟前面所有词块的 特征牌(Key) 挨个碰一下、算一个匹配度分数;算完一圈之后,谁的匹配分高,就给谁分配更高比例的注意力,最后按这个比例把前面各个词块的 信息包(Value) 融合起来。
3. 为什么同一个窗口聊久了,AI 一定会变笨?
搞懂了这个「挨个比对打分、按比例吸取信息」的过程,你就立刻明白为什么聊天窗口里的内容一多,AI 就会开始忘事、漏掉细节甚至胡改代码了。
核心原因在于:模型每走一步,分给前面所有词块的注意力比例加起来,永远只有 100%。
当你的对话刚刚开始、窗口里只有一两千字时,你交代的核心需求、代码规范和边界条件目标非常显眼,能稳稳分到很大一部分注意力。
可如果你在同一个窗口里从早干到晚,让 Agent 翻看了三十几个源码文件、塞进了好几万行的终端报错日志,上下文一下子膨胀到了几万甚至十几万个 Token。这时候,模型每生成下一个字,都要拿当前的查询条去跟前面几万个词块的特征牌挨个比对。
那总共 100% 的宝贵注意力,不可避免地会被满屏已经过时的旧日志、无关的代码片段硬生生摊薄。最开始你千叮咛万嘱咐的那两句关键要求,分到的注意力权重可能已经跌到了千分之一以下——这就是为什么哪怕模型的上下文窗口上限标着 200K 甚至 1M,只要塞进太多杂音,它的实际表现依然会肉眼可见地变笨。
什么是 KV Cache?为什么改了开头一个字,速度和费用会差十倍?
看完了注意力机制,细心的朋友马上会发现一个要命的效率问题。
前面我们讲过:大模型是「一个字一个字往下接龙」的,而且每接一个字,都要拿当前字的 Query 去和前面所有字的 Key(特征牌) 与 Value(信息包) 做比对。
假设你给大模型发了一篇 1 万字的文档,让它写一段 200 字的总结:
- 它蹦出第 1 个字时,需要把前面 1 万个字的
Key和Value全算一遍; - 紧接着它要蹦第 2 个字了。这时候,前面那 1 万个字明明一个字都没有变,如果模型傻乎乎地把那 1 万个字的
Key和Value又从头到尾重算一遍,那生成 200 个字,岂不是要把这 1 万字长文重复算上 200 遍?
如果真这么干,每往后蹦一个字就会越来越慢,显卡算力全浪费在毫无意义的重复计算上了。
1. KV Cache(键值缓存):把算过的中间结果存进显存,绝不重算
为了解决这个重复计算的问题,工程师利用了大模型的一个天然特性:大模型读文章是从前往后单向看的。
也就是说,第 100 个词算出来的 Key(特征牌) 和 Value(信息包) 本身融合了前 99 个词的语境,但后面新蹦出来的第 101 个词,绝对不会反过来改变前面 100 个词已经算好的 Key 和 Value。
既然前面算过的 Key 和 Value 永远不会变,那我们每算完一个词,干脆就把它算出来的 K 和 V 直接保存在显卡的内存(显存)里——这个保存在显存里的历史账本,就叫做 KV Cache(键值缓存)。
有了 KV Cache 之后,当模型要蹦出第 101 个字时,前面那 1 万个字根本不需要再算了,模型直接从显存里拿现成的 KV Cache 过来比对,全身心只需要计算最新这 1 个字自己的 Q、K、V 就行。这也是为什么你用 AI 时,刚按下回车等待第一个字出来会稍微停顿一下(这时模型正在一口气计算你输入全文的 KV Cache,叫预填充 Prefill),一旦第一个字蹦出来之后,后面的字就能哗哗地飞快往外吐(叫解码生成 Decode)。
2. 从单次生成到跨请求复用:什么是「提示词缓存(Prompt Caching)」?
原来的 KV Cache 只在「这一次回答问题」的过程中生效,回答一结束就扔掉了。但在 AI Agent 时代,情况发生了很大变化。
当你在用 Claude Code 或 Codex 写代码时,你交代一个任务,Agent 往往会在后台连续调用二三十次工具(读文件、搜代码、改代码、跑测试)。前面我们说过,大模型是无状态的,所以 Agent 每调完一次工具,都要把「系统提示词 + 工具定义列表 + 项目规则文件 + 之前的全部对话和工具记录 + 最新这一步的结果」打包成一次全新的请求发给模型。
你会发现,在连续几十次请求里,前面 90% 以上的文本内容完全一模一样,仅仅是在最末尾追加了一小段新内容。
如果每次请求都让服务器把前面几万字一模一样的内容重新算一遍 KV Cache,不仅慢,而且极度浪费算力。于是 OpenAI、Anthropic、DeepSeek 等厂商纷纷把 KV Cache 升级成了跨请求的 提示词缓存(Prompt Caching):
服务器算完你这次请求的 KV Cache 后,不会立刻删掉,而是替你在高速缓存里保留几分钟(甚至更久)。当你紧接着发出下一次请求时,只要前面那一大段内容跟上次完全一致,服务器就直接把存好的 KV Cache 拿出来用——这部分命中的内容不仅不用重新计算、让等待时间大幅缩短,而且各家厂商通常都会直接给这部分 Token 打 1 折(省下高达 90% 的费用)!
3. 为什么「开头改一个字,后面几万字的缓存全作废」?
这也是很多开发者自己写 Agent 或配提示词时最容易踩的大坑:为什么提示词缓存要求严苛的「精确前缀匹配(Exact Prefix Match)」?
比如有的人为了省事,在系统提示词的最开头写了一句:当前系统时间:2026-10-30 14:20:05。结果每次发请求时,这一秒钟的时间变了,后面哪怕跟着 5 万字完全没变的代码和文档,提示词缓存也会 100% 失效。
懂了前面的原理,原因其实一想就通:
因为大模型是从前往后读的,后面每一个词块算出来的 KV Cache,都把排在它前面所有词块的信息融进去了。一旦排在最开头的第 5 个词变了,那从第 6 个词一直到第 50,000 个词,它们眼里的「前文语境」就全都发生了偏移,之前保存在显存里的 KV Cache 在数学上全部作废,服务器只能从你改动的那个位置开始,把后面几万字老老实实重算一遍。
懂了这两个底层原理,平时怎么用好 AI Agent?
把「注意力机制的总量固定为 100%」和「KV Cache 依赖从前往后的前缀一致」这两件事装进脑子里,回过头再看我们日常使用 AI Agent 的各种技巧,你就会发现所有最佳实践全都是顺着这两个底层脾气来设计的:
1. 用 Skill 做「按需渐进式加载」,别把所有规范全塞进全局文件
如果你把几万字的 UI 规范、文案规范、发布流程全写进每次对话默认必读的 AGENTS.md 或 CLAUDE.md 里,哪怕你今天只是想改一行简单的逻辑,这几万字也会白白分走模型的注意力,让它更容易漏掉你的核心指令。
把专项知识拆成独立的 Skill(详见 《从 0 到 1 了解什么是 Skill》),平时只让模型看两行简短的目录描述,真正用到时再去读对应的详细文档,本质上就是在帮模型守住那 100% 的注意力预算。
2. 产生大量中间噪音的脏活累活,交给「子 Agent」在独立窗口干
像「去几十个文件里搜某段逻辑写在哪」「分析几千行的报错日志」这类任务,中间过程会塞进海量文本,但最后只需要几句话的结论。
如果全在主窗口里干,这几万字的过程垃圾就会一直留在主窗口的历史记录里,此后你每发一句话,它们都在持续稀释主 Agent 的注意力、持续消耗显存与额度。派一个便宜模型的子 Agent 去独立窗口里把这些文件读完,只把几百字的干净结论带回主窗口(详见 《怎样用好子 Agent》),主窗口的上下文就能一直保持清爽。
3. 完成一个独立任务后及时开新话题,或用 /compact 压缩历史
不要把一个聊天窗口当成永久备忘录从早聊到晚。当这一个功能模块已经开发完、或者前面踩过的几个报错已经彻底修好之后,前面那几十轮拉扯的历史对下一个任务来说就已经变成干扰噪音了。
这时候果断新开一个干净的话题(或者在长任务中途使用 /compact 把冗长的工具调用历史浓缩成简短摘要),既能让模型的注意力重新聚焦在新任务上,也能避免每次发消息都背着几万字的历史包袱。虽然执行 /compact 替换历史的那一刻会重置一次前缀缓存,但比起后面几十轮一直背着几万字垃圾既分散注意力又按轮计费,做一次清理依然划算得多。
4. 遵循「静态内容焊死在开头,动态变化永远追加在末尾」
无论你是在配置项目的规则文件,还是在开发自己的 Agent 工具,都要牢记 KV Cache 的前缀匹配脾气:
- 把固定不变的系统指令、工具列表和核心项目规范放在最开头,对话中途尽量不要去增删工具或改动前面的内容;
- 像当前时间、最新状态或新一轮的工具输出这类会变化的信息,永远只往消息列表的最末尾追加。
只要保住前面的长前缀纹丝不动,你的 Agent 在多轮连续执行时就能一直稳稳命中 KV Cache——响应速度最快,花掉的 Token 账单也最便宜。
