首页
/ exo 本地 AI 集群实战指南:从多设备自动组网、RDMA 雷雳到多 API 兼容与完整配置参考

exo 本地 AI 集群实战指南:从多设备自动组网、RDMA 雷雳到多 API 兼容与完整配置参考

2026-09-05 20:10:51作者:虞亚竹Luna

本文以 exo 仓库的 README.md 为核心,系统梳理 exo"把多台设备变成一台 AI 集群"的完整使用方法:包括 macOS 与 Linux 的源码运行流程、macOS 26.2 上启用 RDMA 的全部步骤、环境变量与文件位置参考、四套 API 兼容接口的调用方式,以及 exo-bench 压测工具的参数说明。读完你可以独立完成从克隆仓库、构建 Dashboard、启动集群到通过 REST API 部署模型并发起推理的完整闭环,并理解每项配置在源码中的落点。

exo 内置 Dashboard 集群视图:4 × M3 Ultra Mac Studio 加载 DeepSeek v3.1 与 Kimi-K2-Thinking

定位:把多台设备连接成一台 AI 集群

exo 的核心目标用仓库一句话概括:"Run frontier AI locally"——将你手里的所有设备连接成一个 AI 集群。它带来两件事:

  1. 跑单机放不下的模型:模型参数被切分到多台设备的内存中;
  2. 加设备反而更快:借助 Thunderbolt 5 上的 RDMA(README 称其为 day-0 支持),设备间通信延迟大幅下降,设备越多、张量并行带来的加速越明显。

README 的功能清单与对应实现证据如下:

功能 说明(来自 README) 源码佐证
自动设备发现 运行 exo 的设备自动互相发现,无需手动配置 路由层基于 gossipsub 订阅/发布,Router.create 传入身份、命名空间、监听端口与发现服务端口
RDMA over Thunderbolt 雷雳 5 上的 RDMA,设备间延迟显著降低 macOS 端通过 Recovery 模式 rdma_ctl enable 启用,见下文 RDMA 章节
拓扑感知自动并行 基于实时设备拓扑(资源 + 链路延迟/带宽)决定模型如何切分 src/exo/master/placement.py 负责放置决策
张量并行 官方给出 2 设备最高约 1.8x、4 设备最高约 3.2x 的加速(README 数据) src/exo/master/placement.py、测试见 src/exo/master/tests/test_placement.py
MLX 后端 使用 MLX 作为推理后端,MLX distributed 负责分布式通信 引擎实现位于 src/exo/worker/engines/mlx/
多 API 兼容 OpenAI Chat Completions、Claude Messages、OpenAI Responses、Ollama 适配器位于 src/exo/api/adapters/,含 chat_completions.pyclaude.pyresponses.pyollama.py
自定义模型 从 HuggingFace Hub 加载自定义模型 POST /models/add 端点,见下文 API 章节

从源码结构看,每台设备上的 exo 进程由 Node 组装,内部同时包含 Router(网络)、EventRouter(事件)、DownloadCoordinator(模型下载)、Worker(推理执行)、Master(放置与编排)与 API(HTTP 服务)六个角色。值得注意的一个设计是:每个节点都运行 Master 并参与选举src/exo/main.py 中注释明确写了"Every node participates in election"),集群通过选举选出当前 master 节点,API 请求由 master 统一编排。

内置 Dashboard

exo 自带一个内置 Dashboard,用于管理集群和与模型对话。运行后访问 http://localhost:52415/ 即可看到集群视图。上图为 4 × 512GB M3 Ultra Mac Studio 集群同时加载 DeepSeek v3.1 (8-bit) 与 Kimi-K2-Thinking (4-bit) 的截图(来自 README)。

Dashboard 是一个 Svelte 前端项目,位于 dashboard/,包含集群拓扑图(TopologyGraph.svelte)、聊天界面、HuggingFace 搜索结果等组件,构建产物会被主程序直接服务。

快速上手:从源码运行

方式一:Nix(macOS,最省事)

如果已安装 Nix,可以跳过大部分依赖步骤直接运行:

nix run .#exo

提示:若要接受 Cachix 二进制缓存(避免本地编译 Xcode Metal ToolChain),在 /etc/nix/nix.conf 中添加:

trusted-users = root    (或你的用户名)
experimental-features = nix-command flakes

然后重启 Nix 守护进程:sudo launchctl kickstart -k system/org.nixos.nix-daemon

方式二:macOS 手动安装

