03 · 流程图
运行时结构 · 状态机 · 核心流程
OpenHuman 的核心运行时由多个并发异步循环组成:Agent 工具调用循环、20 分钟 Autofetch 循环、Socket.IO 实时通信层,以及 Memory 摄入管道。
启动流程
应用启动时,Tauri Shell 初始化 Rust 后端,依次建立数据库连接、加载配置、启动 Socket.IO 服务器,最后激活所有已启用的渠道监听器。
flowchart TD
A([🖥️ 用户启动 OpenHuman]) --> B[Tauri App 初始化]
B --> C[加载 config.toml]
C --> D[初始化 SQLite 数据库\nmemory.db + migrations]
D --> E[解密并加载 Credentials\nAES-GCM + Argon2]
E --> F[启动 tokio 异步运行时]
F --> G{首次启动?}
G -->|是| H[引导 Onboarding UI\n连接第一个集成]
G -->|否| I[启动 Socket.IO 服务器\nsocketioxide on localhost]
H --> I
I --> J[注册 RPC 控制器\n三层路由初始化]
J --> K[启动 Channel 监听器\nSlack / Email / Telegram …]
K --> L[启动 Scheduler Gate\n20min autofetch 定时器]
L --> M[发送 ready 事件到前端]
M --> N([✅ 应用就绪])
style A fill:#1a2e1a,stroke:#22c55e,color:#86efac
style N fill:#1a2e1a,stroke:#22c55e,color:#86efac
style G fill:#2a1f3a,stroke:#a855f7,color:#d8b4fe
style H fill:#1a2030,stroke:#3b82f6,color:#93c5fd
Socket.IO 实时通信流程
前端 React 与后端 Rust 通过 Socket.IO WebSocket 实时双向通信。连接建立后,后端将 7 个异步事件桥激活,将内部领域事件广播至对应房间。
sequenceDiagram
participant FE as 前端 React
participant SIO as Socket.IO Server (Rust)
participant CORE as openhuman Core
participant LLM as LLM Provider
FE->>SIO: connect()
SIO-->>FE: ready { client_id }
Note over FE,SIO: 自动加入个人房间 + "system" 房间
FE->>SIO: chat:start { message, thread_id }
SIO-->>FE: chat:accepted { request_id }
SIO->>CORE: 分发到 Agent Engine
loop Agent 工具调用循环
CORE->>LLM: 发送 prompt + 工具列表
LLM-->>CORE: streaming text_delta
CORE-->>SIO: text_delta / tool_call 事件
SIO-->>FE: 实时推送 delta
CORE->>CORE: 执行工具调用
CORE-->>SIO: tool_result 事件
SIO-->>FE: tool_result
end
CORE-->>SIO: chat_done { full_response }
SIO-->>FE: chat_done
FE->>SIO: rpc:request { method, params }
SIO->>CORE: RPC 三层路由
CORE-->>SIO: rpc:response
SIO-->>FE: rpc:response
FE->>SIO: chat:cancel { request_id }
SIO->>CORE: 取消正在进行的推理
7 个异步事件桥:chat · voice · notifications · auth · companion_state · screen_intelligence · wallet — 每个桥独立转发对应领域的内部 Rust 事件到 Socket.IO 房间。
Agent 工具调用循环(Tool-Calling Loop)
Agent 引擎实现标准的 ReAct 模式:LLM 输出工具调用指令 → 本地执行工具 → 结果回传 → 继续推理,直到 LLM 输出最终回答或触发子 Agent 调度。
flowchart TD
START([收到用户消息 / 触发事件]) --> TRIAGE[Trigger Triage 分类\n判断优先级和路由]
TRIAGE --> HARNESS[Session Harness 初始化\n加载 Memory Context\nTokenJuice 压缩]
HARNESS --> PROMPT[构建 System Prompt\n角色 + 工具列表 + 记忆片段]
PROMPT --> LLM_CALL[调用 LLM Provider\nHTTP / Local 路由]
LLM_CALL --> DECISION{LLM 输出类型?}
DECISION -->|text delta| STREAM[流式推送 text_delta\n到 Socket.IO]
STREAM --> DECISION
DECISION -->|tool_call| TOOL_EXEC[工具执行器\n校验 Agent Tool Policy]
TOOL_EXEC --> TOOL_TYPE{工具类型?}
TOOL_TYPE -->|文件系统/Git| FS[文件系统工具\ncoding toolkit]
TOOL_TYPE -->|Web搜索| WEB[搜索 + 爬虫]
TOOL_TYPE -->|MCP 工具| MCP[MCP Client/Server]
TOOL_TYPE -->|集成 API| API[118+ 集成 API]
FS & WEB & MCP & API --> TOOL_RESULT[工具结果回传\n推送 tool_result 事件]
TOOL_RESULT --> LLM_CALL
DECISION -->|sub_agent| SUB[子 Agent 调度\norchestrator/planner\nresearcher/code_executor]
SUB --> SUB_LOOP[子 Agent 独立循环]
SUB_LOOP --> MERGE[合并子 Agent 输出]
MERGE --> LLM_CALL
DECISION -->|final_answer| MEMORY_WRITE[写入 Memory Tree\n更新知识图谱]
MEMORY_WRITE --> DONE([✅ chat_done 推送])
style START fill:#1a2e1a,stroke:#22c55e,color:#86efac
style DONE fill:#1a2e1a,stroke:#22c55e,color:#86efac
style DECISION fill:#2a1f3a,stroke:#a855f7,color:#d8b4fe
style TOOL_TYPE fill:#2a1f3a,stroke:#a855f7,color:#d8b4fe
Autofetch 20 分钟同步周期
Scheduler Gate 每 20 分钟(可配置)触发一次全渠道数据拉取,将新邮件、日历事件、代码提交、文档编辑转换为 Markdown chunks,写入 Memory Tree。
flowchart LR
TIMER([⏰ 20min 定时器触发]) --> GATE{Scheduler Gate\n检查条件}
GATE -->|系统空闲\n非睡眠| FETCH[并行拉取所有已授权集成]
GATE -->|系统繁忙| SKIP[跳过本轮\n记录 skip_count]
FETCH --> GMAIL[Gmail 新邮件]
FETCH --> GCAL[Google Calendar 事件]
FETCH --> SLACK[Slack 新消息/频道]
FETCH --> GITHUB[GitHub Commits/PRs]
FETCH --> NOTION[Notion 页面更新]
FETCH --> MORE[... 118+ 渠道]
GMAIL & GCAL & SLACK & GITHUB & NOTION & MORE --> CONVERT[转换为 Markdown\n~3000 token 分块]
CONVERT --> EMBED[生成向量嵌入\n实体/关系提取]
EMBED --> INGEST[IngestionQueue 写入]
INGEST --> SQLITE[(SQLite\nmemory.db)]
INGEST --> VAULT[📁 Obsidian Vault\n~/.openhuman/vault]
INGEST --> TREE_UPDATE[更新 Memory Tree\nsource → topic → global]
TREE_UPDATE --> NOTIFY[推送 sync_complete\n到 Socket.IO]
NOTIFY --> TIMER
style TIMER fill:#0d2020,stroke:#14b8a6,color:#5eead4
style GATE fill:#2a1f3a,stroke:#a855f7,color:#d8b4fe
style SKIP fill:#1e1010,stroke:#6b7280,color:#9ca3af
Memory Tree 摄入管道
Memory 系统采用三层摘要树架构:原始数据层 → 主题摘要层 → 全局摘要层。新文档经过分块、嵌入、实体提取后进入 SQLite,背景作业维护三层树的一致性。
flowchart TD
INPUT[📥 输入:文档/邮件/聊天/代码] --> PARSE[文档解析\nMarkdown / PDF / IMAP / JSON]
PARSE --> CHUNK[分块器\n~3000 tokens / chunk\n重叠滑窗]
CHUNK --> PARALLEL
subgraph PARALLEL[并行处理]
direction LR
EMB[向量嵌入生成\nembedding model]
ENTITY[实体/关系提取\nNER via LLM]
FTS[FTS5 全文索引\nSQLite 写入]
end
PARALLEL --> STORE[(SQLite memory.db\n向量表 + 关系图 + FTS5)]
STORE --> TREE_L1[🌿 第一层:Source Tree\n原始来源摘要\n每个 doc 独立摘要]
TREE_L1 --> TREE_L2[🌲 第二层:Topic Tree\n跨来源主题聚合\n定期后台合并]
TREE_L2 --> TREE_L3[🌳 第三层:Global Tree\n全局知识摘要\n周期性重建]
TREE_L3 --> RECALL{查询召回}
RECALL -->|关键词匹配| FTS_QUERY[FTS5 全文检索]
RECALL -->|语义相似| VEC_QUERY[向量近邻搜索 ANN]
RECALL -->|实体溯源| GRAPH_QUERY[知识图谱遍历]
FTS_QUERY & VEC_QUERY & GRAPH_QUERY --> RANK[打分 + 排序\nRecallOpts 过滤]
RANK --> CONTEXT[注入 Agent Context\nTokenJuice 压缩至预算]
STORE --> VAULT_SYNC[定期同步到 Vault\nObsidian Markdown 文件]
style INPUT fill:#1a2030,stroke:#3b82f6,color:#93c5fd
style RECALL fill:#2a1f3a,stroke:#a855f7,color:#d8b4fe
style CONTEXT fill:#1a2e1a,stroke:#22c55e,color:#86efac
RPC 三层路由(Tiered Dispatch)
所有来自前端的 RPC 请求经过三层路由:Legacy 别名重写 → Core 核心方法 → Controller 注册表 → Legacy 兜底 → 未知方法错误。
flowchart TD
REQ([📨 RPC Request\nmethod + params]) --> ALIAS[Tier 0: Legacy 别名重写\n将旧方法名映射到规范名]
ALIAS --> T1{Tier 1: Core Methods\nmethod 以 core. 开头?}
T1 -->|core.ping| PING[返回 {ok: true}]
T1 -->|core.version| VERSION[返回运行中的 binary 版本]
T1 -->|否| T2{Tier 2: Controller Registry\n已注册的 domain handlers?}
T2 -->|匹配| SCHEMA[JSON Schema 参数校验]
SCHEMA -->|校验失败| ERR_SCHEMA[错误: 参数格式错误]
SCHEMA -->|校验通过| CTRL[调用注册 Controller Handler]
CTRL --> RESP([✅ 返回响应])
T2 -->|不匹配| T3{Tier 3: Legacy Dispatcher\nOpenHuman 旧路由?}
T3 -->|匹配| LEGACY[调用 Legacy Handler]
LEGACY --> RESP
T3 -->|不匹配| ERR_UNKNOWN[错误: unknown method\n⚠️ warning 日志]
style REQ fill:#1a2030,stroke:#3b82f6,color:#93c5fd
style RESP fill:#1a2e1a,stroke:#22c55e,color:#86efac
style ERR_SCHEMA fill:#2e1010,stroke:#ef4444,color:#fca5a5
style ERR_UNKNOWN fill:#2e1010,stroke:#ef4444,color:#fca5a5
style T1 fill:#2a1f3a,stroke:#a855f7,color:#d8b4fe
style T2 fill:#2a1f3a,stroke:#a855f7,color:#d8b4fe
style T3 fill:#2a1f3a,stroke:#a855f7,color:#d8b4fe
Mascot 桌面伴侣状态机
桌面 Mascot 是一个有状态的动画角色,根据系统状态(空闲/思考/说话/监听/错误)切换动画和表情。状态由 companion_state 事件桥驱动。
stateDiagram-v2
[*] --> Idle : 应用启动
Idle --> Listening : 检测到唤醒词\n/ 用户点击
Idle --> Thinking : 收到 autofetch 通知\n/ 后台任务触发
Idle --> Speaking : TTS 播放开始
Listening --> Thinking : STT 识别完成\n发送消息
Listening --> Idle : 超时无输入\n/ 取消
Thinking --> Speaking : LLM 生成完成\n开始 TTS
Thinking --> Idle : 静默回答(文字)
Thinking --> Error : 推理错误\n/ 超时
Speaking --> Idle : TTS 播放完毕
Speaking --> Listening : 打断检测\n用户重新发言
Error --> Idle : 错误消除\n/ 重试
note right of Thinking
tokenjuice 压缩中
工具调用执行中
子 Agent 运行中
end note
note right of Speaking
唇形同步激活
ElevenLabs TTS
动画播放
end note
并发状态:Mascot 状态机与 Agent 循环、Autofetch 循环 并发运行,通过
companion_state 事件桥(Socket.IO 七桥之一)解耦,前端只消费状态事件,不直接调用后端逻辑。运行时并发结构总览
OpenHuman 运行时由多个 tokio 异步任务并发运行,彼此通过 Event Bus 和 Socket.IO 解耦通信。
flowchart LR
subgraph TOKIO[tokio 异步运行时]
direction TB
SIO_SERVER[Socket.IO 服务器\n持续监听前端连接]
AGENT[Agent 引擎\n按需启动 per session]
SCHEDULER[Scheduler Gate\n20min autofetch 循环]
CHANNEL_SUP[Channel 监听器\nSlack/Email/Telegram…]
INGEST_Q[IngestionQueue\n后台内存写入]
HEALTH[心跳 + 健康检查\nPrometheus 指标]
end
subgraph BUS[Event Bus]
direction LR
EVT_CHAT[chat events]
EVT_VOICE[voice events]
EVT_NOTIF[notification events]
EVT_SYNC[sync events]
EVT_COMPANION[companion_state]
end
AGENT <-->|publish/subscribe| BUS
SCHEDULER <-->|publish/subscribe| BUS
CHANNEL_SUP <-->|publish/subscribe| BUS
INGEST_Q <-->|subscribe| BUS
SIO_SERVER <-->|7 async bridges| BUS
subgraph FRONTEND[React 前端]
CHAT_UI[Chat UI]
MASCOT_UI[Mascot]
STATUS_UI[状态栏]
end
SIO_SERVER <-->|WebSocket| FRONTEND
style TOKIO fill:#0d1a0d,stroke:#22c55e,color:#86efac
style BUS fill:#1a1030,stroke:#a855f7,color:#d8b4fe
style FRONTEND fill:#0d1a30,stroke:#3b82f6,color:#93c5fd