在长上下文语言模型(LLM)普及的今天,智能体(Agent)似乎拥有了无限的“短期记忆”。然而,当开发任务跨越数天、数周,经历成百上千次会话时,简单的“把所有历史塞进上下文”很快就会遭遇上下文窗口爆满、检索退化(Needle-in-a-Haystack 问题)以及巨额 Token 成本的瓶颈。最关键的是,Agent 在过去会话中辛苦 Debug 出来的 Gotchas、探索出的项目架构认知以及沉淀的高频工作流,在开启新会话时均会付诸东流。

如何让 AI Agent 实现不依赖人工干预、跨越会话周期的经验累积与能力进化? 这是当前 Agent 落地工程实践的核心痛点。

MiMo-Code(一个开源的自主 Agent 编码辅助工具)通过引入 Dream(每 7 天自动整理记忆)Distill(每 30 天提炼可复用技能) 的双循环自进化机制,提供了一种优雅的解法。本文将深入 MiMo-Code 的源码底层,剖析这套让 Agent 实现“自我成长”的跨会话进化设计。

为避免正文里反复打断阅读,本文把常用术语统一收拢到下表;后文默认直接使用简称。

简称/术语全称中文说明
FTSFull-Text Search全文检索:基于 SQLite FTS5 虚拟表的本地 Markdown 记忆索引
BM25Best Matching 25一种经典的文本相关性计算算法,用于 FTS 搜索评分
TrajectoryTrajectory轨迹:存储在本地 SQLite 数据库中的 Agent 历史会话、工具调用及执行结果的完整流水记录
MEMORY.mdProject Memory File项目级持久记忆文件,包含项目上下文、硬性规则、架构决定与 Gotchas 等内容
checkpoint.mdSession Checkpoint File会话级检查点文件,由后台 checkpoint-writer 维护的当前会话状态快照
checkpoint-writerCheckpoint Writer检查点写入器:当会话 Token 数量超限时,自动唤醒的非交互后台子智能体,用以维护 checkpoint.md 并推荐记忆晋升
dreamDream Agent记忆整理智能体:每 7 天自动运行,读取近期会话轨迹与临时 notes,并通过数据库校验后增量合并至项目级持久记忆中
distillDistill Agent技能提炼智能体:每 30 天自动运行,分析长期轨迹中的重复模式,提炼并封装为 Skill/Subagent/Command 资产
ALSAsyncLocalStorage异步本地存储:Node.js/Bun 运行时的 API,用于在异步调用链路中隔离并透传当前的实例与项目上下文

1. 最小使用示例

在 MiMo-Code 中,Dream 与 Distill 默认是由系统在后台周期性自动触发的。然而,为了便于开发者介入与调试,系统也提供了手动的命令入口。

1.1 手动运行 Dream 记忆整合

在终端或 Agent 交互界面中输入 /dream 命令,即可手动强制唤醒 dream 智能体执行记忆整理:

1
2
/dream "focus on build tools"

输出结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Run one manual dream memory consolidation pass for the current project.

Orienting:
- Found project memory: memory/projects/a1b2c3d4e5f6/MEMORY.md (32 lines)
- Scanning recent checkpoints from last 7 days... Found 4 sessions.

Consolidation Output:
- Consolidated [New]:
- ## Rules: Always use bun run instead of npm run for building hexo pages. [ses_abc123]
- ## Gotchas: Netlify deployments will fail if public/ folder contains symlinks. [ses_xyz789]
- Updated:
- ## Architecture decisions: Upgraded to Astro-based frontend (2026-06-12). [ses_def456]
- Deleted:
- (none)

Health: project memory line count 34/200 and size 2.1KB/10KB.
Notes cleared.

1.2 手动运行 Distill 技能提炼

使用 /distill 命令,可以让 Agent 挖掘并封装近 30 天内的重复工作流:

1
2
/distill

输出结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Run one manual distill pass for the current project.

Inventorying:
- Skills found: 2 (.mimocode/skills/build-docs, .mimocode/skills/deploy-site)
- Commands found: 1 (.mimocode/commands/db-migrate)

Distillation Output:
- Shortlist:
- repeated workflow: compile and test hexo templates
evidence: [ses_101, ses_202]
frequency: 4 times in 20 days
recommended form: skill
- Created assets:
- Created skill: .mimocode/skills/test-theme/SKILL.md
Purpose: Run compilation and linting tests on Hexo theme modifications.
- Skipped:
- Git branch cleanups (frequency high but trivial tool operation).

2. 产物分析(Artifact Analysis)

