OpenClaw / 01
为什么要自己写 Agent
你大概已经用过 LangChain、Dify 或者 Coze。Demo 阶段很顺,部署到生产后开始出问题:
- 行为不稳定,但 debug 时全是框架内部堆栈
- 想改一个细节,发现要翻三层抽象
- 上了生产遇到安全漏洞,依赖链太长修不动
这不是你用得不好,是框架架构决定的。
真正跑在生产的 Coding Agent 用什么
Claude Code、Cursor、Pi——这些主流 Coding Agent 都没有把 LangChain 或 Dify 作为核心运行时。
它们的共同模式:
| 特征 | 框架方案 | 生产级方案 |
|---|---|---|
| 执行模型 | 链式调用 / DAG | EventStream + Agent Loop |
| 上下文管理 | 截断或摘要 | 可插拔 Context Engine |
| 工具调用 | 串行 + 同步 | 并行(Promise.all)+ MCP |
| 记忆 | 向量数据库存对话 | 文件级持久化(MEMORY.md) |
| 语言 | Python | TypeScript(性能 + 类型安全) |
Pi 与 pi-mono:项目和源码仓库
Pi 是 Mario Zechner 发起的 TypeScript Agent Harness 项目,也提供用户可直接运行的终端 Coding Agent。pi-mono 是它早期的 monorepo 仓库名,不是另一个产品;当前上游仓库已经更名为 earendil-works/pi。
本模块遵循一个术语规则:谈整套项目或终端产品时写 Pi,谈早期仓库和历史资料时写 pi-mono,谈可安装模块时写具体 npm 包名。
packages/
agent/ ← @earendil-works/pi-agent-core:通用 Agent Runtime
ai/ ← @earendil-works/pi-ai:多 Provider LLM API
coding-agent/ ← @earendil-works/pi-coding-agent:提供 pi 命令
tui/ ← @earendil-works/pi-tui:终端 UI
orchestrator/ ← 实验性编排包
Pi 把 Provider、Agent Runtime 和终端产品拆成独立层,是研究生产级 Coding Agent 的公开源码样本。
OpenClaw:从 Pi 出发的独立产品
OpenClaw 的部分实现改编自 Pi / pi-mono,但它不是 Pi 的改名或简单套壳。OpenClaw 面向长期运行的个人助理场景,重点建设完整的应用层:
OpenClaw
├── Gateway 与 WebSocket 控制面
├── Telegram / Slack / Discord 等消息渠道
├── Session、Workspace 与长期记忆
├── Skills、Cron、浏览器和设备节点
├── 工具策略、沙箱与多用户安全
└── OpenClaw 自有 Agent Runtime
截至 2026 年 7 月,OpenClaw 的内置 Runtime已由项目自身维护,核心位于 packages/agent-core 与 src/agents。它保留 Pi 带来的架构和代码渊源,但当前并不是直接运行外部 pi-agent-core;明确保留的 Pi 包依赖主要是 @earendil-works/pi-tui。
核心能力差异:
| 能力 | Pi | OpenClaw |
|---|---|---|
| 核心运行时 | pi-agent-core |
自有 @openclaw/agent-core + embedded runtime |
| 主要入口 | 终端 pi 命令 |
长期驻留 Gateway |
| 多模型适配 | pi-ai |
OpenClaw 自有 Provider 层 |
| 长期记忆 | 以 Coding Session 为中心 | Workspace 文件 + Session 存储 |
| 渠道与自动化 | 终端交互 | 多消息平台、Cron、设备节点 |
Agent 的本质:一个循环
不管框架怎么包装,所有 Agent 的核心结构是同一个循环:
// 伪代码,简化自 Pi 的 Agent Loop 模式
async function agentLoop(messages: Message[]): AsyncGenerator<Event> {
while (true) {
// 1. 调用 LLM
const response = await llm.chat(messages)
yield { type: 'message_end', content: response }
// 2. 如果没有 tool_calls,结束
if (!response.toolCalls?.length) break
// 3. 并行执行所有工具
const results = await Promise.all(
response.toolCalls.map(call => executeTool(call))
)
yield { type: 'tool_execution_end', results }
// 4. 把工具结果追加到 messages,继续循环
messages.push(...results.map(toMessage))
}
}
这就是全部。Agent 和 Chatbot 的区别只有一个:当模型返回 tool_calls 时,执行工具并继续循环,而不是直接结束。
这条路适合什么人
- 你想理解 Claude Code / Cursor 这类产品的底层原理
- 你在准备 Agent 方向的技术面试,需要展示源码级理解
- 你要开发部署一个真正可控的 Agent
它不适合:只想快速出 Demo、不关心底层原理的场景。
学习路径
阅读 Pi(原 pi-mono)源码(TypeScript)
↓
理解 Agent Loop / EventStream 模式
↓
理解 Context Engine / Memory 架构
↓
基于 Pi 的分层改造并接入你的 LLM
↓
部署为你自己的 [YourName]Claw
推荐学习资源
| 资源 | 类型 | 内容 |
|---|---|---|
| OpenClaw-Internals | 源码拆解 | 最深入的中文架构分析(WebSocket 协议、Agent Loop、安全) |
| build-your-own-openclaw | 动手教程 | 18 步 Python 实现(从 0 到 OpenClaw 克隆) |
| how-to-build-a-coding-agent | 动手教程 | Go 语言 6 阶段 Workshop |
| AI-Coding-Guide-Zh | 对比指南 | Claude Code + OpenClaw + Codex 三合一(39 篇教程) |
| openclaw-architecture-analysis | 可视化 | D3.js 交互式架构演进图(15,000+ commits) |
| claude-code-vs-openclaw | 对比分析 | 11 维度机制对比 |