记忆的自我认知:agent怎么知道自己记得什么

一次实测把问题摆了出来:让跑在OpenProgram里的agent讲一讲"当前的记忆系统是怎么设计的", 它讲的是宿主机上Claude Code的配置文件和技能,推荐用一个上一代早已删掉的工具去沉淀知识, 而openprogram/memory/下真正跑着的那套一个字没提。 这页把上下文装配摊开,逐条核对模型每轮能拿到的记忆线索,对照八个参考实现,给出界碑怎么加、 自省工具放哪、记忆坏掉时模型该看到什么。

00四层怎么分 01十五个组件的实际形态 02记忆那块长什么样 03失败为什么是静默的 04模型每轮拿到的记忆线索 05八家总表 06界碑:八家的字面文本 07自省工具与空坏可见性 08宿主与项目怎么分 09我们计划怎么改 10代价与落点 11理想状态 12怎么核实的

00四层怎么分第一层

四层各答一个问题:现在的上下文里记忆长什么样、别人怎么做、下一步动哪几行、不受实现约束该长成什么样。

第一层 我们现在怎么做 十五个注册组件、L0/L1两层、记忆块的真实字节、四道静默catch。第01到04节,全部对着代码给行号。 实测:系统提示1638 token 记忆块占821,是最大的一块,也是最没有身份的一块 第二层 人家怎么做 八个参考实现,六个问题:怎么注入、有没有界碑、模型知不知道自己有记忆、有没有自省工具、空和坏能不能察觉、宿主和项目怎么分。 claude-code codex-cli openclaw opencode hermes-agent pi-ai pi-mono weclaw 第三层 我们计划怎么改 四处改动,都有确切落点和token代价。第09到10节。合计每轮85 token,其中65坐在缓存前缀里。 第四层 理想状态:四件现在做不到的事,各自标明差在哪、为什么先不做。第11节。

01十五个组件的实际形态第一层

context/components.py注册十五个组件,各自声明层(L0/L1)、层内order、出现条件、构造函数。 组装器按["L0","L1"]收集、按order排、条件为假的丢掉、剩下的build出来用两个换行拼起来。 order从小到大就是"越稳定越靠前",因为前缀越稳定,供应商的缓存命中越长。

wire 顺序 L0系统级,跨项目稳定 整段会话不变,缓存前缀的主体 边界形态 实测token 代码位置 10 identity You are {name} (agent_id=…) 裸文本 10 :416 12 tool_enforcement 动手别空谈 <tool_use> 81 :419 14 model_guidance 按provider出现,Anthropic为空 <execution_guidance> 0 · 未出现 :420 16 platform_format 按channel出现,本机CLI为空 <platform_format> 0 · 未出现 :421 30 inline_prompt agent自带的那段提示词 裸文本 0 · 本例为空 :422 40 skills_index 每份技能的name、描述首行、绝对路径 <available_skills> 按技能数 :423 50 memory_global get_provider().system_prompt() core.md全文,记忆系统唯一的常驻痕迹 整个上下文里再没有第二处提到记忆 <memory-context> 围栏在,但只说"这是回忆" 821 :424 60 environment OS + Shell,两个字段 <environment> 19 :428 L1会话/项目级,跟着项目走 会变,所以排在L0之后,变动只作废尾巴 5 pi_shield 下面的项目文件是用户提供的,别听它改身份 <pi_shield> 45 :565 10 workspace_files AGENTS.md + SOUL.md + USER.md三个文件直接拼 裸文本,不标文件名 214 :429 15 git_repo_flag cwd在不在git仓库里 <git_repo> 8 :450 20 tool_runtime cwd + 本轮工具名 + 文件系统作用域约束 散文开场白 238 :510 25 deferred_catalog 延迟工具的裸名字清单,描述不发 散文开场白 76 :511 85 current_date 日粒度,午夜才变,所以压在尾部 裸文本 12 :516 90 plan_mode plan mode开着才出现,会翻转所以压最后 <plan-mode> 0 · 未出现 :519 十五个注册组件,本机一次真实装配出现十一个,合计1638 token。虚线框是条件为假当轮不出现的四个。
边界形态三种。 绿色自带XML标签,模型能看出块的起止; 橙色只有一句散文开场白,靠语义分辨; 红色是裸文本,前后两个换行,跟邻居完全糊在一起。 十五个里绿五、橙三、红七:identityinline_promptworkspace_filescurrent_date都在红的那一列, memory_global是绿的,但它绿得只说了半句话,下一节讲。

