# nanobot-backend 基于 `nanobot` 的后端服务仓库,当前重点不是上游通用介绍,而是这套实际可运行的后端能力: - `nanobot web`:单用户 FastAPI 后端,供独立前端或 `/docs` 调试使用 - `nanobot gateway`:常驻 worker,负责渠道接入、cron、heartbeat - MCP 动态工具接入 - Outlook 集成:通过外部 `BW_Outlook_Mcp` 服务接入 Microsoft Graph / Exchange EWS - 工作区文件、技能、插件、代理、MCP 管理等 Web API 如果你后续要把它打包成 Docker 丢到服务器,这份 README 就是给开发和部署同事看的执行文档。 ## 这套仓库现在是什么 这不是一个自带前端静态页面的全栈仓库,而是后端仓库: - Web 模式启动的是 FastAPI API 服务 - Gateway 模式启动的是常驻 agent / channel / cron 进程 - WhatsApp 相关逻辑依赖 `bridge/` 里的 Node 20 bridge - Outlook 不是仓库内置模块,而是通过外部 `BW_Outlook_Mcp` 仓库接进来 更细的执行链路可以看 [workflow.md](./workflow.md)。 ## 目录结构 ```text . ├── nanobot/ # Python 主体:CLI、agent、web、channels、config、MCP ├── bridge/ # WhatsApp bridge(Node 20) ├── tests/ # 测试 ├── Dockerfile # 当前镜像构建文件 ├── docker-compose.yml # 当前自带 compose 示例(偏 gateway / CLI) └── workflow.md # 运行链路说明 ``` ## 运行模式 | 命令 | 用途 | 默认端口 | 适合谁 | | --- | --- | --- | --- | | `nanobot agent` | 本地单轮 / 交互调试 | 无 | 开发排查 | | `nanobot web` | 启动 FastAPI 后端 | `18080` | 独立前端、接口调试、单用户使用 | | `nanobot gateway` | 启动常驻 worker | 无固定 HTTP 入口 | Telegram/Slack/Email/cron/heartbeat | | `nanobot status` | 查看配置和 provider 状态 | 无 | 开发、运维 | 注意: - 如果你是给 Web 前端提供后端,请启动 `nanobot web`,不要误用 `gateway` - `gateway` 当前不是对外 Web API 服务 - `web` 和 `gateway` 都会碰到同一份 workspace / cron / MCP 状态,通常不要在同一份数据目录上无脑同时跑两套 ## 环境要求 - Python `>=3.11` - 推荐使用 `uv` - 如果要构建 WhatsApp bridge 或使用仓库自带 Dockerfile,需要 Node.js `20` 本地开发最省事的方式: ```bash uv sync --extra dev ``` 如果你不用 `uv`,也可以: ```bash python3 -m venv .venv . .venv/bin/activate pip install -e ".[dev]" ``` ## 本地快速启动 ### 1. 初始化配置 ```bash nanobot onboard ``` 初始化后默认会生成: - 配置文件:`~/.nanobot/config.json` - 工作区:`~/.nanobot/workspace` ### 2. 填最小配置 下面是一份适合服务器环境的最小示例,重点是: - 用绝对路径的 workspace - 建议打开 `restrictToWorkspace` - 先用 API Key provider,少踩 OAuth 交互坑 ```json { "agents": { "defaults": { "workspace": "/root/.nanobot/workspace", "model": "openai/gpt-5" } }, "providers": { "openai": { "apiKey": "sk-xxxx" } }, "tools": { "restrictToWorkspace": true } } ``` 如果你不是跑在容器里,把 `/root/.nanobot/workspace` 换成你自己的绝对路径。 ### 3. 检查配置 ```bash nanobot status ``` ### 4. 本地调试 agent ```bash nanobot agent -m "你好" ``` ### 5. 启动 Web 后端 ```bash nanobot web --host 0.0.0.0 --port 18080 ``` 启动后可直接访问: - `http://127.0.0.1:18080/docs` - `http://127.0.0.1:18080/api/ping` ## Web API 能力概览 当前 `nanobot web` 提供的 API 大致包括: - 聊天与流式输出 - 会话管理 - cron 任务管理 - skills / plugins / agents 管理 - 工作区文件浏览、上传、下载、删除 - MCP server 管理与测试 - Outlook 集成状态、连接测试、连接/断开、Overview、Message Detail 如果你有独立前端,这个后端就是给前端接的;如果没有前端,也可以直接走 `/docs` 调试。 ## Outlook MCP 集成 这是当前仓库里最容易部署时踩坑的一块。 ### 关系先说清楚 当前后端不会自己实现 Outlook 协议,它依赖外部仓库 `BW_Outlook_Mcp`: - 后端代码位置:`nanobot/web/outlook.py` - 默认查找逻辑: 1. 先看环境变量 `NANOBOT_OUTLOOK_MCP_ROOT` 2. 再看与本仓库同级目录的 `../BW_Outlook_Mcp` 3. 如果以上都没有,就尝试直接执行 PATH 里的 `bw-outlook-mcp` 也就是说,部署同事必须额外把 `BW_Outlook_Mcp` 这个仓库准备好,或者把它直接安装进镜像。 ### 推荐的两种接法 #### 方案 A:把 `BW_Outlook_Mcp` 安装进同一个 Python 环境 这是生产环境更稳的方案。 部署同事需要: ```bash git clone <你们的 BW_Outlook_Mcp 仓库地址> /srv/BW_Outlook_Mcp cd /srv/BW_Outlook_Mcp pip install -e . ``` 安装完成后,容器或宿主机里能直接执行: ```bash bw-outlook-mcp --help ``` 这样 nanobot 就会直接用 PATH 里的 `bw-outlook-mcp`,不依赖额外挂载路径。 #### 方案 B:把 `BW_Outlook_Mcp` 作为外部目录挂进来 这是开发或临时部署更方便的方案。 部署同事需要至少做到两件事: 1. 把 `BW_Outlook_Mcp` 仓库拉到服务器 2. 让这个目录里存在一个可执行的 `bw-outlook-mcp` 最简单的约定是: ```bash git clone <你们的 BW_Outlook_Mcp 仓库地址> /srv/BW_Outlook_Mcp cd /srv/BW_Outlook_Mcp python3 -m venv .venv . .venv/bin/activate pip install -e . ``` 然后给 nanobot 设置: ```bash export NANOBOT_OUTLOOK_MCP_ROOT=/srv/BW_Outlook_Mcp ``` 因为当前后端会优先寻找: ```text $NANOBOT_OUTLOOK_MCP_ROOT/.venv/bin/bw-outlook-mcp ``` 如果你挂了仓库目录但里面没有 `.venv/bin/bw-outlook-mcp`,那就必须确保 `bw-outlook-mcp` 已经在容器 PATH 里。 ### Outlook 的认证和配置 `BW_Outlook_Mcp` 本身支持两套后端: - `graph`:Microsoft 365 / Exchange Online - `ews`:本地或回迁后的 Exchange Server #### Graph 登录 ```bash bw-outlook-mcp auth login-graph \ --workspace /root/.nanobot/workspace \ --client-id YOUR_CLIENT_ID \ --tenant-id YOUR_TENANT_ID ``` #### EWS 配置 ```bash bw-outlook-mcp auth setup-ews \ --workspace /root/.nanobot/workspace \ --email you@example.com \ --username your_username \ --domain example.com \ --server mail.example.com ``` 如果你已经有固定 EWS URL,也可以改用: ```bash bw-outlook-mcp auth setup-ews \ --workspace /root/.nanobot/workspace \ --email you@example.com \ --username your_username \ --service-endpoint https://mail.example.com/EWS/Exchange.asmx ``` #### 查看状态 ```bash bw-outlook-mcp auth status --workspace /root/.nanobot/workspace ``` ### Outlook 状态文件会落在哪里 所有 Outlook 相关状态默认都落在 workspace 下: ```text /state/bw_outlook_mcp/ ├── config.json ├── secrets.json ├── graph_token_cache.bin ├── delta_store.json └── idempotency.sqlite3 ``` 所以 Docker 部署时,不要只挂配置文件;要把整份 `~/.nanobot` 或至少 workspace 做持久化。 ### Nanobot 里如何注册 Outlook MCP 如果你通过 Web 接口完成 Outlook 连接,后端会自动把 MCP server 注册到配置里。 手工写配置时,结构类似这样: ```json { "tools": { "mcpServers": { "outlook": { "command": "bw-outlook-mcp", "args": ["serve", "--workspace", "/root/.nanobot/workspace"], "sensitive": true, "toolTimeout": 60 } } } } ``` 这里一定要用绝对路径,不要写 `~/.nanobot/workspace`。 ### 可选的 Outlook 环境变量 | 变量 | 作用 | | --- | --- | | `NANOBOT_OUTLOOK_MCP_ROOT` | 指向外部 `BW_Outlook_Mcp` 仓库目录 | | `NANOBOT_OUTLOOK_MCP_COMMAND` | 强制指定 `bw-outlook-mcp` 可执行文件 | | `NANOBOT_OUTLOOK_MCP_EXTRA_ARGS` | 给 `bw-outlook-mcp serve` 追加参数 | | `NANOBOT_OUTLOOK_DEFAULT_DOMAIN` | Web 连接表单的默认域名 | | `NANOBOT_OUTLOOK_DEFAULT_EWS_URL` | Web 连接表单默认 EWS 地址 | | `NANOBOT_OUTLOOK_DEFAULT_EWS_SERVER` | Web 连接表单默认 Exchange 主机 | | `NANOBOT_OUTLOOK_DEFAULT_TIMEZONE` | Web 连接表单默认时区 | | `NANOBOT_OUTLOOK_DEFAULT_AUTODISCOVER` | Web 连接表单默认是否启用 autodiscover | ## Docker 部署 ### 先说结论 服务器部署时,最重要的是持久化这份目录: ```text /root/.nanobot ``` 因为它里面不只是 `config.json`,还包括: - workspace - sessions - cron 状态 - Web 登录信息 - Outlook 状态与 token 缓存 ### 构建镜像 ```bash docker build -t nanobot-backend:latest . ``` ### 首次初始化 第一次跑容器时,先执行一次: ```bash docker run --rm \ -v /srv/nanobot/data:/root/.nanobot \ nanobot-backend:latest \ onboard ``` 然后去编辑宿主机上的: ```text /srv/nanobot/data/config.json ``` 或者先进去执行: ```bash docker run --rm -it \ -v /srv/nanobot/data:/root/.nanobot \ nanobot-backend:latest \ status ``` ### 作为 Web 后端启动 如果你是给前端项目配后端,推荐这样跑: ```bash docker run -d \ --name nanobot-web \ -p 18080:18080 \ -v /srv/nanobot/data:/root/.nanobot \ -e NANOBOT_OUTLOOK_MCP_ROOT=/opt/BW_Outlook_Mcp \ -v /srv/BW_Outlook_Mcp:/opt/BW_Outlook_Mcp \ nanobot-backend:latest \ web --host 0.0.0.0 --port 18080 ``` 如果你已经把 `bw-outlook-mcp` 安装进镜像了,就不需要挂 `/srv/BW_Outlook_Mcp`,也不需要 `NANOBOT_OUTLOOK_MCP_ROOT`。 ### 作为 Gateway/Worker 启动 如果你要接 Telegram / Slack / Email / cron 之类的常驻能力,再跑 gateway: ```bash docker run -d \ --name nanobot-gateway \ -v /srv/nanobot/data:/root/.nanobot \ nanobot-backend:latest \ gateway ``` ### 推荐的服务器 compose 片段 仓库自带的 [docker-compose.yml](./docker-compose.yml) 更偏本地 gateway/CLI 示例。 如果你是部署 Web 后端到服务器,更建议单独写成这样: ```yaml services: nanobot-web: image: nanobot-backend:latest container_name: nanobot-web command: ["web", "--host", "0.0.0.0", "--port", "18080"] restart: unless-stopped ports: - "18080:18080" volumes: - /srv/nanobot/data:/root/.nanobot - /srv/BW_Outlook_Mcp:/opt/BW_Outlook_Mcp environment: NANOBOT_OUTLOOK_MCP_ROOT: /opt/BW_Outlook_Mcp ``` 如果你想把 Outlook 依赖做得更稳,推荐直接把 `BW_Outlook_Mcp` 安装进镜像,而不是运行时挂载仓库。 ## 部署给同事时,至少要交代这几件事 1. 这是后端仓库,不带前端静态页面,前端请单独部署 2. Web API 用 `nanobot web` 启动,不是 `gateway` 3. 数据目录必须持久化到 `/root/.nanobot` 4. 如果要 Outlook,必须额外拉取 `BW_Outlook_Mcp` 5. Outlook 有两种接法:装进镜像,或者挂外部仓库并设置 `NANOBOT_OUTLOOK_MCP_ROOT` 6. Outlook 的状态文件也在 workspace 里,删容器不挂卷就会丢 ## 常用命令 ```bash nanobot onboard nanobot status nanobot agent -m "你好" nanobot web --host 0.0.0.0 --port 18080 nanobot gateway nanobot provider login openai-codex ``` ## 开发备注 - `workflow.md` 记录了当前代码实际运行链路,和旧版 README 更接近“真实代码” - `nanobot/web/outlook.py` 是当前 Outlook 集成入口 - `tests/` 里有 Web API、Email、Docker 相关测试 - 如果要上服务器,建议在配置里显式打开 `tools.restrictToWorkspace=true` ## 排错 ### Web 启动了,但 Outlook 相关接口报错 优先检查: - `bw-outlook-mcp` 是否能在当前容器里执行 - `NANOBOT_OUTLOOK_MCP_ROOT` 是否指向正确目录 - 如果走目录挂载模式,目录里是否真的有 `.venv/bin/bw-outlook-mcp` ### MCP 注册了,但工具没有出现 检查: - `config.json` 里的 `tools.mcpServers` - `nanobot web` 或 `nanobot agent` 启动时是否用了同一份 `~/.nanobot` - Outlook MCP 是否能单独执行 `bw-outlook-mcp auth status --workspace ...` ### Docker 里配置改了没生效 优先检查你挂载的是不是整份: ```text /srv/nanobot/data:/root/.nanobot ``` 不是只挂了某一个文件。