TuriX长任务如何不失忆:recent、summary、snapshot三层记忆压缩与read_files召回机制源码解析
【免费下载链接】TuriX-CUAThis is the official website for TuriX Computer-use-Agent项目地址: https://gitcode.com/gh_mirrors/tu/TuriX-CUA
TuriX 是一款开源的桌面端 AI Agent(Computer-use Agent),它通过"Brain + Actor"双模型架构驱动 macOS 完成多步 GUI 操作。当任务步骤超过几十步时,上下文窗口很快就会被挤爆。TuriX 用recent(短期记忆)→ summary(压缩摘要)→ snapshot(原始快照)三层记忆压缩配合read_files 按需召回,让长任务始终"记得"关键信息而不撑爆 token。下面结合源码逐层拆解这套机制。
一、为什么长任务会"失忆"
TuriX 的 Brain 模型每一步都要接收:系统提示词 + 记忆摘要 + 两张截图 + UI 树状态。以 32000 token 的输入上限为例,十几步之后历史消息就会逼近天花板。
如果简单地把所有步骤塞进上下文,结果只有两种:
- 直接报错(token 超限)
- 强行截断(丢失早期步骤的关键信息,Agent 开始"失忆")
TuriX 的解法是:分级压缩 + 按需召回——热数据留在内存,冷数据压缩成摘要,原始数据落盘成快照,需要时再按文件名精确取回。
二、三层记忆架构总览
核心状态变量定义在 service.py:
| 层级 | 变量 | 内容 | 预算 |
|---|---|---|---|
| 缓冲区 | pending_recent_memory | 当前步骤(尚未被评估) | 不计入预算,最多 20 行 |
| 短期记忆 | recent_memory | 已评估的最近步骤(Step N | Eval: success | Goal: …) | memory_budget_tokens |
| 长期摘要 | summary_memory | 多轮压缩后的高层摘要 | memory_budget_tokens × 4 |
预算比例在 service.py 中配置:memory_warn_ratio = 0.7(预警线)、memory_hard_ratio = 1.0(触发压缩线)。
三层数据最终拼接为 Brain 模型的输入,拼接逻辑见_refresh_brain_memory:
Summarized memory (compressed from earlier steps…) → Recent steps → Pending steps
三、第一层:pending 缓冲区与 recent 短期记忆
3.1 步骤如何进入 pending
Actor 执行完动作后,_update_memory会写入一行:
Step 5 | Eval: pending | Goal: 在 Safari 中搜索航班这一行进入pending_recent_memory,此时不参与预算计算,避免刚执行完就被压缩掉。
3.2 pending → recent 的晋升
下一步 Brain 评估完前一步后(brain_step),会把Eval: pending替换为真实结果success/failed,并把该行从 pending 移入recent_memory。同时调用_save_step_record将该步骤的 eval + goal + analysis持久化到磁盘(文件名如step_5.txt),为后续召回留底。
3.3 预算检查与触发压缩
每次晋升后,brain_step检查recent_memory的 token 数:
- 超过
budget × 1.0(硬限制)→ 立即调用_summarise_recent_memory - 超过
budget × 0.7(预警线)→ 仅打日志提醒
四、第二层:summary 压缩与 snapshot 快照
4.1 压缩前先拍快照
_summarise_recent_memory的核心流程:
- 调用
memory_llm对recent_memory做摘要 - 无论成功还是失败,都先调用
_save_memory_snapshot把原始文本落盘 - 摘要通过
_is_summary_valid校验(必须 ≥10 token 且比原文短) - 校验通过 →
recent_memory清空,摘要追加到summary_memory
快照文件的描述固定为"Pre-summarization snapshot of recent memory at step N",record_type标记为snapshot,这样 Brain 在索引中能看到它和step/info记录的区别。
4.2 摘要的二次压缩
summary_memory也会增长。当它超过自己的预算(memory_budget × 4)时,_summarise_summary_memory会再做一轮更高层的压缩——相当于"摘要的摘要",始终保持总占用可控。
4.3 快照存储位置
所有快照通过 RecordStore 写入memory_snapshots/目录,每个文件带 YAML frontmatter(name、description、type、step_id、created_at),为 read_files 召回提供结构化元数据。
五、第三层:read_files 按需召回
5.1 记忆索引
每步 Brain 调用前,_format_memory_index会渲染一份索引(最多 50 条,最新在前):
- step_12 (step, step 12): Step 12 (Success): 点击"下一步"按钮 - user_profile (info, step 3): 用户偏好:靠窗座位 - memory_snapshot_recent_step_8 (snapshot, step 8): Pre-summarization snapshot of recent memory at step 8这份索引随截图一起送入 Brain 模型。
5.2 Brain 发起召回
Brain 提示词 明确告诉模型:
当索引中某条记录的描述表明它包含你需要的细节时,输出
{"read_files": {"files": ["step_5", "user_profile"]}}代替正常的 analysis/current_state。
5.3 召回执行流程
BrainSearchFlow 负责拦截与执行:
parse_response解析 Brain 的 JSON 输出extract_read_files检测是否存在read_files字段maybe_reinvoke从 RecordStore 读取文件全文,拼入 state_content,重新调用 Brain- Brain 拿到完整内容后输出正常的
analysis+current_state
这意味着:被压缩掉的早期步骤,只要 Brain 判断需要,就能按文件名精确取回原始记录,而不必把所有历史都留在上下文里。
六、容错与断点恢复
- 孤儿清理:
_sweep_orphaned_pending会回收因 Brain 调用失败而滞留的 pending 行,将其标记为Eval: unknown并补写 step 记录 - 断点恢复:
load_memory从memory.jsonl恢复全部三层状态,兼容旧版本格式(如 pending 行混在 recent 里的情况会自动迁移) - Token 兜底:当消息窗口仍超限时,MessageManager.cut_messages 按"先删图片 → 再删最旧消息 → 最后截断尾部"三阶段降级
七、关键源码文件索引
| 文件 | 职责 |
|---|---|
| src/agent/service.py | 三层记忆状态管理、压缩调度、快照保存、断点恢复 |
| src/agent/prompts.py | Brain 提示词中 read_files 召回协议 |
| src/utils/record_store.py | 带 frontmatter 的文件级记录存储 |
| src/utils/brain_search.py | read_files 检测与二次调用 |
| src/agent/message_manager/service.py | Token 计数与消息裁剪 |
这套机制让 TuriX 在 OSWorld 基准上保持高成功率——记忆压缩保证上下文不溢出,read_files 召回保证关键信息可回溯,两者结合正是长任务"不失忆"的核心。
【免费下载链接】TuriX-CUAThis is the official website for TuriX Computer-use-Agent项目地址: https://gitcode.com/gh_mirrors/tu/TuriX-CUA
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考