My Agent Core Design - Memory
Memory
- Memory 是用户级长期知识.
- 第一版只支持用户级 memory, 固定落在
${SOONG_AGENT_HOME}/memory. - 第一版不支持项目记忆:
- 不存在
<project>/.soong-agent/memory. - 不把项目文件、Task DAG、Plan 正文或完整 transcript 当作可恢复的项目记忆源.
- 不存在项目级 memory 配置.
- 不存在
- Memory 自动提取自动写入, 但不是 fork agent / sub agent / child agent.
- Memory Extraction Job:
- 是 core 内部的后台 job.
- 不创建 agent_id.
- 不创建 run_id.
- 不进入 agent tree.
- 不占用 child/sub/fork agent 并发名额.
- 不通过
agent.create_sub_agent或agent.fork_agent创建. - 使用独立可配置的模型配置
memory.extract_model_profile. memory.extract_model_profile未配置时, 默认完全使用主模型配置.- 可以像主模型一样配置 provider / base_url / api_key_env / model name / context window / output tokens / temperature / timeout / retry.
- 通过受限 Memory Writer 直接创建/更新
${SOONG_AGENT_HOME}/memory下的 memory md 和MEMORY.md.
- 通过受限 Memory Writer 直接创建/更新
- 不返回候选 memory 给 core 再由 core 改写或代写.
- core 仍负责路径、schema、敏感信息和写入边界校验.
- Core 负责:
- 持久化 context.
- 根据触发条件调度 Memory Extraction Job.
- 提供待扫描 context range、
MEMORY.mdcatalog 和现有 memory frontmatter. - 为 Memory Extraction Job 构造模型请求.
- 提供只能写
${SOONG_AGENT_HOME}/memory的受限 Memory Writer. - 校验 memory 文件路径不能逃逸
${SOONG_AGENT_HOME}/memory.
- 提供只能写
- 校验 memory frontmatter 和
source_node_ids. - 写 memory_extraction lifecycle event 和审计信息.
- Memory Extraction Job 负责:
- 判断是否值得写入 memory.
- 判断类别: user / feedback / reference.
- 生成 memory md.
- 判断 new / duplicate / update / conflict / ignore.
- 维护
MEMORY.md目录. - 直接创建/更新 memory md 和
MEMORY.md. - 输出 structured result, 包括 created memory ids, updated memory ids, ignored candidates, duplicate decisions, conflicts, source_node_ids, files changed.
- Memory Extraction Job 扫描:
- scan cursor 存在 SQLite session metadata, 例如 last_memory_scanned_node_id 或 sequence.
- 下次扫描只处理 cursor 之后的新内容.
- 候选源主要是 cursor 之后的新 user messages.
- 附带这些 user messages 周边 assistant context, recent active path 摘要, 当前 compact summary.
- tool result 默认只给摘要/artifact refs.
- Memory Extraction Job 用周边上下文解释用户表达, 但避免把 assistant 未确认内容写进 memory.
- Memory source 规则:
usermemory 必须能追溯到 user message.feedbackmemory 必须能追溯到 user message.referencememory 可以来自用户确认过的 tool/assistant context.- 每条 memory 记录
source_node_ids. - 如果 reference 来自 tool/assistant, 需要记录确认它的 user node id 或 evidence metadata.
- worker/sub/fork agent 的中间过程不能直接成为长期 memory.
- worker/sub/fork agent 结果如果需要写入 memory, 必须已经被 main/Orchestrator 可见, 并且能追溯到用户确认或用户显式偏好.
- 不写入 secret、credential、token、cookie、私钥或明显敏感的环境信息.
- 不写入 Task DAG 当前状态、Plan 正文、完整对话 transcript、临时命令输出或一次性调试过程.
- Memory 去重和覆盖:
- 先读取
MEMORY.md和所有 memory frontmatter. - 根据 category, summary, tags, source 和语义相似度初筛疑似重复.
- 必要时读取疑似重复 memory 正文.
- Memory Extraction Job 决定 new / duplicate / update / conflict / ignore.
- conflict 第一版直接覆盖旧 memory.
- 覆盖时保留原 memory id 和文件路径, 更新 body/frontmatter/summary/tags/updated_at.
- 覆盖时
source_node_ids替换成新来源.
- 先读取
- Memory 落库策略:
- Memory md 和
MEMORY.md是 source of truth. - 不把 memory 正文存入 SQLite.
- 不把 memory 正文存入项目目录.
- Memory Writer 只能写
${SOONG_AGENT_HOME}/memory/MEMORY.md和${SOONG_AGENT_HOME}/memory/{user,feedback,reference}/*.md.
- Memory Writer 只能写
- 创建新 memory 时先写 memory md, 再更新
MEMORY.md. - 更新已有 memory 时保留原文件路径, 原子更新 memory md 和
MEMORY.md. - 任一文件写入或校验失败时, 本次 Memory Extraction Job 标记 failed, scan cursor 不推进.
- 本次扫描只有 duplicate / ignore 且没有文件变更时, 可以在记录 completed event 后推进 cursor.
- scan cursor 只在 Memory Extraction Job 完成且所有必要写入成功后推进.
memory_extraction_completedevent 记录 created / updated / ignored / duplicate / conflict / source_node_ids / files_changed.- debug 模式可以保存 redacted model request/response artifact, 默认不保存原始 memory extraction prompt.
- Memory md 和
- Memory 分类只保留:
- user
- feedback
- reference
- Memory 触发条件采用 hybrid strategy, 避免固定每 N 条消息机械抽取:
- 显式记忆意图立即触发:
- 当前 root user message 出现明确长期记忆表达时, 本轮 run 完成后立即抽取.
- 典型表达包括: “记住”, “remember”, “以后都”, “我的偏好”, “I prefer”, “don’t forget”, “always/never do”.
- 显式触发只决定是否运行 Memory Extraction Job, 不绕过模型抽取和写入校验; 最终仍可返回 empty memories.
- Backlog 兜底触发:
- 未扫描 root user messages 数量达到
memory.extract_every_messages时, 本轮 run 完成后抽取. - 该字段语义是
max_pending_messages, 不是”每 N 条必定值得写 memory”.
- 未扫描 root user messages 数量达到
- Token 兜底触发:
- 未扫描 root user messages 的估算 token 数达到
memory.extract_every_tokens时, 本轮 run 完成后抽取. - token 估算只用于触发阈值, 不作为精确计费或上下文裁剪依据.
- 未扫描 root user messages 的估算 token 数达到
- Idle 整理触发:
- 如果本轮 run 完成后仍有未扫描 root user messages, 但未命中上述立即/兜底条件, runtime 安排一个 session-level idle job.
memory.idle_seconds内没有同 session 新 run 开始时, idle job 后台运行 Memory Extraction Job.- 同 session 新 run 开始时取消旧 idle job, 等新 run 完成后重新评估.
memory.enabled = false时禁用所有 extraction 触发.- 触发结果需要写入
memory_extraction_started/completed/failedevent, payload 记录reason:explicit,message_backlog,token_backlog, oridle.
- 显式记忆意图立即触发:
- Memory 文件结构: -
${SOONG_AGENT_HOME}/memory/MEMORY.md: 所有 memory md 的目录. -${SOONG_AGENT_HOME}/memory/user/*.md-${SOONG_AGENT_HOME}/memory/feedback/*.md-${SOONG_AGENT_HOME}/memory/reference/*.md - Memory 文件命名:
<category>/<model-chosen-name>-<created_at>-<id>.md- category 只能是
user / feedback / reference. - Memory Extraction Job 根据 memory 内容给出候选文件名.
- core 不从 summary 硬编码生成 slug.
- core 只校验模型给出的文件名安全性和冲突.
- created_at 文件名部分用紧凑日期, 例如
YYYYMMDD. - id 使用
mem_<YYYYMMDDHHMMSS>_<rand>.
- 每条 memory 是独立 md 文件, 带 frontmatter:
- id
- category
- summary
- tags
- created_at
- updated_at
- source_session_id
- source_node_ids
MEMORY.mdcatalog:- 按 memory
created_at倒序排列. - 如果太长, 第一版直接裁剪末尾.
- recall 是纯读取, 不更新
created_at,updated_at,last_seen_at. - Memory Extraction Job 更新已有 memory 内容时允许更新
updated_at, 但不改变MEMORY.md的 created_at 排序.
- 按 memory
- Memory recall 也采用渐进式披露:
MEMORY.md是 main agent / Orchestrator 常驻 memory catalog, 放入 dynamic system prompt.MEMORY.md只包含目录/摘要, 不包含 memory 全文.- main agent / Orchestrator 模型根据
MEMORY.md判断是否调用recall_memory. recall_memory是 readonly + internal tool, 不需要用户确认, event log 标记 internal.recall_memory读取所有 memory md 的 frontmatter.- Memory Recall Selector 使用
memory.recall_model_profile; 未配置时默认使用主模型配置. - Recall Selector 基于 recall query, 当前用户请求, recent active path 摘要,
MEMORY.md, 所有 memory frontmatter, 决定加载哪些 memory 全文. - 加载 memory 全文使用 top K + memory_context_token_budget 双限制.
- 选中 memory 写入内部 node type
memory_context. memory_context发给模型时映射为 synthetic user/context message.memory_context用带 ids/categories 的 XML-like 标签包裹, 例如<memory ids="..." categories="...">...</memory>.memory_contextmetadata 记录 recalled_memory_ids, categories, query, selected_by_model, source_paths.- 如果 active path 已有同 memory id 且文件 hash 未变, 返回 already_recalled, 不重复注入.
- 如果同 memory id 但文件 hash 变了, 重新读取并注入新版 memory_context.
- Memory 写权限和召回权限:
- 普通 main/Orchestrator/sub/fork/worker agent 默认没有 memory write tool.
- sub/fork/worker agent 不允许直接写长期 memory.
- sub/fork/worker agent 不允许调用
recall_memory. - compact fork 不允许新召回 memory; 它只能总结触发时 active path 已经可见的内容, 包括已经存在的
memory_context. - Memory Writer 不是普通模型 run 的 effective tool set 成员.
- Memory Writer 只给 Memory Extraction Job 使用.
recall_memory是只读 internal tool, 只能把选中的 memory 写入memory_contextnode.
- 第一版只做内存 cache, 不做持久 embedding/index 数据库.
This post is licensed under CC BY 4.0 by the author.