编程基础篇 · 线性结构:你天天在用
数组:你聊的每句话都躺在里面
上一课说好了:数据结构 = 收纳方式。第一种收纳方式你其实天天在喂它数据——你和 AI 的每一段对话,在程序眼里就是一个 message list,而 message list 的真身,是最朴素的收纳方式:一排编了号的格子,叫数组。这一课先把你的对话摊开来看,再顺手回答一个老疑问:为什么聊久了它会忘事。
先玩 · 你的对话正躺在一排格子里
下面就是一个 message list:每条消息占一个格子,格子上方是它的索引(编号,从 0 开始数——程序员的老习惯)。点「发一条消息」,看新消息落在哪;再拖「上下文窗口」滑块,留意哪些格子变灰了、哪个格子永远不灰。
system(📌 钉住)
user(你)
assistant(AI)
底下有蓝条 = 在上下文窗口里
这就是「聊久了忘事」的全部真相。模型一次能读的内容有上限(上下文窗口),装不下时就得扔。扔哪头?掐头不掐尾——最早的闲聊先扔,最近几句必须留着,不然它连你刚说什么都不知道。唯独
[0] 号格子的 system 提示词被钉住了:它是人设和规矩(「你是一个贴心的助理」),扔了它,AI 就忘了自己是谁。所以「忘事」不是玄学,就是数组切片:留下 system + 最近 K 条,其余不再送进模型。
第二局 · 往中间插一条,有多贵?
数组的格子在内存里是紧挨着排的,中间不许有空位。这带来一个麻烦:想往中间塞一个新元素,右边的所有元素都得挨个往右搬一格给它腾地方。点任意一个格子,在它的位置插入一颗 ⭐,盯着「搬动次数」;再点「在末尾追加」对比一下。
👇 点格子 = 在这个位置插入 ⭐(越靠左,右边要搬家的越多)
搬动次数0次
先点一个靠左的格子试试,比如 [1]
看出规律了吗?末尾追加永远只要 1 步,中间插入要搬动一大片——格子越多、插得越靠前,搬得越狠。这就是为什么对话历史被设计成只往后长(append-only):每条新消息 push 到末尾,谁也不搬家,快且省。你从来没见过哪个聊天软件让你「把一句话插到十分钟前」,不是产品经理没想到,是这么干真的贵。
数组的看家本领(和它的软肋)
看家本领:按编号直达
想拿第 3 条消息?messages[3],不用从头数,一步到位。因为格子紧挨着排,编号本身就是地址——这叫 O(1),翻译成人话是「不管数组多长,耗时都一样」。按位置取数据,数组是所有收纳方式里最快的,没有之一。
软肋:中间插入贵
你刚才亲手搬过了。顺带认识一个亲戚:链表——它中间插入很便宜(改两根「下一个是谁」的指针就行),代价是失去了按编号直达,找第 100 个得从头挨个走。没有全能的收纳方式,只有取舍。对话这种「只追加、常整段读」的场景,数组完胜,所以 message list 用它。
✅ 这一课想和你分享的
- message list 就是数组:一排编了号的格子,每条消息躺一格,索引从 0 开始
- 索引直达 O(1):按位置取数据,数组是最快的收纳方式
- 上下文截断 = 数组切片:留 system + 最近 K 条,「掐头不掐尾」,这就是聊久了忘事的真相
- 中间插入贵、末尾追加便宜:所以对话历史只往后长(append-only)
- 收纳方式都是取舍:链表中间插得快但没了直达——场景决定选择