news 2026/9/18 10:20:16

nanobot 架构解析:从 AgentLoop 到 Provider、Channel、Tools 的源码级运行地图

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
nanobot 架构解析:从 AgentLoop 到 Provider、Channel、Tools 的源码级运行地图

nanobot 架构解析:从 AgentLoop 到 Provider、Channel、Tools 的源码级运行地图

【免费下载链接】nanobotUltra-lightweight, open-source, self-hosted personal AI agent framework in Python with WebUI, tools, memory, MCP, multi-agent workflows, automation, and chat apps项目地址: https://gitcode.com/gh_mirrors/nanob/nanobot

nanobot 是一个超轻量级的自托管个人 AI Agent 框架,其架构文档的核心价值在于把运行时行为精确映射到源码文件。读完本文,你将掌握 nanobot 的完整消息流转链路(Channel → MessageBus → AgentLoop → AgentRunner → Provider → Tools),理解 AgentLoop 与 AgentRunner 的职责分界、Provider 注册与自动推断机制、Channel/Tool/MCP 等扩展点的接入方式,以及配置与路径体系的默认值约定——这些内容能帮助你快速定位任意一个用户可见行为背后的源码位置。

核心消息流:从 InboundMessage 到 OutboundMessage

nanobot 的运行时可以概括为一条闭环消息流:

官方文档给出了核心文件对照表,这张表是调试内部的起点:

区域文件
消息事件与队列nanobot/bus/events.pynanobot/bus/queue.py
回合(turn)编排nanobot/agent/loop.py
Provider/工具对话循环nanobot/agent/runner.py
上下文构建nanobot/agent/context.py
会话存储与压缩nanobot/session/manager.py
长期记忆与 Dreamnanobot/agent/memory.py

从源码看,消息总线由两个数据类和一个队列组件构成。在 nanobot/bus/events.py 中:

  • InboundMessage携带channelsender_idchat_idcontenttimestampmediametadata等字段,并提供session_key属性——默认为f"{channel}:{chat_id}",也支持session_key_override做线程级会话覆盖;
  • OutboundMessage携带回复内容、reply_tomediabuttons,以及用于内部运行时/UI 语义的event字段;
  • 源码中还定义了内部保留的 metadata 键(如INBOUND_META_RUNTIME_CONTROL),注释明确这些键只能由受信传输层铸造,不能来自不受信客户端——这是消息边界安全的一个细节体现。

MessageBus(nanobot/bus/queue.py)负责入站/出站队列的流转。无论消息来自 CLI、WebUI、Telegram 还是 Discord,进入 AgentLoop 之后的流程完全一致。

AgentLoop 与 AgentRunner 的职责分界

这是整个架构中最关键的设计决策:一个偏"渠道侧"的编排层,一个偏"模型侧"的执行层

AgentLoop(nanobot/agent/loop.py 中的class AgentLoop)拥有面向渠道的回合:

  • 接收入站消息;
  • 确定生效的 session 与 workspace 作用域;
  • 构建上下文(历史、记忆、技能、渠道元数据);
  • 接入 hooks、进度事件与渠道元数据;
  • 发布出站消息。

AgentRunner(nanobot/agent/runner.py 中的class AgentRunner)拥有面向模型的循环:

  • 向选定的 provider 发送消息;
  • 处理流式 delta 与推理(reasoning)块;
  • 执行工具调用;
  • 把工具结果回喂给模型;
  • 在产出最终答案或触及运行时限制(迭代上限等)时停止。

AgentRunner的类注释也印证了这一定位:"Run a tool-capable LLM loop without product-layer concerns."(运行一个无产品层关切的可调用工具的 LLM 循环)。从源码看,AgentRunner的执行结果封装在共享结果对象中,包含final_contentmessagestools_used、逐轮round_usagesstop_reason、provider 会话状态检查点等字段,支撑恢复(recovery)与继续注入(injection)等机制。

MCP 连接属于应用层基础设施