为了了解进化机制对项目的实际影响,我们先看看它们所操纵和生成的产物结构。

2.1 项目持久记忆文件 MEMORY.md

dream 智能体的核心工作是增量改写位于数据目录下对应项目 ID 中的 MEMORY.md。其标准结构被限制为 4 个主要 H2 分节:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Project Memory

## Project context
A static blog engine using Hexo and deployed via Netlify CI/CD.

## Rules
- R1: Always use `pnpm` instead of `npm` or `yarn` [ses_abc123]
- R2: Never check in production environment variables to `.env.local` [ses_xyz456]

## Architecture decisions
- 2026-06-12: Switched deployment pipeline to GitHub Actions runner [ses_actions_01]

## Discovered durable knowledge
- Bun's File implementation in v1.1.0 does not support range queries [ses_bun_bug]

## Gotchas
- Netlify build fails if directory structure exceeds 5 nested layers [ses_net_5]

[!NOTE] 这里的每一条事实尾部都会附带原作者会话 ID(例如 [ses_abc123])。这是极度重要的元数据设计,便于后续 dream 再次整理时溯源,也方便人在阅读时回溯原始上下文。

2.2 自动提炼的 Skill 资产

distill 提取的产物则直接写入项目的工作树目录 .mimocode/skills/<name>/SKILL.md

1
2
3
4
5
6
7
8
9
10
11
12
13
14
---
name: test-theme
description: Run compilation and linting tests on Hexo theme modifications
---

# Test Theme Playbook

Use this skill when theme source files under `themes/` are modified.

## Steps
1. Run `pnpm run lint` to check JS/TS quality.
2. Run `pnpm run build` to verify Hexo static generation.
3. Inspect postcss compilation outputs under the public folder to verify compilation.

3. 系统架构与数据流

MiMo-Code 的自进化系统是一个由底层数据库支撑、中层服务索引、上层 Agent 执行并由沙箱过滤的多层架构。

以下是系统的模块结构与关键文件映射:

模块职责关键文件
自动触发层检测会话周期、判定冷却时间并静默生成后台子智能体进程auto-dream.ts
prompt.ts
策略设定层定义 dreamdistill 的行为范式、多阶段流程及约束规范dream.txt
distill.txt
agent.ts
检索服务层提供内存数据到本地 SQLite FTS5 的双向同步以及 BM25 的检索能力service.ts
reconcile.ts
paths.ts
fts-query.ts
安全沙箱层文件级权限拦截,防止后台自进化 Agent 污染用户源码或越权写库memory-path-guard.ts
external-directory.ts
运行轨迹层对会话日志、工具调用数据及检查点进行持久化存储与库级别过滤db.ts
checkpoint.ts

4. 核心实现机制(Core Mechanisms)

下面我们将拆解 4 个最为核心的技术实现机制。

4.1 自动双循环触发与静默调度机制

4.1.1 解决什么问题

频繁唤醒大模型整理记忆极其耗费资源(Token 成本与耗时)。如何在用户开启新会话时,既保证记忆时效性,又不影响用户前端的交互响应速度?

4.1.2 如何实现

packages/opencode/src/session/prompt.ts 的会话主循环中,当判定为会话的第一步且不是子会话时(step === 1 && !session.parentID),会自动读取系统配置并执行调度判断:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// packages/opencode/src/session/prompt.ts:2253-2287 (42e7da3)
if (step === 1 && !session.parentID) {
const cfg = yield* config.get()
const dreamTrigger = yield* shouldAutoDream(cfg).pipe(Effect.catch(() => Effect.succeed(false)))
const distillTrigger = yield* shouldAutoDistill(cfg).pipe(Effect.catch(() => Effect.succeed(false)))
const mdl = { providerID: lastUser.model.providerID, modelID: lastUser.model.modelID }

if (dreamTrigger || distillTrigger) {
const { AppRuntime } = yield* Effect.promise(() => import("@/effect/app-runtime"))
if (dreamTrigger) {
AppRuntime.runPromise(
Session.Service.use((svc) =>
Effect.gen(function* () {
const s = yield* svc.create({ title: AUTO_DREAM_TITLE })
const sp = yield* Service
yield* sp.prompt({ sessionID: s.id, agent: "dream", model: mdl, parts: [{ type: "text", text: DREAM_TASK }] })
}),
),
).catch((err) => log.error("auto-dream prompt failed", { error: String(err) }))
}
// ... 对 distillTrigger 做类似后台跑 Promise 的操作
}
}

