前段时间群里一个朋友提出一个问题:有很多的文档(一个或多个),总量文字超过模型上下文,必须从中取出相关样本,需要用大模型分类并且给出原因。需要怎么做。
首先让我们想想,假设我直接强硬的把所有文档都输入模型,模型会直接拒绝或者直接胡言乱语。因为模型无法处理如此大量的上下文,这直接超出了它的context window。其次,作为一个普通开发者,我也不想重新训练模型或者改架构,因为以我的能力很可能会破坏模型的现有能力,并且烧光钱包。
那么折中一点,我们需要更加复杂的流程,用户和 AI 轮流提问和生成答案,通过检索和查询后经过处理,再一次与AI对话。那么现在又有两个挑战出现了:上下文管理和 KV 缓存。
我们都知道KV缓存的概念:当你调用 LLM 时,大多数提供商会将其输入和输出缓存到他们的系统中。如果用与之前相同的提示重新调用 LLM,提供商会重用你之前调用的计算结果。这使得整个模型的推理成本大大降低。这就是为什么围绕最大化 KV 缓存来设计聊天的原因。如果能够让系统提示和过去上下文保持不变,就能从而以大幅折扣获得全部记忆。
接下来我们来说上下文管理:当老板让你处理第一个问题时,你通过检索找到了最相关的五篇文章,你将这五篇文章输入llm,llm做出回应。之后老板让你处理第二个问题,你又拿到了最相关的五篇文章。
接下来,你要怎么做,去调用llm?
好的,现在llm拥有了过去对话的上下文,但是他没有了原本KV缓存。同时llm不会从先前的文档中获取信息。
3. 传递所有的内容,消息,文档,everything
现在你拥有kv缓存了,但是新的风险出现了:context rot
context rot:随着上下文不断变长、信息不断叠加,早期的重要信息逐渐被"稀释、遗忘或误用",导致模型理解变差、回答质量下降。
现在我们穷途末路了,我们来想想,问题是什么?
如何在避免向 LLM 提供大量不必要结果的情况下,高效地管理代理的上下文。
我们都知道最常见的解决方法:RAG(Retrieval-Augmented Generation)
RAG 的核心思想并不复杂:不试图让模型"记住一切",而是在需要时,从外部知识库中检索相关信息,并将其作为上下文提供给 LLM。在典型的 RAG 流程中,原始文档会被切分为多个片段(chunk),经过向量化(embedding)后存储在向量数据库中。当用户发起查询时,系统对查询进行同样的向量化处理,通过相似度搜索选取若干相关片段,再将这些片段输入给 LLM 以生成最终回答。
这种方式的优势非常明显。
首先,RAG 显著缓解了上下文长度受限的问题。模型不再需要在一个固定的上下文窗口中承载全部知识,而是只在推理时引入与当前问题最相关的内容。其次,它提高了答案的可解释性与可更新性:知识存储在外部系统中,文档的更新无需重新训练模型。此外,在多数"语义相关"的问答任务中,基于 embedding 的检索通常能够取得相当不错的效果,这也是 RAG 能够迅速成为事实标准的重要原因。
在实践中,RAG 通常效果相当好,人们也围绕这一基础 RAG 流程进行了多种扩展,例如:引入 BM25 等关键词检索技术;采用多跳(multi-hop)工具调用,在不同阶段使用由 LLM 自适应生成的查询进行多次检索;使用 HyDE(Hypothetical Document Embeddings) 等查询预处理方法,以提升初始查询的表达质量;以及通过上下文摘要(context summarization)来压缩检索结果。
然而,RAG 并非万能解法。
一个常被忽视的问题是,embedding 本质上只捕捉语义相似性,而并不理解结构、顺序或精确约束。当问题依赖于"之后发生了什么""第几步做了什么"这类结构性信息时,相似度检索往往会返回大量"看起来相关、但实际上无关"的片段。结果是,为了提高召回率,系统不得不向 LLM 提供更多上下文,而这恰恰又加剧了上下文膨胀的问题。
当答案依赖结构或精确性,而不仅仅是语义相关性时,相似度搜索往往会失效。例如,在排查"制冷问题"时查找某个冰箱的具体型号,这类任务需要精确匹配而非语义近似。。
上下文摘要和关键词检索同样存在根本性局限。上下文摘要通过压缩信息来缓解上下文压力,但这一过程本质上是有损的,一旦关键信息被丢弃便无法恢复。BM25 等关键词检索方法在明确知道要查找的唯一关键词时效果很好,但在需要全局推理或穷举搜索的任务中往往难以奏效。即便结合工具调用,这类方法仍然需要谨慎的上下文控制,否则 Agent 的上下文很容易失控。
最后,多数多跳 Agent 仍然以线性方式运行(行动 A →→ 结果 A →→ 行动 B),所有检索结果和中间推理都会不断累积到同一个上下文窗口中。当任务需要阅读大量文档时,上下文迅速增长,模型要么触及长度上限,要么在冗余信息中逐渐丧失推理稳定性。
现在,为了解决上述的这些问题,让我来介绍RLM,MIT CSAIL团队提出的递归语言模型。同样不修改模型本身,而是在输入输出层做文章,它希望使 LLM 能够在任意长度的输入(无限长度)上解决需要全局推理的任务(阅读所有内容,跨巨大距离进行比较),而无需将整个文本一次性输入模型。
而从结果来看,它做出了非常革命性的贡献。

