• 完整学习LLM(六):上下文窗口是什么,为什么模型会忘东西


    完整学习LLM(六):上下文窗口是什么,为什么模型会忘东西

    好家伙,

    上一篇我们聊了 Embedding.

    当时说到一个很关键的点:

    Embedding 负责把文本变成向量.
    RAG 负责根据语义找到相关资料.
    大模型负责基于资料组织答案.

    但这里马上就会遇到一个新问题:

    资料找到了以后,到底怎么交给大模型?

    比如我问:

    请根据这份部署文档,告诉我 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.

    但如果历史太长,系统只保留最近几轮:

    用户: 日志怎么看?
    助手: ...
    用户: 健康检查接口是什么?
    助手: ...
    用户: 那这个项目上线前还要检查什么?

    这时候模型可能只知道:

    有日志
    有健康检查
    可能是某个项目

    但它不一定知道:

    这是 battle monitor
    用了 Go API、PostgreSQL、Nginx

    所以它可能会给一个泛泛的上线检查清单.

    这就解释了为什么有时候我们会觉得:

    它前面明明知道,后面又忘了.

    实际上更准确的流程是:

    早期信息
    -> 曾经在上下文里
    -> 后来窗口滑动
    -> 早期信息被裁掉
    -> 新请求里看不到
    -> 回答变泛

    5.上下文窗口和 RAG 是什么关系

    上一篇讲 Embedding 时,我们说 RAG 大概是:

    用户提问
    -> 检索相关资料
    -> 把资料交给模型
    -> 模型基于资料回答

    现在可以把中间那步说得更准确一点:

    把资料交给模型 = 把资料塞进上下文窗口.

    所以 RAG 的效果不只取决于:

    能不能搜到资料.

    还取决于:

    搜到的资料有没有放进上下文
    放进去的位置是否合理
    放进去的资料是否太多
    有没有和用户问题放在一起
    有没有保留足够输出空间

    这也是为什么一个 RAG 系统可能会出现这种情况:

    向量数据库确实搜到了正确文档.
    但模型最后回答还是不准.

    原因可能不是 Embedding 错了.

    而是:

    正确文档被别的片段挤掉了
    正确文档埋在太长上下文中间
    提示词没有要求模型优先依据来源
    输出空间不够

    所以工程上经常会做这些事:

    只取 TopK
    对召回结果 rerank
    把长文档压缩成短摘要
    把关键来源放到问题附近
    让模型按引用回答

    这不是形式主义.

    而是在管理上下文窗口.

    6.上下文窗口越大越好吗

    我的答案是:

    大窗口很有用,但不是越大就自动越好.

    大窗口能解决一些问题.

    比如:

    能放更长的文档
    能保留更多历史对话
    能一次处理更多工具返回结果

    这当然有价值.

    但它也带来新的问题:

    输入成本更高
    响应可能更慢
    无关内容更容易混进去
    模型不一定能稳定利用所有位置的信息
    调试更难

    所以我现在更愿意把上下文窗口当成一个资源.

    不是说:

    有多少塞多少.

    而是问:

    这次回答真正需要哪些信息?
    哪些是噪音?
    哪些可以摘要?
    哪些必须原文保留?

    这和写代码有点像.

    内存大当然好.

    但你不能因为内存大,就把所有东西都塞进一个函数.

    上下文窗口也是一样.

    大窗口给了更大的空间.

    但工程上还是要管理.

    7.它和 Token、Embedding 的关系

    到这里,前面几篇文章可以串起来了.

    先是 Token:

    文本进入模型之前,要先被切成 token.

    然后是 Embedding:

    文本可以被表示成向量,用来做语义相似度比较.

    现在是上下文窗口:

    模型一次请求最多能处理多少 token.

    这三件事放在一起:

    Token 决定文本怎么被切开.
    Embedding 帮我们找到相关资料.
    上下文窗口决定这些内容最多能带进去多少.

    如果放到 RAG 里,流程就是:

    用户问题
    -> 切成 token
    -> 生成向量
    -> 检索相关资料
    -> 选择一部分资料
    -> 塞进上下文窗口
    -> 模型生成答案

    所以这篇文章其实是在回答上一篇留下的问题:

    Embedding 找到资料以后,资料怎么进入模型?

    答案就是:

    进入上下文窗口.

    8.容易踩的坑

    第一个坑:

    以为上下文窗口等于模型记忆.

    不是.

    它更像一次请求的临时工作台.

    第二个坑:

    以为窗口越大,回答一定越好.

    也不是.

    窗口大只是能放更多内容.

    但内容是否相关、位置是否合理、提示词是否清楚,仍然很重要.

    第三个坑:

    RAG 把资料全塞进去.

    这很容易让模型在一堆材料里迷路.

    应该先筛选,再重排,必要时压缩.

    第四个坑:

    忽略输出空间.

    如果输入塞满了,模型后面生成答案的空间也会受影响.

    第五个坑:

    长对话不做总结.

    长对话里,可以定期把历史压缩成摘要.

    再把摘要放进后续上下文.

    这样比机械保留所有历史更稳.

    9.这些引用到底说明了什么

    这里还是按之前的习惯,把引用说人话一点.

    OpenAI 的 Conversation State 文档

    OpenAI 的文档里会讲到,多轮对话本质上需要把历史消息作为上下文提供给模型.

    这说明一件事:

    模型能接上前文,不是因为它自动永久记住了所有对话.
    而是因为系统把相关历史作为输入的一部分传给了它.

    所以这篇文章里说的"上下文窗口是一次请求的可见范围",可以从这里理解.

    Anthropic 的 Context Windows 文档

    Anthropic 的文档会直接把 context window 解释为模型一次能处理的文本范围.

    这对初学者最有帮助的一点是:

    上下文窗口有上限.
    输入、输出、历史、资料都要在这个上限里安排.

    也就是说,窗口不是一个抽象概念.

    它会直接影响你怎么设计长文档问答、Agent、RAG 和多轮对话.

    Lost in the Middle 论文

    这篇论文讨论的是长上下文里一个很现实的问题:

    模型不一定总能稳定利用上下文中间的信息.

    它提醒我们:

    窗口变大,不代表所有位置的信息都一样容易被模型用到.

    所以在写 RAG 或长上下文应用时,不能只追求塞更多.

    还要关心信息的位置、排序和压缩.

    Attention Is All You Need

    这篇论文是 Transformer 的起点之一.

    本文引用它不是为了提前讲 Transformer 细节.

    而是为了引出下一篇:

    模型为什么能在一段上下文里看不同 token 之间的关系?

    这个问题就会走到 Transformer 和 Attention.

    10.总结

    这篇文章先记住一句话:

    上下文窗口不是长期记忆.
    它是模型一次请求里能看到和处理的 token 范围.

    所以模型会"忘东西",常见原因是:

    那段内容没有进这次请求
    内容太长导致关键信息被淹没
    用户把上下文当成了长期记忆

    放到工程里,我现在会这样理解:

    Embedding 负责找资料.
    上下文窗口负责装资料.
    Prompt 负责告诉模型怎么用资料.
    模型负责组织答案.

    上下文窗口越大越有价值.

    但它不是免死金牌.

    真正要做的是:

    把最该看的内容,放到模型这次最容易看见的位置.

    下一篇我们继续往下走:

    Transformer 为什么重要?
    为什么现在主流 LLM 基本都绕不开它?

    参考资料

    主要看它对多轮对话状态的说明:模型能接上前文,是因为历史消息被作为上下文提供给模型.

    主要看它对 context window 的直观解释:模型一次能处理的内容有范围,输入和输出都要在这个范围里安排.

    主要看它说明长上下文里的信息位置问题:上下文变长以后,模型不一定能同等稳定地使用每个位置的信息.

    这篇论文提出 Transformer 架构,下一篇聊 Transformer 和 Attention 时会正式展开.

  • 相关阅读:
    [附源码]计算机毕业设计JAVA科院垃圾分类系统
    致医生的一封信:感谢“医”路有你,天天好心情
    【C语言学习者必会的题目集锦1】巩固基础,稳步提高
    python使用unittest进行单元测试
    为何过去因为穷经常吃咸菜,却很少得高血压、癌症?真相来了
    【AcWing16】【LeetCode】并查集Union Find-128/130/*1020-学完广度优先/深度优先要回来再看
    【Spring面试】Spring事务(图文详解)
    Thinkpad T14升级Windows11ver22h2失败问题解决小记
    3428. 放苹果
    [架构之路-231]:计算机硬件与体系结构 - 性能评估汇总,性能优化加速比
  • 原文地址:https://www.cnblogs.com/FatTiger4399/p/20191880