前置依赖:

  • Xcode(提供 MLX 编译所需的 Metal ToolChain)

  • Homebrew(macOS 包管理):

    /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
    
  • uv(Python 依赖管理)与 Node(构建 Dashboard):

    brew install uv node
    
  • Rust(构建 Rust 绑定,目前需要 nightly):

    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
    rustup toolchain install nightly
    
  • macmon(Apple Silicon 硬件监控)。README 特别提醒:请安装仓库指定的固定 fork 修订版,而不是 Homebrew 的 macmon(Homebrew 的 macmon 0.6.1 在 Apple M5 上仍会崩溃):

    cargo install --git https://github.com/vladkens/macmon \
      --rev a1cd06b6cc0d5e61db24fd8832e74cd992097a7d \
      macmon \
      --force
    

克隆仓库、构建 Dashboard、启动 exo:

# 克隆 exo
git clone https://gitcode.com/GitHub_Trending/exo8/exo

# 构建 Dashboard
cd exo/dashboard && npm install && npm run build && cd ..

# 运行 exo
uv run exo

启动后 Dashboard 与 API 均运行在 http://localhost:52415/

注意:macOS >= 26.2 的用户请继续阅读下文"在 macOS 上启用 RDMA"章节以启用该特性。

方式三:Linux 手动安装

前置依赖: uv、Node(18 或更高版本)、Rust(nightly)。

安装方式 1:系统包管理器(Ubuntu/Debian 示例)

# 安装 Node.js 和 npm
sudo apt update
sudo apt install nodejs npm

# 安装 uv
curl -LsSf https://astral.sh/uv/install.sh | sh

# 安装 Rust(rustup)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup toolchain install nightly

安装方式 2:Linux 上使用 Homebrew(可选)

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
brew install uv node

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup toolchain install nightly

注意macmon 是 macOS 专属包,Linux 上不需要。

随后执行与 macOS 相同的克隆与运行命令(cd exo/dashboard && npm install && npm run build,然后 uv run exo)。

Linux 用户的重要说明:exo 目前在 Linux 上以 CPU 方式运行,Linux GPU 支持仍在开发中;如果希望支持你的特定 Linux 硬件,可以到仓库 issue 中查找或创建 feature request。

命令行选项与文件位置(Linux)

README 列出两个常用启动参数:

  • --no-worker:不启动 worker 组件,仅运行协调节点(负责网络与编排,不执行推理)。适合没有足够 GPU 资源但网络良好的机器:

    uv run exo --no-worker
    
  • --legacy-daemon:以传统 SysV 风格的双 fork 后台守护进程方式运行,面向遗留 init 脚本;systemd 与 launchd 用户应以前台方式运行 exo,无需此参数:

    uv run exo --legacy-daemon
    

src/exo/main.py 的参数解析看,实际可用的参数面比 README 列出的更广,例如 --api-port(默认 52415)、--no-api--no-downloads--offline(对应 EXO_OFFLINE 环境变量)、--no-batch(禁用连续批处理)、--force-master--zenoh-port(默认 52414)与 --discovery-port(默认 52413,UDP 发现服务端口)、--fast-synch / --no-fast-synch(强制 MLX FAST_SYNCH 开/关)。其中设备发现的"零配置"正是由 UDP 52413 上的发现服务与 gossipsub 组网共同实现的。

exo 在 Linux 上遵循 XDG Base Directory 规范:

