源码目录与阅读顺序
对应源码
| 阅读层级 | 对应目录或文件 |
|---|---|
| Python 产品代码 | nanobot/ |
| Agent 核心 | nanobot/agent/loop.py、runner.py、context.py、tools/ |
| 运行表面 | nanobot/cli/、api/、sdk/、channels/、gateway/、webui/ |
| 前端与 Bridge | webui/、bridge/ |
| 可执行行为说明 | tests/ |
| 官方用法说明 | docs/ |
| 打包入口 | pyproject.toml、hatch_build.py |
学习目标
读完后,你应当能够:
- 说出主要目录的职责;
- 从用户可见行为定位到负责模块;
- 按调用链阅读,而不是机械遍历文件;
- 识别核心层、接入层和展示层。
前置知识
- 已阅读 五个入口与运行边界;
- 知道 AgentLoop、Provider、Tool、Session 的基本含义。
项目顶层
| 路径 | 职责 | 初学优先级 |
|---|---|---|
nanobot/ | Python runtime | 高 |
tests/ | Python 行为和回归测试 | 高 |
docs/ | 当前仓库官方使用和开发文档 | 高 |
webui/ | React/TypeScript WebUI | 后学 |
bridge/ | 需要独立桥接服务的集成 | 选学 |
pyproject.toml | 包、依赖、CLI entry point、插件入口说明 | 中 |
.agent/ | 设计、安全和常见陷阱 | 高 |
nanobot/ 内部地图
| 目录 | 主要职责 | 先看什么 |
|---|---|---|
agent/ | Turn、Runner、Context、Memory、Tools、Subagent | loop.py、runner.py、context.py |
bus/ | Inbound/Outbound 消息队列和 runtime events | events.py、queue.py、runtime_events.py |
session/ | Session JSONL、key、goal、continuation、WebUI turn 协调 | manager.py、keys.py |
providers/ | LLM Provider 契约、注册、创建、fallback 和适配 | base.py、registry.py、factory.py |
channels/ | 外部聊天平台和 WebSocket adapter | base.py、registry.py、manager.py |
config/ | Pydantic schema、加载、路径 | schema.py、loader.py |
cli/ | Typer CLI 和主要 runtime 组装入口 | commands.py |
api/ | OpenAI-compatible HTTP API | server.py |
sdk/ | SDK 类型、stream 和 runtime helper | 先从 nanobot/nanobot.py 进入 |
cron/ | workspace-scoped 调度、绑定 Turn 和投递 | service.py、bound_runner.py |
command/ | slash command 路由和内置命令 | router.py、builtin.py |
pairing/ | 未授权私聊发送者的配对审批 | store.py |
security/ | workspace 和网络边界 | workspace_policy.py、network.py |
webui/ | Python 侧 WebUI API、token、thread 和媒体协调 | 按问题选文件 |
apps/ | App 协议和 CLI app 服务 | 不要与 Skill/Tool 混为一谈 |
audio/ | 转录 Provider 注册和调度 | 选学 |
skills/ | 内置 Skill 文件 | 先读 README.md |
templates/ | system prompt、bootstrap、Dream、Heartbeat 模板 | 理解上下文时阅读 |
核心层与边缘层
项目设计约束强调“核心保持小,能力放在边缘”:
flowchart TD CORE["核心路径<br/>AgentLoop + AgentRunner"] CTX["Context / Session / Provider 契约"] --> CORE CORE --> TOOLS["Tools"] CORE --> EVENTS["通用 Runtime Events"] CH["Channels"] --> CORE MCP["MCP"] --> TOOLS SK["Skills"] --> CTX EVENTS --> UI["WebUI / WebSocket 协调"] CRON["Cron / Heartbeat"] --> CORE
设计含义:
- 新能力优先放在 Tool、Skill、MCP 或 Channel;
- 只有无法在边缘表达的通用 Turn 行为才进入 AgentLoop/Runner;
- UI wire 细节不应进入 AgentLoop;
- Provider 和 Channel 允许少量重复,避免建立难懂的万能基类。
源码依据:
.agent/design.md
推荐阅读顺序
第 1 轮:只看骨架
nanobot/bus/events.pynanobot/bus/queue.pynanobot/agent/loop.py::TurnStateAgentLoop.process_directAgentLoop._process_messageAgentRunner.runAgentRunner._run_core
目标:只画出主调用链,不进入每个 helper。
第 2 轮:补上下文和状态
nanobot/agent/context.pynanobot/session/manager.pynanobot/session/keys.pynanobot/agent/memory.pynanobot/bus/runtime_events.py
目标:回答“本轮模型看到了什么、磁盘保存了什么、UI收到了什么”。
第 3 轮:补 Provider 和 Tool
nanobot/providers/base.pynanobot/providers/factory.pynanobot/providers/registry.pynanobot/agent/tools/base.pynanobot/agent/tools/registry.pynanobot/agent/tools/loader.py
目标:回答“模型怎么选、工具怎么发现、参数怎么校验、结果怎么回填”。
第 4 轮:选择一个运行表面
根据需要选一条:
- CLI:
nanobot/cli/commands.py - API:
nanobot/api/server.py - SDK:
nanobot/nanobot.py - Channel:
nanobot/channels/base.py→manager.py→ 一个具体 Channel - WebUI:
nanobot/channels/websocket.py→session/webui_turns.py→ Python WebUI API → React
第 5 轮:高级机制
agent/autocompact.pyagent/memory.pysession/turn_continuation.pysession/goal_state.pyagent/subagent.pycron/security/
测试阅读顺序
不要先运行全部测试。按主题选择最接近的文件:
| 主题 | 测试起点 |
|---|---|
| Loop/Runner 主链 | tests/agent/test_loop_runner_integration.py |
| Runner 基本分支 | tests/agent/test_runner_core.py |
| 工具执行 | tests/agent/test_runner_tool_execution.py |
| 错误和安全 | tests/agent/test_runner_errors.py、test_runner_safety.py |
| Context | tests/agent/test_context_builder.py |
| Session | tests/agent/test_session_manager_history.py、test_session_atomic.py |
| Runtime events | tests/bus/test_runtime_events.py |
| Provider | tests/providers/ |
| Channel | tests/channels/test_base_channel.py、test_channel_plugins.py |
| Subagent | tests/agent/test_subagent_lifecycle.py |
从现象定位目录
| 现象 | 优先目录 |
|---|---|
| 模型选错、key 取错 | config/、providers/ |
| Tool schema 或参数错误 | agent/tools/、agent/runner.py |
| 同一对话历史错乱 | agent/loop.py、session/ |
| Channel 不收或不发 | channels/、pairing/ |
| UI 状态不刷新 | bus/runtime_events.py、session/webui_turns.py、channels/websocket.py |
| Cron 没运行 | cron/、Gateway 组装、workspace cron store |
| 文件越权 | agent/tools/filesystem.py、security/ |
| HTTP 工具访问内网 | security/network.py、Web/MCP 调用方 |
| Prompt 行为变化 | agent/context.py、templates/、Skills、Session history |
常见错误阅读方式
错误一:按文件名猜职责
例如 gateway/runtime.py 名字看起来像 Gateway 的全部 runtime,但主要服务组装路径在 cli/commands.py::_run_gateway。
错误二:把测试当实现替代品
测试证明受保护的行为,但不能替代阅读实际调用链。先确认测试覆盖的入口和边界。
错误三:看到共享代码就假设所有入口一致
API、SDK 和 CLI 都可调用 process_direct(),但它们的 session key、stream adapter、锁和错误格式不同。
错误四:深入单个 Provider 后忘记统一契约
先理解 LLMProvider 和 LLMResponse,再看 DeepSeek 使用的 OpenAI-compatible adapter。
自检
- 如果 WebUI 的
_turn_end不正确,应先检查哪些模块? - 如果一个新能力只是“教模型如何完成某项任务”,为什么应先考虑 Skill?
- 如果 API 返回 400,但 CLI 正常,为什么先看
api/server.py? - 为什么 AgentLoop/Runner 的修改门槛应高于 Tool?
下一步
阅读 最小故障定位流程,然后进入“一次 Turn”。