1. 为什么我要拆 nanobot 的架构nanobot 是香港大学数据科学实验室开源的一个 Agent 框架核心代码不到 4000 行却在短时间内拿到了两万多 Star。这个数字对比很有意思LangChain 核心代码四十多万行nanobot 只有它的百分之一不到。我第一反应是这么点代码能干什么实际跑起来之后发现它把 Agent 框架最核心的几件事——ReAct 循环、工具注册、技能扩展、记忆管理、子任务分发——全部用最直接的方式实现了没有一层套一层的抽象。这篇文章不是单纯读代码而是带你从架构拆解走到实际跑通。我会先讲清楚 nanobot 的模块划分和运行链路然后重点落在怎么用 TaoToken 的统一 Key 通道把它接起来给出可以直接复制的 settings.json 和 config.toml 配置骨架最后给启动验证和日志排查的具体动作。适合两类人想理解 Agent 框架最小实现的开发者以及想快速搭一个自托管助手但不想被框架概念淹没的人。nanobot 的架构有一个决定性特征控制面完全集中在 AgentLoop 里。没有 Chain、没有 Runnable、没有 DAG 编排层所有决策路径都穿过同一个 while 循环。这个约束让代码可理解性最大化代价是弹性空间收窄。理解了这个取舍后面所有模块的设计就都能串起来了。2. TaoToken 前置统一 Key 与 API 通道在跑 nanobot 之前先把模型通道准备好。nanobot 支持多种 provider但如果你不想在配置文件里塞一堆不同厂商的 Key用 TaoToken 做统一入口会省很多事。它提供一个兼容 OpenAI 格式的 API 端点你只需要一个 Key 就能切换不同模型。先到官网注册并拿到 Keyhttps://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content注册后在控制台创建 API Key地址是https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewriteAPI 的基础端点是https://taotoken.net/api注意这个地址不加 UTM 参数。拿到 Key 之后先别急着写进 nanobot 配置用一条 curl 验证通道是否通curl https://taotoken.net/api/v1/chat/completions \ -H Content-Type: application/json \ -H Authorization: Bearer sk-你的Key \ -d { model: claude-sonnet-4-20250514, messages: [{role: user, content: 回复ok}], max_tokens: 16 }如果返回里有正常的 choices 结构说明通道没问题。这一步很关键因为后面 nanobot 启动失败时你要能区分是框架配置问题还是通道问题。模型列表和可用模型名可以在模型对话页确认https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite注意Key 只存在服务端配置里不要提交到 Git 仓库。建议用环境变量注入后面配置骨架里我会用${TAOTOKEN_API_KEY}这种占位写法。3. nanobot 核心模块拆解3.1 AgentLoop20 行撑起整个编排agent/loop.py是整个框架的心脏核心的_run_agent_loop大约 20 行。它做的事情就是标准的 ReAct 循环把消息发给模型模型返回工具调用就执行工具把结果追加回消息列表再发给模型直到模型不再调用工具为止。这里有几个工程细节值得单独说。第一错误响应不会持久化到 session history。这个设计是为了防止400 中毒循环——如果一条格式错误的响应被存进历史后续每次调用都会带着这条坏数据触发 API 400然后产生更多错误形成无法自愈的死循环。第二工具结果存入 session 时会截断为 500 字符。当前 turn 内模型能看到完整结果但历史记录只保存摘要这控制了上下文增长速率代价是跨会话的工具结果不可追溯。第三错误处理只有一行if result.startswith(Error)把错误恢复的全部责任交给模型。这在强模型上工作良好在弱模型上可能导致无效循环。3.2 Tool 系统最小接口Tool 是一个抽象基类execute的返回值强制为str。这个选择的好处是接口统一模型天然消费字符串代价是结构化数据需要在 execute 内部自己序列化丢失类型信息。注册方式也很直接class MyDatabaseTool(Tool): name query_db description 查询数据库并返回结果 parameters { type: object, properties: { sql: {type: string, description: SQL 查询语句} }, required: [sql] } async def execute(self, sql: str) - str: rows await self.conn.fetch(sql) return \n.join(str(r) for r in rows) # 初始化时注册 self.tools.register(MyDatabaseTool(connection_string...))没有装饰器、没有注册配置文件、没有元类。代价是 JSON Schema 要手写不像 LangChain 的tool装饰器能从 docstring 和类型注解自动生成。3.3 Skill 系统Markdown 即能力这是 nanobot 最独特的设计。Skill 不是 Python 代码而是 Markdown 文档教模型如何使用已有的 CLI 工具。一个天气 skill 长这样--- name: weather description: 查询城市天气 bins: - curl --- # 天气查询 使用以下命令查询天气 curl wttr.in/{{city}}?format3系统通过shutil.which检查 bins 里的工具是否存在存在则这个 skill 标记为 available。System prompt 里告诉模型需要用某个 skill 时用read_file读取它的 SKILL.md。这是用文件系统做懒加载——模型自主决定何时加载哪个 skill不用的 skill 零 token 开销。相比向量检索它的优势是确定性、可审计、零额外成本。局限是当 skill 数量到几百个时XML 索引本身会占满 context window。3.4 记忆系统grep beats RAG两个 Markdown 文件搞定记忆不用向量库。MEMORY.md存长期事实和用户偏好每次都注入 system promptHISTORY.md存对话摘要追加写入模型用exec grep按需搜索。当未整合消息数超过 100 时异步触发记忆整合以独立 asyncio.Task 运行不阻塞主流程。作者的设计论据是grep beats RAG for agent memory——在个人规模数百条历史下这个论断成立企业规模下文件 grep 的局限会暴露。3.5 Subagent 与 MCPspawn 工具允许主 agent 把长任务委托给后台 asyncio.Task。Subagent 有明确约束没有 message 工具、没有 spawn 工具防递归、最多 15 次迭代、无 memory/history。结果通知的设计很巧妙——子任务完成后通过消息总线重新注入一条 InboundMessage主 agent 像处理普通用户消息一样处理它无需特殊的结果传递协议。代价是 asyncio.Task 在同一进程内运行无法跨机器分布。MCP 工具被自动包装为原生 Tool 对象支持 stdio 和 streamable-http 两种服务器对模型完全透明只是名字带mcp_{server}_前缀做命名空间隔离。4. 可复制配置settings.json 与 config.tomlnanobot 的配置分两层config.toml管框架行为settings.json管模型通道和工具开关。下面是我实测可用的骨架。先看config.toml[agent] name nanobot max_iterations 40 tool_result_truncate 500 error_persist false [memory] memory_file MEMORY.md history_file HISTORY.md consolidate_threshold 100 [skills] dir skills lazy_load true [subagent] max_iterations 15 allow_spawn false allow_message false [mcp] enabled true [[mcp.servers]] name filesystem transport stdio command npx args [-y, modelcontextprotocol/server-filesystem, ./workspace]再看settings.json重点是模型通道指向 TaoToken{ provider: { type: openai-compatible, base_url: https://taotoken.net/api/v1, api_key: ${TAOTOKEN_API_KEY}, model: claude-sonnet-4-20250514, max_tokens: 4096, temperature: 0.7 }, tools: { exec: { enabled: true, blacklist: [rm -rf /, mkfs, dd if] }, read_file: { enabled: true }, spawn: { enabled: true } }, channels: { cli: { enabled: true } } }把 Key 写进环境变量再启动export TAOTOKEN_API_KEYsk-你的Key python -m nanobot --config config.toml --settings settings.json提示base_url一定要带/v1因为 nanobot 走的是 OpenAI 兼容协议。如果只写https://taotoken.net/api请求会 404。5. 启动验证与成功结果启动后先看日志有没有报通道错误。正常启动会输出类似[INFO] agent loop started, max_iterations40 [INFO] provider: openai-compatible, modelclaude-sonnet-4-20250514 [INFO] skills loaded: 3 available, 1 unavailable [INFO] mcp servers: filesystem connected [INFO] channel cli ready然后在 CLI 里发一条测试消息验证工具调用链路 帮我查一下当前目录有哪些文件 [agent] 调用工具 read_file / exec: ls [agent] 工具返回: config.toml settings.json skills/ [agent] 当前目录下有 config.toml、settings.json 和 skills 目录。如果能看到工具调用和最终回复说明 ReAct 循环、工具注册、模型通道全部打通。再测一次 skill 加载 用天气 skill 查一下北京天气 [agent] 读取 skills/weather/SKILL.md [agent] 调用 exec: curl wttr.in/Beijing?format3 [agent] 北京: 晴, 12°C这一步验证了懒加载机制——模型自己决定去读 SKILL.md而不是一开始就全部加载。6. 本篇常见错排查报错一401 Unauthorized。九成是 Key 没注入或写错。先确认echo $TAOTOKEN_API_KEY有值再用第 2 节的 curl 单独测通道。如果 curl 通但 nanobot 不通检查 settings.json 里是不是把${TAOTOKEN_API_KEY}当字面量传进去了——有些启动方式不做环境变量替换需要手动展开。报错二404 Not Found。检查base_url是否带了/v1。TaoToken 的 API 端点是https://taotoken.net/api但 OpenAI 兼容路径是/api/v1/chat/completions配置里必须写到/v1。报错三工具调用死循环。日志里反复出现同一个工具调用和同样的错误。这是弱模型上的典型问题因为 nanobot 把错误恢复全交给模型。解决办法是换更强的模型或者在 config.toml 里把max_iterations调低让它早点停下来。报错四skill 显示 unavailable。检查 SKILL.md 的 frontmatter 里bins列的工具是否在 PATH 里。用which curl确认。如果工具存在但 skill 还是不可用检查 YAML frontmatter 的缩进---必须顶格。报错五MCP 服务器连不上。stdio 模式下检查 command 和 args 是否正确npx是否可用。streamable-http 模式下检查端口和路径。MCP 连接失败不会阻塞主流程但对应工具会缺失日志里会有mcp server xxx failed的警告。报错六记忆整合不触发。确认consolidate_threshold配置生效且未整合消息数确实超过了阈值。整合是异步的不会立即在日志里看到结果可以搜consolidate关键字确认任务是否启动。7. 接入文档与后续动作配置跑通之后如果你想深入调模型参数或换模型可以直接在模型对话页测试不同模型的表现确认哪个更适合你的场景https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite如果你打算把 nanobot 用在长期编码或 Agent 任务上建议看一下 Coding Plan它在长会话场景下的额度策略更划算https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite接入过程中遇到通道或 Key 的问题先回到 API Keys 页面确认 Key 状态再对照接入文档检查 base_url 和协议格式https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewritenanobot 的架构取舍很诚实代码量和功能声明是匹配的没有用复杂抽象隐藏简单实现。它的控制面集中化让可理解性最大化代价是定制点有限。如果你需要精确的多 agent 协调或大规模记忆管理它可能不够但如果你想理解 Agent 框架的最小实现或者快速搭一个自托管助手它是个很好的起点。
企业数字化 ERP 产品动态
相关推荐
Atlas 300V 24G NPU推理卡部署YOLO实战全解析 1. 先回答那个热词:Atlas 300V 24G到底是不是运算加速卡先说结论:是,但它不是那种能跑通用计算的“GPU显卡”,而是面向AI推理场景的专用加速卡,准确说是一张NPU(神经网络处理器)卡。我最近被问最… · 2026/9/25 13:49:24
web-vitals v6 升级指南:破坏性变更、Soft Navigation 支持与迁移清单 前端可观测性 【免费下载链接】web-vitals Essential metrics for a healthy site. 项目地址: https://gitcode.com/gh_mirrors/we/web-vitals 点击查看 免费下载 web-vitals v6 是 Google Chrome 团队维护的真实用户性能指标采集库的一次重要版本升级,… · 2026/9/25 13:49:05
用JMeter压测大模型服务:流式接口QPS统计与容量评估实战 近两年大模型相关的性能测试需求越来越多,但很多人一上来就习惯性掏出JMeter按接口压测的老套路走,结果压出来的QPS要么虚高,要么低得离谱,根本没法作为容量评估的依据。这个问题的核心不在于JMeter本身,而在于大模型服… · 2026/9/25 14:19:20
Obsidian第二大脑:10分钟构建可演进的知识操作系统 1. 为什么“第二大脑”不是营销话术,而是可落地的思维操作系统Obsidian 这个词最近两年在知识管理圈里出现的频率,已经快赶上“早C晚A”在护肤圈的地位了。但和那些被过度包装的概念不同,“第二大脑”在 Obsidian 语境下,是有一套… · 2026/9/25 14:19:14
智慧城市方案怎么写:从顶层设计到落地避坑全解析 简介:一份87页的新型智慧城市建设方案PPT,面向智慧城市、数字政府领域的产品经理、方案架构师及政府信息化规划人员,系统梳理了从市场需求、总体设计到落地交付的完整路径。压缩包内仅含一个PPT演示文件,共87页,大小约… · 2026/9/25 14:19:08
OpenCode 2.0 深度解析:API重构、Bun迁移与内存优化实战 1. 从一次深夜调试说起:OpenCode 2.0 到底改了什么凌晨两点,我盯着终端里那行error from provider (console): opencodes free tier can only be used from wi发呆。这不是我第一次被 OpenCode 的环境问题卡住,但这次不一样——项目刚升级到 … · 2026/9/25 14:19:08
1 分钟上手:将 Memoria 接入 OpenClaw 的 config.toml 配置与验证 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 14:19:07
创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31
MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:37