内容 路径
配置文件 ~/.config/exo/(或 $XDG_CONFIG_HOME/exo/
数据文件 ~/.local/share/exo/(或 $XDG_DATA_HOME/exo/
缓存文件 ~/.cache/exo/(或 $XDG_CACHE_HOME/exo/
日志文件 ~/.cache/exo/exo_log/(自动轮转)
自定义模型卡 ~/.local/share/exo/custom_model_cards/

可通过设置对应 XDG 环境变量覆盖上述位置。这些路径的实现集中在 src/exo/shared/constants.py:非 Linux 平台统一落在 ~/.exo;另外该文件还暴露了一个 README 未提及的 EXO_HOME 环境变量——一旦设置,所有目录都会重定位到 $HOME/<EXO_HOME> 之下,便于多实例隔离。

macOS App

exo 提供一个在 Mac 后台运行的原生应用(SwiftUI 工程位于 app/EXO/)。

要求 macOS Tahoe 26.2 或更高版本。 可通过 Homebrew 安装最新构建:

brew install --cask exo

应用首次运行时会请求修改系统设置的权限并安装一个新的 Network profile(README 说明相关体验正在改进中)。

自定义命名空间实现集群隔离:

macOS App 支持自定义命名空间,将你的 exo 集群与同一网络上的其他集群隔离开。典型场景:

  • 在同一网络中运行多个相互独立的 exo 集群
  • 将开发/测试集群与生产集群隔离
  • 防止设备误加入其他集群

配置方式:在 App 的 Advanced 设置中配置,或从源码运行时设置 EXO_LIBP2P_NAMESPACE 环境变量。命名空间会在启动时打印到日志,方便排查。

从源码结构看,这里有一个需要注意的演进:src/exo/main.py 中,若检测到 EXO_LIBP2P_NAMESPACE 会被直接报错,提示改用 EXO_ZENOH_NAMESPACE;同时命令行提供 --namespace 参数(默认取程序版本号,不同命名空间的节点互不连接,见 src/exo/main.py)。因此实际部署时建议以当前源码行为为准:使用 --namespaceEXO_ZENOH_NAMESPACE 来做集群隔离。

卸载 macOS App:

推荐通过 App 自身卸载:点击菜单栏图标 → Advanced → Uninstall,可干净移除所有系统组件。如果 App 已被删除,可运行独立卸载脚本:

sudo ./app/EXO/uninstall-exo.sh

它会移除:

  • Network setup LaunchDaemon
  • 网络配置脚本
  • 日志文件
  • "exo" 网络位置

注意:还需手动在 系统设置 → 通用 → 登录项 中移除 EXO。

在 macOS 上启用 RDMA

RDMA 是 macOS 26.2 新增的能力,适用于所有带 Thunderbolt 5 的 Mac(M4 Pro Mac Mini、M4 Max Mac Studio、M4 Max MacBook Pro、M3 Ultra Mac Studio)。

启用步骤:

  1. 关机。

  2. 长按电源键 10 秒,直到出现启动菜单。

  3. 选择"Options"进入恢复模式(Recovery)。

  4. 在恢复界面打开 Utilities 菜单中的终端。

  5. 在终端输入并回车:

    rdma_ctl enable
    
  6. 重启 Mac。

之后 RDMA 即在 macOS 层启用,剩余工作由 exo 自动完成(macOS 侧的 RDMA 桥接检测逻辑可见 app/EXO/EXO/Services/ThunderboltBridgeDetector.swift)。

重要注意事项(README 原文归纳):

  1. 希望加入 RDMA 集群的设备必须两两直连所有其他集群设备(全互联拓扑)。
  2. 线缆必须支持 TB5。
  3. Mac Studio 上不能使用紧邻以太网口的 Thunderbolt 5 口
  4. 从源码运行时,请使用 tmp/set_rdma_network_config.sh 脚本——它会禁用 Thunderbolt Bridge 并为每个 RDMA 口配置 DHCP。
  5. 不同 macOS 版本的 RDMA 端口可能互相发现失败:所有设备的系统版本必须完全一致(连 beta 版本号都要一致)。

环境变量参考

exo 支持的环境变量及默认值(与 README 表格一致,并在 src/exo/shared/constants.py 中逐一对应实现):

变量 说明 默认值
EXO_DEFAULT_MODELS_DIR 模型下载与缓存的默认目录,永远位于可写目录列表首位 ~/.local/share/exo/models(Linux)或 ~/.exo/models(macOS)
EXO_MODELS_DIRS 冒号分隔的额外可写模型目录。按顺序检查,第一个有足够剩余空间的目录被使用
EXO_MODELS_READ_ONLY_DIRS 冒号分隔的只读目录,用于查找已预下载的模型(如 NFS 挂载、共享存储)。此处的模型不可删除
EXO_OFFLINE 无互联网连接运行(仅使用本地模型) false
EXO_ENABLE_IMAGE_MODELS 启用图像模型支持 false
EXO_LIBP2P_NAMESPACE 自定义命名空间,用于集群隔离 无(见上文:当前源码已迁移至 EXO_ZENOH_NAMESPACE
EXO_FAST_SYNCH 控制 MLX_METAL_FAST_SYNCH 行为(面向 JACCL 后端) Auto
EXO_TRACING_ENABLED 启用分布式追踪做性能分析 false

示例用法:

# 使用 NFS 挂载的预下载模型(只读)
EXO_MODELS_READ_ONLY_DIRS=/mnt/nfs/models:/opt/ai-models uv run exo

# 把模型下载到外置 SSD(满时回退默认目录)
EXO_MODELS_DIRS=/Volumes/ExternalSSD/exo-models uv run exo

# 离线模式运行
EXO_OFFLINE=true uv run exo

# 启用图像模型
EXO_ENABLE_IMAGE_MODELS=true uv run exo

# 使用自定义命名空间隔离集群
EXO_LIBP2P_NAMESPACE=my-dev-cluster uv run exo

目录选择逻辑可以从 constants.py 直接读出:只读目录列表与可写目录列表同时解析,若某目录同时出现在两个列表中则按只读处理;可写列表 = 默认目录 + EXO_MODELS_DIRS 中剔除只读目录后的其余项,与表格中"先默认、再按序找空间"的描述一致。离线模式与下载校验的行为有专门测试覆盖:src/exo/download/tests/test_offline_mode.pysrc/exo/download/tests/test_model_dirs.py

使用 API:四步完成模型部署与推理

exo 同时暴露多套兼容接口,方便直接复用现有工具链:

  • OpenAI Chat Completions API/v1/chat/completions
  • Claude Messages API/v1/messages
  • OpenAI Responses API/v1/responses
  • Ollama API/ollama/api/...,兼容 OpenWebUI 等工具)

下面以一个小模型(mlx-community/Llama-3.2-1B-Instruct-4bit)为例,走一遍"预览放置 → 建实例 → 等待就绪 → 聊天 → 删除"的完整流程。

第 1 步:预览实例放置

/instance/previews 会返回该模型的所有有效放置方案:

curl "http://localhost:52415/instance/previews?model_id=llama-3.2-1b"

示例响应:

{
  "previews": [
    {
      "model_id": "mlx-community/Llama-3.2-1B-Instruct-4bit",
      "sharding": "Pipeline",
      "instance_meta": "MlxRing",
      "instance": {...},
      "memory_delta_by_node": {"local": 729808896},
      "error": null
    }
    // ...可能还有更多放置方案...
  ]
}

sharding 表明切分策略(Pipeline/Tensor),memory_delta_by_node 给出每个节点的内存增量。挑选一个方案,例如取第一个无错误项:

curl "http://localhost:52415/instance/previews?model_id=llama-3.2-1b" | jq -c '.previews[] | select(.error == null) | .instance' | head -n1

第 2 步:创建模型实例

把第 1 步拿到的放置方案放入 instance 字段,POST 到 /instance(完整请求体需匹配 CreateInstanceParams 类型定义):

curl -X POST http://localhost:52415/instance \
  -H 'Content-Type: application/json' \
  -d '{
    "instance": {...}
  }'

示例响应:

{
  "message": "Command received.",
  "command_id": "e9d1a8ab-...."
}

创建是异步命令,发推理请求前要先等 API 看到新实例。轮询 /instance/await(SSE 流):

curl -N "http://localhost:52415/instance/await?model_id=mlx-community/Llama-3.2-1B-Instruct-4bit"

该端点返回 SSE 流:成功时发出 "type": "ready" 及匹配的实例;超时则发出 "type": "timeout"。默认无限等待,可设 timeout_seconds 为正值限定等待时长。这些端点在 src/exo/api/main.py 中注册。

第 3 步:发送聊天补全

/v1/chat/completions 发起与 OpenAI 格式一致的 POST:

curl -N -X POST http://localhost:52415/v1/chat/completions \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "mlx-community/Llama-3.2-1B-Instruct-4bit",
    "messages": [
      {"role": "user", "content": "What is Llama 3.2 1B?"}
    ],
    "stream": true
  }'

第 4 步:删除实例

用完按 ID 删除(实例 ID 可通过 /state/instance 端点查询):

curl -X DELETE http://localhost:52415/instance/YOUR_INSTANCE_ID

Claude Messages API

/v1/messages 端点使用 Anthropic 的 Messages 格式:

curl -N -X POST http://localhost:52415/v1/messages \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "mlx-community/Llama-3.2-1B-Instruct-4bit",
    "messages": [
      {"role": "user", "content": "Hello"}
    ],
    "max_tokens": 1024,
    "stream": true
  }'

