exo 本地 AI 集群实战指南:从多设备自动组网、RDMA 雷雳到多 API 兼容与完整配置参考
本文以 exo 仓库的 README.md 为核心,系统梳理 exo"把多台设备变成一台 AI 集群"的完整使用方法:包括 macOS 与 Linux 的源码运行流程、macOS 26.2 上启用 RDMA 的全部步骤、环境变量与文件位置参考、四套 API 兼容接口的调用方式,以及 exo-bench 压测工具的参数说明。读完你可以独立完成从克隆仓库、构建 Dashboard、启动集群到通过 REST API 部署模型并发起推理的完整闭环,并理解每项配置在源码中的落点。
定位:把多台设备连接成一台 AI 集群
exo 的核心目标用仓库一句话概括:"Run frontier AI locally"——将你手里的所有设备连接成一个 AI 集群。它带来两件事:
- 跑单机放不下的模型:模型参数被切分到多台设备的内存中;
- 加设备反而更快:借助 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.py、claude.py、responses.py、ollama.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)。因此实际部署时建议以当前源码行为为准:使用 --namespace 或 EXO_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)。
启用步骤:
-
关机。
-
长按电源键 10 秒,直到出现启动菜单。
-
选择"Options"进入恢复模式(Recovery)。
-
在恢复界面打开 Utilities 菜单中的终端。
-
在终端输入并回车:
rdma_ctl enable -
重启 Mac。
之后 RDMA 即在 macOS 层启用,剩余工作由 exo 自动完成(macOS 侧的 RDMA 桥接检测逻辑可见 app/EXO/EXO/Services/ThunderboltBridgeDetector.swift)。
重要注意事项(README 原文归纳):
- 希望加入 RDMA 集群的设备必须两两直连所有其他集群设备(全互联拓扑)。
- 线缆必须支持 TB5。
- Mac Studio 上不能使用紧邻以太网口的 Thunderbolt 5 口。
- 从源码运行时,请使用 tmp/set_rdma_network_config.sh 脚本——它会禁用 Thunderbolt Bridge 并为每个 RDMA 口配置 DHCP。
- 不同 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.py、src/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-bench(bench/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 |
按 ring、jaccl 或 both 过滤 |
both |
--sharding |
按 pipeline、tensor 或 both 过滤 |
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)与峰值内存。
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.jpeg 与 docs/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。
进一步深入的路径:
- API 全量参考:docs/api.md
- 集群拓扑与放置:src/exo/shared/topology.py、src/exo/master/placement.py
- 网络层:src/exo/routing/router.py、Rust 网络绑定 rust/exo_rs/src/networking.rs
- 多节点集成测试(1/2/4 节点与韧性场景):tests/test_1node.py、tests/test_2node.py、tests/test_4node.py、tests/test_resilience.py
- 贡献指南:CONTRIBUTING.md
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00

