1. 从 24 万行到 4000 行Nanobot 源码到底解决了什么问题如果你最近在关注自主 AI Agent大概率听过 OpenClaw 这个名字。它能在本地跑通过聊天软件接收指令然后驱动大模型去读写文件、执行命令、搜索网页、跑定时任务。功能很完整但它的 TypeScript 工程体量摆在那里直接啃源码对很多人来说门槛不低。Nanobot 是香港大学数据科学实验室做的一个 Python 极简复刻大约 4000 行代码把 OpenClaw 的核心机制重新实现了一遍而且 Skills 格式直接兼容 OpenClaw 生态。换句话说你可以用 4000 行代码看清 24 万行背后的设计骨架。这篇文章面向想深入理解 AI Agent 框架内部机制的开发者。我会带你拆解 Nanobot 的模块化架构给出可复制的目录结构解析、关键类之间的关系以及本地跑通最小示例的完整步骤。读完之后你应该能准确定位入口文件理解消息是怎么从聊天平台流到 LLM 再流回用户的并且能自己动手改一个 Channel 或 Skill 来验证。Nanobot 适合谁适合已经用过 LangChain、AutoGPT 这类框架但觉得它们太抽象、想看看一个真实可运行的 Agent 到底怎么组织代码的人。也适合想自己写一个轻量 Agent 但不知道从哪下手的人。它不依赖复杂的编排引擎核心就是 asyncio 队列加工具调用循环读起来不累。我试过把它的核心模块画成一张图Channel 负责收发消息MessageBus 负责搬运AgentLoop 负责消费和决策Provider 负责和 LLM 通信Skills 和 Memory 负责给 LLM 提供上下文。五块拼起来就是一个完整的自主 Agent。2. 前置准备用 TaoToken 给 Nanobot 接上模型能力Nanobot 本身不绑定任何模型厂商它通过 Provider 抽象层调用 LLM。你要跑通最小示例需要一个能用的 API Key。这里我用 TaoToken 来做演示因为它兼容 OpenAI 的接口格式Nanobot 的 Provider 可以直接对接。TaoToken 是一个模型 API 聚合服务官网在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。它的 API 端点统一是 https://taotoken.net/api 不额外加 UTM 参数。你注册之后在控制台创建 API Key就能拿到一个 sk- 开头的密钥。为什么选它来做这个教程因为 Nanobot 的 Provider 层默认走 OpenAI 兼容协议TaoToken 的接口格式一致改一个 base_url 就能用。而且它支持多种模型你在调试 Agent 循环的时候可以随时切换模型对比行为不用改代码。具体操作步骤先打开 https://taotoken.net/api-keys 创建密钥然后记下你的 Key。接着在 Nanobot 的配置里把 base_url 指向 https://taotoken.net/api model 填你想要的模型 ID。Nanobot 的 Provider 会自动处理请求格式。如果你只是想先验证模型能不能通可以打开 https://taotoken.net/chat 在网页里直接对话测试。确认 Key 有效之后再回到本地配置 Nanobot。对于长期跑 Agent 任务的场景比如让 Nanobot 定时执行后台检查可以考虑 Coding Plan地址是 https://taotoken.net/coding-plan 。它的计费方式更适合持续调用不会因为频繁请求产生意外开销。配置的时候注意一点Nanobot 的 Provider 抽象里LLMResponse 会把不同厂商的差异标准化包括思维链字段和工具调用格式。所以你在配置里填的模型 ID 必须和 TaoToken 支持的模型列表一致否则会返回模型不存在的错误。接入文档在 https://taotoken.net/doc 里面有完整的模型列表和参数说明。3. 可复制配置Nanobot 目录结构与核心文件定位先把 Nanobot 的源码拉下来。它的目录结构大致是这样的nanobot/ ├── nanobot/ │ ├── __init__.py │ ├── main.py # 入口文件 │ ├── config.py # 配置加载 │ ├── bus/ │ │ ├── queue.py # MessageBus │ │ └── events.py # InboundMessage / OutboundMessage │ ├── channels/ │ │ ├── base.py # BaseChannel 抽象 │ │ ├── telegram.py │ │ ├── discord.py │ │ └── cli.py # 本地命令行 Channel │ ├── agent/ │ │ ├── loop.py # AgentLoop 主循环 │ │ ├── context.py # 系统提示组装 │ │ ├── memory.py # 记忆系统 │ │ ├── subagent.py # 子 Agent │ │ └── tools/ # 工具注册 │ ├── providers/ │ │ ├── base.py # LLMProvider 抽象 │ │ └── openai_compat.py # OpenAI 兼容实现 │ ├── skills/ │ │ └── loader.py # Skills 加载 │ ├── cron/ │ │ └── service.py # 定时任务 │ └── heartbeat/ │ └── service.py # 心跳自检 ├── workspace/ │ ├── skills/ # 用户自定义 Skill │ ├── memory/ # 记忆存储 │ └── cron/ │ └── jobs.json # 定时任务持久化 └── pyproject.toml入口文件是nanobot/main.py。它做的事情很直接加载配置、初始化 MessageBus、注册 Channel、启动 AgentLoop。你可以从这里开始读顺着调用链往下走。配置文件我建议用 TOML放在项目根目录的config.toml[provider] base_url https://taotoken.net/api api_key sk-你的密钥 model 你的模型ID context_window 128000 [agent] max_iterations 20 workspace ./workspace [channels.cli] enabled true allow_from [*] [memory] enabled true这个配置里base_url指向 TaoToken 的 API 端点api_key填你在控制台创建的密钥model填模型 ID。context_window用来做 token 估算记忆系统会根据它决定什么时候归档旧消息。如果你用 Claude Code 或者类似的编码工具来辅助读源码可以把 Base URL、Key、Model ID 三件套配好这样在编辑器里就能直接问模型关于代码的问题。Claude Code 的接入方式在 https://taotoken.net/claude-code-anthropic 有说明。配置写完之后用pip install -e .安装依赖然后python -m nanobot.main启动。如果一切正常你会看到 CLI Channel 启动等待你输入消息。4. 验证请求跑通最小 Agent 循环并观察工具调用启动之后在 CLI 里输入一句简单的话比如“列出当前目录的文件”。Nanobot 的处理流程是这样的CLI Channel 收到输入包装成 InboundMessage通过publish_inbound()扔进 MessageBus 的 inbound 队列。AgentLoop 的主循环用consume_inbound()取出这条消息然后进入 Tool-Use 迭代循环。第一轮AgentLoop 把系统提示和用户消息发给 LLM。LLM 返回一个工具调用请求比如list_dir。AgentLoop 执行这个工具把结果追加到 messages 里然后continue进入第二轮。第二轮 LLM 看到工具结果生成最终回复没有工具调用break退出循环。最终回复通过publish_outbound()扔进 outbound 队列CLI Channel 取出并打印。你可以在nanobot/agent/loop.py里找到这段逻辑for iteration in range(spec.max_iterations): messages self._snip_history(session, messages) response await self._request_model(messages, tools) if response.has_tool_calls: results await self._execute_tools(response.tool_calls) messages.append(tool_message) continue final_content clean(response.content) break else: stop_reason max_iterations这段代码就是所有自主 Agent 的基础范式LLM 输出、工具执行、结果追加、再次调用直到模型认为任务完成或达到最大迭代次数。验证成功的一个标志是你在 CLI 里能看到工具调用的中间过程。Nanobot 会把每次工具执行的结果打印出来你能看到 LLM 是怎么一步步决策的。如果模型返回了think块AgentLoop 会在存入 session 之前把它过滤掉避免推理过程污染上下文。另一个验证点是/stop指令。每条消息的处理都是独立的 asyncio Task/stop到达时会逐一 cancel。你可以发一个耗时任务然后立刻发/stop观察任务是否被中断。如果你想验证记忆系统可以连续对话几轮然后发/new。这个指令会把当前会话归档进 HISTORY.md然后清空 session。你再问一个之前聊过的话题Agent 应该能从长期记忆里回忆起相关内容。5. 常见报错排查401、local proxy failed 与 reading choices跑 Nanobot 的时候最容易遇到的几个报错我列一下附上排查思路。401 Unauthorized这个通常是 API Key 没配对。检查config.toml里的api_key是不是完整的 sk- 开头字符串有没有多余空格。如果你用的是 TaoToken确认 Key 是在 https://taotoken.net/api-keys 创建的并且没有过期。另一个可能是base_url写错了必须是https://taotoken.net/api结尾不要多加斜杠。local proxy failed这个报错一般出现在网络请求层。Nanobot 的 Provider 用 httpx 发请求如果本地有代理设置干扰会连接失败。检查环境变量里有没有HTTP_PROXY或HTTPS_PROXY临时 unset 掉再试。另外确认你的网络能正常访问 TaoToken 的 API 端点。reading choices 相关报错这个通常出现在解析 LLM 响应的时候。如果模型返回的 JSON 格式不符合预期Provider 层在读取choices字段时会抛异常。排查方法是打开 debug 日志看原始响应内容。可能是模型 ID 填错了TaoToken 返回了错误信息而不是正常的 completion 结构。确认model字段和接入文档里列出的模型 ID 完全一致。OAuth 相关报错如果你在配置 Claude Code 或其他需要 OAuth 的工具时遇到问题检查回调地址和密钥是否正确。Nanobot 本身不走 OAuth它只用 API Key。但如果你用 Claude Code 辅助读源码OAuth 配置在 https://taotoken.net/claude-code-anthropic 有详细步骤。工具调用不执行如果 LLM 返回了工具调用但 Nanobot 没执行检查ToolRegistry里有没有注册对应的工具。子 Agent 的 ToolRegistry 故意移除了 MessageTool 和 SpawnTool防止绕过主 Agent 发消息或无限递归。如果你在子 Agent 里调用这两个工具会静默失败。记忆整合不触发记忆整合是基于 token 估算的如果对话很短prompt 大小没超过context_window / 2就不会触发归档。这是正常行为。你可以把context_window调小来测试整合逻辑。排查的时候建议把日志级别调到 DEBUG这样能看到完整的请求和响应内容。Nanobot 的日志用的是标准 logging 模块在配置里加一行log_level DEBUG就行。6. 继续深入从 Channel 扩展到 Skills 与 Cron跑通最小示例之后你可以从几个方向继续深入。想理解 Channel 的插件式注册去看nanobot/channels/base.py里的BaseChannel抽象。它用pkgutil扫描包下的所有模块配合 entry_points 机制第三方包安装后自动注册。你可以照着写一个自定义 Channel实现start()、stop()、send()三个方法然后 pip install 就能用。想理解 Skills 的渐进式加载去看nanobot/skills/loader.py。它把 Skill 分成三种加载模式always:true的每次请求注入全文普通 Skill 只注入 XML 摘要依赖未满足的标记为 unavailable。LLM 看到摘要后自己决定要不要用read_file读完整内容。这种设计把“何时加载文档”的决策权交给了模型。想理解 Cron 和 Heartbeat 的主动性去看nanobot/cron/service.py和nanobot/heartbeat/service.py。Cron 用动态定时器而不是轮询计算所有 job 中最近的下一次触发时间asyncio.sleep()精确等到那一刻。Heartbeat 每 30 分钟唤醒 Agent 检查 HEARTBEAT.md执行完后还有一步通知评估决定结果是否值得打扰用户。如果你想让 Agent 长期跑后台任务Coding Plan 的计费方式更适合持续调用地址是 https://taotoken.net/coding-plan 。配置好之后Nanobot 的 Cron 和 Heartbeat 可以稳定运行不会因为频繁请求产生意外开销。最后给一个实用技巧读源码的时候从main.py开始顺着MessageBus的两个队列往下追。inbound 队列的消费者是 AgentLoopoutbound 队列的消费者是各个 Channel。把这两条线理清楚整个框架的脉络就清晰了。
阅读完成 · 觉得有帮助?