OpenAI Responses API

/v1/responses 端点使用 OpenAI Responses 格式:

curl -N -X POST http://localhost:52415/v1/responses \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "mlx-community/Llama-3.2-1B-Instruct-4bit",
    "messages": [
      {"role": "user", "content": "Hello"}
    ],
    "stream": true
  }'

Ollama API

面向 OpenWebUI 等工具的 Ollama 兼容端点:

# Ollama 聊天
curl -X POST http://localhost:52415/ollama/api/chat \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "mlx-community/Llama-3.2-1B-Instruct-4bit",
    "messages": [
      {"role": "user", "content": "Hello"}
    ],
    "stream": false
  }'

# 列出模型(Ollama 格式)
curl http://localhost:52415/ollama/api/tags

从 HuggingFace 加载自定义模型

curl -X POST http://localhost:52415/models/add \
  -H 'Content-Type: application/json' \
  -d '{
    "model_id": "mlx-community/my-custom-model"
  }'

安全提示:config 中需要 trust_remote_code 的自定义模型必须显式启用(默认 false)。只有在你信任该模型的远程代码执行能力时才开启。模型从 HuggingFace 拉取后,以自定义模型卡的形式保存在本地(Linux 路径见上文 ~/.local/share/exo/custom_model_cards/)。

其他常用端点

  • 列出所有模型:curl http://localhost:52415/models
  • 仅列出已下载模型:curl http://localhost:52415/models?status=downloaded
  • 搜索 HuggingFace:curl "http://localhost:52415/models/search?query=llama&limit=10"
  • 查看实例 ID 与部署状态:curl http://localhost:52415/state

