每个 Mycel Agent 都内置了持久记忆。对话历史跨重启保留,长会话自动管理以保持上下文窗口可用,无需任何外部记忆服务。
记忆的工作原理
Mycel 的记忆分为两层:
-
持久化 — 对话历史使用 LangGraph
AsyncSqliteSaver checkpointer 存储在 SQLite(~/.leon/leon.db)。每条消息、工具调用和结果都被记录。恢复 Thread 时,完整历史会被重放。
-
上下文管理 — 随着对话增长,
MemoryMiddleware 会在每次模型调用前自动裁剪和压缩历史,确保上下文窗口始终可用。这一过程对用户完全透明。
裁剪(Pruning)
裁剪负责截断过长的工具结果,防止单个输出占用过多上下文。
工具结果超过软阈值时被截断,超过硬阈值时直接清除。最近的消息受到保护不被裁剪,确保 Agent 保有当前的工作上下文。
默认配置:
| 字段 | 默认值 | 说明 |
|---|
soft_trim_chars | 3,000 | 超过此长度(字符数)的工具结果被截断 |
hard_clear_threshold | 10,000 | 超过此长度的工具结果被直接清除 |
protect_recent | 3 | 最近 N 条工具消息不被裁剪 |
trim_tool_results | true | 启用裁剪 |
在 runtime.json 中配置:
{
"memory": {
"pruning": {
"soft_trim_chars": 5000,
"hard_clear_threshold": 20000,
"protect_recent": 5
}
}
}
压缩(Compaction)
压缩通过 LLM 对旧的对话历史进行摘要,在上下文窗口填满时自动触发。这让 Agent 在非常长的任务中也不会丢失有意义的上下文 — 旧消息被摘要后替换为紧凑的表示。
压缩触发条件(同时满足):
- 对话消息数不少于
min_messages
- 上下文窗口已使用超过 70%
触发时,压缩模型会对除最近 keep_recent_tokens token 之外的所有内容进行摘要,然后用摘要替换旧历史。
默认配置:
| 字段 | 默认值 | 说明 |
|---|
reserve_tokens | 16,384 | 为新消息预留的 token 数 |
keep_recent_tokens | 20,000 | 保留最近 N 个 token 的原文 |
min_messages | 20 | 压缩可以触发的最少消息数 |
在 runtime.json 中配置:
{
"memory": {
"compaction": {
"enabled": true,
"reserve_tokens": 32768,
"keep_recent_tokens": 40000,
"min_messages": 30
}
}
}
溢出缓冲区(Spill buffer)
对于输出非常大的工具(例如在大型代码库上运行 Grep),溢出缓冲区会自动将输出写入临时文件,而不是内联到对话中。这保持了上下文的整洁,同时数据仍然可以访问。
按工具配置阈值:
{
"tools": {
"spill_buffer": {
"default_threshold": 50000,
"thresholds": {
"Grep": 20000,
"run_command": 100000
}
}
}
}
持久化详情
| 存储 | 位置 | 内容 |
|---|
| Thread 历史 | ~/.leon/leon.db | 所有消息、工具调用、结果(LangGraph checkpoints) |
| 沙箱状态 | ~/.leon/sandbox.db | 会话租约、指标 |
| 聊天消息 | ~/.leon/chat.db | Entity-Chat 社交层消息 |
Thread 历史是追加式的。回退 Thread 时移动的是活跃 checkpoint 指针,不会删除中间历史。
禁用压缩
如果你想自己管理上下文,或只运行短会话:
{
"memory": {
"compaction": {
"enabled": false
}
}
}
裁剪可以独立禁用:
{
"memory": {
"pruning": {
"trim_tool_results": false
}
}
}