
先在开发中遇到一个很实际的痛点大模型LLM的上下文窗口是有限的但真实业务里的对话记录、项目代码、日志文本却可以无限增长。早期我习惯把重要内容全部塞进 Prompt 里结果要么因为 Token 超限报错要么因为上下文过长得不偿失模型反而“记不住”重点了。最近在研究上下文管理方案时看到了阿里开源或内部讨论较多的“Scroll”思路——让模型通过生成代码、调用工具、按需读写上下文而不是单纯依赖 Prompt 里堆文字。这是一个把“上下文管理”从人工设计 Prompt 升级为“交给程序化调度”的方向。本文不打算只做概念搬运而是围绕“模型写代码管理上下文”这个主题拆解核心原理、对比主流方案并给出一个可运行的 Python 原型示例。无论你是做大模型应用开发、Agent 设计还是想优化 Prompt 工程这篇内容都值得收藏。1. 背景为什么大模型也需要“上下文管理”1.1 上下文窗口并不是越大越好先来看一个基础概念。大模型的上下文窗口Context Window指的是模型一次能接收和处理的 Token 数量范围比如 8K、32K、128K。窗口越大能输入的信息越多表面上看起来“记忆力”越强。但在实际项目中把上下文窗口做大只是第一步真正难的是如何组织窗口里的内容信息过载把所有历史对话、代码、文档全部塞进 Prompt模型面对大量无关内容时会稀释关键指令的权重生成的回答质量反而下降。Token 成本陡增大模型大多按 Token 计费上下文越长单次请求成本越高。如果每轮对话都把历史全量携带费用增长非常夸张。首字延迟变大输入 Token 越多模型预填充Prefill的时间就越长用户等待第一个字的时间明显增加。无效遗忘即便窗口“装得下”模型对中间位置内容的注意力通常也会偏弱这就是常见的“lost in the middle”问题。所以上下文管理并不是“把窗口开大”这么简单而是要考虑哪些内容值得放入上下文什么时候放入以什么形式放入旧信息是压缩、丢弃还是归档1.2 一个典型的长上下文场景假设你在做一个代码辅助 Agent用户会持续提问用户帮我看看 order_service.py 这个文件里的 bug 模型好的我分析了 345 行代码发现第 120 行可能有问题…… 用户那 payment_service.py 呢 模型…… 用户刚才说的 order_service 第 120 行如果改成异步会怎样可以看到第二轮用户问的是新文件第三轮又绕回旧文件。如果 Agent 只保留最近一轮对话就丢失了 order_service 的关键背景如果全量保留所有文件内容又会产生大量冗余 Token。这种情况下最简单的解决办法是让模型生成一段代码或一个查询指令按需从“知识存储区”读取 order_service.py 中第 120 行相关的代码片段同时把历史分析结论压缩成结构化摘要。这其实就是 Scroll 这类方案的核心价值——把上下文管理从“人工拼 Prompt”变成“模型主动用代码调度”。2. 主流上下文管理方案对比2.1 方案一滑动窗口截断滑动窗口是最简单、也最常用的上下文管理方式。系统只保留最近 N 条消息超出范围的历史内容直接丢弃。消息序列M1 → M2 → M3 → M4 → M5 窗口大小为 3 时模型只看到M3 → M4 → M5这种方案的优点是实现简单、Token 成本稳定缺点是会丢失早期关键信息比如用户一开始提的需求背景。2.2 方案二摘要压缩摘要压缩是指定期把早期对话交给 LLM 生成一段总结再用这段总结代替原始长文本。M1 → M2 → M3 → M4 → M5 压缩后Summary[1-3] → M4 → M5摘要方案的优点是对长对话场景友好缺点是压缩过程会丢失细节而且每轮都需要额外调用一次 LLM增加延迟和成本。2.3 方案三检索增强RAGRAG 的核心思路是不把所有内容都塞进窗口而是提前把文档/历史切块并做向量化存储用户提问时先检索出最相关的几个片段再放入上下文。用户提问 ↓ 检索相关片段Top-K ↓ 组装 Prompt问题 相关片段 ↓ 模型生成RAG 是目前比较成熟的方案适合知识库问答、企业文档检索等场景。但它依赖向量检索的质量关键词和语义理解都可能影响效果。2.4 方案四Scroll 的思路——让模型写代码管理上下文Scroll 这类方案在思路上更进一步不再由外部业务代码固定决定“应该保留哪些上下文”而是把上下文的读写逻辑暴露给模型让模型在推理过程中生成代码、调用上下文 API实现动态的、按需的上下文管理。也就是说模型不再只是“被投喂上下文的对象”它自己也参与上下文的调度模型可以主动读取某份文件、某段历史、某个变量的最新状态。模型可以主动写入新的记忆、更新摘要、清理不再需要的内容。模型可以决定当前步骤是否需要完整上下文还是只需要少量关键信息。这种思路更接近 Agent 自动化的方向上下文不是静态的 Prompt 拼接而是一个可以被程序化操作的数据层。3. 核心原理解析从 Prompt 工程到“上下文即代码”3.1 为什么“写代码”比“写 Prompt”更可靠传统的 Prompt 工程里我们希望模型基于给定的上下文做推理。但 Prompt 本身是自然语言很多操作是模糊的比如“记住之前讨论的内容”模型并不知道具体要记住什么、从哪读取。如果把上下文管理写成代码事情就变得明确得多context load_context(user_idu_1001) order_code read_file(order_service.py, start_line110, end_line130) new_summary summarize(history) save_memory(user_idu_1001, summarynew_summary)每一行代码都有确定的行为读取、写入、删除、更新。模型只需要学会“生成合适的代码序列”即可。从工程角度看这种方式的确定性、可测试性和可观测性都更强。3.2 Scroll 思路的通用工作流程一个典型的“模型写代码管理上下文”流程可以拆成下面几步用户输入问题。模型根据问题判断需要哪些上下文生成一段获取上下文代码或查询指令。上下文执行器Context Executor运行这段代码从存储层召回、压缩或更新数据。执行结果作为新的上下文片段回传给模型。模型基于返回结果进行最终回答并可选地将本轮结论写回上下文存储。这个过程形成了闭环用户输入 ↓ 模型决策需要什么上下文 ↓ 执行代码读取/压缩/检索/写入 ↓ 得到结构化信息 ↓ 模型生成回复 ↓ 更新长期记忆3.3 模型如何“写代码”这里的“写代码”并不是让模型从零写出整个上下文管理系统而是让模型在推理过程中产生结构化的操作指令。常见的实现方式有三种Function Calling函数调用模型输出一个函数名和参数由外部系统执行比如search_files(filename, line_start, line_end)。代码解释器模式模型生成一段 Python 代码交给沙箱环境执行并返回结果。结构化指令协议模型输出特定的 JSON/指令序列外部引擎解析并执行。三种方式没有绝对好坏关键是确定上下文操作的边界。对于在线低延迟场景Function Calling 更合适对于需要灵活计算的场景代码解释器模式更自由。3.4 上下文存储的分层设计当上下文需要被“代码管理”时存储层就不能只是简单的列表而要像数据库一样分层层级示例特点短期记忆当前轮对话、最近 N 条消息低延迟直接放入 Prompt工作记忆当前任务相关文件、临时变量任务导向按需读取长期记忆历史对话摘要、用户偏好、项目知识持久化存储容量大外部知识文档库、数据库、API 返回结果通过检索或 SQL 读取每一层都有不同的读写策略。Scroll 思路的价值在于让模型自己决定何时晋升Promotion或降级Demotion某段上下文而不是全部放在同一个窗口里。4. 实战示例用 Python 实现一个“上下文即代码”的最小原型下面我们动手实现一个可运行的最小原型。这里用 Python 模拟“模型生成代码 → 上下文执行器运行 → 更新记忆”的链路。为了不让示例依赖特定大模型 API我们用规则函数模拟模型的输出重点演示上下文管理器的设计。4.1 项目结构context_scroll_demo/ ├── main.py # 主流程 ├── context_manager.py # 上下文管理器 ├── memory_store.py # 记忆存储 └── executor.py # 代码执行器模拟模型生成的代码执行4.2 实现记忆存储层先实现一个简单的记忆存储类负责保存长期摘要和关键文件片段。# 文件路径context_scroll_demo/memory_store.py import json import os class MemoryStore: 长期记忆存储这里用 JSON 文件模拟持久化。 def __init__(self, pathmemory.json): self.path path self.data {summary: , file_cache: {}, history: []} self._load() def _load(self): if os.path.exists(self.path): with open(self.path, r, encodingutf-8) as f: self.data json.load(f) def _save(self): with open(self.path, w, encodingutf-8) as f: json.dump(self.data, f, ensure_asciiFalse, indent2) def set_summary(self, summary: str): self.data[summary] summary self._save() def get_summary(self) - str: return self.data.get(summary, ) def add_history(self, item: dict): self.data[history].append(item) # 只保留最近 100 条 if len(self.data[history]) 100: self.data[history] self.data[history][-100:] self._save() def cache_file(self, filename: str, content: str): self.data[file_cache][filename] content self._save() def get_file(self, filename: str) - str: return self.data[file_cache].get(filename, )这段代码并不复杂但体现了一个关键点长期记忆需要显式地读写而不是每次对话都全量携带。4.3 实现上下文管理器接下来实现上下文管理器。它负责维护“短期上下文”并暴露两个核心方法add_message追加一条消息。build_prompt根据当前消息和长期记忆构造发送给模型的 Prompt。compact_if_needed当上下文过长时触发“摘要压缩”操作。# 文件路径context_scroll_demo/context_manager.py import tiktoken from memory_store import MemoryStore class ContextManager: def __init__(self, max_tokens800, model_namegpt-3.5-turbo): self.messages [] # 短期上下文 self.max_tokens max_tokens self.store MemoryStore() # 这里使用 tiktoken 计算 token 数若未安装可换成 len() 粗略估算 try: self.encoder tiktoken.encoding_for_model(model_name) except Exception: self.encoder None def add_message(self, role: str, content: str): self.messages.append({role: role, content: content}) self.store.add_history({role: role, content: content}) def _count_tokens(self, text: str) - int: if self.encoder is not None: return len(self.encoder.encode(text)) return len(text) // 2 def _system_prompt(self) - str: 每次请求携带长期摘要作为系统提示的一部分。 summary self.store.get_summary() if summary: return f以下是之前的对话摘要\n{summary}\n请基于摘要和当前对话继续回答。 return 你是一个有帮助的助手。 def build_prompt(self) - list[dict]: 构造最终的 Prompt只保留短期上下文 系统摘要。 system_msg {role: system, content: self._system_prompt()} return [system_msg] self.messages def compact_if_needed(self, summary_funcNone): 当上下文 token 数超过阈值时将早期消息压缩为摘要。 total_tokens sum( self._count_tokens(msg[content]) for msg in self.messages ) if total_tokens self.max_tokens: return False print(f[ContextManager] 当前上下文 {total_tokens} tokens超过阈值 {self.max_tokens}开始压缩...) # 将最早的一部分消息合并成摘要 old_messages self.messages[: len(self.messages) // 2] old_text \n.join( f{msg[role]}: {msg[content]} for msg in old_messages ) if summary_func: summary summary_func(old_text) else: # 兜底策略截取前 200 字作为摘要方便演示 summary old_text[:200] ……(已省略) self.store.set_summary(summary) # 保留后半段消息 self.messages self.messages[len(self.messages) // 2:] print(f[ContextManager] 压缩完成保留 {len(self.messages)} 条消息。) return True这里做了一个重要的设计当上下文超长时不是把所有内容都丢掉而是把早期内容“提炼”成长期摘要然后只保留最近的短期上下文。这正是 Scroll 类方案里非常常见的“摘要压缩 分层存储”思路。4.4 实现执行器模拟“模型写代码管理上下文”现在重点来了。我们要模拟“模型生成代码执行器运行代码完成上下文调度”的过程。为了演示我定义了一个execute_code函数它接收模型返回的代码字符串在受限环境中执行并捕获read_file、write_summary、search_file这些上下文操作函数。# 文件路径context_scroll_demo/executor.py import json from memory_store import MemoryStore class ContextExecutor: 执行模型生成的上下文操作代码。 def __init__(self): self.store MemoryStore() self.sandbox_globals {} def register_context_api(self): 注册模型可以调用的上下文 API。 def read_file(filename: str, start_line: int None, end_line: int None) - str: source f# {filename} 的模拟代码内容\n for i in range(1, 30): source fdef func_{i}():\n return {i}\n\n lines source.split(\n) if start_line is not None and end_line is not None: lines lines[start_line:end_line] return \n.join(lines) def save_summary(summary: str) - str: self.store.set_summary(summary) return f已保存摘要{summary[:30]}... def get_summary() - str: return self.store.get_summary() def add_note(note: str) - str: self.store.data.setdefault(notes, []).append(note) self.store._save() return 笔记已保存。 # 把这些 API 暴露给模型生成代码 self.sandbox_globals.update({ read_file: read_file, save_summary: save_summary, get_summary: get_summary, add_note: add_note, }) def execute(self, code: str) - dict: 执行模型生成的代码返回输出结果。 self.register_context_api() result {stdout: , error: None} try: exec(code, self.sandbox_globals, self.sandbox_globals) except Exception as e: result[error] str(e) return result这里要注意真实项目中的沙箱环境需要更严格的安全隔离比如使用subprocess容器、Docker、或者云函数。上面的示例只是为了演示上下文操作的交互流程。4.5 完整主流程最后把各个模块串起来模拟一次完整对话。# 文件路径context_scroll_demo/main.py from context_manager import ContextManager from executor import ContextExecutor def main(): cm ContextManager(max_tokens600) executor ContextExecutor() print( 第一轮对话 ) cm.add_message(user, 帮我看看 order_service.py 第 10 行附近的代码有没有问题) # 假设模型回复 model_reply 我读取了这段代码逻辑本身没问题但建议把重复的校验逻辑抽出来。 cm.add_message(assistant, model_reply) print(系统提示片段) for msg in cm.build_prompt(): print(f [{msg[role]}] {msg[content][:50]}...) print() # 模拟模型生成上下文操作代码 model_code summary get_summary() if not summary: code read_file(order_service.py, start_line1, end_line20) save_summary(用户正在检查 order_service.py 的代码重点在第 10 行附近。) add_note(需要对比 payment_service.py) print(读取完成摘要已更新) print( 模型生成的上下文管理代码 ) print(model_code) exec_result executor.execute(model_code) print(执行结果, exec_result) print() print( 继续对话触发压缩 ) for i in range(1, 8): cm.add_message(user, f补充一个问题 {i}这个逻辑在第 {i * 10} 行附近是否也能复用) cm.add_message(assistant, 这个需要再分析一下我建议结合整体重构来看。) # 触发压缩判断 cm.compact_if_needed(summary_funclambda text: f用户连续询问代码复用问题相关历史共 {len(text)} 字。) print() print( 压缩后的长期摘要 ) print(cm.store.get_summary()) print() print( 最终 Prompt 结构 ) for msg in cm.build_prompt(): print(f [{msg[role]}] {msg[content][:80]}...) if __name__ __main__: main()4.6 运行与预期输出如果你安装了tiktokenpip install tiktoken直接运行cd context_scroll_demo python main.py预期输出类似 第一轮对话 系统提示片段 [system] 以下是之前的对话摘要 用户正在检查 order_service.py 的代码重点在第 10 行附近。 ... [user] 帮我看看 order_service.py 第 10 行附近的代码有没有问题... [assistant] 我读取了这段代码逻辑本身没问题但建议把重复的校验逻辑抽出来。... 模型生成的上下文管理代码 ... 执行结果 {stdout: , error: None} 继续对话触发压缩 [ContextManager] 当前上下文 1024 tokens超过阈值 600开始压缩... [ContextManager] 压缩完成保留 4 条消息。 压缩后的长期摘要 用户连续询问代码复用问题相关历史共 765 字。 最终 Prompt 结构 [system] 以下是之前的对话摘要 用户连续询问代码复用问题相关历史共 765 字。... [user] 补充问题 4... ...当然实际运行时的 Token 数会根据文本内容变化但只要超过阈值压缩逻辑就会生效。这个原型说明了什么它说明“让模型写代码管理上下文”本质上是一个可编程的上下文协议模型不直接面对几百条原始消息而是通过read_file、save_summary、add_note这类 API按需获取和更新状态。工程上我们可以对这个协议进行单元测试、日志追踪和权限控制这是纯 Prompt 方案很难做到的。5. 常见问题与排查思路在实际实现和部署“上下文即代码”方案时你可能会遇到很多问题。下面整理了一份高频排查清单问题现象常见原因解决思路模型生成的代码语法错误模型对上下文 API 的接口不熟悉在系统提示中明确给出 API 文档和示例代码或使用 Function Calling 约束格式上下文压缩后回答质量下降摘要丢失了关键细节摘要中保留“问题结论”结构必要时把重要文件片段单独缓存执行上下文代码开销过大每轮都调用代码执行器合理利用缓存减少重复读取使用预编译函数代替逐轮生成Token 数计算不一致不同模型编码器不同使用目标模型对应的 tokenizer不要用中文字符数粗略估算代码执行安全性问题沙箱隔离不严生产环境使用容器、云函数或独立进程限制文件系统和网络权限模型不知道何时读取上下文缺乏触发机制在系统提示中设定“当用户问到历史内容时先调用查询函数再回答”5.1 排查顺序建议遇到问题时建议按下面的顺序定位问题先确认上下文存储层数据是否正常。直接查看memory.json或者数据库记录确认摘要、文件缓存是否写入成功。再确认模型生成的代码是否被正确执行。打印执行日志查看是否报错、返回了什么结果。然后检查最终 Prompt 是否合理。把构建好的 Prompt 打印出来人工阅读一遍看看上下文是否冗余或缺失。最后再判断模型回答质量。上下文没问题还答错才需要调整大模型本身的推理策略。6. 最佳实践与工程建议6.1 明确上下文操作权限边界“模型写代码”是一把双刃剑。如果允许模型读取任意文件、调用任意系统命令会带来很大的安全风险。最佳实践是只暴露白名单 API比如read_file、save_summary、search_docs。禁止模型直接操作文件系统、网络、数据库连接。每次执行都要有审计日志记录模型生成的代码和操作结果。对敏感操作增加二次确认机制。6.2 为上下文 API 编写清晰的“工具文档”模型要正确“写代码”前提是它能理解上下文 API 的用法。建议在系统提示中附带精简版 API 文档可用操作 - read_file(filename, start_lineNone, end_lineNone) 读取文件指定行区间的内容。 - save_summary(summary) 将长期摘要更新为 summary。 - get_summary() 返回当前长期摘要。 - add_note(note) 保存一条临时笔记。API 文档越明确模型生成代码的成功率越高。6.3 区分高频操作和低频操作在上下文管理设计中不是所有信息都有同等价值高频高价值用户偏好、当前任务目标、项目核心约束 → 长期记忆始终携带。高频低价值反复出现的寒暄、临时闲聊 → 丢弃或极简摘要。低频高价值之前某轮讨论过的技术方案、踩坑记录 → 长期记忆按需检索。低频低价值历史日志细节、无关文件内容 → 直接清理。6.4 监控与可观测性上下文管理一旦变成代码就要像普通后端服务一样做监控。建议采集以下指标每轮请求的 Token 数变化趋势。摘要压缩触发次数和压缩前后 Token 数。模型生成的上下文操作代码的成功率。上下文存储层的读写延迟和容量。用户对最终回答的点赞/点踩用于评估上下文管理效果。6.5 不要为了“自动化”而过度设计Scroll 思路虽然高级但并不适用于所有场景。如果你只是在做一个单轮问答、或者上下文总长度很小完全不需要引入“模型写代码管理上下文”这种复杂度。先评估上下文是否经常超过模型窗口是否存在跨多轮、跨文件的关键信息依赖是否需要模型主动决定读取哪些数据如果以上答案都是“否”乖乖用普通的 Prompt 拼接就行。6.6 版本与依赖管理这类方案通常依赖大模型 API、向量库、缓存中间件。建议在生产环境固定依赖版本不要把模型版本和框架版本随意升级。每次升级都要回归测试模型是否还能正确生成上下文操作代码。压缩后的摘要质量是否稳定。上下文 API 的兼容性是否没有破坏。7. 总结与下一步本文围绕“模型写代码管理上下文”这一主题梳理了长上下文场景下的核心痛点对比了滑动窗口、摘要压缩、RAG 以及 Scroll 这类“上下文即代码”方案的差异并且用 Python 实现了一个最小原型。你可以把原型中的ContextManager、MemoryStore、ContextExecutor三部分拆开理解存储层负责持久化管理层负责控制长度执行层负责让模型按需操作上下文。如果想继续深入建议按下面几个方向学习学习大模型 Function Calling / Tool Use 的官方 API理解模型如何输出结构化调用参数。研究向量数据库如 Milvus、Chroma、pgvector在上下文检索中的用法。熟悉 LangChain、LlamaIndex 等框架的 Memory 模块看看它们是如何实现摘要记忆和实体记忆的。尝试把上下文操作封装成 REST API让模型通过 HTTP 调用而不是本地exec扩展到多机分布式场景。最后提醒一句不要把上下文管理做成“什么都往记忆里塞”。真正好用的上下文系统应该像人一样只记住重要的忘记冗余的并且在需要时能快速想起关键细节。希望这篇文章能为你带来一些实用的启发。