架构文档特别强调:MCP 连接是应用拥有的(application-owned)基础设施,而非 AgentLoop 的一部分。组合根(composition root)负责创建MCPProvider、把它的ToolRegistry共享给AgentLoop、在使用前await connect()、并在关停时保证aclose();AgentLoop 不管理这个生命周期。因此AgentLoop.from_config()要求调用方提供自己拥有的ToolRegistry——使用 MCP 的调用方需将其与自有的MCPProvider共享。相关实现可见 nanobot/agent/tools/mcp.py 中的MCPProvider与 nanobot/agent/tools/registry.py 中的ToolRegistry

调试时的分工口诀:如果问题涉及渠道路由、session 键、workspace 选择或出站投递,从 nanobot/agent/loop.py 入手;如果涉及 provider 调用、工具调用、流式输出或迭代限制,从 nanobot/agent/runner.py 入手。

Providers:集中注册表驱动的模型后端

Provider 元数据集中在 nanobot/providers/registry.py,配置字段在 nanobot/config/schema.py。该注册表文件头部注释写得很直白——新增 provider 只需两步:

  1. PROVIDERS中添加一个ProviderSpec
  2. config/schema.pyProvidersConfig中添加对应字段。

环境变量、配置匹配、状态展示都由此派生。从源码看,ProviderSpec是一个 frozen dataclass,关键字段包括:

  • name/keywords/env_key:配置字段名、模型名匹配关键词、API key 环境变量名;
  • backend:决定使用哪个 provider 实现(openai_compatanthropicazure_openaiopenai_codexbedrock等);
  • is_gateway/is_local/detect_by_key_prefix/detect_by_base_keyword:网关型(可路由任意模型)、本地部署、API key 前缀与 base URL 推断的标记;
  • thinking_stylereasoning_effort_remapsupports_prompt_caching等:处理各厂商推理开关、提示缓存等协议差异;
  • is_oauth/is_direct:OAuth 型(如 OpenAI Codex)与直连型 provider 的区分。

Provider 选择遵循以下优先级(与文档一致):

  1. 显式的agents.defaults.provider或 preset 中的 provider;
  2. provider 注册表关键词(模型名匹配);
  3. API key 前缀与 API base URL 提示;
  4. 配置了apiBase时的本地 provider 回退;
  5. 网关型 provider 回退(可路由多模型家族)。

Provider 实现位于nanobot/providers/:大多数托管 provider 复用 OpenAI 兼容实现,而 Anthropic、Azure OpenAI、AWS Bedrock、OpenAI Codex 与 GitHub Copilot 走专门实现路径(对应 anthropic_provider.py、azure_openai_provider.py、bedrock_provider.py、openai_codex_provider.py、github_copilot_provider.py)。实操配置可参考 providers.md 与 configuration.md#providers。

Channels:自包含包式发现

Channel 负责把外部平台翻译成InboundMessage事件,并把OutboundMessage发回平台。核心文件:

区域文件
基础 Channel 契约nanobot/channels/base.py
各 Channel 包nanobot/channels/<channel>/
发现与生命周期nanobot/channels/manager.py
WebSocket/WebUI 通道nanobot/channels/websocket/

从源码结构看,Channel 采用插件化发现机制:ChannelManager通过nanobot.channels.registry.discover_plugins()扫描nanobot/channels/下的自包含包,每个包导出一个ChannelPlugin描述符(定义于 nanobot/channels/plugin.py),并把运行时与可选的 setup 表面封装在同一个包内。新增 channel 只需贡献一个遵循 channel-package-guide.md 规范的包,无需改动核心代码。

WebUI 与 Gateway:长驻进程的组成

nanobot gateway启动的内容包括:

  • 已启用的聊天 channel;
  • 配置了 WebSocket 时的 WebSocket channel;
  • workspace 作用域的 cron 服务;
  • Dream、heartbeat 等系统作业;
  • gateway.port上的健康检查端点。

一个容易混淆的点:打包的 WebUI 由 WebSocket channel 提供,而不是健康检查端点。两者的默认地址:

