产品架构
本文是 NeoMind 主项目(camthink-ai/NeoMind)的技术架构深度参考。读完后你应能定位任何功能在哪一层、哪个 crate,并理解进程与并发边界。
面向用户/决策者的非技术架构(产品由哪些部分组成、数据如何流动)见 产品介绍 — 什么是 NeoMind。
分层视图
┌──────────────────────────────────────────────────────────────┐
│ Desktop App / Web UI │
│ React 18 + TypeScript │
├──────────────────────────────────────────────────────────────┤
│ Tauri 2.x / Browser │
└────────────────────────┬─────────────────────────────────────┘
│ REST / WebSocket / SSE
▼
┌──────────────────────────────────────────────────────────────┐
│ API Gateway │
│ Axum Web Server │
│ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │
│ │ Auth │ │Devices │ │Automate│ │Messages│ │Extension│ │
│ └────────┘ └────────┘ └────────┘ └────────┘ └────────┘ │
└────────────────────────┬─────────────────────────────────────┘
│ Event Bus
┌──────────────┼──────────────┬────────────────┐
▼ ▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────────┐
│ Agent │ │ Devices │ │ Rules │ │ Extensions │
│ (LLM) │ │ (MQTT) │ │ (JSON) │ │ (Process) │
└────┬─────┘ └────┬─────┘ └────┬─────┘ └─────┬──────┘
│ │ │ │
└──────────────┴──────┬───────┴────────────────┘
▼
┌──────────────────┐
│ redb Storage │
│ (time-series) │
└──────────────────┘
Crate 布局
NeoMind 是一个 Rust workspace。每个 crate 有清晰单一的责任:
| Crate | 职责 |
|---|---|
| neomind-core | 核心 trait 与类型:EventBus、DataSourceId、LLM trait、能力探测 |
| neomind-api | Axum Web 服务器,HTTP / WebSocket / SSE handler,路由定义集中在 src/server/router.rs |
| neomind-agent | AI Agent:LLM 后端、工具调用、记忆系统、技能系统、调度器 |
| neomind-devices | 设备管理:MQTT / Webhook 适配器、设备注册、命令队列、草稿审批 |
| neomind-storage | redb 嵌入式存储:所有 *.redb 表的 schema 与访问层 |
| neomind-messages | 消息通知:7 种外部渠道(webhook/email/telegram/wecom/dingtalk/slack/feishu)+ 应用内消息中心 |
| neomind-rules | JSON 规则引擎:解析、执行、事件触发 |
| neomind-extension-sdk | 扩展 SDK:neomind_export! 宏、capability、ML 模型生命周期(公开 API) |
| neomind-extension-runner | 扩展进程宿主:隔离沙箱、FFI 桥、崩溃循环保护 |
| neomind-data-push | 数据推送:把遥测数据转发到外部 Webhook / MQTT |
| neomind-cli | 命令行入口(neomind 二进制) |
| neomind-cli-ops | CLI 命令实现:每个领域(device/rule/agent/...)一个模块,进程内分发 |
Tauri crate 在 workspace 外:
web/src-tauri/是独立 Cargo 项目,通过edge_api::start_server()调用 neomind-api。改 neomind-api 的 re-export 时要同步检查 Tauri 是否还在用。
进程模型
NeoMind 运行时由两类进程组成:
1. 主进程(neomind serve)
唯一进程,承载所有核心功能:
- Axum HTTP / WS / SSE 服务(端口 9375)
- MQTT Broker(端口 1883,内嵌)
- 事件总线
- Agent 执行池
- 规则引擎
- 存储(redb)
2. 扩展进程(每个扩展一个)
由 neomind-extension-runner 启动并监管:
- 每个扩展独立 OS 进程,进程级隔离
- 扩展动态库(
.so/.dylib/.dll)由 runner 进程经 FFI(C ABI)在进程内加载,runner 再通过 stdin/stdout 的 JSON IPC 与主进程通信 - 崩溃不影响主进程:runner 有崩溃循环保护(自动重启 + 最大重试次数 + 冷却期),达到重试上限后不再自动重启
- Capability 受控:扩展通过 SDK 的
ExtensionCapability(含Custom自定义名)声明所需能力,未声明的能力调用会被拒绝;runner 另对扩展进程施加资源限制(内存 / CPU)
┌─────────────────────────┐
│ 主进程 (neomind) │
│ ┌───────────────────┐ │
│ │ extension-runner │──┼──→ 进程 A (weather)
│ │ (监管子模块) │──┼──→ 进程 B (yolo-video)
│ └───────────────────┘──┼──→ 进程 C (ocr)
│ ↑ FFI │
│ 主进程内的 │
│ ExtensionProxy │
└─────────────────────────┘
事件总线
neomind-core::event_bus 是组件解耦的神经系统。所有跨模块通信走事件,不直接 import 对方。事件枚举 NeoMindEvent(crates/neomind-core/src/event.rs)共 45 个变体,按域分组:
| 域 | 事件(NeoMindEvent 变体) | 典型订阅者 |
|---|---|---|
| 设备 | DeviceOnline / DeviceOffline / DeviceTransportOnline / DeviceTransportOffline / DeviceMetric / DeviceCommandResult / DeviceDiscovered | 规则引擎、数据推送、仪表板 WS、自动接入 |
| 规则 | RuleEvaluated / RuleTriggered / RuleExecuted | 消息通知、审计 |
| 告警与消息 | AlertCreated / AlertAcknowledged / MessageCreated / MessageAcknowledged / MessageResolved | 应用内消息中心、通知渠道、Agent |
| IM | ImMessageReceived | IM 桥接会话 |
| Agent | AgentExecutionStarted / AgentThinking / AgentDecision / AgentProgress / AgentExecutionCompleted / AgentMemoryUpdated / AgentStreamChunk / AgentStreamEnd | 记忆系统、消息通知、Chat SSE |
| LLM 决策回路 | PeriodicReviewTriggered / LlmDecisionProposed / LlmDecisionExecuted | Agent 决策执行 |
| 工具 | ToolExecutionStart / ToolExecutionSuccess / ToolExecutionFailure | Agent 过程展示 |
| 扩展 | ExtensionOutput / ExtensionLifecycle / ExtensionCommandStarted / ExtensionCommandCompleted / ExtensionCommandFailed | 存储、仪表板 |
| 系统 | ModelDownloadProgress / SystemUpgradeProgress / DashboardUpdated / DataChanged / UserMessage / LlmResponse / Custom | 前端事件流(SSE/WS) |
订阅语义:发布订阅模型,多订阅者并行触发,单订阅者内串行处理;订阅者处理过慢时事件会被丢弃(丢弃计数可通过 /api/metrics 的 neomind_eventbus_dropped_total 观测)——所以订阅者里不要做慢操作,慢活先 spawn。
最核心的一条:DeviceMetric 是"主事件"——设备数据写入(含 MQTT / Webhook / 扩展虚拟指标)都会发布它,规则引擎、数据推送、仪表板 WS 都由它驱动。
完整枚举定义
// crates/neomind-core/src/event.rs
pub enum NeoMindEvent { /* 45 个变体,见上表;serde 按变体名序列化 */ }
以变体名为准:新增事件时同步更新本表。
一次数据写入的生命周期
以「LoRaWAN 温度传感器上报 23.5°C」为例,穿越整个架构的完整路径:
MQTT 消息到达 (rmqtt, :1883)
→ neomind-devices 适配器解析 + 设备匹配(未知设备 → 草稿/自动接入)
→ 写入 neomind-storage(telemetry.redb,秒级时间戳)
→ 发布 NeoMindEvent::DeviceMetric 到事件总线
├→ neomind-rules:立即评估所有匹配规则(>30°C → notify 动作)
├→ 数据转换(neomind-api automation):input 解包 → JS 管道 → 派生指标再入库
├→ neomind-data-push:匹配推送目标 → 外部 Webhook / MQTT
└→ 仪表板 WebSocket:实时推送到订阅的图表
理解这条路径就能解释大部分行为:为什么规则是"写入即评估"(事件驱动)、为什么转换读的是已入库数据、为什么仪表板不需要轮询。
扩展加载时序
.nep 从安装到可用的完整序列(neomind-core/src/extension/loader/isolated.rs):
安装:上传/市场下载 → 解包校验(zip 结构 + ABI 3 + 平台二进制)→ 落盘 extensions/<id>/
启动:API spawn → neomind-extension-runner 子进程
→ runner dlopen 平台二进制 → 校验 neomind_extension_abi_version() == 3
→ JSON 桥握手(hello → capabilities → descriptor)
→ 主进程注册扩展指标/命令/组件 → 状态 Running
崩溃:进程退出/挂起(liveness Ping 超时)→ 自动重启(最多 3 次,间隔 5s)
→ 超限 → 状态 Crashed,停止自动重启并经通知渠道告警
扩展 ABI
扩展用 Rust 写,但编译产物与主进程是两个二进制,靠 FFI 桥接:
neomind_export!宏(在 SDK 里):把Extensiontrait impl 自动导出为 C ABI 入口(extern "C"函数,如neomind_extension_abi_version/neomind_extension_metadata/neomind_extension_execute_command_json)- 主进程的 isolated loader 启动 runner 进程 → runner 加载扩展动态库并调用约定入口 → 主进程用
ExtensionProxy(neomind-core::extension::proxy)包装与扩展进程的全部通信 - 数据用 serde JSON 序列化跨 FFI / IPC 边界(metric、command、配置)
Capability 系统:扩展通过 SDK 的 ExtensionCapability 枚举声明所需能力(内置 20 种 + Custom(String) 自定义名,如 network / filesystem:read / ml-model),平台在运行时校验每次能力调用,未声明的能力调用会被拒;runner 另对扩展进程施加资源限制(内存上限 / CPU 亲和 / nice 值,见 runner 的 resource_limits.rs)。
详细 macro 用法与生命周期见 Extension SDK。
存储层
NeoMind 用 redb(纯 Rust 嵌入式 KV 数据库,类似 lmdb)。所有数据在 data/ 目录下:
| 表 | 内容 |
|---|---|
telemetry.redb | 时序遥测(所有设备的指标历史)。这是最大的表,按 (device_id, metric, timestamp) 索引 |
devices.redb | 设备注册表(id、name、type、adapter、config) |
dashboards.redb | 仪表板定义(布局、组件配置) |
rules.redb | 规则定义(JSON 配置、启用状态) |
agents.redb | Agent 定义(prompt、schedule、resources) |
messages.redb | 消息投递记录 |
sessions.redb | AI Chat 会话历史 |
llm_backends.redb | LLM 后端配置 |
settings.redb | 系统设置 |
users.redb / api_keys.redb | 用户与 API Key |
extensions.redb | 已安装扩展清单 |
instances.redb | 多实例后端注册 |
无外部数据库。备份 = 停服 + 拷贝 data/ 目录。迁移到新机器相同路径即可。
存储访问全部经 neomind-storage crate 的 repository 模式,不允许其他 crate 直接打开 redb 表。
并发与线程模型
Tokio 异步运行时,多线程调度器。
并发上限(防止雪崩):
| 信号量 | 上限 | 作用域 |
|---|---|---|
| 全局执行信号量 | 10 | 整个主进程同时运行的 Agent 执行数 |
| 每 LLM 后端信号量 | 2 | 同一后端并发请求数(防 429) |
| 工具并发信号量 | 6 | 同时运行的工具调用数 |
Agent 执行保护:
- 全局 5 分钟(300s)超时,包整个
execute_internal - RAII
StatusGuard:无论 panic / 超时 / drop,状态都会从Executing复位为Active,防止卡死 - 调度器跳过的执行(并发上限内未抢到)不推进
next_execution,下一 tick 重试
WebSocket / SSE:每个前端连接一个 task,订阅事件总线,断开后自动重连与补数据。
关键不变量(写代码时牢记)
- 后端 snake_case / 前端 camelCase:所有 API 响应必须经
fromDashboardDTO()(web/src/store/persistence/types.ts)转换。新代码从 API 加载仪表板必须用这个函数。 - Ollama 用
/api/chat,不是/v1/chat/completions。 - DataSourceId 格式:
{type}:{id}:{field},解析与生成都在neomind-core。 - 多模态能力层次:用户覆盖 > 运行时探测 > LiteLLM 注册表 > 启发式 > false(详见 memory 与
crates/neomind-core/src/llm/registry.rs)。 - 扩展组件渲染:不要给
ComponentRenderer加mountedRef模式(React 18 StrictMode 双挂载会断);不要在renderDashboardComponent里包 ErrorBoundary。
下一步
- 写扩展 → Extension SDK + 扩展开发实战
- 加 HTTP API → REST API 参考
- 调代码 → 各 crate 的
lib.rs顶部注释与docs/guides/对应模块文档
最后更新: 2026-09-09