其触发规则定义在 packages/opencode/src/session/auto-dream.ts 中。shouldAutoRun 函数的核心判定逻辑为:

  1. 从数据库 SessionTable 搜索历史会话中是否存在标题等于 "Auto Dream" 的最新记录:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // packages/opencode/src/session/auto-dream.ts:49-59 (42e7da3)
    const lastRun = yield* Effect.sync(() =>
    Database.use((db) =>
    db
    .select({ time_created: SessionTable.time_created })
    .from(SessionTable)
    .where(eq(SessionTable.title, input.title))
    .orderBy(desc(SessionTable.time_created))
    .limit(1)
    .get(),
    ),
    )
  2. 计算当前时间到上一次运行时间的差值 elapsed
    • lastRun 不存在,则查找首个非子会话创建时间作为项目起点。如果项目创建时间小于设定的周期(例如 7 天),则直接跳过(免得项目刚建还没产生足够积累就跑 Dream 浪费资源)。
    • elapsed 小于设定的 intervalMs,跳过执行。
    • 否则,通过后台 Detached Promise 执行 AppRuntime.runPromise 异步抛出任务,不阻塞前端 UI 的消息回复。

4.1.3 为什么这样设计

MiMo-Code 没有去实现一个常驻内存的 cron 定时器进程,而是将调度寄生在“会话初始化”的生命周期中。这种设计对于 CLI 和 IDE 插件工具来说是天然适配的——没有程序运行就无需调度;只要有新任务启动,就能立刻从轨迹数据库中检测时差并触发执行。

4.1.4 Trade-off(取舍)

  • 缺点:若用户连续几个月完全不使用本工具,在这期间就不会触发后台任务进行自动化演练。
  • 优点:规避了死机、无故占用后台 CPU 资源的窘境,完全按需触发。

4.2 基于 SQL 轨迹与 FTS 索引的记忆检索与校验

4.2.1 解决什么问题

单纯靠 LLM 的 Prompt 容易产生幻觉。如果 Dream 和 Distill Agent 在整理经验时,纯凭记忆联想,往往会编造错误的文件路径或指令。

4.2.2 如何实现

该机制分两步联动配合:

1. 基于 SQLite FTS5 的本地文件搜索引擎

packages/opencode/src/memory/reconcile.ts 中,系统维护了一张包含 FTS 虚拟索引表的内存映射关系:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// packages/opencode/src/memory/reconcile.ts:66-90 (42e7da3)
Database.use((db) =>
db
.insert(MemoryFtsTable)
.values({
path: absPath,
scope: loc.scope,
scope_id: loc.scope_id,
type: finalType,
body,
fingerprint,
last_indexed_at: Date.now(),
})
// ... onConflictDoUpdate 更新
)

在执行 Memory.search 时,系统会使用经典的 BM25 检索评分对所有 Markdown 节点(例如以前生成的 checkpoint.md)进行匹配。它还使用了一个相对 floor 过滤策略来过滤杂音:

1
2
3
4
5
// packages/opencode/src/memory/service.ts:131-133 (42e7da3)
const topScore = mapped[0].score
const cutoff = floorRatio > 0 ? topScore * floorRatio : -Infinity
return mapped.filter((r, i) => i === 0 || r.score >= cutoff).slice(0, limit)
2. Trajectory 底层校验

一旦 dreamdistill 从近期 checkpoints 或 notes 中发现了“候选事实”(比如一条 Gotcha 规则),系统不直接写入,而是强迫 Agent 使用 bash 工具和底层 SQLite 关系直接校验真实轨迹库。 在 dream.txt 的 Phase 3 中,有明确的 Query 模板和指令,让 Agent 检查并确定事实:

1
2
3
4
5
6
7
8
9
10
11
-- packages/opencode/src/agent/prompt/dream.txt:81-89 (42e7da3)
SELECT m.id, m.agent_id,
json_extract(p.data, '$.type') as part_type,
json_extract(p.data, '$.tool') as tool,
substr(p.data, 1, 800) as preview
FROM message m
JOIN part p ON p.message_id = m.id
WHERE m.session_id = '<SESSION_ID>'
AND json_extract(m.data, '$.role') = 'assistant'
ORDER BY m.time_created, p.time_created;

Agent 需要通过此 SQL 获取该 session 里的工具执行轨迹。只有当该事实具有用户明确的要求(Explicit statement)或两次以上工具失败重试的实际轨迹(Repeated evidence)支撑时,才被允许合并到持久记忆。

同样,distill 在 Phase 3 会执行一个聚合查询,查明究竟什么工具/指令在过去 30 天内执行次数最多,从而避免主观臆测:

