用 1% 的代码实现 AI Agent 框架的全部灵魂——nanobot 深度解析
作者思考:当我们谈论 AI 能力的边界时,我们本质上是在谈论「上下文的边界」。而上下文,不应该被关在一个盒子里。
写在前面:为什么要看 nanobot?
2025 年,每周都有新的 AI Agent 框架冒出来。LangChain、AutoGen、CrewAI、OpenHands(原 OpenClaw)……每一个都有几万行代码、庞大的抽象层和学习曲线。
然后出现了 nanobot。
它的 README 里有一行话让我停下来:
Inspired by OpenHands (formerly OpenClaw), but with ~99% less code.
99% 更少的代码。
这不是在炫技,这是一个架构问题。它在说:OpenClaw 的核心本质,只需要 1% 的代码就能表达。 剩下 99% 是什么?是企业级适配、各种边缘 case 的防御代码、UI、和工程化包袱。
如果你想真正理解 AI Agent 的工作原理,nanobot 是目前我见过最好的「解剖标本」。
一、先说结论:nanobot 是什么
一句话定位:一个极轻量的个人 AI 助手框架,把「带工具的有状态对话循环」这件事,用最精炼的方式实现出来。
它能做什么(不是 toy,是真实可用的):
| 能力维度 | 具体内容 |
|---|---|
| 多模型 | OpenRouter、OpenAI、Claude、DeepSeek、Moonshot、阿里云、火山引擎、Ollama、本地 vLLM |
| 多渠道 | CLI、Telegram、Discord、Slack、飞书、钉钉、QQ、WhatsApp、邮件、企业微信、Matrix |
| 工具系统 | 文件读写、Shell 执行、Web 搜索/抓取、MCP、定时任务 Cron、子代理 Spawn |
| 记忆系统 | 持久化会话 + 自动记忆压缩归档(Memory Consolidation) |
| 自动化 | Heartbeat 心跳(30 分钟唤醒一次)+ Cron 定时任务 |
| 技能系统 | 用 Markdown + YAML 描述能力,自然语言注入 Prompt |
二、项目结构:每一个目录都「恰好够用」
1 | nanobot/ |
惊人之处:这个目录结构和 OpenHands 几乎等价,但每个文件的规模小了一个数量级。
它没有「过度工程化」——没有 Event Sourcing、没有插件沙箱、没有复杂的状态机框架。它只是把最核心的几个对象用最直接的方式连起来。
三、架构核心:一张图说清楚一切
1 | 用户 / Telegram / Discord / 飞书 |
核心对象只有 5 个,把它们理解透,你就理解了整个框架:
| 对象 | 职责 |
|---|---|
AgentLoop |
整个流程的编排者 |
SessionManager |
持久化的「对话记录本」 |
ContextBuilder |
把 Session 变成 LLM 的 messages[] |
ToolRegistry |
工具的注册表和执行器 |
MemoryConsolidator |
防止上下文溢出的「垃圾回收器」 |
四、为什么只用 1% 的代码?
这个问题的本质是:OpenHands 那 99% 的代码是在做什么?
答案是:处理边界情况、扩展性、可观测性、和复杂的多 Agent 调度。 这些对个人开发者来说,99% 的场景都用不到。
nanobot 做了一个清醒的取舍:
1. 用 litellm 代替自己写 Provider 适配层
1 | # nanobot 里 LLM 调用的核心:用 litellm 屏蔽所有差异 |
OpenHands 为了支持更多模型和特殊情况,自己维护了大量的适配代码。nanobot 说:litellm 已经做得很好了,我直接用。
2. 用 Markdown 文件代替代码级 Skills 系统
OpenHands 的 Skills/Capabilities 是代码实现的,需要注册、接口、版本管理。
nanobot 的 Skill 是这样的:
1 |
|
就这样。一个 .md 文件,注入进 system prompt,模型就知道怎么用了。自然语言就是接口。
3. 用 asyncio.Queue 代替复杂的事件系统
1 | class MessageBus: |
两个队列,搞定解耦。不需要 pub/sub 框架,不需要 EventBus 抽象,不需要序列化。
4. 工具系统:统一接口,零额外框架
1 | class BaseTool: |
一个 dataclass + 一个 __call__,就是一个工具。ToolRegistry 做的事情也极简:
register(tool)→ 存进 dictget_definitions()→ 返回 OpenAI 格式的 tool schemaexecute(name, args)→ 调用对应工具
五、核心循环:带工具的 Agent 状态机
这是整个框架最关键的部分,理解它就理解了现代 AI Agent 的 80%:
1 | messages = context.build_messages(session, user_input) |
就是这么简单。不需要图状态机,不需要 Planner-Executor 分离,不需要 ReAct 框架。
LLM 本身就是最好的控制器——它决定什么时候调工具、调哪个、什么时候给最终答案。
六、Session / Memory / Context 三角关系
这是很多人搞不清楚的地方,nanobot 把它做得非常清晰:
1 | Session(硬盘) |
- Session 是持久状态(存文件)
- Context 是临时视图(只为下一次 LLM 调用服务)
- MemoryConsolidator 是「垃圾回收」(防止 token 爆炸)
七、实际 Demo:5 分钟跑起来
安装
1 | pip install nanobot-ai |
初始化
1 | nanobot onboard |
配置模型(以 DeepSeek 为例)
1 | { |
跑起来
1 | # 单次问答 |
看看它实际干了什么
1 | > nanobot agent -m "帮我分析一下当前目录下代码的复杂度" |
它会自己决定要读哪些文件、读多少、怎么分析——你不需要告诉它。
八、渠道系统的设计哲学:协议适配,仅此而已
nanobot 有 10+ 个渠道,但每个渠道的实现都极其简单,因为它们只做一件事:
1 | 平台原生消息格式 → InboundMessage → MessageBus |
1 | # 一个最简化的渠道实现示意 |
AgentLoop 完全不知道渠道的存在。它只关心 InboundMessage 和 OutboundMessage。
这个设计意味着:理论上,你可以用 10 行代码接入任何新渠道。
九、亮点汇总
| 亮点 | 说明 |
|---|---|
| Markdown-as-Skills | 用自然语言定义 Agent 能力,无需编写代码接口 |
| 心跳机制 | 每 30 分钟自动唤醒,执行 HEARTBEAT.md 中定义的周期任务 |
| 子代理 | SpawnTool 让 Agent 可以派生子 Agent 处理并行子任务 |
| MCP 原生支持 | 直接接 MCP 服务器,工具生态立刻扩展到整个 MCP 市场 |
| 记忆压缩 | 对话变长时自动 LLM 摘要,永远不会 token 爆炸 |
| 多实例隔离 | 不同 config + 不同 workspace,同时跑多个机器人互不干扰 |
| 安全边界 | restrictToWorkspace + allowFrom 白名单,可安全部署 |
| 零框架依赖 | 核心逻辑不依赖任何 Agent 框架,只用 litellm + asyncio |
十、我的思考:跨平台上下文,才是 AI 能力的真正边界
以下是我个人的延伸思考,不局限于 nanobot,但 nanobot 是触发这个思考的起点。
上下文决定智能的上限
nanobot 让我清晰地意识到一件事:AI Agent 的能力,本质上是它的上下文的函数。
你给它更多相关信息,它就能做出更好的决策。给它文件系统访问,它就能读写代码。给它 Shell,它就能执行命令。给它 Web 搜索,它就能获取最新信息。
但我们现在的上下文,几乎都被困在单一平台的边界里。
- Cursor 里的 Agent 只能看到你的代码文件
- ChatGPT 的 Agent 只能看到你上传的文件
- Telegram 上的 nanobot 只能看到你发送的消息
这不应该是 AI 的极限。
跨端上下文:一个被低估的架构方向
想象这样一个场景:
你在 Cursor 里写代码,IDE Agent 的上下文里不仅有你的代码文件,还有:
- 你在浏览器里刚看的那篇 Stack Overflow 答案(来自浏览器插件)
- 你在 Slack 里和同事讨论这个 bug 的消息记录(来自 Slack 渠道)
- 你昨天在手机上记的那条设计思路(来自移动端笔记同步)
- 你公司内网文档里的相关 API 文档(来自企业知识库连接器)
这些信息分散在不同的「端」上,但它们都和你当前的任务高度相关。
如果 Agent 能同时看到这些,它的决策质量将指数级提升。
技术上怎么实现?
nanobot 的架构给了我们一个线索。它的 MessageBus + Channel 设计天然就是一个「多源上下文聚合器」的骨架:
1 | 浏览器插件 Channel |
每个「数据源」都可以实现为一个 Channel,向 MessageBus 推送「上下文增强消息」。Agent 在构建 Context 的时候,聚合所有来源的相关信息。
更进一步,可以设计一个上下文路由层:
1 | 用户的每个 Action(切换文件、搜索关键词、查看文档) |
这不是幻想。浏览器插件可以监听页面访问事件,IDE 插件可以监听文件切换事件,手机 App 可以通过 webhook 推送,企业知识库可以通过 MCP 接入。
协议是现成的(MCP、OpenAI messages 格式),缺的只是「连接器」和「语义路由」。
为什么这件事很重要?
现在的 AI Coding 工具的天花板,不是模型能力,是上下文质量。
当你问 Claude「为什么这个函数会 crash」,它能看到的只是你圈选的那几行代码。但实际上,crash 的原因可能在:
- 三天前你修改的另一个文件(本地 git 历史)
- 你昨天看的那个 issue(浏览器历史)
- 你和后端同事对齐 API 格式的那条 Slack 消息(另一个工具)
模型本身没有问题,是上下文残缺了。
跨平台、跨端的上下文聚合,本质上是在解决这个问题:给 AI 提供和人类工作时同等丰富的信息环境。
人类程序员在解决 bug 时,会同时开着 IDE、浏览器、Slack、设计稿——他的「工作上下文」天然是多端的。AI Agent 现在还做不到这一点,但架构上已经没有障碍了。
nanobot 在这条路上的位置
nanobot 今天已经做到了「多渠道」——你可以从 Telegram 给它发消息,同时它在服务你的 CLI。这是跨端上下文的一个初步形态。
但更有趣的方向是:不只是多渠道输入,而是多源上下文感知。
Agent 不应该只等着用户发消息,它应该主动感知用户在不同端的行为,理解当前的工作语境,在用户还没开口之前,就已经准备好了相关的上下文。
这就是 Heartbeat 机制的价值所在——nanobot 已经在尝试让 Agent 有「主动性」。下一步是让这种主动性建立在「跨端感知」的基础上。
十一、尾声:为什么要读这种代码
很多人学 AI 框架,都是先看文档,然后调 API,然后写 demo,然后”用起来了”。
但真正理解发生了什么,需要找到一份代码量恰好够你一个人读透的实现。
nanobot 就是这样一份代码。它不是玩具,它有真实的生产能力;它也不是黑盒,每一行代码都有清晰的意图。
读完 nanobot,你会理解:
- 一条消息在 Agent 系统里是怎么流动的
- LLM 的 tool calling 协议是怎么被工程化的
- 记忆系统为什么需要压缩,以及怎么压缩
- 一个「多渠道 AI 助手」的最小可行架构是什么样的
然后,你会开始想:如果上下文不只来自这个工具,而是来自我工作时的整个数字世界,会发生什么?
这个问题,值得我们认真去做。
如果你也在思考 AI Agent 的上下文边界问题,欢迎交流。
参考项目:nanobot-ai