:长上下文与上下文工程——从位置外推到分块策略的完整考点)
27届大模型岗面试准备十三长上下文与上下文工程——从位置外推到分块策略的完整考点写在前面上一篇十二我们把 RAG 的完整链路走了一遍其中分块环节埋了一个问题为什么非要把文档切碎直接把整本手册塞进模型不行吗2026 年的模型动辄标称 128K、1M 上下文看起来塞进去已经不是问题。但面试官如果问你上下文窗口 1M 了RAG 是不是可以退休了你要是答是这场面试基本就结束了。长上下文Long Context和上下文工程Context Engineering是这两年面试权重涨得最快的话题之一。它横跨三个层面模型层位置编码怎么外推、系统层KV Cache 怎么扛住百万 token、应用层上下文预算怎么分配、长文本怎么分块。本文按这三层展开最后给一段可直接运行的长文本分块代码。一、先想清楚长上下文到底难在哪很多同学的第一反应是显存不够。对但不全对。长上下文的困难是三个维度叠加的1. 计算与显存的平方/线性增长。自回归注意力对序列长度 n 是 O(n²) 计算KV Cache 是 O(n) 显存。第十篇我们算过一个 7B 模型、4096 序列的 KV Cache 约 2GB把序列拉到 128K单条请求的 KV Cache 就奔着 64GB 去了——比模型权重还大好几倍。2. 位置编码的外推失效。模型在 4K 长度上训练RoPE 的旋转角度只见过 4K 以内的组合。推理时直接跑 32K模型看到的是从没见过的相对位置注意力分布直接崩掉。这就是为什么支持 128K从来不是改一个配置就行的。3. 有效利用率的衰减。这是最容易被忽略、也最容易在面试中出彩的一点模型能读完不代表能用好。Lost in the Middle 现象——关键信息放在长上下文中间位置时召回准确率显著低于放在开头或结尾——说明上下文窗口的标称长度和有效长度是两回事。把这三点讲清楚面试官就知道你不是只会背某某模型支持 1M。二、模型层位置外推的技术谱系第三篇我们讲过 RoPE 的数学原理这里接着往下讲怎么把 4K 训练的模型拉长用。核心思路只有两条路让新位置看起来像旧位置内插或者让模型适应新位置继续训练/调整频率。方法核心思想是否需要微调效果特点代表应用直接外推什么都不做硬跑否超过训练长度后 PPL 爆炸反面教材Position Interpolation (PI)把位置索引线性压缩回训练范围少量微调均匀压缩短程分辨率受损LLaMA 长上下文早期方案NTK-aware 插值高频维度少压、低频维度多压可免微调兼顾短程精度与长程覆盖各开源社区免训练拉长YaRNNTK 分段插值 注意力温度缩放少量微调目前开源侧主流效率高Qwen、DeepSeek 系列ALiBi干脆不用位置嵌入用线性偏置惩罚远距离天然外推外推强但长程建模上限受争议BLOOM、MPT双阶段长训先短后长、调大 RoPE base 继续预训练大量训练效果最好成本最高各家官方长上下文版本面试高频追问NTK-aware 为什么高频少压、低频多压答题抓手RoPE 不同维度的旋转频率不同——高频维度负责编码相邻 token 的精细位置关系压缩它会伤近距离分辨率低频维度负责远距离的粗粒度关系本来就转得慢压缩空间大。这个按频段区别对待的思想借自 NTK神经正切核对高频学习的分析故得名。三、系统层百万 token 的工程账模型能外推了系统还得扛得住。这一层的考点和第十篇推理加速强关联可以串联作答KV Cache 压缩MQA/GQA 从头数上砍第四篇讲过量化 KV Cache 从精度上砍INT8 KV 已是长上下文标配滑动窗口注意力从范围上砍。稀疏注意力不是每个 token 都要看全部历史。StreamingLLM 发现注意力汇聚attention sink现象——保留开头几个 token 最近窗口就能维持流式生成不崩。Prefix Caching多轮对话/多请求共享的长前缀如系统提示、长文档只算一次 KVvLLM 的 RadixAttention 用前缀树管理。这一点在 Agentic 场景尤其重要——B10 讲 Agentic RAG 时提过Agent 每轮都带着几乎相同的长前缀。Chunked Prefill128K 的 prefill 一口气算完会把 decode 请求全部饿死切成小块和 decode 交错调度平衡 TTFT 和 TPOT。一句话总结给面试官长上下文的系统优化本质是围绕 KV Cache 的省压缩、复用前缀缓存、调度chunked prefill三件事。四、应用层上下文工程——把窗口当预算来管理2026 年Prompt 工程这个词在工程圈已经明显让位给上下文工程。区别在哪Prompt 工程琢磨指令怎么写上下文工程琢磨这有限的窗口里到底该放什么、放多少、按什么顺序放。它把上下文当成一种要精打细算的稀缺资源像操作系统管理内存一样管理它。上下文的典型构成与预算分配思路组成部分内容预算特点管理策略系统指令角色、规则、输出格式固定开销常驻尽量精简可用 prefix caching工具定义function schema随工具数线性涨按任务动态挑选工具子集检索内容RAG 召回的文档块弹性最大top-k 截断、重排后按分数分配历史对话/轨迹多轮消息、Agent scratchpad随轮数膨胀滑动窗口 递归摘要B6 讲过少样本示例few-shot 演示边际收益递减强模型可减弱模型保留 2–3 个用户输入当前问题不可压缩保持原样这张表背后的三条工程原则值得在面试里主动说出来相关性优先于完整性塞进 10 万 token 的可能相关不如 5 千 token 的高度相关Lost in the Middle 会惩罚前者。位置即权重关键信息放开头或结尾检索结果按重要性做两头高、中间低的排布是常见 trick。压缩是常态操作摘要、去重、截断不是退而求其次而是常规手段——窗口再大注意力预算和成本预算也是有限的。长上下文 vs RAG 不是二选一。标准答法长上下文解决单次能看多少RAG 解决从海量知识里挑什么给它看。知识库是 TB 级的1M 窗口也塞不下而且全量塞入的推理成本prefill 计算 KV 显存比检索后精准投喂高一到两个数量级。真实系统是RAG 负责粗筛 长上下文负责细读的配合关系。五、代码实战三种长文本分块策略的实现与对比分块chunking是上下文工程最基础也最容易被问你实际怎么做的环节。下面用纯标准库实现三种策略固定长度切分、句子边界切分、滑动窗口重叠切分并对比它们在语义完整性上的差异。可直接python chunking.py运行。# -*- coding: utf-8 -*- 三种长文本分块策略固定长度 / 句子边界 / 滑动窗口重叠纯标准库可运行 import re def chunk_fixed(text: str, size: int 100) - list[str]: 策略1固定长度硬切。实现最简单但会把句子拦腰斩断。 return [text[i:i size] for i in range(0, len(text), size)] def chunk_by_sentence(text: str, max_size: int 100) - list[str]: 策略2先按句子边界切再贪心合并到不超过 max_size。 保证每个 chunk 都由完整句子组成语义不被截断。 sentences [s for s in re.split(r(?[。\n]), text) if s.strip()] chunks, buf [], for sent in sentences: if len(buf) len(sent) max_size: buf sent else: if buf: chunks.append(buf) # 单句超长时退化为硬切 buf sent if len(sent) max_size else if len(sent) max_size: chunks.extend(chunk_fixed(sent, max_size)) if buf: chunks.append(buf) return chunks def chunk_sliding(text: str, size: int 100, overlap: int 20) - list[str]: 策略3滑动窗口重叠切分。相邻 chunk 共享 overlap 字符 缓解答案恰好跨在切点上的边界丢失问题代价是索引膨胀。 assert 0 overlap size, overlap 必须小于 size step size - overlap return [text[i:i size] for i in range(0, max(len(text) - overlap, 1), step)] def boundary_break_rate(chunks: list[str]) - float: 度量不以句末标点结尾的 chunk 占比越低说明语义越完整。 bad sum(1 for c in chunks if c and c[-1] not in 。\n) return bad / max(len(chunks), 1) if __name__ __main__: doc ( 长上下文不等于无限上下文。窗口越大注意力越稀释成本越高。 上下文工程的目标是让每个 token 都物有所值。分块是其中最基础的操作。 固定切分实现简单却常把句子拦腰斩断导致检索命中后语义残缺。 句子边界切分尊重语义单元是多数生产系统的默认选择。 滑动窗口用冗余换鲁棒适合答案常跨越切点的问答场景。 实践中还会叠加父子分块小块负责精准检索大块负责提供上下文。 ) * 3 strategies { 固定长度: chunk_fixed(doc, 100), 句子边界: chunk_by_sentence(doc, 100), 滑动窗口: chunk_sliding(doc, 100, 20), } print(f原文长度: {len(doc)} 字\n) print(f{策略:8}{块数:4}{平均块长:8}{断句率:8}) for name, chunks in strategies.items(): avg sum(map(len, chunks)) / len(chunks) print(f{name:8}{len(chunks):4}{avg:8.1f}{boundary_break_rate(chunks):8.0%}) print(\n句子边界切分示例第1块:) print( , strategies[句子边界][0])运行后能直观看到固定长度切分的断句率接近 100%几乎每块都切断句子句子边界切分为 0%滑动窗口介于两者之间但对跨切点信息更鲁棒。面试时如果能主动补一句生产里我会在句子切分之上再做父子分块——检索用小块保精度喂给模型用父块保上下文就把 A12 的内容也串起来了。六、高频面试题与答题要点上下文窗口 1MRAG 还有必要吗—— 有。知识库规模 窗口全量塞入成本高一到两个数量级Lost in the Middle 让有效利用率打折。RAG 与长上下文是粗筛与细读的配合。模型标称 128K怎么验证它真的可用—— 大海捞针NIAH测不同深度的召回率但要指出 NIAH 偏简单进阶用 RULER/LongBench 这类含多跳、聚合任务的基准同时压测 128K 下的 TTFT 和显存。YaRN 和 PI 的区别—— PI 均匀线性内插伤高频YaRN 按维度频率分段处理 注意力温度补偿同等微调量下长短程质量更均衡。Agent 跑几十轮后上下文爆了怎么办—— 滑动窗口 递归摘要 关键事实外置到长期记忆向量库即 B6 的三层记忆架构工具返回值做截断与结构化压缩。小结长上下文是模型外推 系统扛量 应用会用三层问题的交集位置编码决定能不能读KV Cache 工程决定扛不扛得住上下文工程决定用得好不好。把标称长度 ≠ 有效长度和窗口是预算不是仓库这两个观念讲透这个话题你就立住了。下一篇十四进入多模态大模型 VLM——当上下文里不只有文字还有图像时问题又升了一个维度。