0. 为什么要手搓 OpenClaw
OpenClaw 很强,但完整工程体量也很大。对于大多数开发者来说,直接阅读全量代码会有三个痛点:
- 模块多:Gateway、Agent、Tools、Sessions、Channels 互相耦合
- 路径长:一条消息从输入到回复,跨越多个子系统
- 调试难:没有自己的“最小版本”,很难定位问题
所以这个系列采用一个更实用的学习路径:
先做最小闭环,再逐步补齐能力。
代码地址: 代码下载
1. 目标
用 Python 从 0 到 1 复现 OpenClaw 的核心能力:
- Agent Loop(工具调用 + 多轮推理)
- Session 与并发隔离
- 记忆系统(短期 + 长期)
- Skills 系统(分层加载)
- Web/Telegram 等渠道接入
第一篇的阶段目标是:
- 跑起 FastAPI 服务
- 打通一个最小
/v1/chat对话接口 - 具备会话隔离与并发控制(每会话锁 + 全局信号量)
2. 目标架构
3. 本篇目标
在第 6 篇“能写能存”的基础上,完成“能找能用”:
- 记忆可按 query 召回(
recall) - 记忆可在会话启动时自动注入(
boot_context) - 对压缩摘要做长期归档,避免历史断层
4. 为什么要单独做记忆
只解决“写入”不解决“召回”,长期记忆就是冷数据仓库。
真正有价值的是让 Agent 在正确时机拿到正确记忆,并且不把上下文撑爆。
5. 关键实现一:召回接口
文件:openclaw_py/app/memory/manager.py
defrecall(self,query:str,top_k:int|None=None)->str:all_entries=self.store.list_all()ifnormalized_queryin{"","*","all","everything"}:return"\n".join(f"-{k}:{v}"fork,vinall_entries.items())query_terms=[tfortinre.split(r"\s+",normalized_query)ift]forkey,valueinall_entries.items():haystack=f"{key}{value}".lower()score=sum(1forterminquery_termsifterminhaystack)当前是轻量关键词检索,优点是简单稳定、零外部依赖。
缺点是语义召回能力有限,这正是第 8 篇 RAG 的升级点。
6. 关键实现二:会话启动自动注入
文件:openclaw_py/app/core/agent.py
memory_snapshot=self.memory.boot_context(max_chars=settings.long_term_memory_bootstrap_chars,)ifmemory_snapshot:self.history.append({"role":"system","content":memory_snapshot})这一步让 Agent 在每个 session 起始就具备“基本人设与长期偏好”。
同时通过字符预算限制,避免注入过长。
7. 关键实现三:压缩摘要归档到长期记忆日志
文件:openclaw_py/app/core/agent.py
summary=self._compact_messages(head)ifnotsummary.strip():returnself.memory.store.append_compaction_summary(summary)压缩不是“删除历史”,而是“把历史迁移到更便宜的层”。
这样即便主上下文被压缩,排查和追溯仍有 daily log 可用。
8. 与 OpenClaw 的对齐点
MEMORY.md作为默认长期记忆入口文件- 记忆文件纳入 bootstrap 体系(与
AGENTS.md同类) - 通过“主记忆 + 记忆目录日志”分层管理,控制上下文体积
源码参考(OpenClaw):
repos/openclaw/src/hooks/bundled/bootstrap-extra-files/HOOK.md
Only recognized bootstrap basenames are loaded (`AGENTS.md`, ..., `MEMORY.md`, `memory.md`).9. 下一篇衔接
第 8 篇把当前关键词召回升级为 RAG:
- BM25 召回
- 向量召回
- 融合重排(RRF / rerank)
- 引用片段可解释输出
10. 支持一下
如果这篇对你有帮助,欢迎点赞、收藏、关注。
有余力的话欢迎打赏支持,我会抽时间多多写博客。