# NimbusOne **Repository Path**: carzychen/nimbus-one ## Basic Information - **Project Name**: NimbusOne - **Description**: 云枢 · NimbusOne 运行在云端,多租户隔离的AI Agent - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-20 - **Last Updated**: 2026-10-08 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 云枢 NimbusOne 运行在云端、**多租户隔离**的 AI Agent 平台。 每个注册用户独占一个 Agent 实例(运行在独立 Docker 容器中);请求经网关完成鉴权、限频与并发排队后, 路由到该用户专属容器;Agent 的 LLM 原始流事件被语义化封装为统一的 `NimbusEvent`, 前端以「思考 / 文本 / 工具调用 / 多模态」分块实时渲染,并实时显示上下文占用水位。 --- ## 核心特性 | 能力 | 说明 | |---|---| | 独占 Agent 实例 | 1 用户 = 1 租户 = 1 独占容器;按需创建并绑定该用户工作区,空闲 TTL 回收,租户状态外置,实例可回收/漂移 | | 工具调用 | 内置计算器、时间、工作区文件读写、检索、图片生成;`risky` 工具走前端人工确认(HITL) | | 图循环模式 | 每条消息可选「常规 / 图循环」:先把任务分解为 DAG,再按拓扑分层并行执行子任务(同层并发、层间串行),每个子任务复用完整 Agent Loop;页面实时渲染任务图与各节点进展 | | Skill 流程 | 渐进式披露:只有 `name + description` 常驻 prompt,命中后用 `load_skill` 加载正文 | | 多模态 | 图片/文件上传、Agent 产物回传,以 `multimodal` 事件内联渲染 | | 语义化流式事件 | LLM 原始流 → 归一化 → `NimbusEvent`(块生命周期、工具聚合、用量),支持 `Last-Event-ID` 断线重放 | | 网关治理 | 全局并发信号量 + 可见排队位次(可超时/取消);单用户令牌桶限频;两者语义分离,均支持 Redis 分布式 | | 会话与上下文 | 会话增删改查、历史回放;上下文水位实时更新,达阈值自动压缩 | --- ## 架构 ```mermaid flowchart TB UI[Vue3 前端\n会话/消息流/水位条] -- SSE 下行 --> GW UI -- HTTP 上行(发消息/上传/确认/取消) --> GW subgraph GW[FastAPI 网关] AUTH[JWT 鉴权] --> RL[per-user 令牌桶限频] RL --> Q[全局并发信号量 + 排队队列] Q --> ROUTER[容器注册表路由] ROUTER --> FWD[SSE 流式转发 + 事件持久化] end subgraph POOL[调度层] REG[(路由表)] --> SCHED[池化调度器] SCHED --> D1[Docker 驱动] SCHED --> D2[本地进程驱动 降级] end GW --> POOL subgraph RT[Agent Runtime 容器] MODE{mode} -->|default| LOOP[Agent Loop] MODE -->|graph| GL[Graph Loop] GL --> PLAN[Planner: DAG 分解/拓扑分层] GL --> SCHED[按层并行调度 + 信号量] SCHED --> LOOP LOOP --> TOOLS[工具注册表] LOOP --> SKILL[Skill Loader] LOOP --> LLM[LLM 适配器 Mock/OpenAI] LLM --> NORM[事件归一化器] --> EMIT[NimbusEvent 发射器] end GW -- HTTP/SSE --> RT GW --> DB[(SQLite: 用户/会话/消息/事件日志)] RT --> VOL[(租户 workspace 卷 + 多模态文件)] ``` **一次请求的完整链路** 前端发消息 → 网关鉴权 → 限频 → 获取全局并发槽位(不足则入队并推送排队位次)→ 路由表查/分配容器 → 流式 POST 到 Runtime → Agent Loop 消费 LLM 原始流并归一化 → 网关边转发边落盘 → 前端按 block 渲染, `usage` 事件驱动上下文水位条。 --- ## 事件协议 跨三层的唯一契约,定义在 [`packages/protocol`](packages/protocol)(Python 与 TS 双份,字段严格对齐)。 ```python class NimbusEvent(BaseModel): seq: int # 单调递增,用于去重/排序/断线重放 turn_id: str session_id: str type: EventType # turn.* / block.* / tool.* / multimodal / graph.* / usage / queue.* / error ... block_id: str | None # 渲染块 id,增量追加到同一块 block_kind: BlockKind | None # text | reasoning | tool | multimodal | graph node_id: str | None # 图循环子任务归属;常规模式与图级事件为 None payload: dict ts: float ``` - 块生命周期:`block.start → block.delta* → block.end`,`block_kind` 决定渲染形态 - 工具:`tool.call`(含参数)→ `tool.result` / `tool.error`;`risky` 工具先发 `tool.confirm_request` - 图循环:`graph.plan`(完整 DAG:nodes/edges/layers)→ 逐层 `graph.node.start` → 该节点内 `block.* / tool.*`(带 `node_id`)→ `graph.node.end` - 网关治理事件使用**负数 seq**,与 Runtime 事件序号空间隔离,前端据此只更新状态、不入块 --- ## 目录结构 ``` nimbus-one/ ├── packages/protocol/ # 跨三层共享事件协议(Python + TS) ├── backend/ │ ├── shared/nimbus_common/ # 配置、日志、存储抽象、JWT、SSE 工具 │ ├── gateway/ # 网关:鉴权/限频/排队/路由/转发/会话 │ │ ├── api/ # auth / sessions / chat / files / admin │ │ ├── services/ # auth_service / rate_limiter / admission / router_registry / event_hub │ │ ├── scheduler/ # drivers(Docker|本地进程) + pool_manager │ │ └── repositories/ # SQLite 仓储(可切换 PostgreSQL) │ ├── runtime/ # 容器內 Agent Runtime │ │ ├── agent/ # loop / graph_loop / planner / context / tool_registry / skill_loader / skills │ │ ├── llm/ # adapters(Mock|OpenAI) + normalizer │ │ ├── events/emitter.py # 事件发射器 │ │ └── sandbox/workspace.py # 租户工作区与路径穿越防护 │ └── tests/ # 导入冒烟 / 端到端冒烟 / 治理冒烟 ├── docker/ # Runtime 与网关镜像 ├── frontend/ # Vue3 + TS + Vite + Tailwind └── scripts/ # 一键启动脚本 ``` --- ## 快速开始 ### 1. 安装依赖 ```bash # 后端 pip install -r backend/requirements.txt # 前端 cd frontend && npm install ``` ### 2. 启动 ```powershell # Windows .\scripts\dev.ps1 ``` ```bash # Linux / macOS ./scripts/dev.sh ``` 等价于手动启动: ```bash # 网关(仓库根目录执行,或设置 PYTHONPATH=backend;backend/shared;仓库根) cd backend && python -m uvicorn gateway.main:app --host 127.0.0.1 --port 8000 # 前端 cd frontend && npm run dev ``` 打开: - 前端: - 网关 API 文档: ### 3. 可选:Redis / Docker ```bash docker compose up -d redis # 分布式限频/排队/路由表 docker build -f docker/runtime.Dockerfile -t nimbus-one/runtime:latest . ``` 设置 `NIMBUS_REDIS_URL=redis://localhost:6379/0`、`NIMBUS_RUNTIME_DRIVER=docker` 即启用。 --- ## 降级策略(无需任何外部依赖也能跑通) | 依赖缺失 | 行为 | |---|---| | Docker 不可用 / 镜像不存在 | 自动降级为 `LocalProcessDriver`(子进程 uvicorn,独立端口与工作目录) | | 未配置 Redis | 限频、排队、路由表降级为进程内实现(多副本网关请配置 Redis) | | 未配置 LLM API Key | 使用 `MockStreamAdapter`,可产出思考/文本/工具调用/用量全类型事件 | | 未安装 tiktoken | token 估算回退为字符近似 | 三者同时缺失也能完整跑通全链路。 --- ## 主要环境变量 见 [`.env.example`](.env.example)。常用项: | 变量 | 默认 | 说明 | |---|---|---| | `NIMBUS_MAX_CONCURRENT_TURNS` | 4 | 全局并发上限,超出排队 | | `NIMBUS_RATE_LIMIT_CAPACITY` | 10 | 单用户令牌桶容量 | | `NIMBUS_RATE_LIMIT_REFILL_PER_SEC` | 0.2 | 令牌补充速率 | | `NIMBUS_RUNTIME_DRIVER` | auto | `auto` / `docker` / `local` | | `NIMBUS_INSTANCE_IDLE_TTL_SEC` | 600 | 空闲实例回收阈值 | | `NIMBUS_LLM_PROVIDER` | mock | `mock` / `openai` | | `NIMBUS_CONTEXT_LIMIT` | 32000 | 上下文上限,达 75% 触发压缩 | | `NIMBUS_AGENT_MAX_STEPS` | 30 | 单轮最大工具迭代步数 | | `NIMBUS_AGENT_CONFIRM_TIMEOUT_SEC` | 120 | HITL 确认等待超时(秒) | | `NIMBUS_AGENT_GRAPH_MAX_PARALLEL` | 3 | 图循环同层子任务并行上限 | | `NIMBUS_AGENT_GRAPH_MAX_NODES` | 8 | 单次任务分解的最大节点数 | --- ## 测试 ```bash python backend/tests/smoke_import.py # 导入冒烟:协议 + 网关 + Runtime python backend/tests/e2e_smoke.py # 端到端:注册/登录/会话/流式/工具/多模态/重放 python backend/tests/governance_smoke.py # 治理:并发排队 + 单用户限频 python backend/tests/graph_smoke.py # 图循环:DAG 分解/分层并行/节点事件归属/汇总 ``` 端到端冒烟会真实拉起网关与 Runtime(本地进程驱动),覆盖: 注册登录鉴权 → 会话 → 流式事件(思考/文本/工具调用/结果/用量/结束)→ 工具结果正确性 → 历史块结构 → 事件重放 → 多模态 → 运行态观测。 --- ## 安全说明 - 容器内以非 root 用户运行,限制内存/CPU/进程数,仅挂载该租户 workspace 卷 - 工作区路径做穿越校验(禁止 `..`、绝对路径与越界解析) - 计算器使用 AST 白名单求值,禁止任意代码执行 - 写入类工具标记 `risky`,执行前需前端人工确认 - JWT 密钥走环境变量;日志不打印密钥与完整消息体 --- ## 路线图 - [ ] 上下文压缩的并行摘要与增量压缩策略 - [ ] 真实检索 / 代码执行沙箱(网络与资源白名单) - [ ] Token 计量与租户配额计费 - [ ] 多副本网关下的会话亲和与实例漂移演练 - [ ] 更丰富的 Skill 生态与工具市场 --- ## 参与贡献 1. Fork 本仓库 2. 新建 `Feat_xxx` 分支 3. 提交代码 4. 新建 Pull Request