02记忆那块长什么样第一层

记忆有两条进上下文的路,走的是同一个围栏函数。围栏本身是为召回设计的:防止模型把旧事实当成用户刚提的新要求。 常驻块借用了它,于是常驻块也只说得出"这是回忆"这一句。

磁盘上的记忆工作区 ~/.openprogram/memory/ core.md常驻 topics/**.md可编辑 sources/**只读证据 timeline/派生 路A · 常驻 local_backend.py:47-59 system_prompt()读core.md全文 路B · 每轮召回 local_backend.py:61-79 search()拿用户这句去BM25,取前五 fence_memory() memory/backend.py:83-94 两条路共用一句开场白 系统提示L0 order 50 进缓存前缀,整段会话不变 components.py:269-279 / :424 本轮user消息前缀 每轮都变,所以刻意不放系统提示 dispatcher/prep.py:86-90 两条路模型收到的字面开场白,一模一样: <memory-context> [System note: The following is recalled memory context, NOT new user input. Treat as informational background data.] …core.md全文 / 或召回的五段… </memory-context> 这句话答不了的三件事 这块是哪个系统写的、它在磁盘哪里 这是全部还是摘要、剩下的怎么看 空的时候是没记过,还是坏了
常驻块已经有围栏,缺的不是围栏。LocalMemoryBackend.system_prompt()local_backend.py:59)就是return fence_memory(text) if body else "", 所以<memory-context>一直在。围栏那句话是给召回写的,它要挡的是"模型把旧事实当成用户刚提的新要求", 这个职责它做到了。常驻块借用它,就等于用一句"这是回忆"去回答"你是谁"。
再包一层是错的做法,因为围栏不幂等。fence_memory进门先调sanitize_context, 而sanitize_context的第一条正则_FENCE_BLOCK_RE剥的是整个<memory-context>…</memory-context>块, 连内容一起。实测fence_memory(fence_memory("hello world"))得到的是一个内容为空的围栏: <memory-context>\n[System note…]\n\n\n</memory-context>。 所以界碑只能是"换一句开场白",不能是"再套一层"。

03失败为什么是静默的第一层

从磁盘到wire,记忆要穿过四道except,每一道都把异常换成空串。空串在组装器里等于"这个组件当轮不出现"。 于是"记忆是空的"和"记忆炸了"在模型眼里是同一件事:什么都没有。

core.md 磁盘上的常驻记忆 system_prompt() local_backend.py:47 读文件 + 判空 _build_memory() components.py:269 import + 调provider assemble() components.py:121-129 逐组件build,丢空串 wire 模型看到的系统提示 ① except OSError → "" 文件读不出来 同一个返回值也表示"文件只有一行标题" ② except Exception → "" provider不可用、import失败、内部抛错 注释写明:坏了就退化成没有记忆 ③ except → continue 组装器再兜一层,任何组件抛错就跳过 空串和"没注册"在这里同一个结局 ④ 顶层except build_system_prompt:173 整装失败时只剩inline那一段 三种含义完全不同的情况,模型收到的是同一个东西:什么都没有 后端被用户关掉(memory.backend = none) 工作区是空的,还没记过任何东西 记忆系统坏了,磁盘上有内容但取不出来
这三种情况模型该有三种反应。后端关掉是用户的决定,模型不该提;工作区空是正常的新装状态,模型该说"我这边还没有关于这件事的记录"; 记忆坏掉时模型绝对不该推断"用户从没跟我说过这个",那是把系统故障读成了事实。现在的代码让它无从分辨。

04模型每轮拿到的记忆线索第一层

把"模型能不能知道X"逐条拆开,对着这一轮真实的wire核对。这就是那次实测里agent会答错的完整原因。

问题 现在的答案 证据 我有一套长期记忆系统吗 不知道。上下文里没有一句话说这件事 十五个组件全文搜不到 "memory" 这个词 它在磁盘上哪 不知道。上下文里唯一的路径是cwd tool_runtime给cwd;environment给OS+Shell 我这轮看到的那块是记忆吗 半知道。围栏说"这是回忆",没说是谁的、哪来的 fence_memory的System note只有一句 这是全部,还是摘要 不知道。它其实是2000 token预算下的渲染结果 memory/overview.md「The always-on block」 剩下的怎么看 六个memory_* 工具存在,但一个都不在默认工具表里 functions/__init__.py:72-114逐行核对,零个 空的时候是没记过还是坏了 不知道。四道catch之后两者同形 见第03节 哪些东西属于跑我的框架,哪些属于我在改的项目 这条是那次现象的直接来源 不知道。上下文从不点名自己跑在OpenProgram里 于是"我的记忆系统"这个问题,模型只能靠满硬盘找答案 environment组件只有OS + Shell两个字段 七问里六个"不知道",一个"半知道"。模型手上有bash有grep,它会去找,找到的第一个像记忆系统的东西就是宿主机上别的agent的配置。
压垮那次回答的最后一根:默认工具表里唯一像知识系统的,是一个上一代的东西。 DEFAULT_TOOLS二十四个工具里有wiki_agentfunctions/__init__.py:113), 描述字面是Maintain a wiki vault — route to ingest, enrich, browse, lint, or search depending on the task. 六个memory_*一个都不在。模型每轮扫一遍自己的工具表,问"我拿什么沉淀知识", 表上写着答案,而那个答案指向的是重构时已经删掉的目录。这不是模型幻觉,是工具表在指路。

05八家总表第二层

同样六个问题问八个参考实现。没有这个功能的如实标"没有"。全部读源码,给文件加行号。

实现 怎么注入 有没有界碑 告不告诉模型"你有记忆" 自省工具 空/坏能不能察觉 宿主vs项目
OpenProgram
我们
系统提示L0 order 50常驻 + 每轮user消息前缀召回components.py:424 / prep.py:90 有,但只说了半句
<memory-context> + 一句System notememory/backend.py:83-94
没有。上下文十五个组件里搜不到memory这个词components.py全文 有六个,一个都不默认给
只在toolset="memory""full"functions/__init__.py:72-114, 195
不能。关掉 / 空 / 坏三态同形,四道catch都返回空串components.py:269-279 没有<environment>只有OS + Shellcomponents.py:282-296
hermes-agent 系统提示三段式的volatile段,会话开始快照一次agent/system_prompt.py:277-286 。46个的横幅 + 标题行 + 横幅tools/memory_tool.py:442-458 有,最直白的一家
You have persistent memory across sessions…agent/prompt_builder.py:150-156
memory工具在默认集里,但只有add/replace/remove;每次调用回全量列表当作listmemory_tool.py:649, 425-440 空则整块消失,无占位;工具调用时才报Memory is not available…memory_tool.py:444, 582 有,而且点名这次的坑
Your memory and user profile describe the USER, not the system you are running on.prompt_builder.py:306-308
openclaw 系统提示,# Project Context段,MEMORY.md固定排最后agents/system-prompt.ts:176-212, 54-62 。Markdown标题 + 逐文件说明句 + ## MEMORY.md小标题system-prompt.ts:176-212 ## Memory Recall一整段,加模板AGENTS.md里的You wake up fresh each session…memory-core/src/prompt-section.ts:16-26 memory_search + memory_get,memory-core是默认插件槽memory-core/src/tools.ts:229-484 / plugins/slots.ts:13 三态都能。空工作区有There is no memory yet…;检索坏了返回disabled:true + 让模型告诉用户templates/BOOTSTRAP.md:12 / tools.shared.ts:120-136 You are a personal assistant running inside OpenClaw. + These user-editable files are loaded by OpenClaw…system-prompt.ts:1016, 1207-1209
opencode 系统提示数组一项;子目录AGENTS.md走read工具结果后缀session/instruction.ts / tool/read.ts:355-356 顶层只有Instructions from: <path>一行;嵌套那条用<system-reminder>instruction.ts:166 / read.ts:356 看模型分档beast.txt(GPT系)有一整段You have a memory that stores information about the user…anthropic.txt没有prompt/beast.txt:113-123 没有tool/下没有memory工具,全靠read/write/greptool/ 目录 不能。文件不在就整项不产出,falsy被过滤掉instruction.ts:166 / request.ts:64 <env>块给cwd / workspace root / git / platform / date;全局和项目的AGENTS.md不加标签,只有路径session/system.ts:59-70
pi-mono 系统提示,<project_context>段排在身份和技能之间core/system-prompt.ts:132-172 <project_context> + Project-specific instructions and guidelines: + 逐文件<project_instructions path="…">system-prompt.ts:61-67 没有。开场白只说operating inside pi, a coding agent harness,没有一句提记忆system-prompt.ts:132 没有。默认工具read/bash/edit/write四个system-prompt.ts:90 不能contextFiles.length > 0才产出;读失败只打终端warningresource-loader.ts:57-73 代码里分全局(~/.pi/agent)和项目,散文里不标;模型只能从path="…"config.ts:477 / resource-loader.ts:85-110
claude-code 两条道分开。用法说明进系统提示;记忆内容进message[0],一条合成的user消息constants/prompts.ts:495 / utils/api.ts:449-474 有,最完整的一家
<system-reminder>外层 + 逐文件Contents of {path} ({来源标注})utils/api.ts:463-469 / utils/claudemd.ts:1179-1186
You have a persistent, file-based memory system at {memoryDir}. This directory already exists — write to it directly with the Write tool…memdir/memdir.ts:239 没有专用工具,记忆就是文件,用通用Write/Read/Grep;召回是后台sideQuery,模型调不动memdir/findRelevantMemories.ts 主会话静默消失;子agent那条有占位句Your MEMORY.md is currently empty. When you save new memories, they will appear here.claudemd.ts:652-654 / memdir.ts:307-313 五个来源各带各的标注
全局私有 / 项目签入 / 项目私有 / 自动记忆 / 团队共享claudemd.ts:1169-1177
codex-cli 三条道分开:基础提示、AGENTS.md走user角色的world-state片段、记忆摘要走developer角色的policy片段context/user_instructions.rs:11 / ext/memories/src/extension.rs:64-68 。AGENTS.md用# AGENTS.md instructions + <INSTRUCTIONS>;记忆用========= MEMORY_SUMMARY BEGINS =========user_instructions.rs:18-19 / templates/memories/read_path.md:125 功能开了才有You have access to a memory folder with guidance from prior runs…;基础提示default.md全文不含memoryread_path.md:3-4 四个真工具,但双重开关默认关
List / Read / Search / AddAdHocNoteext/memories/src/tools/mod.rs:36-51 / features/src/lib.rs:996-999
记忆缺失静默;AGENTS.md换掉或撤掉时会明说The previously provided AGENTS.md instructions no longer apply.prompts.rs:32-43 / agents_md.rs:10-11 。全局与项目用--- project-doc ---分隔;多工作区逐块标for {env_id} with root {cwd}agents_md.rs:43, 366-370
weclaw 一个运维配置的静态串,--append-system-prompt或首条system消息agent/cli_agent.go:122-124 没有。原样拼接,无任何标记同上 没有。全仓库没有记忆概念 没有 N/A。串为空就整条省掉cli_agent.go:122 没有。cwd直接传给子进程,不加任何说明
pi-ai 这个目录是pi-mono传输层的只读快照(OAuth + provider请求流),没有agent循环、没有系统提示装配、没有工具系统。六问全部N/A。references/pi-ai/README.md:1-23

06界碑:八家的字面文本第二层

把各家包记忆的那一层原样抄出来对齐看。分成三档:有标签有措辞、只有标签、什么都没有。

claude-code 外层标签 + 逐文件来源标注 <system-reminder> As you answer the user's questions, you can use the following context: Contents of ~/.claude/CLAUDE.md (user's private global instructions for all projects) hermes-agent 标签 + 措辞 + 活的数量 ══════════════════════════ MEMORY (your personal notes) [63% — 1284/2048 chars] ══════════════════════════ 名字、身份、占了预算多少,一眼可核对 openclaw 标题 + 逐文件说明句 # Project Context The following project context files have been loaded: MEMORY.md: durable user preferences and behavior guidance. Keep following it throughout the session… codex-cli 记忆和项目文档各一套标签 ## Memory ========= MEMORY_SUMMARY BEGINS ==== …memory_summary.md… ========= MEMORY_SUMMARY ENDS ====== # AGENTS.md instructions for /path <INSTRUCTIONS> … </INSTRUCTIONS> pi-mono 标签齐,措辞不提记忆 <project_context> Project-specific instructions and guidelines: <project_instructions path="…"> 来源全靠path属性,模型自己推 opencode 顶层一行路径;嵌套那条才有标签 Instructions from: /path/AGENTS.md <system-reminder> …子目录读到的AGENTS.md… </system-reminder> OpenProgram现状 标签在,措辞是给召回写的,常驻块借用同一句 <memory-context> [System note: The following is recalled memory context, NOT new user input. Treat as informational background data.] 没有名字、没有路径、没有数量、没有"这是你的记忆" weclaw / pi-ai 没有 weclaw:运维配的一个静态串,原样拼上去。没有标签, 没有开场白,全仓库没有记忆这个概念。 pi-ai:pi-mono传输层的只读快照,没有agent循环, 没有系统提示装配。六问全部N/A。 一个界碑要回答的四件事,八家各答了几件 ① 这块是什么 ② 谁写的 ③ 怎么看更多 ④ 现在有多少 claude-code 三件。② 最强:五种来源各带一句标注,还写明这份走多远 openclaw 三件。③ 靠 ## Memory Recall那段直接教模型调哪个工具 hermes 三件,唯一答了 ④ 的一家:横幅上带活的字符数和百分比 codex / pi-mono / opencode 一件半。有标签有路径,但没有一句话说"这是你的记忆" OpenProgram 半件。① 只说了"是回忆",②③④ 全空,这就是要补的那三格
claude-code那一套值得整段抄。它把记忆内容放进message[0]一条合成的user消息, 外层是<system-reminder>,里层逐文件写Contents of {path} ({来源标注}), 五种来源五句标注:user's private global instructions for all projectsproject instructions, checked into the codebaseuser's private project instructions, not checked inuser's auto-memory, persists across conversationsshared team memory, synced across the organization。 模型不但知道这是记忆,还知道每一份是谁的、走多远。八家里只有这一家把"谁写的"答到这个精度。

07自省工具与空坏可见性第二层

自省工具 · 谁默认给

只有openclaw和hermes默认给

工具存在,和工具默认在场,是两件事

openclawmemory_search + memory_get放进memory-core, 而memory-core是memory插件槽的默认实现,装上就有,不用挑。 工具描述本身就是指令:Mandatory recall step…,还写明If response has disabled=true, memory retrieval is unavailable and should be surfaced to the user.

hermesmemory在核心集里默认开,但只有add/replace/remove,没有list; 靠"每次写完回全量列表"顶替。

codex-cli有四个真自省工具(List / Read / Search / AddAdHocNote), 但双重开关默认关:Feature::MemoryTooldefault_enabled: false, 还要再开一个memories.dedicated_toolsclaude-code干脆不做专用工具,记忆就是文件,用通用Write/Read/Grep, 提示里直接教模型往那个目录写。

我们的位置跟codex一样:六个工具做完了,默认不给。差别是codex的开关是明写的功能位, 我们的是"不在DEFAULT_TOOLS里"这个没人会注意到的事实。

openclaw默认给2个hermes默认给1个codex有4个默认关我们有6个默认关
空 / 坏 · 谁让模型看得见

只有openclaw三态都说得清

空、坏、关掉,模型能不能分辨

:模板BOOTSTRAP.md直接写给模型看: There is no memory yet. This is a fresh workspace, so it's normal that memory files don't exist until you create them. 这句话把"空"从可疑变成正常。claude-code的子agent那条路上有同类占位句: Your MEMORY.md is currently empty. When you save new memories, they will appear here., 但主会话那条没有。

:openclaw的检索失败返回结构化的disabled:true + unavailable:true + warning + action: Check embedding provider configuration and retry,并要求模型告诉用户。八家里独一份。

变了:codex是唯一会说"旧的作废了"的一家: The previously provided AGENTS.md instructions no longer apply. 但那是给项目文档用的,记忆缺失时它一样静默。

其余五家(含我们)都是静默消失:块不出现,模型无从分辨"没记过"和"取不出来"。

openclaw三态齐claude-code子agent有占位codex会说作废其余五家 静默

08宿主与项目怎么分第二层

这条是那次现象的直接来源,单独看。问题是:模型能不能知道"我看到的这些东西,哪些属于跑我的这个框架,哪些属于我正在改的项目"。

第一档 · 逐份标注来源,模型看一眼就知道这份属于谁 claude-code 每一份注入的记忆文件后面跟一句括号,五种来源五句话: user's private global instructions for all projects project instructions, checked into the codebase user's private project instructions, not checked in user's auto-memory, persists across conversations shared team memory, synced across the organization 另有独立的 <env> 块给cwd / git / platform hermes Your memory and user profile describe the USER, not the system you are running on. openclaw You are a personal assistant running inside OpenClaw. +These user-editable files are loaded by OpenClaw and included below… codex-cli 全局与项目用字面分隔符--- project-doc ---隔开;多工作区逐块标for {env_id} with root {cwd} 第二档 · 代码里分了,散文里不说,模型只能从路径推 pi-mono 全局在 ~/.pi/agent、项目沿cwd往上找,两者渲染同形,只差path属性里的字符串 opencode <env> 块给cwd / workspace root / git / platform / date;全局与项目的AGENTS.md只差一行Instructions from后面的路径 第三档 · 完全没有 weclaw:cwd直接传给子进程,一句说明都没有 · pi-ai:没有agent层 OpenProgram现在在第三档 <environment> 只有OS和Shell,从不点名自己叫什么、状态在哪 一句话的差别 说了的四家,模型问"我的记忆系统是怎么设计的"时会先看自己的框架;没说的四家,模型会去满硬盘找,找到谁的算谁的。 我们这边已经有一处做对了:记忆写手跑Claude Code SDK时传setting_sources=[],明确不加载宿主的CLAUDE.md(memory/agent_runtime/claude_code.py:249)。 写手隔离了宿主,主对话agent却从没被告知宿主是谁,隔离做了一半。

09我们计划怎么改第三层

四处改动,按"能独立落地、各自能验证"切开。合计每轮85 token,其中65坐在L0缓存前缀里,第二轮起不再重复计费。

改动A · 最小的那个

工具目录本身就是界碑

functions/__init__.py:72-114 + :123-136

六个memory_*DEFAULT_TOOLS,同时六个全进DEFERRED_DEFAULT_TOOLS。 延迟目录只发裸名字不发描述(deferred_catalog_text就是"\n".join(name …)), 实测六行合计20 token

模型每轮扫工具表,看到memory_searchmemory_browsememory_status 这几个名字,"我有记忆"这件事不再靠猜。wiki_agent也就不再是表上唯一像知识系统的东西。 不用删它,让真正的答案跟它并排即可。

+20 tok/轮改两个列表test_tool_defer.py加一条
改动B · 常驻块的身份

换一句开场白,不是再包一层

memory/backend.py:83旁边 + local_backend.py:59

加一个跟fence_memory平行的fence_core,共用sanitize_context, 常驻那条路把fence_memory换成它。不是套两层,套两层会因为 _FENCE_BLOCK_RE剥掉整个内块而得到空围栏(第02节实测)。

召回那条路一个字不动:它的开场白是对的,要挡的就是"旧事实被当成新要求"。

+65 tok,在缓存前缀里一个新函数一处调用点
改动C · 空和坏分开

三态各说一行

context/components.py:269-279

_build_memory不再一律返回空串。后端关掉就真的什么都不出(那是用户的决定); 工作区空就出一行"记忆开着,是空的,会自己长,没出问题"; provider抛异常就出一行"这轮没取到记忆,不要据此推断用户没说过"。

后两种情况下这一行取代原来的821 token,是净减。

约15行三态各一条单测空/坏时净省token
改动D · 宿主与项目

点名自己叫什么、状态在哪

context/components.py:282-296

<environment>现在只有OS和Shell。加两行:框架名字,以及框架自己的状态目录。 再由改动B的开场白点名记忆工作区的绝对路径。

抄hermes那句话的意思:这些是跑你的框架的东西,不是你正在改的项目。 模型再去~/.claude找"我的记忆系统"就没有借口了。

约6行已在缓存前缀内+0 tok净增(并入B的65)
改动B + D之后,模型每轮在L0 order 50看到的: <memory> [System note: this is your long-term memory — the always-on summary the memory subsystem keeps at ~/.openprogram/memory . It is written in the background, not by the user, and it is a summary under a 2000-token budget — browse the rest with memory_browse / memory_search .] …core.md渲染结果… </memory> 同一段里,<environment> 补上两行: <environment> OS: Darwin · Shell: /bin/zsh Harness: OpenProgram · its own state lives at ~/.openprogram (not the project you are working on)
三个设计问题的答案。 界碑怎么加:不加新围栏,换常驻块那一条的开场白,因为围栏不幂等,而且已有的那句话是给召回写的、职责不同。 模型怎么知道自己有记忆:两者都要,但顺序有先后:工具目录(20 token)比散文(65 token)更便宜也更硬, 因为模型对工具表的信任度高于对散文的;散文补的是工具名答不出的那三件事(在哪、是不是摘要、谁写的)。 坏了该不该让模型知道:该,而且要跟"空"分开说,否则模型会把系统故障读成"用户没说过"。

10代价与落点第三层

每轮token代价(本机实测,1638 token的系统提示为底) 现在的系统提示 记忆块821 其余十个组件817 1638 方案一 · 六个工具全常驻 +670 tok的schema +670 常驻工具从5397涨到6067,多数会话一次都不调 方案二 · 只加散文说明 +65 说了有记忆,但模型调不到工具,只能干着急 选定 · 目录 + 开场白 +85 其中65在缓存前缀里,第二轮起实际增量是20 记忆空或坏的那一轮 −800 一行状态取代821 token的陈旧内容,是净减 工具schema逐个实测:memory_search 147 · memory_get 131 · memory_update 166 · memory_grep 99 · memory_browse 82 · memory_status 45。延迟之后这些全不发,只发六个名字共20 token。
为什么六个全延迟而不是留两个常驻。延迟目录只发名字,而memory_searchmemory_browse 这两个名字本身就说清了用途,schema里的参数说明属于"要调的时候才需要"。 tool_search永不延迟,是加载其余工具的唯一入口,模型看到名字就能一步接上。

11理想状态第四层

不受当前实现约束的目标形态。四条,各自标明差在哪、为什么这一轮先不做。

现在 理想 差在哪 / 为什么先不做 记忆块的头由上下文层写死 一句常量开场白,改动B之后也还是常量 块里有多少东西、上次写在什么时候,模型无从核对 记忆系统自己描述自己 头部由provider渲染:名字、根路径、主题文件数、 上次写入时间、这块占了预算的百分之几 差:MemoryBackend要多一个describe() 先不做:那个接口只有五个方法是有意的, 而改动C的一行状态已覆盖实测到的失败 十五个组件里七个是裸文本 identity / inline_prompt / workspace_files / current_date 前后两个换行,跟邻居糊在一起 每个组件都带产地,照claude-code的写法 ContextComponent多一个origin字段,渲染成 Contents of {path} ({这份是谁的、走多远}) 差:动十五个builder,作废所有缓存前缀 先不做:出问题的是其中一块, 先修那一块是十五分之一的工作量 模型看不到自己的上下文 context/breakdown.py已经能按组件拆token分类 但只喂给UI,模型这一侧没有入口 一个context_status工具 模型自己能答:我这轮看到了什么、哪块是记忆、 哪块条件为假没出现、各占多少 差:把breakdown接成工具,定它算不算常驻 先不做:改动A和C让模型不必问就知道, 这个是能自查而非必须自查 状态行是一句话,不可核对 改动C之后模型知道"是空的", 但没法判断这句话本身对不对 状态带可核对的量 主题文件数、上次写入时间戳、当前revision, 模型能拿memory_status的返回值对上 差:同第一条,需要describe() 先不做:跟第一条一起做才划算, 单独做等于把同一个接口改两遍
四条共一个方向:把"模型能知道什么"从散文常量变成系统自报。 现在每一句关于记忆的话都是上下文层替记忆系统说的,说的是设计时的样子; 理想状态是记忆系统自己报当下的样子,散文只留下不会变的那部分。 第三层的四处改动全部落在"散文常量"这一侧,是有意的:它们不改接口,所以可以单独上线、单独回退。

12怎么核实的第四层

第一层

装配跑真的

直接调components.assemble(agent, ["L0","L1"], tools=…), 逐块数token,得到1638与记忆块的821。工具表调agent_tools(names=DEFAULT_TOOLS, only_available=True) 逐个打印,二十四个里零个memory_*、一个wiki_agent

围栏不幂等是跑出来的:fence_memory(fence_memory("hello world"))返回内容为空的围栏。

第二层

八个仓库读源码

每一条都给文件路径加行号,字面文本原样抄。 references/pi-ai/是pi-mono传输层的只读快照,没有agent层,六问全部N/A,不是"没查到"。 references/openclaw-full/不存在,references/openclaw/就是全量源码。

references/claude-code/只剩五个BashTool文件,记忆相关的全在 references/claude-code-leaked/;那份<system-reminder>的字面文本跟本机实际收到的一致,可以交叉核对。

现场

本机记忆工作区

~/.openprogram/memory/core.md 4494字节, topics/sources/都是零个文件。

那份core.md是上一代留下的:开头是═══ … 188% (3854/2048 chars) ═══的横幅(hermes的格式), 正文全是[[双括号]]维基链接,结尾一句 [for full context start with memory_browse],而memory_browse不在默认工具表里。 模型每轮读到的就是这段,难怪它答"wiki"。

相关文档。 上下文分层与十五个组件的正文在composition.md; 记忆子系统本身(磁盘三层、写入游标、事务、夜间重排)在memory/overview.md; 记忆写入路径的可视化在memory-architecture.html, 八家的写入与追踪对照在memory-comparison.html。 这页只管一件事:模型每轮能不能知道自己有记忆。