RLM提出的方法并不令人新奇:它让 LLM 编写自己的 Python 代码来搜索答案,但这里的处理方式很有意思。主模型不是一次性读取整个输入文档,而是通过编写代码在 REPL 环境中操作 `context` 变量(例如用正则搜索、分块处理、调用子模型等),根据执行结果决定下一步行动,最终通过 `FINAL(...)` 或 `FINAL_VAR(...)` 给出答案。
RLM 是递归的。作者们认为,在长上下文设置中的性能下降不仅取决于输入长度,还取决于任务复杂度。而那些需要反复、有针对性地访问输入中较远部分的任务往往会受到上下文腐化的最大影响。
他们没有将非常长的提示直接输入模型,而是将完整输入放置在外部执行环境(如 Python REPL)中。然后,LLM 编写代码来检查、过滤、分块并选择性地处理这些输入,递归地(通过使用更小、过滤和分块后的输入调用更多 LLM 调用)。
它使用子模型来执行这个分块读取任务。当主模型在 REPL 中编写的代码执行到 `llm_query(...)` 时,会触发预先配置的子模型调用。子模型处理分块后的文本片段,返回结果给 REPL,主模型再根据这些结果决定下一步行动(是否需要更多子模型调用,或者直接给出最终答案)。
具体来说,RLM 的工作流程是这样的:
1. 初始化:将超长 context(如百万行文本)放入 REPL 环境的 context 变量中,主模型只收到系统提示和用户问题,看不到完整 context。
2. 迭代推理:主模型在每一轮中:
- 生成包含 `repl ...` 代码块的回复
- REPL 执行这段代码(可能包括正则搜索、分块、调用 `llm_query(...)` 等)
- 将执行结果反馈给主模型
3.递归调用:当代码中执行 `llm_query("分析这段文本:...")` 时,会触发子模型调用。子模型处理分块后的文本,返回结果给 REPL,主模型再根据这些结果决定下一步行动。
4.终止条件:主模型输出 `FINAL(答案)` 或 `FINAL_VAR(变量名)` 时,整个流程结束。
这种设计的核心优势在于:主模型的上下文窗口里只有"对话历史 + 执行结果摘要",而不是百万 token 的原始文档。这使得它在长对话中能保持稳定的推理能力,同时通过代码执行和子模型调用,实现对超长输入的全局访问。

