第 00 篇:导读与全景地图
本系列基于 Octop
1.0.2b6源码。这一篇不深入任何模块,只做三件事:界定问题、画出地图、给出阅读路径。已经熟悉项目可直接跳到 第 01 篇。
1. 一句话定义
Octop 是腾讯云开源的自托管 AI 助手平台 :一个 Python wheel,内含 FastAPI 后端、React 前端、Click CLI、IM 渠道桥接、定时调度、知识库、插件系统与 MCP 连接器。整个平台以单进程 运行在你自己的机器上,数据全部落在 ~/.octop/。
它的差异化不在"能聊天",而在于把下面四个约束同时满足:
| 约束 | 含义 |
|---|---|
| 多用户 | 一个部署,多个用户;JWT 隔离,共享专家与知识库 |
| 多智能体 | 每个用户可拥有多个 Agent(专家);Agent 之间可协作(AgentTeams) |
| 多渠道 | Web / CLI / 飞书 / 钉钉 / QQ / 微信 / Telegram / Discord / 企业微信 / Cron 全部汇入一条管线 |
| 自托管 | 零外部依赖(除 LLM provider 外无 Redis/MQ/worker),单进程、单端口 |
2. 四条贯穿全局的设计主线
带着这四条主线读代码,能解释绝大多数"为什么这样写":
主线一:单进程,无外部队列
见 ADR 001。没有 Redis、Celery、独立 worker。所有 surface 通过进程内的 HarnessProcessor 汇流。后果是"所有阻塞调用必须 run_in_executor",且没有任何跨进程一致性难题------进程状态可以完全从数据库重建。
主线二:双数据库后端
见 ADR 002。控制面可跑在 SQLite(默认,WAL)或 PostgreSQL(psycopg3 连接池)上。同一套 Repo 代码,靠池边界的 ? → %s 重写与 _CompatRow 抹平方言差异。存储被明确划分为三层:Octop 控制面、harness checkpoint、agent memory,三者不许混为一谈。
主线三:依赖只向内流动
arduino
dashboard/ ──HTTP──► api/ ──► infra/ ──► infra/utils/, octop.config
cli/ ──► launch.py ──► api/ + infra/
infra/ 永不 import api/ / cli/ / launch.py;api/ 不 import cli/;infra/db/repos/ 只依赖 _base 与 utils。这些不是建议,而是 AGENTS.md §5 的硬禁令,并有"唯一例外"------只有 launch.py 能同时看到 infra.server 与 api.app。
主线四:一张数据库,一个进程,一次重启
OctopServer.start() 每次按依赖顺序重建整棵运行时树。进程内没有"不可恢复的隐性状态"。这也是为什么 replace_services() 能把所有单例热重绑到新库------状态本来就是"可从库重建"的。
3. 运行时全景
scss
OctopServer
├─ DatabasePool SQLite (WAL) 或 PostgreSQL (psycopg 池)
├─ SharedServices DI 根:paths + config + RepoBundle
├─ ExpertCatalog 启动时扫描 agents/experts/library/
├─ UserManager
│ └─ AgentManager(全局注册表,一个进程一个)
│ └─ HarnessAgentRuntime(每个 agent)
│ ├─ HarnessAgent 智能体运行时(octop-harness / LangGraph)
│ ├─ GlobalProcessor IM / Web / CLI / Cron 统一入口
│ ├─ ChannelManager IM 连接(octop-gateway)
│ └─ BackendWorkspace 可插拔文件 I/O
├─ Gateway(ChannelManager + WebSocketHub + Cron 触发器)
├─ CronManager(进程级 APScheduler)
├─ TrajectoryService(轨迹投影 + 实时总线)
└─ FastAPI app(uvicorn)
4. 一次对话的完整路径(玻璃盒)
以"用户在 Web 里发一句话"为例,把本系列的章节串起来:
bash
用户输入
│ WebSocket /api/agents/{aid}/chat/ws ← 第 11 篇(前端)
▼
api/routers/chat/ws.py → gateway/ws/ws_channel.py ← 第 03 篇
│ 组装 InboundMessage,解析 session_key
▼
GlobalProcessor.iter_turn_chunks(msg) ← 第 03 篇
│ ① 解析用户/locale/session_key ② 处理 slash ③ HITL 分支
│ ④ 构造 harness request ⑤ 订阅轨迹
▼
AgentManager.stream() → HarnessAgent ← 第 02 篇
│ LangGraph 执行,逐 chunk 产出
▼
TrajectoryService.observe_chunk() ← 第 04 篇
│ 投影为 assistant / tool / user 事件,聚合 token,落库 + 实时发布
▼
WebSocketHub.push_to_thread() → 浏览器渲染 ← 第 11 篇
同一路径换成 IM 渠道时,ws_channel 换成对应 ChannelManager,iter_turn_chunks 换成 __call__ → project_stream → MessageEvent,下游完全复用。这正是"统一管线"的价值。
5. 关键术语表
| 术语 | 定义 | 主要源码 |
|---|---|---|
| surface | 消息入口:Web / CLI / IM / Cron | infra/gateway/* |
| turn | 一次用户→助手的完整往返 | history/trajectory/* |
| session_key | 全局会话寻址键 agent:channel:subject:chat_type |
gateway/threads.py:31 |
| thread | session_key 绑定的具体会话线程(含历史元数据) | gateway/threads.py |
| chunk | harness 产出的流式事件(token / tool_call_chunk / tool_result / ...) | gateway/process/stream_project.py |
| trajectory | 由 chunk 投影出的、可持久化可回放的事件流 | history/trajectory/* |
| TaskRouter / Processor | GlobalProcessor,把 InboundMessage 路由到正确 Agent |
gateway/process/processor.py:135 |
| expert | 面向用户的 Agent 概念(一个 Agent = 一个专家) | infra/agents/* |
| workspace | Agent 的文件空间,可插拔后端 | infra/backend/* |
6. 技术栈速查
| 层 | 技术 |
|---|---|
| 语言 | Python 3.12+(asyncio,无线程池除非 run_in_executor) |
| Web | FastAPI + uvicorn;Scalar 文档在 /api/docs(默认关) |
| Agent 运行时 | octop-harness(LangGraph) |
| 渠道桥接 | octop-gateway |
| 记忆 | octop-memory(分层召回 + FTS) |
| 浏览器 | octop-browser(CDP + 持久 profile) |
| 控制面 DB | SQLite(WAL,默认)或 PostgreSQL(psycopg3) |
| 前端 | React 18 + TypeScript + Vite + Ant Design |
| 调度 | APScheduler(AsyncIOScheduler) |
| 包管理 | uv(uv run pytest,禁止裸 pytest) |
| 质量门 | hatchling · ruff · mypy --strict · pytest(跨 Linux/Windows) |
7. 阅读路径建议
- 架构向:01(组合根)→ 02(运行时)→ 03(管线)→ 04(投影)→ 05(历史)。
- 集成向:03(管线)→ 10(连接器/MCP)→ 11(前端)。
- 运维/存储向:06(双库)→ 08(工作区后端)→ 12(定时)。
- 安全向:09(安全体系)单读。
准备好了就从 第 01 篇:单进程架构的抉择 开始。