摘要 :nanobot 是一个极简主义的 AI Agent 框架,它用不到 4000 行代码,构建了一个包含 多端接入 (Channel) 、消息总线 (Bus) 、ReAct 循环 、多层记忆 (Memory) 以及 技能扩展 (Skills) 的完整系统。本文将从源码视角,剖析其核心设计理念,帮助开发者理解现代 AI Agent 的底层运作机制。
一、 项目概览
1.1 为什么关注 nanobot?
在 AI Agent 爆发的今天,像 OpenClaw 这样的大型项目虽然功能强大,但往往代码复杂,难以快速上手理解核心逻辑。nanobot 则提供了一个完美的"解剖样本"------它剥离了复杂的业务逻辑,只保留了 Agent 最核心的骨架。
理解了 nanobot,你就理解了大多数基于 ReAct 范式的 AI 助理是如何工作的。
1.2 源码结构图解
nanobot 的项目结构非常扁平,核心模块一目了然:
text
nanobot/
├── agent/ # [核心] 智能体大脑
│ ├── loop.py # ReAct 主循环 (引擎心脏)
│ ├── context.py # 上下文组装 (Prompt 构建)
│ ├── memory.py # 记忆系统 (三层存储)
│ ├── skills.py # 技能管理
│ └── tools/ # 工具箱 (Shell, Web, File 等)
├── bus/ # [通信] 消息总线
│ ├── queue.py # 异步消息队列 (核心解耦机制)
│ └── events.py # 事件定义
├── channels/ # [触角] 多平台接入
│ ├── base.py # 标准接口定义
│ ├── manager.py # 渠道管理器
│ └── feishu/ # 具体实现 (如飞书、微信等)
├── config/ # [配置] Pydantic 配置管理
├── session/ # [存储] 会话持久化 (JSONL)
└── cli/ # [入口] 命令行启动器
二、 核心架构设计
nanobot 采用了一种经典的 分层架构,确保了各个模块的高内聚和低耦合。
graph TD User((用户)) <--> Channels["Access Layer: Channels (飞书/微信/CLI)"] %% 消息总线子图 subgraph "Message Bus (异步解耦)" InQueue["Inbound Queue"] OutQueue["Outbound Queue"] end Channels --> InQueue OutQueue --> Channels %% Agent核心引擎子图 subgraph "Agent Core (核心引擎)" Loop["Agent Loop<br/>(ReAct 循环)"] Context["Context Builder"] Mem["Memory System"] Skills["Skill Registry"] end InQueue --> Loop Loop --> OutQueue Loop <--> Context Context <--> Mem Loop <--> Skills %% 基础设施子图 subgraph "Infrastructure" LLM["LLM Provider"] Storage["File System"] end Loop <--> LLM Mem <--> Storage
架构分层解析
- 接入层 (Access):负责与外部世界交互,无论是飞书消息还是命令行输入,都统一封装为标准 Message 对象。
- 总线层 (Bus):全异步的消息高速公路,通过双向队列隔离了"通信"与"思考"。
- 核心层 (Agent Core):系统的"大脑",负责调度 LLM、管理记忆、执行工具。
- 基建层 (Infrastructure):提供模型能力 (Provider) 和数据持久化能力。
三、 亮点设计:虚拟工具 (Virtual Tools)
设计哲学 :如何让不可控的 LLM 稳定输出结构化数据?nanobot 给出的答案是------利用 Function Calling 协议,而不是依赖 Prompt 指令。
3.1 痛点:Prompt 的局限性
通常我们要求 LLM 输出 JSON 时,会使用如下 Prompt:
"请返回 JSON 格式,包含 action 和 reason 字段..."
但 LLM 经常会"自作聪明"地添加 Markdown 代码块,或者在 JSON 前后废话,导致解析失败。即使使用 JSON Mode,也难以严格约束字段类型(Schema)。
3.2 解决方案:幽灵工具
nanobot 引入了 "虚拟工具" 的概念。这是一种不注册到执行列表,但发送给 LLM 的工具定义。
工作流程:
- 定义 Schema:构造一个 Function Definition,描述你想要的 JSON 结构。
- 欺骗 LLM:在 API 调用时传入这个 Tool,让 LLM 以为它需要调用这个函数。
- 截获参数 :当 LLM 返回
tool_calls时,直接读取其arguments参数------这就是经过严格校验的结构化数据。 - 跳过执行:Agent 并不真的执行这个 Tool,而是直接使用数据。
代码示意:
python
# 定义一个并不存在的工具,仅用于约束输出格式
VIRTUAL_TOOL_SCHEMA = [{
"type": "function",
"function": {
"name": "submit_decision",
"parameters": {
"type": "object",
"properties": {
"decision": {"type": "string", "enum": ["ignore", "reply"]},
"reason": {"type": "string"}
},
"required": ["decision", "reason"]
}
}
}]
# 调用 LLM
response = await llm.chat(messages, tools=VIRTUAL_TOOL_SCHEMA)
# 直接获取结构化结果,无需正则解析
result = response.tool_calls[0].arguments
# result = {"decision": "reply", "reason": "User is asking for help"}
这种模式在 nanobot 的 记忆归档 和 心跳检测 模块中被广泛使用,极大地提高了系统的稳定性。
四、 核心模块深度拆解
4.1 Message Bus:45 行代码的解耦艺术
nanobot 的总线设计极度精简,却实现了完美的异步解耦。
- Inbound Queue:所有 Channel 接收到的消息,经过标准化封装后,扔进这个队列。
- Outbound Queue:Agent 思考产生的回复,扔进这个队列,由 Channel Manager 派发回对应的渠道。
sequenceDiagram participant User participant FeishuChannel participant Bus participant Agent User->>FeishuChannel: 发送消息 "你好" FeishuChannel->>Bus: put(InboundMessage) Note over FeishuChannel, Agent: Channel 此时可以继续处理其他请求,无需等待 loop 异步监听 Agent->>Bus: get(InboundQueue) Bus-->>Agent: 收到消息 end Agent->>Agent: 思考 (ReAct Loop) Agent->>Bus: put(OutboundMessage) loop 异步监听 FeishuChannel->>Bus: get(OutboundQueue) Bus-->>FeishuChannel: 获取回复 end FeishuChannel->>User: 回复消息
4.2 Agent Loop:ReAct 引擎
agent/loop.py 是整个系统的主循环。它并不复杂,本质上是一个状态机:
- Observe (观察):获取当前上下文(Context)。
- Reason (推理):将上下文和工具列表发送给 LLM。
- Act (行动) :
- 如果 LLM 决定调用工具:执行工具 -> 获得结果 -> 将结果追加到上下文 -> 回到步骤 2。
- 如果 LLM 决定回复用户:生成最终文本 -> 结束循环。
4.3 Memory:三层记忆体系
为了解决 LLM 上下文窗口限制(Context Window)的问题,nanobot 设计了一套精巧的 三层记忆模型。
第一层:Session (原始会话)
- 形式 :
.jsonl文件。 - 内容:原汁原味的对话日志,包含所有的 Tool Call 和详细参数。
- 作用:作为"短期记忆"的物理载体,用于保持对话的连贯性。
- 策略 :永不删除,但为了节省 Token,超长工具输出会被截断,图片会被替换为占位符。
第二层:History (事件日志)
- 形式 :
HISTORY.md。 - 内容 :带时间戳的关键事件摘要。
[2024-03-20 10:00] 用户询问了 nanobot 的部署方式。 - 作用 :作为"中长期记忆",供 Agent 在需要时通过
grep工具主动搜索查阅。 - 特点 :只追加 (Append Only)。
第三层:Memory (认知快照)
- 形式 :
MEMORY.md。 - 内容 :去时间化的事实性知识。
用户偏好使用 Python 语言。项目的部署环境是 Ubuntu 22.04。 - 作用 :作为"长期记忆",全量注入 System Prompt,让 Agent 始终"记得"这些关键信息。
- 特点 :全量覆写 (Rewrite)。每次记忆整理时,LLM 会重新生成一份完整的认知快照覆盖旧文件。
五、 总结
nanobot 并没有发明新的算法,它的高明之处在于工程实现的克制与优雅:
- 极简的接口设计:让接入一个新的 IM 渠道变得异常简单。
- 巧妙的 Prompt 工程:利用虚拟工具解决了结构化输出的难题。
- 务实的记忆管理:通过分层存储,在 Token 消耗和记忆持久性之间找到了平衡。
对于想要从零构建 AI Agent 的开发者来说,nanobot 绝对是教科书级别的最佳实践。