状态:设计讨论中,未实现
本文档设计两个紧密关联的能力:
当前框架已实现两种元思考能力,都基于侧分支(side branch)机制:
目的:从执行过程中提取客观知识,保存到 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()。
目的:评估被注入的知识是否有用,记录到本地 knowledge_log.json。
触发时机(详见 knowhub/docs/cognition-log.md):
store.py:update_goal,设置 pending_knowledge_eval 标志)输出:每条被注入的知识获得评估(irrelevant / unused / helpful / harmful / neutral),写入 knowledge_log.json。
当前局限:评估结果只存本地,不自动回传 KnowHub。只有用户通过 API 手动反馈才同步。
两种元思考通过 force_side_branch 队列协调执行顺序(runner.py:1198-1207):
压缩触发时的典型队列:
["reflection", "knowledge_eval", "compression"]
任务完成时:
["knowledge_eval"](先评估)→ ["reflection"](再提取)
每个侧分支执行完后 pop 队首,继续下一个,直到队列清空回到主路径。
现有的两种元思考都面向全局共享知识(KnowHub)。但有一类 Agent 需要维护主观的、属于自己的长期记忆:
KnowHub 不适合承担这个职责——它是"大众点评",不是"个人日记"。
记忆以 Markdown 文件存储在指定目录下。每个文件覆盖一个语义维度。
{base_path}/
├── taste.md # 偏好判断
├── strategy.md # 当前策略
├── skills.md # 积累的技巧
└── ...
为什么是 Markdown 文件:
共享读写:同一身份下的多个 Agent run 可以读写同一组记忆文件。哪个 Agent 该关注哪些文件、怎么更新,由 dream prompt 来定义。
| 知识提取(已有) | 知识评估(已有) | 记忆反思(新增) | |
|---|---|---|---|
| 回答的问题 | 我学到了什么客观知识? | 给我的知识有没有用? | 这些经历对我的偏好/策略意味着什么? |
| 输出目的地 | KnowHub(全局共享) | knowledge_log.json(本地) | 记忆文件(Agent 身份私有) |
| 触发时机 | 压缩前、任务完成后 | Goal 完成、压缩前、任务结束 | Dream 时(见下文) |
| 时效性要求 | 高(压缩会丢上下文) | 高(压缩会丢上下文) | 低(可以延迟处理) |
| 实现机制 | reflection 侧分支 | knowledge_eval 侧分支 | dream 操作(新增) |
关键区分:知识提取和知识评估必须在上下文丢失前完成(压缩前/任务结束时),所以是侧分支、即时触发。记忆反思可以延迟——甚至应该延迟,因为用户可能还有反馈、Agent 可能继续执行。
trace 结束只意味着 Agent 行动完一个轮次。后续可能发生:
如果 trace 一结束就做记忆反思,这些后续信息会被忽略。记忆反思的价值在于综合一段时间的经历,不是记录每次行动的即时感受。
这不矛盾。知识提取保存的是客观知识(工具用法、调研结果),这些不会因为后续反馈而失效。而且压缩会删除历史,如果不在压缩前提取,知识就永久丢失了。
Dream 是 memory-bearing Agent 的记忆整理操作。它不是一个侧分支(不在某个 trace 的执行过程中插入),而是一个独立的顶层操作,回顾多个 trace 的执行历史,更新记忆文件。
dream 工具("我觉得该整理一下了")Trace 模型新增字段:
- reflected_at_sequence: Optional[int] # 上次记忆反思时的最新 message sequence
# None = 从未被记忆反思处理
反思摘要不存在 Trace 模型中,而是作为 reflection 事件写入 cognition_log.json(详见 knowhub/docs/cognition-log.md)。
reflected_at_sequence 自然落后于实际 sequencereflected_at_sequence 为当前最新 sequencereflected_at_sequence < latest_sequence 的 traceDream 触发
│
├─ 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 事件为已消化
│
└─ 完成
反思 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 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()。应区分场景:
# 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 新增字段:
reflected_at_sequence: Optional[int] = None # 上次记忆反思的 sequence
# 反思摘要存在 cognition_log.json 中(type="reflection" 事件),不在 Trace 模型中
3. RunConfig 扩展
agent/core/runner.py:RunConfig 新增可选字段:
memory: Optional[MemoryConfig] = None
1. MemoryConfig
@dataclass
class MemoryConfig:
"""持久化记忆配置"""
base_path: str = "" # 记忆文件目录
files: Optional[Dict[str, str]] = None # {文件名: 用途说明}
dream_prompt: str = "" # Dream 整合 prompt(空用默认)
reflect_prompt: str = "" # Per-trace 反思 prompt(空用默认)
2. Run 启动时记忆加载
Memory-bearing Agent 的 run 启动时,框架读取 base_path 下所有 files 中声明的文件,注入上下文。
3. Dream 操作
以 dream 工具形式提供,Agent 可主动调用:
@tool
async def dream() -> ToolResult:
"""整理长期记忆。回顾最近的执行历史,更新记忆文件。"""
# 1. 扫描需要反思的 trace
# 2. 逐个 per-trace 反思
# 3. 跨 trace 整合,更新记忆文件
也可以作为 AgentRunner 的方法暴露,供外部调度直接调用。
Agent 执行任务(Trace)
│
├─ 知识查询(ask)→ cognition_log: type="query"(含整合回答 + source_ids)
│
├─ Goal 完成 → 触发 knowledge_eval 侧分支 → cognition_log: type="evaluation"
│
├─ 压缩触发 →
│ 队列: [reflection, knowledge_eval, compression]
│ reflection: 提取客观知识 → upload → KnowHub + cognition_log: type="extraction"
│ knowledge_eval: 评估各 source → cognition_log: type="evaluation"
│ compression: 压缩上下文
│
├─ 任务完成 →
│ knowledge_eval(如有 pending)→ cognition_log: type="evaluation"
│ reflection → upload → KnowHub + cognition_log: type="extraction"
│
└─ 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)在全局视角下更新个人记忆。两者互补。
┌─────────────────────────────────────────────────────────────┐
│ Layer 3: Skills(技能库) │
│ - Markdown 文件,领域知识和能力描述 │
└─────────────────────────────────────────────────────────────┘
▲
│ 归纳
┌─────────────────────────────────────────────────────────────┐
│ Layer 2: Knowledge(知识库)— 全局共享 │
│ - KnowHub 数据库,客观知识 + 向量索引 │
│ - 来源:reflection 侧分支提取 │
│ - 质量信号:knowledge_eval 评估结果 │
└─────────────────────────────────────────────────────────────┘
▲
│ 提取(reflection)/ 评估(knowledge_eval)
┌─────────────────────────────────────────────────────────────┐
│ Layer 1.5: Memory(个人记忆)— Agent 身份私有 │
│ - Markdown 文件,主观记忆(偏好/策略/反思) │
│ - 来源:dream 操作(per-trace 反思 + 跨 trace 整合) │
│ - 人类可直接编辑 │
└─────────────────────────────────────────────────────────────┘
▲
│ dream 反思
┌─────────────────────────────────────────────────────────────┐
│ Layer 1: Trace(任务状态) │
│ - 当前任务的工作记忆 │
│ - Messages + Goals + cognition_log │
└─────────────────────────────────────────────────────────────┘
| 默认 Agent | Memory-bearing Agent | |
|---|---|---|
| 知识提取(reflection) | ✅ 配置 KnowledgeConfig | ✅ 配置 KnowledgeConfig |
| 知识评估(knowledge_eval) | ✅ 自动 | ✅ 自动 |
| 个人记忆 | ❌ | ✅ 配置 MemoryConfig |
| Dream | ❌ | ✅ 可调用 dream 工具 |
| Run 启动加载记忆 | ❌ | ✅ 自动注入 |
默认行为不变。Memory 是 opt-in 的增量能力。
dream())vs AgentRunner 方法 vs 两者都提供?