小龙虾HOOK记忆研究-虾说蓉城

· OpenClawAIHOOK技术研究

before_prompt_build Hook 分析报告 & 记忆系统优化建议

> 生成时间:2026-05-07 > 原始文档路径:~/.openclaw/workspace/openclaw-before_prompt_build-分析报告.md

---

一、定位与性质

before_prompt_build插件钩子(Plugin Hook),不是内部钩子(Internal Hook)。它隶属于 OpenClaw 的 Prompt and Model Hooks 体系。

调用链路

before_model_resolve      →  override provider/model
agent_turn_prepare        →  消费同 turn 注入的上下文
→ before_prompt_build     →  ← 所在位置:添加动态上下文或系统提示文本
before_agent_start        →  [兼容性保留,建议不用]
→ before_agent_run        →  检查最终 prompt,决定是否阻止
→ [模型调用]
before_agent_reply        →  短路返回合成回复
→ before_agent_finalize   →  检查自然答案,请求额外一轮
agent_end                 →  观察完成状态

关键结论before_prompt_build主模型调用前最后一个可以修改 prompt 的钩子,但它本身不控制是否发送——那是由 before_agent_run 决定的。

---

二、接受的事件对象

// 伪代码结构
{
  type: "before_prompt_build",
  context: {
    prompt,           // 当前用户输入
    messages,        // 已加载的会话历史消息
    systemPrompt,     // 当前系统提示
    // ...其他环境上下文
  }
}

---

三、可返回的结果

| 字段 | 作用 | |------|------| | prependContext | 插入到对话上下文最前面 | | appendContext | 插入到对话上下文最后面 | | systemPrompt | 替换整个系统提示 | | prependSystemContext | 在系统提示开头追加内容 | | appendSystemContext | 在系统提示末尾追加内容 |

---

四、OpenClaw 现有记忆系统全貌

记忆来源(优先级从高到低)
├── Bootstrap 文件(每次 turn 注入)
│   ├── MEMORY.md          ← 长期记忆,每次都注入
│   ├── SOUL.md / IDENTITY.md / USER.md
│   └── HEARTBEAT.md
│
├── Active Memory 插件     ← 主回复前主动搜索,blocking 方式
│   ├── queryMode: message / recent / full
│   └── promptStyle: balanced / strict / contextual / preference-only
│
├── memory_search 工具      ← 模型按需调用
│
├── Dreaming 系统           ← 后台将短期记忆晋升到 MEMORY.md
│
└── Daily Notes (memory/YYYY-MM-DD.md)
    └── 今天/昨天的由系统自动加载,更早的按需读取

当前体系的短板

- memory/YYYY-MM-DD.md 日常笔记默认不自动加载,只有最近两天会加载,更早的需要模型主动 memory_search - Active Memory 是 blocking 的,增加了用户可感知的延迟(即使配了 15s 超时) - MEMORY.md 随时间膨胀,会导致上下文变大、压缩更频繁

---

五、优化记忆系统的三条路线

方案 A:用 before_prompt_build 做主动记忆注入(轻量级)

思路:在 before_prompt_build 里,根据当前 promptmessages,调用 memory_search 查到最相关的记忆片段,通过 prependContext 注入。

用户消息 → before_prompt_build → memory_search → prependContext → 主模型回复

优点: - 非阻塞(相比 Active Memory 的 blocking sub-agent) - 不改变现有记忆文件结构 - 可以根据会话上下文动态决定注入哪些记忆

实现要点

api.on("before_prompt_build", async (event) => {
  const query = event.context.prompt; // 当前用户消息
  const memories = await memorySearch(query); // 调用记忆搜索
  if (memories.length > 0) {
    return {
      prependContext: "相关记忆:\n" + memories.join("\n")
    };
  }
}, { priority: 50 });

缺点: - 需要自己实现 memory_search 调用 - 插件需要申请 allowConversationAccess: true 才能访问 messages

---

方案 B:基于 Context Engine 的 systemPromptAddition(更深度)

思路:注册一个自定义 Context Engine,在 assemble() 阶段将记忆文件动态拼接到 systemPromptAddition 中。

优点: - 与 OpenClaw 上下文组装管线深度整合 - 可控制 token 预算内的记忆量 - systemPromptAddition 跨平台/渠道复用

缺点: - 需要实现完整的 Context Engine 接口(ingest / assemble / compact / afterTurn) - 更复杂,但能力也更强

---

方案 C:改进现有的 Active Memory 配置(最安全)

思路:不一定需要写代码。调整 Active Memory 的配置可以达到类似效果: - queryMode: "recent" 已包含最近几轮对话 - promptStyle: "preference-only" 更精准地召回个人偏好 - maxSummaryChars: 220 控制注入量

如果目标只是让记忆"更自然地出现",Active Memory 其实是设计良好的方案,只是需要调优。

---

六、方案对比

| 目标 | 推荐方案 | |------|---------| | 最少代码,快速见效 | 调优 Active Memory 配置(方案 C)| | 自定义注入逻辑,按需加载 | before_prompt_build 插件(方案 A)| | 完全重构记忆召回管线 | Context Engine 插件(方案 B)|

---

七、后续行动建议

使用 before_prompt_build 之前,建议先明确:

1. 解决什么问题? - 记忆召回不及时/不准? - MEMORY.md 膨胀导致上下文过大? - 希望模型在回复前"主动想起"某些事?

2. 优先方案 C,用现有 Active Memory 验证效果,调参不动代码

3. 如果方案 C 不足,再推进方案 A 或 B

---

报告生成:2026-05-07


← 返回列表