1
2
3
4
5
6
7
8
9
10
11
12
13
-- packages/opencode/src/agent/prompt/distill.txt:106-118 (42e7da3)
SELECT json_extract(p.data, '$.tool') as tool,
substr(json_extract(p.data, '$.state.input'), 1, 200) as input_preview,
count(*) as n
FROM message m
JOIN part p ON p.message_id = m.id
WHERE json_extract(m.data, '$.role') = 'assistant'
AND json_extract(p.data, '$.type') = 'tool'
AND m.time_created > <CUTOFF_MS>
GROUP BY tool, input_preview
ORDER BY n DESC
LIMIT 50;

4.2.3 为什么这样设计

这种**「上层 NLP 意图提取 + 底层 SQL 关系化事实校验」**的双重验证(Double-Check)模式,将 LLM 的推理生成和确定性的数据库统计有机结合。FTS 快速定位“有什么想法”,SQLite SQL 查询判定“该想法是否符合客观历史事实”,极大提升了进化产物的可信度。

4.2.4 Trade-off(取舍)

  • 性能消耗:每次执行 FTS 搜索或重新索引时需要解析所有 Markdown 文件。如果记忆文件超长、超大,会导致延迟。这也是为什么系统严格限制 MEMORY.md 必须在 200 行以内的原因之一。

4.3 自动化非交互写入与安全隔离沙箱

4.3.1 解决什么问题

Dream 与 Distill 是自动跑在后台的任务,不能弹出 UI 界面询问用户“是否同意创建 Skill 文件夹”或“是否允许修改 MEMORY.md”,否则会直接卡死。但如果直接授予其全局写入权限,Agent 极有可能在意外的任务中把项目本身的源代码文件写坏。

4.3.2 如何实现

1. 非交互授权降级

packages/opencode/src/agent/config.ts 中,SYSTEM_SPAWNED_AGENT_TYPES 包含了 "checkpoint-writer", "dream", "distill"。这些 Agent 类型在进入执行循环时,其外部交互反馈权限机制会被强制降级为非交互模式(interactive: false)。在需要写文件的地方,如果写在沙箱外,将直接静默失败,而不会挂起等待用户点击“批准”。

2. 精准的 Memory Path Guard 校验

系统为 dreamdistill 配置了只允许读写 <DATA>/memory/ 目录的特权,并且通过代码层面的硬拦截进行了隔离防御。在 packages/opencode/src/tool/memory-path-guard.ts 中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// packages/opencode/src/tool/memory-path-guard.ts:122-134 (42e7da3)
if (agentName === "checkpoint-writer") {
if (!isCheckpointWriterAllowed(parts)) {
throw new Error(
`Path '${rel}' is not in the checkpoint-writer allowlist.\n` +
`Writer may only write to:\n` +
` ${memoryFile} — project memory (or memory-<topic>.md spillover)\n` +
` ${checkpointFile} — session checkpoint (or checkpoint-<topic>.md spillover)\n` +
` ${taskMemDir}/<task_id>/*.md — per-task narratives (any .md filename)\n` +
`You attempted: ${target}.`
)
}
return
}

同时,该机制完美限制了不同 Agent 对任务树进度文件的操作权限:

  • 只有被绑定了特定任务 ID 的子 Agent 才可以写自己对应的 tasks/<tid>/*.md,防止发生跨任务的日志篡改。
  • 主 Agent(主线程)则完全不允许碰这些路径,从而形成了清晰的控制链。

4.3.3 为什么这样设计

如果纯用 Prompt 约束 Agent:“你不能动 src 文件夹下的代码”,大模型一旦出现逻辑偏离就会越界。MiMo-Code 在底层文件工具的入口处(external-directory.ts:assertWriteAllowed)利用代码硬编码直接判断 Agent 类型和目标路径,彻底杜绝了进化引擎损坏主程序代码的隐患。

4.3.4 Trade-off(取舍)

  • 高度耦合:修改外部文件路径拦截规则或引入新的后台智能体类型时,必须同步修改 memory-path-guard.ts 中的白名单正则表达式。

4.4 增量合并与垃圾回收机制(Garbage Collection)

4.4.1 解决什么问题

记忆数据越积越多,会导致 MEMORY.md 发生信息爆炸,同样会吃掉大量上下文。怎么保证记忆始终是“高亮精华”?

4.4.2 如何实现

MiMo-Code 从三个维度实施了垃圾回收(GC):

1. 物理尺寸熔断与外溢(Spillover)

dream.txt 的最后设定了严格的产物上限:200 行(200 lines)且不大于 10KB。当文件体积逼近上限时,Agent 需要实施外溢策略,将某个具体的复杂技术问题外溢保存至独立子文件 memory/projects/<id>/memory-<topic>.md 中,而在 MEMORY.md 里只保留一条带有索引指向的单行链接:

1
2
- See memory-postgreshb.md (12 items) - Postgres connection pooling rules.

这让大文件变成了分布式存储的“星型拓扑”结构,便于按需按目录读取。

2. 擦除式更新(Pruning)

在 Dream 进行整理时,如果发现最近的 trajectory 数据或新写入的架构决定证明以前的某些规则(如某版本 bug)已经被消除,则 Agent 拥有主动删除/更新旧事实的责任(Phase 5)。

3. 消费后即刻清空(Consume-and-Clear)

在会话中产生的 notes.md 往往是杂乱无章的灵感和备忘录。Dream 智能体完成对 notes.md 的消费提炼后,会直接使用 Write 工具把 notes.md 重写恢复为干净的基础模板状态:

1
2
3
// packages/opencode/src/agent/prompt/checkpoint-writer.txt:114 (42e7da3)
For notes.md: Use Write tool to overwrite notes.md with the NOTES_TEMPLATE byte-for-byte...

这能够确保每次 Dream 的输入事实均是“增量”的,不会对同样的历史信息进行反复冗余的提炼。

4.4.3 为什么这样设计

人类的大脑在睡眠(Dreaming)时,会自动剪枝弱连接的神经元,并保留强化重要的反射弧。MiMo-Code 将这种生物学机制映射在了记忆体系中。只进不出、一味累加的记忆不仅消耗 Token,而且往往因为前后矛盾产生幻觉,主动剪枝对于长期自演化至关重要。


5. 执行路径追踪(Execution Path Trace)

下面我们以一次由于冷却时间满足触发的 Auto Dream 后台执行过程为例,理清底层的核心调用流水:

sequenceDiagram
    autonumber
    participant Main as Session Loop (prompt.ts)
    participant Svc as Session Service
    participant Scheduler as auto-dream.ts
    participant DB as SQLite DB (mimocode.db)
    participant Guard as memory-path-guard
    participant DreamAgent as Dream Subagent

    Main->>Scheduler: shouldAutoDream(config)
    Scheduler->>DB: 查询上一次包含 "Auto Dream" 的 Session 记录
    DB-->>Scheduler: 返回最近一次创建时间 t_last
    Note over Scheduler: 判定 elapsed = now - t_last > 7天
    Scheduler-->>Main: 返回 dreamTrigger = true

    Main->>Svc: svc.create({ title: "Auto Dream" })
    Svc-->>Main: 创建后台隔离会话 s_id
    Main->>DreamAgent: sp.prompt({ sessionID: s_id, agent: "dream", parts: [DREAM_TASK] })
    
    activate DreamAgent
    Note over DreamAgent: Phase 0/1: 定位数据与环境对齐
    DreamAgent->>DB: FTS 全文检索 recent checkpoint & notes
    DB-->>DreamAgent: 返回近期临时事实碎片
    
    Note over DreamAgent: Phase 2/3: SQL轨迹校验
    DreamAgent->>DB: 执行 SQL 统计工具调用、查询指定会话历史消息
    DB-->>DreamAgent: 返回确定性执行日志
    
    Note over DreamAgent: Phase 4/5: 合并与剪枝
    DreamAgent->>Guard: 写文件请求: projects/<pid>/MEMORY.md
    activate Guard
    Note over Guard: 检测为 dream 智能体,检验路径匹配
    Guard-->>DreamAgent: 校验通过,允许无询问静默写入
    deactivate Guard
    
    DreamAgent->>Guard: 重置清空 notes.md
    Guard-->>DreamAgent: 写入成功
    
    DreamAgent-->>Main: 返回 Dream 整理总结输出
    deactivate DreamAgent
    Note over Main: 主线程对用户返回消息,进化在后台默默完成

6. 运行时底层原理解析(Runtime & Internals)

我们继续探究一些 MiMo-Code 在 Node.js/Bun 运行时层面是如何支持这一自进化引擎的。

6.1 基于 ALS 的多实例与路径解耦

在 CLI / LSP 执行过程中,Agent 所面对的项目环境、当前工作目录(CWD)经常处于切换状态。为了保证 assertWriteAllowed() 能随时拿到正确的 ProjectIDSessionID,MiMo-Code 利用了异步上下文传播。 在 packages/opencode/src/tool/external-directory.tsassertWriteAllowed 方法中:

1
2
3
4
5
6
7
8
9
// packages/opencode/src/tool/external-directory.ts:89-95 (42e7da3)
const projectID = (() => {
try {
return (Instance.current?.project?.id as ProjectID | undefined) ?? ProjectID.global
} catch {
return ProjectID.global
}
})()

这里通过 Instance.current 从当前的上下文(由 AsyncLocalStorage 维护)中提取绑定了特定会话的运行实例。即使前台用户在执行复杂的任务,后台运行的 Detached Fiber 依然能精准确定当前的任务目录是哪个,做到了运行时状态的多路隔离与并发安全性。

6.2 索引重建的惰性加载(Lazy Reconcile)

我们经常担心,频繁的文件更新会不会导致 FTS 检索卡死? MiMo-Code 采用了惰性对齐的手段。数据库的全文对齐只发生在一类动作的开头:也就是当 Agent 决定调用 memory 工具执行查询时。 在 packages/opencode/src/memory/service.tssearch 方法中:

1
2
3
4
5
6
7
// packages/opencode/src/memory/service.ts:60-64 (42e7da3)
const cfg = yield* config.get()
if (cfg.checkpoint?.memory_reconcile_on_search ?? true) {
const cc = cfg.memory?.cc_index ? ccBase : undefined
yield* Effect.promise(() => reconcileMemory({ mimo: root, cc }))
}

只有在 Agent 确实调用了检索操作时,才会通过 reconcileMemory 收集两端根目录(mimo 内存树 + ccBase 即 Claude Code 外部工程记忆目录)下的文件,对比其物理大小与修改时间戳(fingerprint: ${stat.size}-${stat.mtimeMs})进行增量重建。对于没有检索发生的普通任务,该过程开销为零。


7. 性能与成本(Performance & Trade-offs)

对 Agent 进化机制的引入需要全面衡量其运行时性能与 Token 消耗。

7.1 系统开销与性能指标

操作维度实测耗时资源占用 / 消耗说明
双循环调度检测~2.5 ms单次 O(1) 索引查询,检测上次 session 记录,基本无感知
内存树扫描与 FTS 索引重建12 - 40 ms根据磁盘 I/O 速度而异,通过文件的 size 和修改时间进行增量校验,速度极快
FTS BM25 查询评分与相对 floor 过滤~5 msSQLite 纯 C 实现的 FTS 排序与过滤
Dream 智能体后台执行(单次)30 - 60 s会话创建与非交互多步骤验证,完全不占用主线程,通过后台 Detached Promise 机制,不影响前台交互
Distill 智能体后台执行(单次)45 - 90 s对 30 天轨迹进行 SQL 大批量统计分析及文件创建,占用后台执行资源,消耗一定 Token

7.2 核心取舍

  1. 以 Token 成本换上下文窗口与时效性
    • 牺牲:触发一次 Dream 往往需要 3 - 5 轮 of 后台 LLM 交互验证,每次后台执行会吃掉几千甚至上万 Token(取决于最近的 session 复杂程度)。
    • 获得:将数十万 Token 的原始会话细节压缩成了几百字节的高信噪比 facts。长远来看,这避免了主会话重复带入庞大的历史上下文,实现了更大的经济效应与检索召回率。
  2. 以严格白名单架构换取零人工交互
    • 牺牲:Agent 在后台虽然拥有强大的 bash 工具,却无法通过提示词操作任何 memory 文件夹外的代码。如果由于进化导致需要更新 .gitignore 等全局文件,Agent 只能发出倡议,无法自行操作。
    • 获得:保证了后台全自动化执行的绝对安全性,杜绝了 Agent 在无监督状态下跑坏项目代码或逻辑外泄的问题。

8. 与同类方案的对比

我们选择将 MiMo-Code (Dream & Distill) 与 Hermes Agent (Nous Research) 以及 OpenClaw 这两款极具代表性的开源 AI 智能体持久记忆方案进行横向对比:

维度MiMo-Code (Dream & Distill)Hermes Agent (Nous Research)OpenClaw
存储媒介本地 Markdown + SQLite 轨迹库与 FTS 索引本地 Markdown (~/.hermes/memories/) + FTS5 state.db本地 Markdown (memory/YYYY-MM-DD.md) + SQLite (Hybrid embeddings & BM25)
整理与去重方式后台非交互大模型增量合并 + 相对 floor 剪枝与外溢机制主 Agent 使用内置 Memory 工具实时修改,超长时触发合并压缩会话级 Compaction(压缩)前执行 “Memory Flush” 刷盘保存
事实校验与防幻觉极强。通过 SQLite 校验 raw 轨迹数据(Message/Part 统计)一般。完全基于 Agent 在上下文内自我总结和推理一般。基于预设规则和会话滑动窗口总结
技能与工作流进化有。使用 Distill 提炼重复命令与操作,并包装为 Skills/Agents有。任务完成后生成可复用的 Markdown 技能 Playbook无自动提炼。主要通过用户自定义插件和扩展
对源码的安全性有。Memory Path Guard 严格沙箱拦截(硬编码级别)较弱。主要依赖模型遵从 Prompt 规范或外部沙箱较弱。主要依赖 Agent 逻辑与提示词隔离
可读性与编辑友好度极高。直接暴露 MEMORY.md 给用户,支持手工合并修改极高。可随时手动修改 MEMORY.mdUSER.md极高。平铺的 Daily Logs 和 Wiki markdown 文件

8.1 方案对比分析

  1. 与 Hermes Agent 的对比 Nous Research 的 Hermes Agent 极具特色地将记忆视作核心主权,其采用 ~/.hermes/memories/ 目录存放全局偏好(USER.md)与项目环境事实(MEMORY.md)。然而,Hermes 的记忆整合是实时且阻塞式的——主 Agent 在对话中若发现需要保存的内容,会实时调用工具改写这些 Markdown,并且在上下文容量吃紧时在主进程内直接进行合并。 相比之下,MiMo-Code 的 Dream 机制采用了“离线异步后台协程”的设计,并且引入了轨迹库(Trajectory DB)的双重验证。Hermes 在写入记忆时完全基于大模型自身的联想与推理,容易写错文件路径或编造命令;而 MiMo-Code 能够利用 SQLite 查明当前会话里确实运行过该命令并成功或失败过,才允许在 Dream 中落地为知识,防幻觉能力显著优于 Hermes。此外,在技能进化维度,Hermes 仅能在任务结束时生成平铺的 Skill Playbook Markdown,而 MiMo-Code 能够通过 Distill 提炼出自定义子 Agent 和快捷 Command。

  2. 与 OpenClaw 的对比 OpenClaw 特别强调本地文件的主权,采用每日日志(memory/YYYY-MM-DD.md)作为临时记忆面,并辅以持久化的知识库(MEMORY.md)。OpenClaw 的持久化策略是在会话因为长度超限需要进行 Compaction(消息滑动压缩)之前,触发一次 “Memory Flush”(内存刷盘)。 这一设计解决了单会话的信息遗忘问题,但是它的致命痛点在于缺乏全局的增量剪枝与垃圾回收。随着时间推移,OpenClaw 的 Daily Logs 会无限膨胀,而其 MEMORY.md 往往缺乏像 MiMo-Code 那样的“10KB/200行物理熔断和外溢机制”,极易退化成一个充斥着过期 bug、冗余规则的庞大历史垃圾堆。在安全防线维度,OpenClaw 的写操作缺少 MiMo-Code 这种针对 Agent 角色(如 checkpoint-writer)进行严密路径限定的硬编码沙箱拦截,极易因为模型失控而污染项目主源码。

8.2 Hacker News 社区视角与行业洞察

在 Hacker News 以及相关开发者社区关于 AI 智能体脚手架(Harness)的探讨中,MiMo-Code 的设计引发了关于智能体未来演进路线的深度讨论,主要聚焦于以下三个维度:

  1. Harness War(智能体底座之争)与模型锁死(Model Lock-in) 社区广泛担忧,如果智能体的长期记忆和偏好完全被绑定在特定的闭源云端大模型上,或者被封装进非标的私有记忆格式中,将会产生严重的“模型锁死”风险。根据 Hacker News 社区Nous Research Hermes Agent 等项目的热议,MiMo-Code 作为一款开源的 Harness,通过将记忆与技能解耦成标准 Markdown 文件(如 MEMORY.md)和通用的 SQLite 本地轨迹库,实现了与具体 LLM 提供商的解耦。这种跨模型兼容性意味着开发者可以在不同的底座模型(如 Claude, GPT, DeepSeek)之间自由切换,而无需重构其自进化出的知识体系。
  2. 脚手架质量乘数效应(Harness Quality Multiplier) 不少开发者指出,当前的“智能体军备竞赛”不能仅依靠底座模型上下文窗口 of 扩大或推理能力的微调。相反,智能体脚手架(Harness)的设计质量正成为核心的能力乘数(Multiplier)。在有关智能体内存管理的趋势探讨中(参见 Hacker News 关于文件系统记忆的讨论),社区对于使用文件系统(Filesystem)替代 Vector DB 存放记忆的趋势表示了强烈认可。MiMo-Code 的 Dream/Distill 双循环架构证明了:即使在模型能力保持不变的情况下,通过精妙的静态文件去重、事实校验与技能沉淀,也能够让智能体在解决复杂、长上下文工程任务时的成功率呈现出指数级增长。
  3. 安全性与远程代码执行(RCE)的隐忧OpenClaw 等高频迭代的开源智能体项目,由于其工具调用(特别是 Bash/Terminal 执行)权限过于宽泛,在 Hacker News 上常被指出存在由于提示词注入(Prompt Injection)而导致 RCE(远程代码执行)等高危安全漏洞(参见 Hacker News 上的安全讨论)。MiMo-Code 的核心防线在于其 Memory Path Guard 的硬编码级别白名单沙箱。后台非交互式的 checkpoint-writerdream 协程被物理隔离在 Global.Path.data/memory 目录下,彻底杜绝了模型失控后恶意修改项目源码或删除系统关键文件的可能,提供了一种在无监督进化与系统安全之间达成平衡的绝佳范式。

9. 源码阅读地图 (Source Code Map)

如果你想进一步通过阅读 MiMo-Code 源码验证其细节,建议按照以下路线图进行:

  1. 首先从 触发判断 开始:
    • 阅读 auto-dream.ts:理解 shouldAutoRun 是怎么查库和判定冷却时长的。
    • 查看 prompt.ts:理解后台 Fiber (Detached Promise) 创建新后台会话的方法。
  2. 随后切入 Agent 行为设定
    • 阅读系统提示词 dream.txtdistill.txt:观察它们如何从 Phase 0 逐步进阶到最终的修改与输出。
  3. 接着研究 底层检索服务
    • 阅读 reconcile.ts:研究 Markdown 文件的指纹变更以及是如何利用 SQLite 写入 FTS 数据库的。
    • 深入 service.ts:理解 FTS BM25 查询评分排序、条件过滤与 cutoff floor 的具体计算实现。
  4. 最后阅读 安全防火墙
    • 阅读 memory-path-guard.ts:剖析 Agent 在工具层是如何被拦截判断并防范越权写入的。

10. 最佳工程实践与建议

  1. 设定合理的运行周期: 对于超高频调用的团队,默认的 7 天 Dream 与 30 天 Distill 是被实践证明较为稳妥的。你可以在项目根目录下放置 mimocode.json 进行个性化配置:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    {
    "dream": {
    "auto": true,
    "interval_days": 5
    },
    "distill": {
    "auto": true,
    "interval_days": 15
    }
    }
  2. 人工干预与微调: 因为 MiMo-Code 将持久化记忆通过平铺直叙的 Markdown 文件(MEMORY.md)保存在你的工程目录中。你完全可以在开发过程中像改普通文件一样手工纠正、补充或删除里面的条款。下次 Agent 运行时,会自动通过 FTS 感知你手动编写的内容,并把它们当做绝对可信的真理执行。
  3. 避免技能泛滥: 在编写新技能时,如果觉得 Distill 智能体自动提炼的 Skill 存在细节偏差,可直接进入 .mimocode/skills/ 中对 SKILL.md 的步骤说明进行润色,而不是删掉重新生成。Distill 提炼前会调用 Phase 1 扫描现有资产,直接原地修改会使其在下个周期智能复用现有技能。

11. 总结

MiMo-Code 的自进化经验积淀架构,代表了下一代 AI Agent 的发展方向:不再寄希望于单一会长上下文大模型的记忆能力,而是将记忆的「记录」、「整理」与「提炼」作为一种独立的系统级协程存在。

通过 checkpoint-writer 实现短期的“工作记忆”记录;通过 Dream 在后台异步对历史轨迹和临时便签进行多阶段的数据库校验与长期的事实整合;最后通过 Distill 完成面向业务工作流的技能抽象与可重用资产化。这套基于 SQLite FTS、轨迹数据与硬白名单沙箱组成的双循环架构,成功突破了单会话 Agent 的限制,为大规模团队中 Agent 的“工作经验进化”提供了一条极具参考价值的底层链路。


分析版本声明

  • 分析仓库MiMo-Code
  • 提交哈希 (Commit Hash)42e7da3d51dba1129cd3abfa214e29f7385924a3
  • 标记 (Tag/Branch)main