记忆
用户记忆系统的本质是一个主动的、持续的学习过程,其目标是构建一个关于用户的、尽可能简洁而强大的预测模型。
工作记忆(working memory)容量有限但访问快速,用于当前任务的处理;
长期记忆(long-term memory)容量巨大但提取较慢,存储过去的经验和知识。
轨迹(Trajectory)
是 Agent 实例的工作记忆,记录了该实例从创建到当前时刻的所有事件,按时间顺序排列,形成一个完整的、不可变的事件序列。
提供了 Agent 决策所需的即时上下文
用户长期记忆(User Long-Term Memory)
是跨会话、跨实例的持久化存储,通常以键值对的形式存在。用户偏好,历史摘要。通过工具调用读取和更新。
业务状态(Business State)
是开发者定义的高层状态抽象,用于表示任务的逻辑阶段(如“需要澄清”、“处理请求中”“等待付款”“请求完成”)。
这不同于框架内部的实例生命周期状态(如“运行中”“等待中”),而是从业务逻辑角度对任务进度的总结。
框架会将当前业务状态注入到 LLM 的输入上下文中,帮助 LLM 更好地理解任务进展。
记忆系统设计
Advanced JSON Cards 模式:情境知识
从信息存储到知识管理。这种模式的核心创新在于每个记忆卡片不仅记录事实信息本身,还加入了信息来源的叙事背景(backstory)、信息主体的身份标识(person)、主体与用户的关系(relationship)和时间戳
为了消除歧义,明确作用范围
评估
基础回忆
多会话检索
主动服务
压缩
重要性评分筛选(访问频率,时间衰减,情感强度,独特性)冲突的检测和解决
聚类代表
抽象泛化
SKILL动态提示词
算是一套模板,描述了某一类工作路线或者工作方式,和工具函数还是不一样的。
上下文压缩
三层压缩, 激进程度递增:
1 | Every turn: |
工作原理
- 第一层 – micro_compact: 每次 LLM 调用前, 将旧的 tool result 替换为占位符。这里 tool_results 是一个包含元组(Tuples)的列表,每个元组恰好有 3 个元素:
1
2
3
4
5
6
7
8
9
10
11
12
13def micro_compact(messages: list) -> list:
tool_results = []
for i, msg in enumerate(messages):
if msg["role"] == "user" and isinstance(msg.get("content"), list):
for j, part in enumerate(msg["content"]):
if isinstance(part, dict) and part.get("type") == "tool_result":
tool_results.append((i, j, part))
if len(tool_results) <= KEEP_RECENT:
return messages
for _, _, part in tool_results[:-KEEP_RECENT]:
if len(part.get("content", "")) > 100:
part["content"] = f"[Previous: used {tool_name}]"
return messages(msg_idx, part_idx, part)。在 Python 中,字典(Dictionary)和列表(List)都是可变对象(Mutable Objects)。当你把一个列表里嵌套的字典拿出来放到一个新的列表(如 tool_results)里面时,你并没有“复制”或者“克隆”这个字典,你只是创建了一个指向原内存地址的引用(指针)。1
2
3
4
5
6
7
8
9
10[
(
0, # msg_idx: 外层 messages 列表中的索引
0, # part_idx: 当前消息的 content 列表中的索引
{ # part: 实际的 Tool Result 字典对象
"type": "tool_result",
"tool_use_id": "toolu_01",
"content": "cat file.txt 的超长输出内容..."
}
), - 第二层 – auto_compact: token 超过阈值时, 保存完整对话到磁盘, 让 LLM 做摘要。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18def auto_compact(messages: list) -> list:
# Save transcript for recovery
transcript_path = TRANSCRIPT_DIR / f"transcript_{int(time.time())}.jsonl"
with open(transcript_path, "w") as f:
for msg in messages:
f.write(json.dumps(msg, default=str) + "\n")
# LLM summarizes
response = client.messages.create(
model=MODEL,
messages=[{"role": "user", "content":
"Summarize this conversation for continuity..."
+ json.dumps(messages, default=str)[:80000]}],
max_tokens=2000,
)
return [
{"role": "user", "content": f"[Compressed]\n\n{response.content[0].text}"},
{"role": "assistant", "content": "Understood. Continuing."},
] - 第三层 – manual compact:
compact工具按需触发同样的摘要机制。 - 循环整合三层:完整历史通过 transcript 保存在磁盘上。信息没有真正丢失, 只是移出了活跃上下文
1
2
3
4
5
6
7
8
9def agent_loop(messages: list):
while True:
micro_compact(messages) # Layer 1
if estimate_tokens(messages) > THRESHOLD:
messages[:] = auto_compact(messages) # Layer 2
response = client.messages.create(...)
# ... tool execution ...
if manual_compact:
messages[:] = auto_compact(messages) # Layer 3
TodoManager
多步任务中, 模型会丢失进度 – 重复做过的事、跳步、跑偏。对话越长越严重: 工具结果不断填满上下文, 系统提示的影响力逐渐被稀释。一个 10 步重构可能做完 1-3 步就开始即兴发挥, 因为 4-10 步已经被挤出注意力了。
1 | +--------+ +-------+ +---------+ |
TodoManager 存储带状态的项目。同一时间只允许一个 in_progress。
1 | class TodoManager: |
在TOOLS列表里加上todo,输入指令给下一轮LLM调用,让它调用todo工具进行更新
1 | if block.name == "todo": |
持久化任务记忆
TodoManager 只是内存中的扁平清单: 没有顺序、没有依赖、状态只有做完没做完。真实目标是有结构的 – 任务 B 依赖任务 A, 任务 C 和 D 可以并行, 任务 E 要等 C 和 D 都完成。
没有显式的关系, 智能体分不清什么能做、什么被卡住、什么能同时跑。而且清单只活在内存里, 上下文压缩 (s06) 一跑就没了。
还有也和上下文压缩有关系:对话流(messages list)不应该用来当“数据库”。这就比如人类没办法全凭心算记住 5 杯奶茶做到哪个步骤了,必须挂个订单流水水单。只要主框架把状态和TOOL results写在了那个 JSON 文件或日志板里,模型什么时候想看具体的,使用 task_get 或者 cat .tasks/task_B_err.log 主动索取(Pull in-demand)即可
解决方案
把扁平清单升级为持久化到磁盘的任务图。每个任务是一个 JSON 文件, 有状态、前置依赖 (blockedBy) 和后置依赖 (blocks)。任务图随时回答三个问题:
- 什么可以做? – 状态为
pending且blockedBy为空的任务。 - 什么被卡住? – 等待前置任务完成的任务。
- 什么做完了? – 状态为
completed的任务, 完成时自动解锁后续任务。
初始化
1 | class TaskManager: |
ex: task_4.json {“id”:4, “blockedBy”:[2,3], “status”:”pending”}
2. 依赖解除: 完成任务时, 自动将其 ID 从其他任务的 blockedBy 中移除, 解锁后续任务
1 | def _clear_dependency(self, completed_id): |
- 状态变更 + 依赖关联:
update处理状态转换和依赖边。1
2
3
4
5
6
7
8def update(self, task_id, status=None,
add_blocked_by=None, add_blocks=None):
task = self._load(task_id)
if status:
task["status"] = status
if status == "completed":
self._clear_dependency(task_id)
self._save(task) - 四个任务工具加入 dispatch map。task相关任务被驱动的唯一时机,是当 LLM 在它的推理过程(思考)中,认为有必要去持久化一个任务、更新进度,或者查看当前任务列表时。