更完整的端点参考见 docs/api.md,端点注册集中在 src/exo/api/main.py,各协议适配与响应类型分别位于 src/exo/api/adapters/src/exo/api/types/,对应的行为测试(流式输出、取消、实例删除清理等)见 src/exo/api/tests/

基准测试:exo-bench 与真实集群数据

exo-benchbench/exo_bench.py)用于在不同放置配置下测量模型 prefill 与 token 生成速度,帮助你优化模型性能、验证改进。

前置条件

  • 各节点应已以 uv run exo 运行;
  • 工具通过 /bench/chat/completions 端点发起压测(注册于 src/exo/api/main.py)。

基本用法:

uv run bench/exo_bench.py \
  --model Llama-3.2-1B-Instruct-4bit \
  --pp 128,256,512 \
  --tg 128,256

关键参数:

参数 说明 默认
--model 要压测的模型(短 ID 或 HuggingFace ID) -
--pp prompt 长度提示(逗号分隔整数) -
--tg 生成长度(逗号分隔整数) -
--max-nodes 限制放置方案使用的节点数 4
--instance-meta ringjacclboth 过滤 both
--sharding pipelinetensorboth 过滤 both
--repeat 每种配置的重复次数 1
--warmup 每个放置方案的预热次数 0
--json-out 结果输出文件 bench/results.json

带过滤条件的示例:

uv run bench/exo_bench.py \
  --model Llama-3.2-1B-Instruct-4bit \
  --pp 128,512 \
  --tg 128 \
  --max-nodes 2 \
  --sharding tensor \
  --repeat 3 \
  --json-out my-results.json

工具会为每种配置输出 prompt 每秒 token 数(prompt_tps)、生成每秒 token 数(generation_tps)与峰值内存。

4 × M3 Ultra Mac Studio 张量并行 + RDMA 下 Qwen3-235B (8-bit) 的基准测试截图

README 的 Benchmarks 一节还收录了 Jeff Geerling 文章中的另外两组测试截图:DeepSeek v3.1 671B (8-bit) 与 Kimi K2 Thinking (原生 4-bit) 在 4 × M3 Ultra Mac Studio(张量并行 + RDMA)上的表现,可分别查看 docs/benchmarks/jeffgeerling/mac-studio-cluster-ai-full-2-deepseek-3.1-671b.jpegdocs/benchmarks/jeffgeerling/mac-studio-cluster-ai-full-3-kimi-k2-thinking.jpeg

硬件加速器支持

  • macOS:exo 使用 GPU(MLX/Metal 后端)。
  • Linux:当前以 CPU 运行,GPU 支持在开发中。README 邀请社区对目标硬件平台在 issue 中发起 feature request 并点赞排序优先级。

小结与延伸入口

回到 README 的主线:exo 的卖点链路是"零配置发现(UDP 发现 + gossipsub 组网)→ 全互联 TB5 直连 + RDMA 降低跨机延迟 → 拓扑感知的自动切分(Pipeline/Tensor)→ 多协议 API 直接对接现有工具"。文中所有命令与路径均以当前仓库为准,几个关键限制需要牢记:macOS App 需要 Tahoe 26.2+;RDMA 要求设备两两直连且系统版本完全一致;Linux 目前仅 CPU;命名空间隔离在当前源码中应使用 --namespace/EXO_ZENOH_NAMESPACE

进一步深入的路径:

登录后查看全文
热门项目推荐
相关项目推荐