表面默认地址
Health endpointhttp://127.0.0.1:18790/health
WebUI/WebSockethttp://127.0.0.1:8765

源码印证:nanobot/config/schema.py 中 gateway 配置的port字段默认值即为18790。WebUI 前端源码位于 webui/,生产构建输出到nanobot/web/dist/并打入 wheel。相关文档:webui.md(使用指南)、webui/README.md(前端源码开发)、websocket.md(协议细节)。

Tools:模型契约的一部分

工具从 nanobot/agent/tools/ 与插件入口点发现。重要文件对照:

工具区域文件
工具基类与 schemananobot/agent/tools/base.py、nanobot/agent/tools/schema.py
工具发现nanobot/agent/tools/registry.py
Shell 执行nanobot/agent/tools/shell.py
文件系统工具nanobot/agent/tools/filesystem.py
Web 搜索/抓取nanobot/agent/tools/web.py
MCP 工具nanobot/agent/tools/mcp.py
Cronnanobot/agent/tools/cron.py、nanobot/cron/
图片生成nanobot/agent/tools/image_generation.py
运行时自省nanobot/agent/tools/self.py

当前仓库的工具目录实际还包含apply_patch.pyexec_session.pylong_task.pysearch.pyspawn.py(子代理)、sandbox.pymessage.py等模块,可见工具面比文档表格更宽。架构文档特别警告:工具行为是模型契约的一部分——除非有意变更,应保持用户可见的工具名、schema 与错误消息稳定。

配置与路径:默认值与作用域边界

配置 schema 在 nanobot/config/schema.py,加载与保存在 nanobot/config/loader.py,运行时路径助手在 nanobot/config/paths.py。默认路径:

路径默认值
Config~/.nanobot/config.json
Workspace~/.nanobot/workspace/
Sessions<config-dir>/sessions/<workspace-id>/*.jsonl(默认~/.nanobot/sessions/...
Memory<workspace>/memory/
Cron 存储<workspace>/cron/jobs.json
WebUI/media/日志运行时数据配置目录下的子目录,如webui/media/logs/

源码印证:nanobot/config/paths.py 中get_workspace_path()在未指定时解析到~/.nanobot/workspaceget_runtime_subdir()负责media/cron/logs/webui/等实例级运行时子目录。schema 同时接受 camelCase 与 snake_case 键,但回写磁盘时统一使用 camelCase 别名(如apiKeymodelPresetsintervalS)。

Agent 自有状态 vs 生效项目上下文

运行时区分了"配置的 agent workspace"与会话作用域携带的"生效项目 workspace"。两者常常是同一目录,但 WebUI 聊天可以选择独立项目:

关切路径所有者
会话命名空间、SOUL.mdUSER.md、memory、自定义 skills配置的 agent workspace
项目AGENTS.md、相对工具路径、shell 工作目录生效的项目 workspace
workspace 访问模式与项目元数据会话的 workspace 作用域

ContextBuilder(nanobot/agent/context.py)把项目指令与 agent 自有的 profile、记忆合并;文件系统与搜索工具以项目为常规边界,仅对内置/agent 技能与精确的 agent 历史文件获得能力特定的只读访问。架构文档提醒:保持这些跨根(cross-root)能力只读且显式,不要将整个 agent workspace 当作允许的根目录。

Memory 与 Sessions:两级状态存储

会话历史是"近端"的对话回放,memory 是"远端"的 workspace 状态:

存储文件区域
会话 JSONL 文件<config-dir>/sessions/<workspace-id>/
长期记忆<workspace>/memory/MEMORY.md
整合来源历史<workspace>/memory/history.jsonl
引导身份文件<workspace>/SOUL.md<workspace>/USER.md,模板见 nanobot/templates/

Dream(记忆整合/梳理机制)实现在 nanobot/agent/memory.py,由运行时在启用时调度。会话的存储与压缩逻辑在 nanobot/session/manager.py。

安全边界:与安全相关的代码路径

架构文档列出了安全敏感路径,供修改工具/渠道/文件访问/网络抓取时对照:

边界文件
Workspace 作用域nanobot/security/workspace_access.py、nanobot/security/workspace_policy.py
Shell 沙箱nanobot/agent/tools/shell.py
SSRF/网络检查nanobot/security/network.py、nanobot/agent/tools/web.py
PTH 防护与 CLI 启动安全nanobot/security/ 与 CLI 入口
渠道访问控制nanobot/channels/*.py中的渠道配置

文档同时要求:当变更涉及工具、channel、文件访问、WebUI workspace 行为或网络抓取时,把安全当作功能行为的一部分,并在用户可见边界变化时同步更新文档。

扩展点:接入新能力的官方路径

架构文档以表格形式给出七类扩展的标准做法:

扩展方式
Provider在 providers/registry.py 添加ProviderSpec,在 config/schema.py 添加 schema 字段;仅在通用后端不够时才实现新 provider
Channel导出ChannelPlugin描述符,运行时与 setup 表面放在一个包内,遵循 channel-package-guide.md
Toolagent/tools/下实现工具,或暴露插件入口点
Agent Plugin<workspace>/plugins/下添加 v1 包并从 Apps 启用
MCP添加tools.mcpServers配置,或在 Agent Plugin 中捆绑 server
Skill添加<workspace>/skills/下的 workspace 技能、在 Agent Plugin 中捆绑,或在 nanobot/skills/ 添加内置技能
CLI App加入 CLI Apps 目录;安装器负责可执行文件生命周期并写出一个 skills-only 的 Agent Plugin

总原则是:优先复用已有的注册/发现模式,避免临时接线(ad hoc wiring)

测试与验证:按变更面选择最小验证

常用检查命令:

pytest tests/test_openai_api.py::test_function -v ruff check nanobot/ cd webui && bun run test cd webui && bun run build

按变更面选择最小验证:

变更最小有效验证
Provider 行为Provider 单元测试或 mock API 路径;条件允许时用安全配置跑nanobot agent -m "Hello!"
Channel 行为Channel 测试 +nanobot gateway启动路径
WebUI 行为WebUI 测试/构建;路由/设置/聊天变更需通过 gateway 做浏览器级验证
工具行为工具单元测试;schema 或面向模型行为变化时补充 agent 运行路径
文档链接检查、命令与 CLI/schema 的一致性核对、git diff --check

对面向用户的流程,架构文档建议至少走一条用户实际接触的公共表面:CLI 命令、HTTP 端点、WebSocket/WebUI、聊天渠道或打包导入。

小结

nanobot 的架构可以浓缩为三句话:一条"Channel → MessageBus → AgentLoop → AgentRunner → Provider/Tools → 回写"的消息闭环;一组按"元数据集中在注册表 + 自包含包发现"组织的扩展点(Provider、Channel、Tool、Skill、Plugin、MCP);以及一套围绕~/.nanobot/配置目录与 workspace 的清晰路径边界。这份源码映射文档配合 concepts.md 的产品级心智模型、configuration.md 的参数参考,构成了理解、调试乃至扩展 nanobot 的完整入口。

【免费下载链接】nanobotUltra-lightweight, open-source, self-hosted personal AI agent framework in Python with WebUI, tools, memory, MCP, multi-agent workflows, automation, and chat apps项目地址: https://gitcode.com/gh_mirrors/nanob/nanobot

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/18 10:17:36

Docker-Compose 部署 PostGIS:版本锁定、初始化与生产就绪实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 10:15:11

Create React App 完全指南:零配置创建 React 应用

Create React App 完全指南&#xff1a;零配置创建 React 应用 【免费下载链接】create-react-app Set up a modern web app by running one command. 项目地址: https://gitcode.com/gh_mirrors/cr/create-react-app [!CAUTION] 弃用说明&#xff08;Deprecated&#xf…

作者头像 李华
网站建设 2026/9/18 10:10:22

评估数据管道把 Anthropic SDK 地址切到 TaoToken 后回填标签

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 10:10:09

时序数据库核心原理与主流方案选型指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华