一个具体例子:在百万行文本中找数字
让我们用一个简单的例子来理解 RLM 是如何工作的。
问题设定:假设你有一份包含 100 万行的文本文件,其中 99.9999% 都是随机单词("blah", "random", "text" 等),只有一行写着 `"The magic number is 7505376"`。你的任务是:找出这个 magic number。
RLM 的做法:
1.初始化阶段:将 100 万行文本放入 REPL 的 `context` 变量中,主模型只收到系统提示和问题:"I'm looking for a magic number. What is it?"
2.第一轮:主模型看到问题后,决定先看看 context 长什么样,于是生成代码:
print(len(context))
看看有多长print(context[:1000]) # 看看前1000个字符REPL 执行后返回:"1000000 lines... [大量随机文本]..."。主模型意识到 context 太大,需要换策略。
3.第二轮:主模型决定用正则表达式直接搜索数字,生成代码:
import re
numbers = re.findall(r'\b\d+\b', context)
print(numbers)REPL 执行后返回:`['7505376']`。主模型发现只有一个数字,很可能就是答案。
4.第三轮:主模型确认这就是答案,输出:
FINAL(7505376)流程结束。
关键点:
- 主模型从未直接看到那 100 万行文本
- 它的上下文窗口里只有:"问题 + 系统提示 + 第一轮代码 + 执行结果('1000000 lines...')+ 第二轮代码 + 执行结果('['7505376']')+ 第三轮 FINAL"
- 总共可能只有几千 token,而不是 4000 万字符
- 但通过代码执行,它间接访问了整个 context,并找到了答案
这就是 RLM 的核心:用"工具"(代码执行 + 子模型调用)来扩展模型的"感知范围",而不需要把一切都塞进上下文窗口。
之后,放上原文章链接和仓库链接,欢迎大家去看看源码实现。
文章:https://arxiv.org/abs/2512.24601
仓库链接:https://github.com/alexzhang13/rlm.git
我初次了解RLM是在Sebastian 的博客中,他评价这篇论文是其见过的最有前景的论文之一。(This is one of the most promising papers I've seen, and I think we will see more of that in 2026.)之后我在x上了解了更多,发现这个项目中确实蕴含着更多等待挖掘的潜力,比如,这个方法并不是很复杂,能否做成一个skill,装配在Claude code上?
这里我留下一个仓库地址,我在这里更新了关于rlm的一些示例,之后还会更新关于将它转化为skill的一些尝试,感兴趣的朋友可以来看看:https://github.com/chenyu-dev25/rlm-cn-sample.git
接近年关,这篇文章是在高铁上仓促写成的,很多地方没有经过深思熟虑,欢迎大家讨论与指正。写完之后,我合上电脑,看着窗外飞速后退的景色,开始思考一个问题:我们所说的"聪明的模型",究竟意味着什么?
从脑科学的角度看,海豚的记忆能力与人类几乎相当,它们的大脑体重与身体比例接近灵长类动物,拥有高度发达的新皮层,实验表明,海豚可以在数年后依然识别出同伴的"签名口哨",但这是否就意味着它们与人类同样"聪明"?如果把时间拉得更远一些,从数十万年前开始,猿人学会使用木棍、掌握火种、通过协作完成围猎,让自己和族群得以在危险的环境中生存下来。到了近代的信息时代,人类又发明了编程语言,用来处理那些曾经看似无法完成的任务——从大规模文本处理,到将火箭送入太空。
回过头来看,也许"技能",而非单纯的记忆或参数规模,才是"聪明"的关键。
如果让我们去想象两种大模型:一种不需要编码、不依赖工程,只凭"思考"就能制造出火箭;另一种则是充分继承并使用人类已有的全部技术体系,在其之上不断演化和革新,最终完成同样的目标。我更倾向于认为,后者离我们要近得多。
这并非对某条技术路线的否定,而只是作为一个人类的直觉判断:前一种设想的难度,或许就像让一个三维生物去理解四维空间一样——我至今无法想象它真正实现的路径。相比之下,让模型学会使用工具、分解问题,并在既有技术之上不断扩展自身能力,似乎是一条更清晰、也更可行的道路。
君子生非异也,善假于物也。
回望人类历史,真正让我们走到今天的,从来不是单个个体的大脑,而是工具:是木棍与火种,是文字与数学,是编程语言与操作系统。能力从未只存在于头脑之中,它总是在工具之上被不断放大。
大模型的“聪明”,也并非只定义在 hidden state 里,同样取决于我们递给它的,究竟是火种,还是Docker。
最后,祝大家新年快乐。今年就聊到这,我们明年再见。