跳到主要内容

产品架构

本文是 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 与类型:EventBusDataSourceIdLLM trait、能力探测
neomind-apiAxum Web 服务器,HTTP / WebSocket / SSE handler,路由定义集中在 src/server/router.rs
neomind-agentAI Agent:LLM 后端、工具调用、记忆系统、技能系统、调度器
neomind-devices设备管理:MQTT / Webhook 适配器、设备注册、命令队列、草稿审批
neomind-storageredb 嵌入式存储:所有 *.redb 表的 schema 与访问层
neomind-messages消息通知:7 种外部渠道(webhook/email/telegram/wecom/dingtalk/slack/feishu)+ 应用内消息中心
neomind-rulesJSON 规则引擎:解析、执行、事件触发
neomind-extension-sdk扩展 SDK:neomind_export! 宏、capability、ML 模型生命周期(公开 API)
neomind-extension-runner扩展进程宿主:隔离沙箱、FFI 桥、崩溃循环保护
neomind-data-push数据推送:把遥测数据转发到外部 Webhook / MQTT
neomind-cli命令行入口(neomind 二进制)
neomind-cli-opsCLI 命令实现:每个领域(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 对方。事件枚举 NeoMindEventcrates/neomind-core/src/event.rs)共 45 个变体,按域分组:

事件(NeoMindEvent 变体)典型订阅者
设备DeviceOnline / DeviceOffline / DeviceTransportOnline / DeviceTransportOffline / DeviceMetric / DeviceCommandResult / DeviceDiscovered规则引擎、数据推送、仪表板 WS、自动接入
规则RuleEvaluated / RuleTriggered / RuleExecuted消息通知、审计
告警与消息AlertCreated / AlertAcknowledged / MessageCreated / MessageAcknowledged / MessageResolved应用内消息中心、通知渠道、Agent
IMImMessageReceivedIM 桥接会话
AgentAgentExecutionStarted / AgentThinking / AgentDecision / AgentProgress / AgentExecutionCompleted / AgentMemoryUpdated / AgentStreamChunk / AgentStreamEnd记忆系统、消息通知、Chat SSE
LLM 决策回路PeriodicReviewTriggered / LlmDecisionProposed / LlmDecisionExecutedAgent 决策执行
工具ToolExecutionStart / ToolExecutionSuccess / ToolExecutionFailureAgent 过程展示
扩展ExtensionOutput / ExtensionLifecycle / ExtensionCommandStarted / ExtensionCommandCompleted / ExtensionCommandFailed存储、仪表板
系统ModelDownloadProgress / SystemUpgradeProgress / DashboardUpdated / DataChanged / UserMessage / LlmResponse / Custom前端事件流(SSE/WS)

订阅语义:发布订阅模型,多订阅者并行触发,单订阅者内串行处理;订阅者处理过慢时事件会被丢弃(丢弃计数可通过 /api/metricsneomind_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 里):把 Extension trait impl 自动导出为 C ABI 入口(extern "C" 函数,如 neomind_extension_abi_version / neomind_extension_metadata / neomind_extension_execute_command_json
  • 主进程的 isolated loader 启动 runner 进程 → runner 加载扩展动态库并调用约定入口 → 主进程用 ExtensionProxyneomind-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.redbAgent 定义(prompt、schedule、resources)
messages.redb消息投递记录
sessions.redbAI Chat 会话历史
llm_backends.redbLLM 后端配置
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)。
  • 扩展组件渲染:不要给 ComponentRenderermountedRef 模式(React 18 StrictMode 双挂载会断);不要在 renderDashboardComponent 里包 ErrorBoundary。

下一步


最后更新: 2026-09-09