Nanobot 深度实战:当 4000 行 Python 重写了 AI Agent 的游戏规则——从核心架构到生产级部署的超轻量智能体完全指南(2026)
当 OpenClaw 用 60,000+ 行 TypeScript 构建了一个全功能 AI 助手平台时,香港大学数据科学实验室(HKUDS)的一群开发者反其道而行之:用不到 4000 行 Python,实现了 99% 的核心功能,并且让每一行代码都清晰可读。这不是一个玩具项目——它支持 Telegram、Discord、微信、飞书等十几种聊天平台,内置 MCP 协议、模型路由、持久化记忆、Cron 自动化,甚至有完整的 WebUI。这就是 Nanobot,一个正在重新定义"轻量"边界的 AI Agent 运行时框架。
一、为什么 Nanobot 值得关注?
1.1 AI Agent 框架的"肥胖症"危机
2026 年的 AI Agent 生态繁荣得有些畸形。OpenClaw 仓库突破 60,000 次提交,LangChain 的依赖树深不见底,AutoGen 的配置文件比你的业务代码还长。每一个框架都在试图成为"全能选手",结果就是:
- 启动时间长:一个典型的 Agent 框架从安装到跑通第一个 Hello World,平均需要 30 分钟以上
- 内存占用高:常驻进程动辄数百 MB,对树莓派等边缘设备极不友好
- 调试困难:层层抽象让排查问题变成考古工作
- Token 浪费:把所有 Skills 的完整内容塞进 System Prompt,每轮对话的隐形成本惊人
Nanobot 的出现,本质上是对这种趋势的一次"极简主义反击"。
1.2 Nanobot 的核心定位
核心代码量:约 4000 行 Python
启动时间:2-3 秒(从命令行到接收第一条消息)
内存占用:约 50MB 常驻
支持平台:Telegram | Discord | Slack | WhatsApp | Matrix | 飞书 | 钉钉 | QQ | 微信 | Signal | MSTeams | Email | WebUI
支持模型:OpenAI | Anthropic | DeepSeek | 通义千问 | Kimi | StepFun | MiniMax | VolcEngine | vLLM | Ollama | OpenRouter 等 20+ 提供商
许可证:MIT
Python 要求:>= 3.11
这个定位不是"阉割版 Agent",而是标准 Agent 运行时内核——足够小到可以嵌入任何业务,又足够完整到独立运行。
1.3 与主流框架的硬核对比
| 维度 | Nanobot | OpenClaw | LangGraph | AutoGen |
|---|---|---|---|---|
| 核心语言 | Python | TypeScript | Python | Python |
| 核心代码量 | ~4000 行 | 60,000+ 行 | ~15,000 行 | ~20,000 行 |
| 安装方式 | pip install | npm install | pip install | pip install |
| 配置方式 | config.json | YAML + 插件 | Python 代码 | Python 代码 |
| 启动时间 | 2-3 秒 | 10-15 秒 | 5-8 秒 | 5-8 秒 |
| 聊天平台 | 13+ 内置 | 8+ 内置 | 无(需自行集成) | 无(需自行集成) |
| MCP 支持 | ✅ 内置 | ✅ 内置 | ✅ 通过 langchain-mcp | ❌ 需插件 |
| 记忆系统 | ✅ 持久化 | ✅ 持久化 | ❌ 需自行实现 | ❌ 需自行实现 |
| WebUI | ✅ 内置 | ✅ 内置 | ❌ | ✅ 简单版 |
| 模型路由 | ✅ 20+ 提供商 | ✅ 多提供商 | ✅ LangSmith | ❌ 有限 |
| 扩展方式 | Tool 基类 + Hook | 插件系统 | 中间件 | 消息处理 |
| 学习曲线 | 极低 | 中等 | 中高 | 中高 |
关键发现:Nanobot 在核心功能覆盖度上与 OpenClaw 几乎持平,但代码量只有其 7%。这不是简单的代码压缩,而是架构哲学的根本差异。
二、架构深度解析:四个核心组件的精妙设计
2.1 AgentLoop:引擎的心脏
AgentLoop 是整个框架的入口和调度中心。它不直接执行任何业务逻辑,而是像餐厅的"总传菜员"一样,协调各个组件的协作:
class AgentLoop:
def __init__(self, bus, provider, workspace, model, ...):
self.context = ContextBuilder(workspace) # System Prompt 组装
self.tools = ToolRegistry() # 工具注册表
self.runner = AgentRunner(provider) # LLM + 工具循环
self.subagents = SubagentManager(...) # 子 Agent 管理
self._register_default_tools() # 注册内置工具
这行代码揭示了 Nanobot 的设计哲学:组合优于继承,声明优于编程。AgentLoop 只做一件事——把各个模块组装起来,然后按照固定流程运转。
核心处理流程:
用户消息 → MessageBus → AgentLoop._dispatch()
→ ContextBuilder.build_system_prompt()
→ AgentRunner.run()
→ 调用 LLM 模型
→ 如果有 tool_calls → 执行工具 → 回到 LLM
→ 如果返回文本 → finalize_content()
→ Channel 发送响应
2.2 ContextBuilder:System Prompt 的智能组装
ContextBuilder 是 Nanobot 最精妙的设计之一。它负责从多个来源组装完整的 System Prompt,采用分层组装策略:
class ContextBuilder:
def build_system_prompt(self):
parts = [
self._get_identity(), # 1. 内置身份描述
self._load_bootstrap_files(), # 2. AGENTS.md / SOUL.md
self.memory.get_memory_context(), # 3. memory/MEMORY.md
always_skills, # 4. always=true 的技能
self.skills.build_skills_summary(), # 5. 技能 XML 摘要
]
return "\n\n".join(parts)
每一层都精确控制了信息注入的粒度:
- 第 1 层 - 身份:告诉 Agent "你是谁",只有几行核心描述
- 第 2 层 - 行为规则:AGENTS.md 中的行为规范,约束 Agent 的行动边界
- 第 3 层 - 记忆:从持久化存储加载关键上下文
- 第 4 层 - 常驻技能:标记为
always: true的技能全文注入(目前只有 memory) - 第 5 层 - 技能索引:所有技能的 XML 摘要,只包含名称和描述
这一设计的精妙之处在于:第 5 层只注入摘要而非全文。当 Agent 真正需要使用某个技能时,它会通过 read_file 工具主动读取完整的 SKILL.md。这就是**渐进式加载(Progressive Disclosure)**的核心思想。
2.3 ToolRegistry:显式工具注册的优雅
与 LangChain 的 @tool 装饰器不同,Nanobot 采用显式继承 Tool 基类的方式定义工具:
class MyCustomTool(Tool):
@property
def name(self) -> str:
return "my_tool"
@property
def description(self) -> str:
return "工具描述,告诉模型这个工具能做什么"
@property
def parameters(self) -> dict:
return {
"type": "object",
"properties": {
"param": {"type": "string", "description": "参数描述"}
},
"required": ["param"]
}
async def execute(self, **kwargs) -> Any:
# 执行逻辑
pass
这种设计看似比装饰器啰嗦,实则有几个关键优势:
- 元数据一目了然:name、description、parameters 会被 ToolRegistry 自动收集,转换成 LLM function calling 的 JSON Schema
- 只读标记:
read_only属性让框架知道哪些工具可以安全并行执行 - IDE 友好:继承关系让代码跳转、自动补全完美工作
- 类型安全:Python 类型注解让静态检查工具有用武之地
内置工具清单:
| 工具 | 功能 | 安全级别 |
|---|---|---|
| ReadFileTool | 读取文件 | 只读 |
| WriteFileTool | 写入文件 | 读写 |
| EditFileTool | 编辑文件(精确替换) | 读写 |
| ListDirTool | 列出目录内容 | 只读 |
| ExecTool | 执行 Shell 命令 | 受限(allow-list) |
| WebSearchTool | 网络搜索(5 种引擎) | 只读 |
| WebFetchTool | 获取网页内容 | 只读 |
| SpawnTool | 生成子 Agent | 高权限 |
| MessageTool | 发送消息 | 中权限 |
| CronTool | 定时任务管理 | 中权限 |
| MCP Tool | MCP 协议工具桥接 | 取决于 MCP 服务器 |
2.4 AgentHook:非侵入式生命周期扩展
AgentHook 是 Nanobot 的扩展机制,提供五个生命周期钩子:
class AgentHook:
async def before_iteration(self, ctx) # 每轮 LLM 调用前
async def on_stream(self, ctx, delta) # 流式输出每个 token
async def before_execute_tools(self, ctx) # 工具执行前(可拦截/修改)
async def after_iteration(self, ctx) # 每轮结束后
def finalize_content(self, ctx, content) # 最终输出后处理
每个方法默认都是空实现(pass),不挂钩就零开销。这使得框架保持极简,同时提供了足够的扩展空间。
实际使用场景:
class CostControlHook(AgentHook):
"""Token 成本控制钩子"""
def __init__(self, budget_per_turn: int = 10000):
self.budget = budget_per_turn
self.used = 0
async def on_stream(self, ctx, delta):
self.used += 1
if self.used > self.budget:
ctx.cancel = True # 超预算时取消生成
class AuditLogHook(AgentHook):
"""审计日志钩子"""
async def before_execute_tools(self, ctx):
for tool_call in ctx.tool_calls:
logger.info(f"AUDIT: tool={tool_call.name}, args={tool_call.arguments}")
三、Skills 渐进式加载:Token 经济学的实践
3.1 为什么渐进式加载是关键创新
假设你构建了一个投研 Agent,拥有 8 个技能模块,每个 SKILL.md 平均 2000 字符。如果全部塞进 System Prompt:
8 × 2000 字符 = 16,000 字符 ≈ 4,000 tokens
每轮对话固定成本:4,000 tokens × $0.03/1K = $0.12
每天 100 轮对话:$12
每月:$360 —— 仅技能指导就花掉 $360!
Nanobot 的三级加载系统彻底解决了这个问题:
3.2 三级加载机制
第 1 级:XML 摘要(永远在 context 中,约 100 words/skill)
<skills>
<skill available="true">
<name>web-search</name>
<description>联网搜索实时市场信息</description>
<location>/path/to/web-search/SKILL.md</location>
</skill>
<skill available="false">
<name>database-query</name>
<description>查询本地数据库</description>
<location>/path/to/database-query/SKILL.md</location>
</skill>
</skills>
Agent 看到的是技能清单,知道自己"有什么可以用",但不需要知道"具体怎么用"。10 个技能的摘要只有约 1000 字(250 tokens),成本降低了 16 倍。
第 2 级:always 技能(全文自动注入)
---
name: memory
description: Two-layer memory system with grep-based recall.
always: true
---
标记 always: true 的技能会全文注入 System Prompt。目前只有 memory 技能标记了 always,因为记忆系统需要每轮可用。
第 3 级:按需加载(模型自主 read_file)
System Prompt 中注入引导指令:
# Skills
The following skills extend your capabilities. To use a skill, read its SKILL.md
file using the read_file tool.
当 Agent 判断需要某个技能时,主动调用 read_file 读取完整内容。这是一个lazy loading策略,只在真正需要时才加载。
3.3 Token 成本对比
| 策略 | 每 Skill Token 成本 | 10 Skills 总成本 | 实际使用成本 |
|---|---|---|---|
| 全量注入 | ~500 tokens | ~5,000 tokens | 每轮 5,000 tokens |
| Nanobot 渐进式 | ~25 tokens(摘要) | ~250 tokens | 250 + 按需 500 |
结论:对于拥有 10 个技能的 Agent,Nanobot 的渐进式加载在大多数场景下节省 80%+ 的 Token 成本。对于一个需要 24/7 运行的生产级 Agent,这意味着每月数百美元的成本差异。
四、多平台聊天集成:一个 Agent,无处不在
4.1 Channel 架构
Nanobot 的 Channel 系统采用统一的接口抽象:
class BaseChannel:
async def send(self, message: str) -> None
async def receive(self) -> Message
async def connect(self) -> None
async def disconnect(self) -> None
每个平台实现自己的 Channel 子类:TelegramChannel、DiscordChannel、SlackChannel 等。这种设计让同一个 Agent 实例可以同时连接多个平台。
4.2 实战:让 Agent 同时在 Telegram 和 WebUI 上工作
# 安装
pip install nanobot-ai
# 初始化(交互式引导你选择提供商和模型)
nanobot setup
# 启动 Agent(同时启用 Telegram 和 WebUI)
nanobot run --channel telegram --channel webui
启动后:
- Telegram 用户发消息 → TelegramChannel 接收 → AgentLoop 处理 → TelegramChannel 回复
- WebUI 用户发消息 → WebUIChannel 接收 → AgentLoop 处理 → WebUIChannel 回复
- 两个渠道共享同一个记忆系统、同一个技能库、同一个 Agent 身份
4.3 配置示例
{
"providers": {
"deepseek": {
"apiKey": "sk-xxx",
"apiBase": "https://api.deepseek.com/v1",
"model": "deepseek-chat"
}
},
"agents": {
"defaults": {
"provider": "deepseek",
"model": "deepseek-chat",
"fallbackModels": ["gpt-4o-mini", "claude-3-haiku"]
}
},
"channels": {
"telegram": {
"token": "BOT_TOKEN",
"allowedChats": ["-1001234567890"]
},
"webui": {
"port": 8080,
"auth": true
}
}
}
注意 fallbackModels 字段——当主模型不可用时,Nanobot 会自动切换到备选模型,这是生产级 Agent 的必备功能。
五、自定义工具实战:从零搭建 Text-to-SQL Agent
5.1 项目结构
nanobot-examples/text-to-sql/
├── config.json # 模型和提供商配置
├── AGENTS.md # Agent 身份和规则
├── agent.py # 入口 + QueryDBTool 自定义工具
├── chinook.db # 示例数据库
└── skills/
├── schema-exploration/ # 数据库结构探索技能
│ └── SKILL.md
└── query-writing/ # SQL 编写与错误恢复技能
└── SKILL.md
5.2 核心代码:自定义 QueryDBTool
import sqlite3
from pathlib import Path
from nanobot.agent.tools.base import Tool
class QueryDBTool(Tool):
"""SQL 查询工具 - 在示例数据库上执行只读 SQL"""
def __init__(self, db_path: Path):
self._db_path = db_path
@property
def name(self) -> str:
return "query_db"
@property
def description(self) -> str:
return (
"Execute a read-only SQL query against the Chinook database. "
"Returns query results as formatted text. Only SELECT and "
"PRAGMA statements are allowed."
)
@property
def parameters(self) -> dict:
return {
"type": "object",
"properties": {
"sql": {
"type": "string",
"description": "The SQL query to execute (SELECT only)"
}
},
"required": ["sql"]
}
@property
def read_only(self) -> bool:
return True
async def execute(self, **kwargs) -> str:
sql = kwargs.get("sql", "").strip()
if not sql:
return "Error: empty SQL query"
# 安全校验:只允许 SELECT 和 PRAGMA
upper = sql.upper().lstrip()
if not (upper.startswith("SELECT") or upper.startswith("PRAGMA")):
return "Error: only SELECT and PRAGMA statements are allowed"
try:
conn = sqlite3.connect(str(self._db_path))
cursor = conn.cursor()
cursor.execute(sql)
columns = [desc[0] for desc in cursor.description] if cursor.description else []
rows = cursor.fetchall()
conn.close()
if not rows:
return "Query returned 0 rows."
lines = [" | ".join(columns)]
lines.append("-" * len(lines[0]))
for row in rows[:50]:
lines.append(" | ".join(str(v) for v in row))
if len(rows) > 50:
lines.append(f"... ({len(rows)} total rows, showing first 50)")
return "\n".join(lines)
except Exception as e:
return f"SQL Error: {e}"
这个工具设计的四层防护:
- 元数据层:name/description/parameters 自动转换为 LLM function calling schema
- 安全层:代码级别强制只允许 SELECT 和 PRAGMA,防止写操作
- 执行层:连接数据库、执行查询、获取结果
- 防护层:最多返回 50 行,防止大表查询撑爆上下文
5.3 组装 Agent
from nanobot import Nanobot
from pathlib import Path
WORKSPACE = Path(__file__).parent
DB_PATH = WORKSPACE / "chinook.db"
def build_bot() -> Nanobot:
bot = Nanobot.from_config(
config_path=WORKSPACE / "config.json",
workspace=WORKSPACE,
)
# 注册自定义 SQL 查询工具
bot._loop.tools.register(QueryDBTool(DB_PATH))
return bot
if __name__ == "__main__":
import sys
bot = build_bot()
question = sys.argv[1] if len(sys.argv) > 1 else "What tables are in the database?"
print(bot.chat(question))
5.4 运行效果演示
$ python agent.py "How many customers are from Canada?"
Agent 执行流程:
1. ContextBuilder 组装 System Prompt(注入 AGENTS.md + Skills 摘要)
2. Agent 判断需要探索数据库结构 → read_file(schema-exploration/SKILL.md)
3. 执行 query_db("SELECT name FROM sqlite_master WHERE type='table'")
4. 执行 query_db("PRAGMA table_info(Customer)")
5. Agent 判断需要 SQL 编写指导 → read_file(query-writing/SKILL.md)
6. 执行 query_db("SELECT COUNT(*) FROM Customer WHERE Country = 'Canada'")
7. 返回结果:There are 8 customers from Canada.
全程自主:Agent 先探索 schema,再编写 SQL,执行并返回结果。如果 SQL 出错(如表名大小写不对),Agent 会根据 SKILL.md 中的 Error Recovery 指导自动修正重试。
六、记忆系统:让 Agent 拥有"长期记忆"
6.1 双层记忆架构
Nanobot 的记忆系统采用经典的短期/长期双层架构:
- 短期记忆(Session History):当前会话的完整对话历史,存储在本地文件中
- 长期记忆(Persistent Memory):通过
memory/MEMORY.md和memory/YYYY-MM-DD.md文件持久化存储
6.2 AutoCompact:上下文压缩
当对话历史超过模型上下文窗口时,Nanobot 会自动触发压缩:
# Nanobot 内置的 AutoCompact 机制
# 当对话 token 数接近模型限制时:
# 1. 提取关键信息 → 生成摘要
# 2. 摘要替代原始对话历史
# 3. 保留最近 N 轮完整对话
# 4. 更新持久化记忆文件
这让 Agent 可以执行数十甚至上百步的长期任务,而不会因上下文溢出而"失忆"。这也是 Nanobot 标榜的"Enduring"特性——长航程执行能力。
6.3 Dream 记忆:技能发现与沉淀
Nanobot 的 Dream 系统会在 Agent 空闲时自动分析对话历史,将有用模式提取为可复用的知识:
# memory/2026-06-21.md
---
date: 2026-06-21
learnings:
- 用户偏好简洁的回复风格
- 数据库查询时应先检查表是否存在
- SQL 中 GROUP BY 需要包含所有非聚合列
---
七、MCP 协议集成:打开工具生态的大门
7.1 什么是 MCP?
Model Context Protocol (MCP) 是 Anthropic 提出的开放协议,让 AI Agent 可以标准化地调用外部工具和数据源。Nanobot 内置了完整的 MCP 客户端支持。
7.2 配置 MCP 服务器
{
"mcp": {
"servers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@anthropic/mcp-filesystem", "/home/user/docs"],
"disabled": false
},
"github": {
"command": "npx",
"args": ["-y", "@anthropic/mcp-github"],
"env": {
"GITHUB_TOKEN": "ghp_xxx"
}
},
"sqlite": {
"command": "uvx",
"args": ["mcp-server-sqlite", "--db-path", "/data/app.db"]
}
}
}
}
MCP 工具会自动注册到 ToolRegistry,Agent 可以像使用内置工具一样调用它们。MCP 服务器通过 SSE(Server-Sent Events)或 stdio 两种传输方式与 Agent 通信。
7.3 实际效果
配置完成后,Agent 的工具列表中会自动出现 MCP 工具:
可用工具:
- read_file(内置)
- exec(内置)
- query_db(自定义)
- mcp:filesystem:read_file(MCP)
- mcp:filesystem:write_file(MCP)
- mcp:github:create_issue(MCP)
- mcp:sqlite:query(MCP)
不需要写任何胶水代码,MCP 工具就能无缝融入 Agent 的工作流。
八、生产级部署实战
8.1 Docker 部署
FROM python:3.12-slim
WORKDIR /app
RUN pip install nanobot-ai
# 复制配置和工作空间
COPY config.json /app/config.json
COPY AGENTS.md /app/AGENTS.md
COPY skills/ /app/skills/
# 创建数据目录
RUN mkdir -p /app/memory /app/sessions
EXPOSE 8080
CMD ["nanobot", "run", "--channel", "webui", "--port", "8080"]
docker build -t nanobot-agent .
docker run -d \
-p 8080:8080 \
-v $(pwd)/config.json:/app/config.json \
-v $(pwd)/memory:/app/memory \
--name my-agent \
nanobot-agent
8.2 Systemd 服务部署(适用于 VPS)
[Unit]
Description=Nanobot AI Agent
After=network.target
[Service]
Type=simple
User=nanobot
WorkingDirectory=/opt/nanobot
ExecStart=/opt/nanobot/venv/bin/nanobot run --channel telegram
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
8.3 macOS LaunchAgent(本地开发)
nanobot install-macos-launch-agent
这条命令会自动生成并安装 LaunchAgent plist 文件,让 Nanobot 在后台持续运行,开机自启。
九、性能优化与最佳实践
9.1 Token 成本优化
策略 1:选择合适的模型分层
{
"agents": {
"defaults": {
"provider": "deepseek",
"model": "deepseek-chat",
"thinkingModel": "deepseek-reasoner"
},
"subagents": {
"provider": "openrouter",
"model": "deepseek/deepseek-chat-v3-0324:free",
"maxTokens": 2000
}
}
}
主对话用付费模型保证质量,子任务用免费模型控制成本。
策略 2:精细控制 Skills 加载
只在 SKILL.md 的 frontmatter 中标记 always: true 的技能才全量注入。其余技能通过按需加载控制成本。
策略 3:AutoCompact 配置
{
"context": {
"compactThreshold": 0.8,
"keepRecentTurns": 5
}
}
当对话达到上下文窗口的 80% 时自动压缩,保留最近 5 轮完整对话。
9.2 并发与性能
Nanobot 标记为 read_only 的工具可以安全并行执行:
@property
def read_only(self) -> bool:
return True # 告诉框架这个工具无副作用,可以并行
这意味着当 Agent 需要同时查询数据库、搜索网络、读取文件时,这些操作可以并行发起,大幅减少响应时间。
十、适用场景与选型建议
10.1 Nanobot 最适合的场景
✅ 个人 AI 助手:Telegram/Discord 上的私人助手,处理日常任务、信息查询、代码辅助
✅ 团队知识库 Agent:连接飞书/钉钉,作为团队的知识问答入口
✅ 轻量业务自动化:定时任务 + 工具调用,自动化日常运维工作
✅ 边缘部署:树莓派、NAS 等资源受限环境
✅ Agent 框架学习:4000 行代码,适合研究 Agent 的核心工作原理
✅ 二次开发基座:代码清晰易读,适合在此基础上构建定制化 Agent
10.2 Nanobot 可能不适合的场景
❌ 需要复杂工作流编排:如果需要 DAG、条件分支、人工审批等复杂工作流,LangGraph 更合适
❌ 大规模企业级部署:如果需要多租户、权限管理、审计合规等企业功能,OpenClaw 更完善
❌ 需要丰富中间件生态:如果依赖 LangChain 的大量现成集成(向量数据库、文档加载器等),Nanobot 需要自行对接
10.3 我的选型决策树
需要一个 AI Agent 框架
├── 只需要研究学习 / 轻量部署 → Nanobot
├── 需要全平台集成 + 生产级可靠性 → OpenClaw
├── 需要复杂工作流编排 → LangGraph
└── 需要多 Agent 协作 → AutoGen
十一、总结:极简主义的胜利
Nanobot 用不到 4000 行代码证明了一个重要观点:AI Agent 的核心复杂度并不需要 60,000 行代码来管理。
它的设计哲学可以总结为三句话:
- 小内核,大生态:核心只有 AgentLoop + ContextBuilder + ToolRegistry + AgentHook,但通过 Skills、MCP、Channel 等机制无限扩展
- 渐进式加载,按需付费:不要一开始就把所有东西塞给模型,让它按需获取
- 显式优于隐式:继承 Tool 基类比装饰器更清晰,config.json 比 Python 代码更易维护
在 AI Agent 框架日益臃肿的 2026 年,Nanobot 是一股清流。它提醒我们:好的架构不是功能的堆砌,而是对本质的精准把握。
如果你正在寻找一个"看得懂、改得动、跑得起"的 Agent 框架,Nanobot 值得你花一个下午的时间认真研究。
项目地址:https://github.com/HKUDS/nanobot
官方文档:https://nanobot.wiki
许可证:MIT
安装命令:pip install nanobot-ai