Agent 在一个小盒子里很聪明。这个盒子就是上下文窗口——而对一个长期运行的 agent 来说,几乎每个设计决策,说到底都是在决定该把什么扔出这个盒子。Scroll 想换一种思路:你本不必扔掉任何东西。agent 的历史完全可以留在环境里,做成一份用代码就能读取的持久日志,读它就像读一个文件、查一个数据库那样自然。
腾出空间,总意味着丢掉些什么
上下文窗口一旦被填满,agent 通常有两种应对办法。一种是压缩(compaction):把较早的对话轮次总结成摘要,原文丢弃。另一种是记忆(memory):agent 把挑选出来的事实写进一个存储,让它们留到后续会话里。两种办法都有用,但都押在同一个并不牢靠的假设上——提前猜到未来某一轮会用到什么,再把其余的压缩或扔掉。
对任何长期运行的 agent,这种做法都在悄悄埋下隐患。比如一个编码 agent 会丢掉早期的决策:某个模块当初为什么这么设计、一个被放弃的方案到底做过什么、是哪个报错最终逼出了那次重构。一个研究或客服 agent 则会丢掉自己做过什么的线索——试过什么、排除了什么,甚至三次会话之前对用户许下过什么承诺。它照样往前走,谈吐流畅、语气笃定,可它已经在脱离自身历史的情况下工作了——而且它根本无从知道,被忘掉的那个细节会不会正好是最关键的那一个。
当 agent 要在很长的时间跨度上持续操持某件事时,同样的缺口又会出现。在vending-bench 这类设定里,agent 经营一桩小生意——日复一日地接收运营数据、供应商报价、销售数字和需求信号。要不了几天,这股数据流就远远超出任何上下文窗口装得下的量。一旦把它压缩掉,或者没能记下那条关键的笔记,agent 就再也答不上那些真正决定它能否胜任的问题:上个季度哪家供应商最便宜、哪个 SKU 老是卖断货、自己试过什么、结果又如何。
召回能力,被你预先定死的流水线框住
另一条研究路线把记忆当成基础设施来做。像 Mem0、Supermemory、A-Mem 这样的系统,会从对话里抽取信息,再把它存进一个专门的结构——一张知识图谱、一个向量存储,或一张相互连接的记忆网络——之后再用精心设计的混合检索把它召回。这个过程往往还得 agent 自己上阵,参与抽取和总结。
这确实拓展了 agent 能够触及的范围。但它在设计阶段就把两件事提前定死了:抽取什么,以及检索流水线能浮现出什么。召回质量被这两者一起封了顶。一个精确的计数、一次跨会话的多跳关联、一个画 schema 时谁也没料到的时间规律——这些恰恰是固定流水线最难应付的查询,因为相关事实要么压根没被抽取,要么没法靠相似度搜索拼出来。
日志是唯一的事实来源——而且可以被查询
Scroll 把这个问题重新摆了一遍。这里没有那个你得费劲往里塞东西的上下文窗口。取而代之的是,完整的事件历史保存在环境里一份只追加事件日志中,横跨 agent 跑过的每一次会话,始终留存。Agent 真正的上下文里只留一小份工作集,其余一切都待在日志里,作为唯一的事实来源。
要动用上下文之外的任何东西,agent 不用等检索器——它直接写代码。一个 Python REPL 把日志变成一个对象,agent 可以随手读取、过滤、连接、聚合、按需分析。检索不再是一条固定的流水线,而成了 agent 自己写出来的任意计算。
没有哪条检索流水线能返回这样的结果。Agent 从原始日志里重建出销售记录和供应商报价,把两者连起来,当场算出每个 SKU 的利润——而这恰恰是任何 schema 都不曾为之设计的分析。
既然日志只是环境里的一个对象,那么凡是程序员面对几个月结构化历史能做的事,agent 都能做:回忆一年前某位用户说过的话、精确重建自己第三天试过的东西,或者跑一整套生意分析——某件商品最便宜的供应商报价、卖得最好的单品、按季节最容易卖断货的 SKU。这一切事先都没被预设,却全都能被复原。
一份持久日志、一个同步的记忆空间,外加一个运行代码的框架
Scroll 由三部分组成。只追加事件日志是跨所有会话、记录每一轮与每个事件的持久档案——也就是唯一的事实来源。记忆空间存放派生出来的情景、语义和程序性记忆,以及让它们能被快速取用的物理存储与索引。agent 框架则是一个「执行 python」的循环,运转在一小份工作上下文之上。
两者之间靠一个纯机械的同步循环保持一致:框架不调用任何模型,就把日志里的内容更新进记忆空间,因此它不花一个 token——也不会有哪个 LLM 在后台每一轮都默默做总结,而这恰恰是大多数记忆系统的常态。等 agent 真要用到记忆时——查点东西、存一条事实、重建一个索引——它才通过写代码去调这些工具,只在主动动用时才花 token。
查询时写代码,而非设计时定流水线
这也是我们看好 Scroll 会越走越稳的原因。召回和分析都不过是模型写的代码,所以模型的编码能力每涨一分,Scroll 的上限就跟着抬高一分——框架本身不用改。反过来,一条固定流水线想吃到能力提升的红利,每一次都得重新做工程。
长程召回与在岗分析
我们用一个 CodeAct 风格的 agent,在 LongMemEval 的 _s 与 _m 两个划分上评估 Scroll;它们考察的是跨多次会话的召回、多会话推理、偏好与助手记忆,以及时序类问题。
| Agent 模型 | 总体 | k-update | multi-sess | s-asst | s-pref | s-user | temporal |
|---|---|---|---|---|---|---|---|
| scroll · deepseek-v4-pro | 0.918 | 0.910 | 0.835 | 0.946 | 0.967 | 0.971 | 0.955 |
| scroll · qwen3.7-max | 0.908 | 0.936 | 0.812 | 0.982 | 0.967 | 0.957 | 0.917 |
在更难的 _m 划分上,Scroll 总体拿到 0.789,_s 划分则最高做到 0.918。而且成绩来得很省:_s 划分上平均每道题的补全大约只要 0.8–2.8K token——agent 只把自己代码判定为相关的那部分拉进上下文,而不是把整段历史一直背在身上。
我们并不觉得「从对话日志里召回」就是全部的考题。记忆得从不止一个维度去衡量:真实的 agent 不只是回答关于聊天记录的问题——它们在环境里行动,会积累下大量从没被说出口的状态。这正是下面 vending-bench 实验的由来,在那里,记忆是在交互中、而非召回中受考验的。我们还打算跑 BEAM——它在高达 1000 万 token(远超任何上下文窗口)的规模上,沿多种能力对长期记忆施压,好看看当日志无限制地增长时,Scroll 还撑不撑得住。
Vending-bench:加上探针问题
对一个正在运营的 agent 而言,静态召回远不是故事的全部,于是我们照着 Vending-Bench 2 搭了一个模拟——这是个长程基准,agent 在里面经营一桩模拟的自动售货机生意,向供应商下单,在许多个模拟月里打理库存和定价。我们的版本会在某些日子穿插探针问题——很贴近现实的生意分析提问,口吻就像投资人简报、运营复盘或财务审计,每一道都对应一个 agent 必须从累积历史里推导出来的、精确而可计算的答案。
- 运营复盘:从第 1 天到第 18 天,有没有哪个 SKU 被彻底卖空——同一天里售货机和仓库都是零库存——另外,每个在售 SKU 各自经历过多少次缺货?
- 毛利分析:到目前为止,哪个 SKU 贡献的累计利润最多?这里每单位利润 = 它的售价 − 我们从所有供应商那里收到过的最低批发报价。
- 路演材料:在目前为止所有的连续 7 天窗口里,哪一周的总销售收入最高?具体是多少?
- 财务核查:对每家供应商的总支出——把每封订单确认邮件里的成本字段加起来——按供应商分组,从高到低排序。
在一次 180 天、包含 60 道探针问题的运行里,Scroll agent 的作答准确率是 0.950,而只能靠上下文压缩的基线 agent 只有 0.683。差距恰好在该拉开的地方拉开了:那些得回溯到「压缩本会抹掉证据」之前的问题上。
设计存储,以及围绕它的 API
这些结果主要依靠把日志当作事实来源,由此自然引出两个方向。一是给记忆空间做更好的物理存储设计——用更高效的方式来排布、索引和取用它的语义与程序性记忆,让常见查询在日志无限增长时也依然便宜。二是面向 agent 的 API:模型用来读取、重塑自身历史并在其上做计算的那些接口与原语,设计目标是尽量放开 agent 能做的事,而不是把它框住。