请根据这份部署文档,告诉我 battle monitor 怎么上线.
RAG 检索到了 5 段资料.
历史对话里还有我前面问过的问题.
系统提示词里还写着回答规则.
这些东西最后都要放到哪里?
答案就是:
放进上下文窗口.
所以今天这篇就专门聊一个很基础,但很容易误解的概念:
上下文窗口是什么?
为什么模型会忘东西?
为什么不是窗口越大就一定越好?
0.背景:为什么要学上下文窗口
我最开始用大模型时,对它的感觉是:
它好像能记住我刚才说过什么.
比如我先说:
我现在在写一篇关于 RAG 的文章.
然后我再问:
帮我把刚才那个主题换一个标题.
模型通常能知道"刚才那个主题"指的是 RAG.
这就很像记忆.
但用久了以后会发现:
聊太长,它会忘.
资料太多,它会漏.
前面明明说过,后面它却像没看见.
这个时候就不能只说一句:
模型记性不好.
更准确的说法是:
这次请求里,模型能看到的内容是有限的.
这个有限范围,就是上下文窗口.
1.先给一个直觉:上下文窗口就是这次请求的可见范围
我现在对上下文窗口的理解是:
上下文窗口 = 模型在一次请求里能够看到和处理的 token 范围.
注意这里有两个关键词:
一次请求
token 范围
也就是说,模型不是把我们所有聊天内容都永久放在脑子里.
每次调用模型时,系统会把一批内容打包给它.
这批内容可能包括:
系统提示词
开发者提示词
历史对话
用户当前问题
RAG 检索出来的资料
工具调用结果
预留给模型输出的空间
这些内容加起来,不能超过模型允许处理的上下文范围.
可以先看这张图:
这张图想表达的是:
对话和资料是一条很长的流.
上下文窗口只覆盖其中一段.
新问题和新资料进来时,太早的内容可能会离开窗口.
所以模型"忘了",很多时候不是它真的产生了人类意义上的遗忘.
而是:
那段内容没有被放进这一次请求里.
2.上下文窗口里到底装了什么
很多人会把上下文窗口理解成:
聊天记录长度.
这不算错,但不完整.
上下文窗口不只是聊天记录.
它更像一次请求的总预算.
里面装的东西可能很多:
系统规则
-> 你要怎么回答,哪些不能做
历史对话
-> 用户前面问过什么,模型前面答过什么
当前问题
-> 用户这一次真正要问什么
检索资料
-> RAG 找回来的文档片段
工具结果
-> 数据库查询结果,接口返回内容,代码执行结果
输出空间
-> 模型接下来要生成答案的位置
这些东西都要占 token.
所以一个很现实的问题是:
窗口不是只给输入用的.
输出也要占空间.
比如一个模型的上下文窗口最多能处理一批 token.
如果输入已经塞得很满,那留给输出的空间就会变少.
反过来,如果希望模型输出很长,输入就不能无限塞.
这就是为什么我说上下文窗口更像预算:
这张图里最重要的是第二条:
RAG 资料塞太多时,模型不一定更聪明.
因为塞进去的内容越多,模型越需要在里面找重点.
如果资料里混进了很多无关内容,模型反而可能被干扰.
这也是为什么 RAG 系统不能只做一件事:
把所有搜索结果都扔给模型.
它通常还需要:
召回
重排
过滤低分片段
压缩摘要
控制 TopK
说白了:
上下文窗口有限,所以要把最有价值的内容放进去.
3.为什么模型会忘东西
这里可以拆成三种情况.
第一种:内容根本没进这次请求
这是最常见的.
比如我前面很早说过:
我的项目叫 Lighter.
但后面聊了很长.
如果系统为了控制上下文长度,把早期对话裁掉了.
那模型这次请求里就看不到这句话.
它自然可能不知道 Lighter 是什么.
这不是参数忘了.
而是:
这次输入里没有这条信息.
第二种:内容进来了,但太靠前或太分散
有些时候,内容确实在上下文里.
但上下文太长.
关键信息埋在中间.
模型可能没有稳定抓住它.
这就涉及一个很有名的问题:
Lost in the Middle
简单说就是:
长上下文里,模型不一定能同等有效地利用每个位置的信息.
有些信息放在开头或结尾更容易被用到.
埋在中间时,模型可能更容易漏掉.
所以长上下文不是万能药.
窗口变大以后,还要考虑:
哪些内容放前面
哪些内容放后面
哪些内容要压缩
哪些内容要重复强调
第三种:用户以为它是长期记忆
还有一种误解是:
我告诉过模型一次,它以后就应该一直记得.
但大多数普通调用里,模型并不会因为一次聊天就改写自己的参数.
它能利用的通常是:
这次请求里的上下文
如果系统有记忆功能,那也是另外一层工程能力.
比如:
把用户偏好保存到数据库
下次聊天前再取出来
重新塞进上下文
这和模型参数本身记住不是一回事.
可以用这张图区分:
这张图里我想强调一句话:
上下文窗口是临时工作台.
外部资料是资料库.
模型参数是训练时形成的能力.
这三件事不要混成一个东西.
4.用一个例子理解:为什么它前面知道,后面又不知道
假设我们在写一个后端部署文档问答助手.
一开始用户说:
我的项目是 battle monitor.
它由 Go API、PostgreSQL、Nginx 组成.
然后用户问了很多问题:
数据库怎么配置?
Nginx 怎么反代?
Docker Compose 怎么写?
日志怎么看?
健康检查接口是什么?
聊到后面,用户突然问:
那这个项目上线前还要检查什么?
如果系统把所有历史都完整塞进上下文,模型大概率知道"这个项目"指 battle monitor.
但如果历史太长,系统只保留最近几轮:
用户: 日志怎么看?
助手: ...
用户: 健康检查接口是什么?
助手: ...
用户: 那这个项目上线前还要检查什么?
这时候模型可能只知道:
有日志
有健康检查
可能是某个项目
但它不一定知道: