# Memory 系统与元思考机制

> 状态：已实现（2026-04）。本文档同时承担**设计理由**和**使用规范**。
> 入口、工具、API 清单见文末"十、实现与入口"。
> 一~九节解释"为什么这么做"，改动前请先读懂论证。

---

## 概述

本文档设计两个紧密关联的能力：

1. **Memory**：为需要跨任务维持身份的 Agent 提供持久化记忆（Markdown 文件）
2. **统一元思考机制**：整合现有的知识提取（reflection）、知识评估（knowledge_eval）和新增的记忆反思（dream），形成完整的"Agent 自我认知"能力

---

## 一、现有元思考机制梳理

当前框架已实现两种元思考能力，都基于侧分支（side branch）机制：

### 1.1 知识提取（reflection 侧分支）

**目的**：从执行过程中提取客观知识，保存到 KnowHub（全局共享知识库）。

**触发时机**：
- 压缩前（`_manage_context_usage`，`runner.py:825-829`）
- 任务完成后（`runner.py:1834-1838`，`enable_completion_extraction`）

**输出**：调用 `upload_knowledge` 工具，保存 experience/tool/strategy/case 到 KnowHub。

**Prompt**：`REFLECT_PROMPT`（压缩时）和 `COMPLETION_REFLECT_PROMPT`（任务完成后），定义在 `agent/core/prompts/knowledge.py`。

**已知问题**：任务完成时触发的 reflection 使用 `config.knowledge.get_reflect_prompt()`（`runner.py:1249`），没有区分压缩场景和完成场景。应该在完成场景使用 `get_completion_reflect_prompt()`。

### 1.2 知识评估（knowledge_eval 侧分支）

**目的**：评估被注入的知识是否有用，记录到本地 `knowledge_log.json`。

**触发时机**（详见 `agent/docs/cognition-log.md`）：
- Goal 完成时（`store.py:update_goal`，设置 `pending_knowledge_eval` 标志）
- 压缩前（必须在压缩前完成评估，否则执行上下文丢失）
- 任务结束时（兜底）

**输出**：每条被注入的知识获得评估（irrelevant / unused / helpful / harmful / neutral），写入 `knowledge_log.json`。

**当前局限**：评估结果只存本地，不自动回传 KnowHub。只有用户通过 API 手动反馈才同步。

### 1.3 侧分支队列机制

两种元思考通过 `force_side_branch` 队列协调执行顺序（`runner.py:1198-1207`）：

```
压缩触发时的典型队列：
  ["reflection", "knowledge_eval", "compression"]
  
任务完成时：
  ["knowledge_eval"]（先评估）→ ["reflection"]（再提取）
```

每个侧分支执行完后 pop 队首，继续下一个，直到队列清空回到主路径。

---

## 二、缺失的能力：个人记忆

### 问题

现有的两种元思考都面向**全局共享知识**（KnowHub）。但有一类 Agent 需要维护**主观的、属于自己的长期记忆**：

- 品味偏好、策略判断、风格积累、用户反馈
- 属于特定 Agent 身份，不是公共知识
- 需要人类能直接阅读和修改
- 跨多次 trace 持续积累和演化
- 可能被同一身份下的多个 Agent run 共享读写

KnowHub 不适合承担这个职责——它是"大众点评"，不是"个人日记"。

### 记忆文件

记忆以 Markdown 文件存储在指定目录下。每个文件覆盖一个语义维度。

```
{base_path}/
├── taste.md        # 偏好判断
├── strategy.md     # 当前策略
├── skills.md       # 积累的技巧
└── ...
```

**为什么是 Markdown 文件**：
- 人类可直接阅读和修改（vim/VS Code 打开就能改）
- Git 版本控制
- Agent 用 read_file/write_file 工具即可操作，无需新增工具
- 文件数量少（几个到十几个），不需要检索能力

**共享读写**：同一身份下的多个 Agent run 可以读写同一组记忆文件。哪个 Agent 该关注哪些文件、怎么更新，由 dream prompt 来定义。

---

## 三、统一元思考模型

### 三种元思考及其定位

| | 知识提取（已有） | 知识评估（已有） | 记忆反思（新增） |
|---|---|---|---|
| **回答的问题** | 我学到了什么客观知识？ | 给我的知识有没有用？ | 这些经历对我的偏好/策略意味着什么？ |
| **输出目的地** | KnowHub（全局共享） | knowledge_log.json（本地） | 记忆文件（Agent 身份私有） |
| **触发时机** | 压缩前、任务完成后 | Goal 完成、压缩前、任务结束 | Dream 时（见下文） |
| **时效性要求** | 高（压缩会丢上下文） | 高（压缩会丢上下文） | 低（可以延迟处理） |
| **实现机制** | reflection 侧分支 | knowledge_eval 侧分支 | dream 操作（新增） |

**关键区分**：知识提取和知识评估必须在上下文丢失前完成（压缩前/任务结束时），所以是侧分支、即时触发。记忆反思可以延迟——甚至应该延迟，因为用户可能还有反馈、Agent 可能继续执行。

### 为什么记忆反思不在 trace 结束时做

trace 结束只意味着 Agent 行动完一个轮次。后续可能发生：
- 用户在飞书里说"这个方向不对"
- Agent 被唤醒在同一任务下继续执行
- 新的 trace 产生了推翻前一个 trace 结论的信息

如果 trace 一结束就做记忆反思，这些后续信息会被忽略。记忆反思的价值在于**综合一段时间的经历**，不是记录每次行动的即时感受。

### 但知识提取仍然在压缩/完成时做（采用"提取-审核-提交"两阶段）

知识提取必须在压缩/完成时做，因为压缩会删除历史，不在压缩前提取，知识就永久丢失。

但"立即 upload 到 KnowHub"这一步并不需要立即做。所谓"客观知识"也可能被后续推翻：

- 工具用法可能被后续 trace 发现是错的（例如某个参数其实有副作用）
- 调研结论可能被用户反馈推翻
- 一次 trace 的"成功经验"在更长窗口看可能是反模式

如果 reflection 直接 upload 到 KnowHub，错误知识会立刻污染全局检索，影响所有 Agent。

**两阶段方案**：

```
Step 1: extract（自动，压缩前/任务结束）
  Reflection 侧分支提取知识 → 写 cognition_log: type="extraction_pending"
  不调用 upload_knowledge（信息保全已完成）

Step 2: review（人工，CLI 里逐条决策）
  approve / edit / discard → 写 cognition_log: type="extraction_reviewed"

Step 3: commit（人工触发，批量上传）
  把 reviewed=approved 的批量 upload_knowledge
  写 cognition_log: type="extraction_committed"
```

review 和 commit 分开的理由：review 是逐条语义判断（要不要、内容对不对），commit 是机械批量动作。两者分离允许用户分批 review、最后一次 commit；也允许撤回 review 决策。

**默认行为**：所有 Agent（包括默认 Agent 和 memory-bearing Agent）`reflect_auto_commit` 默认关闭，pending 提取必须人工 review + commit 才会进 KnowHub。如需自动直通（保留旧行为），手动在 `KnowledgeConfig` 里打开 `reflect_auto_commit=True`。

这与"信息保全 vs 全局发布解耦"的原则一致 —— 压缩前必须做的是**保全**（写本地 cognition_log），**发布**到 KnowHub 可以延迟到有人确认时。

---

## 四、Dream：记忆反思的触发与流程

### 什么是 Dream

Dream 是 memory-bearing Agent 的记忆整理操作。它不是一个侧分支（不在某个 trace 的执行过程中插入），而是一个**独立的顶层操作**，回顾多个 trace 的执行历史，更新记忆文件。

### 触发方式

- Agent 主动调用 `dream` 工具（"我觉得该整理一下了"）
- 外部调度触发（定时、或人工 CLI 触发）
- 框架可以在 run 启动时检测距上次 dream 的时间/trace 数量，建议 Agent dream

### 反思状态追踪

```
Trace 模型新增字段：
- reflected_at_sequence: Optional[int]    # 上次记忆反思时的最新 message sequence
                                           # None = 从未被记忆反思处理
```

反思摘要不存在 Trace 模型中，而是作为 `reflection` 事件写入 `cognition_log.json`（详见 `agent/docs/cognition-log.md`）。

- Agent run 产生新 message → `reflected_at_sequence` 自然落后于实际 sequence
- 记忆反思完成 → 更新 `reflected_at_sequence` 为当前最新 sequence
- Dream 扫描 `reflected_at_sequence < latest_sequence` 的 trace

### Dream 流程

```
Dream 触发
  │
  ├─ Step 1: 扫描该 Agent 身份下所有 trace
  │   找到 reflected_at_sequence < latest_sequence 的 trace
  │
  ├─ Step 2: Per-trace 记忆反思（逐个 trace）
  │   对每个需要反思的 trace：
  │   a. 加载 reflected_at_sequence 之后的消息（增量）
  │   b. 同时加载该 trace 的 cognition_log.json（查询、评估、提取事件）
  │   c. 用 reflect_prompt 生成反思摘要
  │   d. 摘要作为 reflection 事件写入 cognition_log.json
  │   e. 更新 reflected_at_sequence
  │
  ├─ Step 3: 跨 trace 整合
  │   a. 收集各 trace 的 reflection 事件（cognition_log 中 type="reflection"）
  │   b. 读取当前记忆文件
  │   c. 汇总 cognition_log 中的评估趋势（多次 harmful/unused 的 source 模式）
  │   d. 用 dream_prompt 指导 LLM 更新记忆文件
  │   e. 标记 reflection 事件为已消化
  │
  └─ 完成
```

### Per-trace 反思的输入

反思 prompt 看到的不只是"发生了什么"，还包括知识评估结果：

```
## 执行过程
[该 trace 中 reflected_at_sequence 之后的消息]

## 知识使用情况（来自 cognition_log.json）
查询 1（sequence 42）："ControlNet 相关工具知识"
  → source knowledge-a1b2: helpful — "准确定位了问题"
  → source knowledge-c3d4: irrelevant — "与当前任务无关"
  → source knowledge-e5f6: harmful — "建议的方法已过时"

## 请反思
1. 这次执行中有什么值得记住的经验？
2. 哪些知识的评估结果反映了我的判断需要调整？
3. 用户的反馈（如果有）说明了什么？
```

这样，已有的 knowledge_eval 结果直接成为记忆反思的输入，不需要重复评估。

### Dream 整合的输入

Dream prompt 看到的是：

```
## 最近的反思摘要
[各 trace 的 reflect_summary，每份几百 token]

## 知识评估趋势（汇总自各 trace 的 cognition_log）
- 最近 N 个 trace 中，被评为 harmful 的 source：[列表]
- 被评为 unused 的高频 source 类型：[统计]
- 被评为 helpful 的查询模式：[统计]

## 当前记忆文件
[各文件内容]

## 请更新记忆
[dream_prompt 的具体指导]
```

---

## 五、与现有实现的集成

### 不改动的部分

| 现有机制 | 保持不变 | 原因 |
|---|---|---|
| reflection 侧分支 | ✅ | 知识提取到 KnowHub，时效性要求高，必须在压缩前做 |
| knowledge_eval 侧分支 | ✅ | 知识评估，时效性要求高，必须在压缩前做 |
| force_side_branch 队列 | ✅ | 侧分支排序机制，成熟可靠 |
| cognition_log.json | ✅ | 统一事件流存储（原 knowledge_log.json 扩展），dream 直接读取 |
| 三个评估触发点 | ✅ | Goal 完成/压缩前/任务结束 |

### 需要修改的部分

**1. 任务完成时的 reflection prompt 选择**

当前 `runner.py:1249` 始终使用 `get_reflect_prompt()`。应区分场景：

```python
# runner.py:1248-1249 修改
if branch_type == "reflection":
    if break_after_side_branch:  # 任务完成后的反思
        prompt = config.knowledge.get_completion_reflect_prompt()
    else:  # 压缩前的反思
        prompt = config.knowledge.get_reflect_prompt()
```

这是一个独立的 bug fix，不依赖 Memory 系统。

**2. Trace 模型扩展**

`agent/trace/models.py:Trace` 新增字段：

```python
reflected_at_sequence: Optional[int] = None    # 上次记忆反思的 sequence
# 反思摘要存在 cognition_log.json 中（type="reflection" 事件），不在 Trace 模型中
```

**3. RunConfig 扩展**

`agent/core/runner.py:RunConfig` 新增可选字段：

```python
memory: Optional[MemoryConfig] = None
```

**4. KnowledgeConfig 扩展**

`agent/core/runner.py:KnowledgeConfig`（或对应类）新增字段：

```python
reflect_auto_commit: bool = False
# False（默认，所有 Agent）: reflection 只写 cognition_log: type="extraction_pending"
#                          人工通过 CLI review + commit 才进 KnowHub
# True（手动开启）         : reflection 直接 upload_knowledge，保留旧的"提取即上传"行为
```

**5. Reflection 侧分支行为变更**

当前 reflection 的 prompt 直接指导 LLM 调用 `upload_knowledge`。需要改为：
- `reflect_auto_commit=False` 时：prompt 指导 LLM 调用新的 `record_pending_extraction` 工具（仅写 cognition_log）
- `reflect_auto_commit=True` 时：保持当前行为

或者更简洁的实现：reflection 始终调用 `record_pending_extraction`，由侧分支结束后的 hook 根据 `reflect_auto_commit` 决定是否立即调用 `commit_approved`（视为全部 approved）。这避免了 prompt 分叉。

### 新增的部分

**1. MemoryConfig**

```python
@dataclass
class MemoryConfig:
    """持久化记忆配置"""

    base_path: str = ""                          # 记忆文件目录
    files: Optional[Dict[str, str]] = None       # {路径: 用途说明}
    # key 是相对 base_path 的路径，支持嵌套（如 "core/identity.md"）或 glob
    # （如 "relationships/*.md"）。框架只负责按 key 读文件内容注入上下文，
    # 组织结构由配置者决定。
    dream_prompt: str = ""                       # Dream 整合 prompt（空用默认）
    reflect_prompt: str = ""                     # Per-trace 反思 prompt（空用默认）
```

**2. Run 启动时记忆加载**

Memory-bearing Agent 的 run 启动时，框架按 `files` 的 key 依次解析（直接路径或 glob 匹配），读取命中的文件内容以字符串形式注入上下文。Agent 可用 write_file 新增文件；只要新文件的路径匹配某条 key（直接路径或 glob），下次 run 启动时自动加载。

**3. Dream 操作**

以 `dream` 工具形式提供，Agent 可主动调用：

```python
@tool
async def dream() -> ToolResult:
    """整理长期记忆。回顾最近的执行历史，更新记忆文件。"""
    # 1. 扫描需要反思的 trace
    # 2. 逐个 per-trace 反思
    # 3. 跨 trace 整合，更新记忆文件
```

也可以作为 `AgentRunner` 的方法暴露，供外部调度直接调用。

**4. 提取审核 CLI 流程**

为支持"提取-审核-提交"两阶段（见第三节），新增 `agent/cli/extraction_review.py` 模块。**不是 Agent 工具**（Agent 不应自我审核），是 CLI 内部模块 + 独立可执行脚本：

```python
# agent/cli/extraction_review.py

async def list_pending(trace_id: str) -> list[PendingExtraction]:
    """读 cognition_log，返回 type=extraction_pending 且未 reviewed 的条目"""

async def review_one(
    trace_id: str,
    extraction_id: str,
    decision: Literal["approve", "edit", "discard"],
    edited_content: Optional[str] = None,
) -> None:
    """写 reviewed 事件到 cognition_log"""

async def commit_approved(trace_id: str) -> CommitReport:
    """批量上传 approved 条目到 KnowHub，写 committed 事件"""
```

可独立调用：

```bash
python -m agent.cli.extraction_review --trace XXX --list
python -m agent.cli.extraction_review --trace XXX --commit
```

**集成到现有交互式 CLI**（`agent/cli/interactive.py:174` 的菜单）扩展两项：

```
  1. 插入干预消息并继续
  2. 触发经验总结（reflect）         ← 现有
  ...
  8. 审核待提交知识（review）        ← 新增
  9. 提交已审核知识到 KnowHub        ← 新增
```

`8` 进入交互式 review 循环：

```
[1/3] tool 经验
─────────────────────
nanobanana 工具的 strength 参数 < 0.3 时会丢失原图轮廓...
─────────────────────
[a]pprove / [e]dit / [d]iscard / [s]kip / [q]uit:
```

`9` 显示 approved 列表 + 用户最终确认 → 调 `commit_approved`，输出 commit 报告（成功/失败条数、KnowHub 返回的 ID）。

**实现注意**：
- 现有 `perform_reflection`（`interactive.py:269`）走 HTTP API（`/api/traces/{trace_id}/reflect`）。新流程同样应该走 API 端点（如 `POST /api/traces/{trace_id}/extractions/{id}/review`、`POST /api/traces/{trace_id}/extractions/commit`），让未来 Web UI 能复用同一套审核流，而不是 CLI 直接读写 cognition_log 文件。
- "edit" 分支允许用户直接修改 LLM 生成的 markdown 内容；初版只支持改正文文本，后续可扩展到改类型/metadata。

---

## 六、完整的元思考数据流

```
Agent 执行任务（Trace）
  │
  ├─ 知识查询（ask）→ cognition_log: type="query"（含整合回答 + source_ids）
  │
  ├─ Goal 完成 → 触发 knowledge_eval 侧分支 → cognition_log: type="evaluation"
  │
  ├─ 压缩触发 →
  │   队列: [reflection, knowledge_eval, compression]
  │   reflection: 提取客观知识 → cognition_log: type="extraction_pending"
  │                            （默认不直接 upload，等人工 review）
  │   knowledge_eval: 评估各 source → cognition_log: type="evaluation"
  │   compression: 压缩上下文
  │
  ├─ 任务完成 →
  │   knowledge_eval（如有 pending）→ cognition_log: type="evaluation"
  │   reflection → cognition_log: type="extraction_pending"
  │
  ├─ 人工审核（CLI 触发，可发生在任意时刻）→
  │   逐条 approve/edit/discard → cognition_log: type="extraction_reviewed"
  │   批量 commit → upload_knowledge → KnowHub
  │                + cognition_log: type="extraction_committed"
  │
  └─ Trace 状态更新（新消息使 reflected_at_sequence 落后）

         ···时间流逝，可能有多个 trace···

Dream 触发（Agent 主动调用 / 外部调度）
  │
  ├─ Per-trace 记忆反思
  │   输入: 未反思的消息 + cognition_log 中的 query/evaluation/extraction 事件
  │   输出: cognition_log: type="reflection"
  │
  ├─ 跨 trace 整合
  │   输入: 各 trace 的 reflection 事件 + evaluation 趋势 + 当前记忆文件
  │   输出: 更新后的记忆文件（taste.md, strategy.md, ...）
  │
  └─ 记忆文件被下次 run 加载 → 影响 Agent 行为 → 新的 Trace → ...
```

### 三种元思考的时间线

```
Trace 执行中:
  ──[Goal完成]──knowledge_eval──[压缩]──reflection→knowledge_eval→compression──

Trace 结束后:
  ──knowledge_eval──reflection(completion)──

之后某个时刻:
  ──dream──per-trace记忆反思──跨trace整合──更新记忆文件──
```

即时的元思考（knowledge_eval、reflection）保护信息不被压缩丢失。
延迟的元思考（dream）在全局视角下更新个人记忆。两者互补。

---

## 七、记忆模型全景

Memory 和 Knowledge 是**两条平行的线**，而不是抽象层级。区分维度是"主观 vs 客观"和"私有 vs 共享"。Memory 不会"升级"成 Knowledge，反过来也不会。

```
                    ┌─────────────────────────────┐
                    │ Trace（任务状态 / 工作记忆）  │
                    │ Messages + Goals             │
                    │ + cognition_log              │
                    └──────────┬──────────────────┘
                               │
              dream 反思       │      reflection 提取（→ pending）
              （延迟、可选）    │      knowledge_eval 评估
                               │      （即时、必做）
                  ┌────────────┴───────────┐
                  ▼                        ▼
        ┌──────────────────┐     ┌──────────────────────┐
        │ Memory           │     │ Knowledge            │
        │ Agent 身份私有    │     │ KnowHub 全局共享      │
        ├──────────────────┤     ├──────────────────────┤
        │ 主观 / 偏好 / 策略 │     │ 客观 / 工具 / 调研    │
        │ Markdown 文件     │     │ DB + 向量索引         │
        │ 人类可直接编辑     │     │ 经 review 才入库      │
        │ 来源: dream       │     │ 来源: reflection      │
        └────────┬─────────┘     └──────────┬───────────┘
                 │                          │
                 └────注入下次 run──────────┘
```

**两条线的交互**（不是层级关系，是同源 + 互相参考）：

- 都源自同一个 Trace（cognition_log 是共同的事件流）
- dream 在生成记忆摘要时可以参考 cognition_log 中的 evaluation 趋势（来自 Knowledge 这条线）
- reflection 也可以参考 Memory 来判断"这条经验我已经记过了"
- 但二者的**读者不同**：Memory 只服务于同一身份的未来 run；Knowledge 服务于所有 Agent

---

## 八、两类 Agent

| | 默认 Agent | Memory-bearing Agent |
|---|---|---|
| 知识提取（reflection） | ✅ 配置 KnowledgeConfig | ✅ 配置 KnowledgeConfig |
| 知识评估（knowledge_eval） | ✅ 自动 | ✅ 自动 |
| 个人记忆 | ❌ | ✅ 配置 MemoryConfig |
| Dream | ❌ | ✅ 可调用 dream 工具 |
| Run 启动加载记忆 | ❌ | ✅ 自动注入 |

Memory 是 opt-in 的增量能力。但**知识提取的提交行为变了**：默认 Agent 也不再自动 upload 到 KnowHub，必须通过 CLI 人工 review + commit；如需保留旧的"提取即上传"行为，手动设置 `KnowledgeConfig.reflect_auto_commit=True`。

---

## 九、开放与已决问题

`[DECIDED]` 已有落地结论；`[OPEN]` 尚未决定，等真实运行数据再定。

1. `[DECIDED]` **记忆注入方式** → system prompt 末尾追加。见 `runner.py:_build_system_prompt` 里调 `format_memory_injection`。代价：若记忆文件很大，每轮 LLM 调用都带 —— 暂时接受，等实际观察到膨胀再换方案。
2. `[OPEN]` **并发写冲突**：多个 Agent run 同时写同一个记忆文件怎么办？文件锁？还是 dream 统一写、其他 run 只读？当前没做并发保护，假设 dream 是单一写入方。
3. `[OPEN]` **记忆膨胀**：记忆文件越来越长怎么办？`DEFAULT_DREAM_PROMPT` 已写"在原有基础上演进不要重写"，但是否真能控制住要看实际使用。
4. `[OPEN]` **Per-trace 反思的成本控制**：很短的 trace 不值得反思。当前 `per_trace_reflect` 无下限阈值，所有 `reflected_at_sequence < last_sequence` 的 trace 都会反思。
5. `[OPEN]` **Knowledge eval 结果回传 KnowHub**：仍然只存本地 cognition_log。
6. `[DECIDED]` **Dream 中评估趋势的呈现方式** → LLM 直接读 cognition_log 原始事件。见 `dream.py:_build_reflect_input`，把 query / evaluation / extraction_pending / extraction_committed 事件摘要化后一并塞给 LLM，不做预计算统计。
7. `[DECIDED]` **Dream 操作的实现形式** → 两者都提供。Agent 主动调用走 `dream` 工具（`agent/tools/builtin/memory.py`，`memory` 组），外部调度走 `AgentRunner.dream()` 方法。
8. `[OPEN]` **未 review 的 pending 提取何时清理**：目前没有 TTL，pending 无限期累积。等观察积压速度再定（例如 30 天未 review 自动 discard / 归档）。
9. `[OPEN]` **review 的"edit"分支允许多深**：初版只支持改 markdown 字段（task/content/score/tags）。改 type 或 metadata 目前需 discard 重写。
10. `[OPEN]` **批量 review 的辅助能力**：当前逐条看。未做批量 approve / 相似条目去重 / LLM 预筛。
11. `[OPEN]` **Dream 的 JSON 解析脆弱性**：`cross_trace_integrate` 依赖 LLM 严格输出 `{updates:[...]}` JSON（见 `dream.py:_parse_dream_output`）。真实 LLM 可能偶尔加前言、用不同键名。首次线上运行需监控 parse 失败率，必要时加重试 + 更严格 prompt。

---

## 十、实现与入口

2026-04 落地清单，供看代码时快速定位。

### 10.1 数据层

| 改动 | 位置 |
|---|---|
| Trace 新字段 `reflected_at_sequence` | `agent/trace/models.py:Trace` |
| cognition_log 事件 schema（含新增的 extraction_pending/reviewed/committed + reflection） | `agent/trace/store.py:append_cognition_event` docstring |

### 10.2 提取-审核-提交两阶段

| 职责 | 位置 |
|---|---|
| LLM 暂存用工具（core 组默认可见） | `agent/tools/builtin/knowledge.py:knowledge_save_pending` |
| 反思 prompts（已改为调 `knowledge_save_pending`） | `agent/core/prompts/knowledge.py` |
| Auto-commit 开关（默认 False） | `KnowledgeConfig.reflect_auto_commit` |
| 反思侧分支退出时的 auto-commit hook | `agent/core/runner.py` 反射分支退出分支内 |
| 核心逻辑（list_pending / review_one / commit_approved / auto_commit_branch） | `agent/trace/extraction_review.py` |
| **独立 CLI 入口** | `python -m agent.cli.extraction_review --trace <ID> [--list/--list-all/--review/--commit]` |
| **交互式菜单入口** | `agent/cli/interactive.py` 菜单项 8（review）/ 9（commit） |
| **HTTP API 入口** | `GET /api/traces/{tid}/extractions`、`POST .../extractions/{eid}/review`、`POST .../extractions/commit`（见 `agent/trace/run_api.py`） |

三种入口共享同一个核心模块 `agent/trace/extraction_review.py`。

### 10.3 Memory + Dream

| 职责 | 位置 |
|---|---|
| MemoryConfig 定义 | `agent/core/memory.py:MemoryConfig` |
| 记忆文件加载（支持 glob + 去重） | `agent/core/memory.py:load_memory_files` |
| 记忆注入格式 | `agent/core/memory.py:format_memory_injection` |
| 注入到 system prompt | `agent/core/runner.py:_build_system_prompt`（memory 段落在 skills 段之后） |
| Dream per-trace 反思 | `agent/core/dream.py:per_trace_reflect` |
| Dream 跨 trace 整合 | `agent/core/dream.py:cross_trace_integrate` |
| Dream 顶层入口 | `agent/core/dream.py:run_dream` |
| **Agent 工具入口（memory 组）** | `agent/tools/builtin/memory.py:dream` |
| **外部调度入口** | `AgentRunner.dream(memory_config, trace_filter=..., reflect_model=..., dream_model=...)` |
| 默认 prompts | `dream.py:DEFAULT_REFLECT_PROMPT` / `DEFAULT_DREAM_PROMPT`（可通过 `MemoryConfig.reflect_prompt`/`dream_prompt` 覆盖） |

### 10.4 启用方式

默认 Agent 不启用 memory，但**提取审核仍然生效**（pending 不自动上传 KnowHub）。

要让某个 example 直接上传（恢复旧行为）：
```python
RunConfig(knowledge=KnowledgeConfig(reflect_auto_commit=True))
```

要让一个 Agent 变成 memory-bearing：
```python
from agent.core.memory import MemoryConfig

RunConfig(
    memory=MemoryConfig(
        base_path="/path/to/agent_memory",
        files={
            "taste.md": "品味偏好",
            "strategy.md": "当前策略",
            "journals/*.md": "执行日记",
        },
    ),
    tool_groups=["core", "memory"],   # memory 组暴露 dream 工具
)
```

然后周期性（或 Agent 主动调用 `dream` 工具）触发：
```python
await runner.dream(memory_config=rc.memory)
```

### 10.5 已知 rough edges

- 实施过程发现旧的 `upload_knowledge` 引用是悬空的（仓内无实现），未清理 `examples/*/prompt` 里的残留引用
- Dream 两次 LLM 调用（reflect + integrate）默认模型写死 `gpt-4o-mini` / `gpt-4o`，未接入 RunConfig 的 utility_llm_call
- `trace_filter` 没提供按 `agent_type` / `owner` 过滤的便捷函数，调用方传 lambda
