架构
系统概览
nanobot 是一个事件驱动的单进程 AI 助手,围绕异步消息总线构建。聊天频道产生入站消息,Agent 循环消费这些消息,携带工具定义调用 LLM,执行工具调用,并通过频道将响应发送回去。整个系统运行在单个 Python asyncio 事件循环中。
高层架构
graph TB
subgraph "Chat Channels"
Telegram[Telegram]
Discord[Discord]
Slack[Slack]
WhatsApp[WhatsApp]
Feishu[Feishu]
DingTalk[DingTalk]
QQ[QQ]
Email[Email]
Matrix[Matrix]
Mochat[Mochat]
end
subgraph "Message Bus"
InQ[Inbound Queue]
OutQ[Outbound Queue]
end
subgraph "Agent Core"
Loop[Agent Loop]
Context[Context Builder]
Memory[Memory Store]
Skills[Skills Loader]
Tools[Tool Registry]
Session[Session Manager]
end
subgraph "Tools"
FS[File System]
Shell[Shell Exec]
Web[Web Search/Fetch]
Cron[Cron Scheduler]
Spawn[Subagent Spawn]
MCP[MCP Bridge]
Msg[Message Tool]
end
subgraph "LLM Providers"
LiteLLM[LiteLLM Provider]
Custom[Custom Provider]
Codex[Codex Provider]
end
subgraph "External LLMs"
OpenRouter[OpenRouter]
Anthropic[Anthropic]
OpenAI[OpenAI]
DeepSeek[DeepSeek]
Gemini[Gemini]
Others[Others...]
end
Telegram & Discord & Slack & WhatsApp & Feishu & DingTalk & QQ & Email & Matrix & Mochat --> InQ
InQ --> Loop
Loop --> Context
Context --> Memory
Context --> Skills
Loop --> Session
Loop --> Tools
Tools --> FS & Shell & Web & Cron & Spawn & MCP & Msg
Loop --> LiteLLM & Custom & Codex
LiteLLM --> OpenRouter & Anthropic & OpenAI & DeepSeek & Gemini & Others
Loop --> OutQ
OutQ --> Telegram & Discord & Slack & WhatsApp & Feishu & DingTalk & QQ & Email & Matrix & Mochat
style Loop fill:#e8f5e9
style InQ fill:#e3f2fd
style OutQ fill:#e3f2fd
组件架构
1. 消息总线 (nanobot/bus/)
MessageBus 是核心的解耦机制,使用两个 asyncio.Queue 实例:
入站队列:频道推送
InboundMessage→ Agent 消费出站队列:Agent 推送
OutboundMessage→ ChannelManager 分发到对应频道
关键设计决策:总线完全异步且非阻塞。频道和 Agent 循环作为独立的 asyncio 任务运行,仅通过队列通信。
文件:
nanobot/bus/queue.py:MessageBus— 队列封装nanobot/bus/events.py—InboundMessage、OutboundMessage数据类
2. 频道 (nanobot/channels/)
每个频道都是 BaseChannel(nanobot/channels/base.py)的子类,需实现三个方法:
start()— 连接平台并监听消息stop()— 断开连接并清理资源send(msg)— 投递出站消息
ChannelManager(nanobot/channels/manager.py)根据配置初始化所有已启用的频道,以 asyncio 任务启动它们,并通过匹配 msg.channel 将出站消息分发到对应的频道实例。
访问控制:每个频道强制执行 allowFrom 白名单。BaseChannel 中的 is_allowed(sender_id) 方法在将消息转发到总线之前检查白名单。
频道 |
协议 |
认证方式 |
|---|---|---|
Telegram |
HTTP long-poll |
Bot token |
Discord |
WebSocket gateway |
Bot token |
Slack |
Socket Mode (WebSocket) |
Bot + App tokens |
WebSocket (Node.js bridge) |
扫描二维码 |
|
Feishu |
WebSocket 长连接 |
App ID + Secret |
DingTalk |
Stream Mode |
Client ID + Secret |
WebSocket (botpy) |
App ID + Secret |
|
IMAP 轮询 + SMTP 发送 |
用户名 + 密码 |
|
Matrix |
Matrix sync API |
Access token |
Mochat |
Socket.IO |
Claw token |
3. Agent 循环 (nanobot/agent/loop.py)
AgentLoop 类是核心处理引擎。其主循环流程如下:
从总线消费一条
InboundMessage以
channel:chat_id为键加载/创建Session通过
ContextBuilder构建上下文 — 从身份信息、引导文件(AGENTS.md、SOUL.md、USER.md等)、记忆和技能中组装系统提示词通过
LLMProvider.chat()调用 LLM,传入消息历史和工具 schema如果
LLMResponse包含tool_calls,则执行工具 — 分发到ToolRegistry如果工具结果需要 LLM 进一步处理,则循环回到第 4 步
将最终的
OutboundMessage发布到总线
sequenceDiagram
participant Channel
participant Bus
participant AgentLoop
participant Context
participant LLM
participant ToolRegistry
participant Session
Channel->>Bus: InboundMessage
Bus->>AgentLoop: consume_inbound()
AgentLoop->>Session: get/create session
AgentLoop->>Context: build_system_prompt()
Context->>Context: identity + bootstrap + memory + skills
AgentLoop->>Session: get_history()
loop Until no tool_calls
AgentLoop->>LLM: chat(messages, tools)
LLM-->>AgentLoop: LLMResponse
alt Has tool_calls
AgentLoop->>ToolRegistry: execute tool
ToolRegistry-->>AgentLoop: tool result
AgentLoop->>Session: append tool result
end
end
AgentLoop->>Session: save_turn()
AgentLoop->>Bus: OutboundMessage
Bus->>Channel: send()
4. 上下文构建器 (nanobot/agent/context.py)
ContextBuilder 通过拼接以下内容来组装系统提示词:
身份信息 — 内置的角色描述
引导文件 — 工作区中用户可自定义的文件:
AGENTS.md、SOUL.md、USER.md、TOOLS.md、IDENTITY.md记忆 — 来自
MemoryStore的整合记忆常驻技能 — 配置为始终加载的技能
技能摘要 — 可用技能的简要描述,让 LLM 知道可以通过
read_file激活哪些技能运行时上下文 — 当前日期/时间、平台信息
5. Provider 系统 (nanobot/providers/)
Provider 系统采用注册表模式(nanobot/providers/registry.py):
ProviderSpec是一个冻结的数据类,描述每个 provider 的元数据(名称、关键词、环境变量、LiteLLM 前缀等)PROVIDERS元组是唯一的事实来源 — 排列顺序决定匹配优先级三种 provider 实现:
LiteLLMProvider— 通过 LiteLLM 路由,支持广泛的模型CustomProvider— 直接发起 OpenAI 兼容的 HTTP 调用(绕过 LiteLLM)OpenAICodexProvider— 基于 OAuth 的认证流程
classDiagram
class LLMProvider {
<<abstract>>
+chat(messages, tools, model, ...) LLMResponse
+get_default_model() str
}
class LiteLLMProvider {
+chat()
+get_default_model()
}
class CustomProvider {
+chat()
+get_default_model()
}
class OpenAICodexProvider {
+chat()
+get_default_model()
}
class ProviderSpec {
+name: str
+keywords: tuple
+env_key: str
+litellm_prefix: str
+is_gateway: bool
+is_direct: bool
}
LLMProvider <|-- LiteLLMProvider
LLMProvider <|-- CustomProvider
LLMProvider <|-- OpenAICodexProvider
6. 工具系统 (nanobot/agent/tools/)
所有工具继承自 Tool 抽象基类(nanobot/agent/tools/base.py):
工具 |
文件 |
描述 |
|---|---|---|
|
|
读取文件内容 |
|
|
写入/创建文件 |
|
|
通过搜索/替换修补文件 |
|
|
列出目录内容 |
|
|
执行 shell 命令 |
|
|
搜索网页(Brave API) |
|
|
抓取并提取网页内容 |
|
|
向用户发送消息 |
|
|
启动后台子 Agent |
|
|
管理定时任务 |
MCP tools |
|
来自 MCP 服务器的动态工具 |
ToolRegistry(nanobot/agent/tools/registry.py)收集所有工具实例并提供:
get_schemas()— 返回 OpenAI 格式的函数 schema 供 LLM 使用execute(name, params)— 分发到对应的工具
工具参数在执行前通过 Tool.validate_params() 按 JSON Schema 进行校验。
7. 会话管理 (nanobot/session/)
会话以 channel:chat_id 为键。每个会话:
以
list[dict]存储消息(仅追加,以提高 LLM 缓存效率)以 JSONL 文件持久化到
~/.nanobot/sessions/目录支持历史整合:超过阈值后,较早的消息会被摘要到
HISTORY.md和MEMORY.md中通过
get_history()仅返回未整合的消息,并对齐到用户轮次以避免孤立的工具结果
8. 技能系统 (nanobot/agent/skills.py)
技能是以 Markdown 编写的能力描述,存储为 SKILL.md 文件:
内置技能:
nanobot/skills/(weather、github、tmux、cron 等)用户技能:
~/.nanobot/workspace/skills/
SkillsLoader 扫描两个目录,为系统提示词构建摘要,并在 LLM 通过 read_file 工具读取 SKILL.md 文件时按需加载完整的技能内容。
9. 心跳与定时任务
HeartbeatService(
nanobot/heartbeat/service.py):每 30 分钟唤醒一次,读取~/.nanobot/workspace/HEARTBEAT.md,并询问 LLM 是否应执行相关任务CronService(
nanobot/cron/service.py):使用croniter进行精确的 cron 表达式调度;cron工具允许 LLM 管理定时任务
横切关注点
错误处理
Agent 循环在工具执行期间捕获异常,并将错误信息作为工具结果返回给 LLM,使 LLM 能够恢复或通知用户。Provider 错误(API 故障、速率限制)会被记录日志,并作为面向用户的错误消息传播。
日志
nanobot 使用 loguru 进行所有日志记录。日志默认输出到 stderr;CLI 模式下的 --logs 标志可在聊天输出旁显示日志。
安全
工作区沙箱:
tools.restrictToWorkspace将所有文件/shell 工具限制在工作区目录内频道访问控制:每个频道的
allowFrom白名单,在BaseChannel.is_allowed()中检查会话隔离:会话以
channel:chat_id为键 — 不存在跨会话泄露MCP 工具超时:可配置的
toolTimeout(默认 30 秒),防止挂起的 MCP 服务器阻塞 Agent
配置
单一的 Config Pydantic 模型(nanobot/config/schema.py)校验所有配置:
同时接受 camelCase 和 snake_case 键(通过
alias_generator=to_camel)嵌套模型:
ProvidersConfig、ChannelsConfig、AgentsConfig、ToolsConfig从
~/.nanobot/config.json加载,带有合理的默认值
相关文档
最后更新:2026-03-15 版本:1.0