
说明:本手册为初稿(2026-10-11,pi 起草、Bot 验收)。命令与代码尚未在 Mac 上实测,凡标“待核实”的地方请以 https://earendil.com/posts/pi-durable/ 和 https://pi.dev/packages/pi-agents 官方文档为准。
本地持久化智能体操作手册
Mac mini M4(16GB,本机)+ Mac mini M6(32GB,LLM 服务器)—— 基于 pi + pi-durable 搭建
1. 目标与架构
1.1 项目目标
构建一个本地持久化智能体:在 Mac mini M4(本机)上长期运行,通过雷雳(Thunderbolt)线直连的 Mac mini M6 进行本地大语言模型(LM)推理,具备对话历史、任务状态和跨会话连续性的能力。核心组件是 Pi(Earendil Works 开发的轻量级智能体框架)、pi-durable(用于长生命周期、崩溃恢复的命名代理)以及 pi-agent CLI(零代码启动与管理)。
1.2 系统架构
1 | ┌──────────────────────────────────────┐ |
两台机器分工
- M4(本机,16GB):跑 pi、pi-durable 服务/子代理、文件、微信入口等;不跑大模型。16GB 内存可选跑 4B-8B Q4 小模型兜底。
- M6(计划中,32GB):专用 LLM 服务器,模型通过雷雳网卡 IP 提供 OpenAI 兼容接口。
关键技术栈
- Pi — 核心框架,提供 CLI、RPC、持久化会话管理
- pi-durable — 命名代理,支持崩溃恢复、长生命周期任务
- pi-agent CLI — 零代码启动,
/llama、/model、/llama等命令 - llama.cpp llama-server — M6 上的本地 GGUF 模型加载器(原文称 router)
- Ollama / LM Studio — M6 上的本地模型来源(可选)
1.3 模型选型(按 M6 的 32GB 统一内存)
| 档位 | 选择 | 说明 |
|---|---|---|
| 最高档 | 约 30B 级 MoE Q4(如 Qwen3-30B-A3B 这一类) | 32GB 统一内存可承载的上限附近 |
| 均衡档 | 14B 稠密 Q4-Q6 | 速度与质量兼顾 |
| 上下文 | 32k | 需预留 KV cache |
| 系统预留 | 约 6-8GB | 不要全部用于模型 |
- M4(16GB)可选跑 4B-8B Q4 小模型兜底。
- 待核实:具体模型的实际内存占用随量化版本、上下文长度与机型而异,务必在 M6 上实测后再定档。
2. 准备阶段
安装清单(按机器拆分)
| 机器 | 要做的事 |
|---|---|
| M4 | 配置雷雳网桥 IP(示例 10.0.0.1);安装 Pi / pi-durable / pi-agent;配置 models.json 指向 M6;启动 pi -c 持久化代理 |
| M6 | 配置雷雳网桥 IP(示例 10.0.0.2);安装 llama.cpp(或 Ollama / LM Studio);下载 GGUF 模型;启动模型服务并绑定雷雳 IP |
2.1 雷雳网桥组网配置(新增)
- 用雷雳(Thunderbolt)线将 M4 与 M6 直连。
- 打开 macOS 系统设置 → 网络 → 雷雳网桥(英文界面为 “Thunderbolt Bridge”;中文名称待核实)。
- 手动设置 IP(以下为示例,可自选其他私有网段):
- M4:
10.0.0.1,子网掩码255.255.255.0(/24) - M6:
10.0.0.2,子网掩码255.255.255.0(/24)
- M4:
- 验证链路:
1
2
3
4
5
6
7# 在 M4 上 ping M6
ping 10.0.0.2
# 带宽测试:M6 上启动服务端
iperf3 -s
# M4 上测试
iperf3 -c 10.0.0.2iperf3需提前安装(如brew install iperf3,待核实)。- 待核实:雷雳网桥接口是否在插线后自动出现、是否需先配置再插线、不同 macOS 版本中的位置差异。
- 待核实:直连后的实际带宽(取决于机型的雷雳接口版本)。
2.2 安装 Pi 及相关包(M4 上做)
2.2.1 官方安装脚本(推荐)
1 | curl -fsSL https://pi.dev/install.sh | sh |
该脚本会将 Pi 安装到当前目录,并自动配置 ~/.pi/agent/ 工作区。
2.2.2 npm 全局安装(离线/无网络环境)
1 | npm install -g --ignore-scripts @earendil-works/pi-coding-agent |
2.2.3 核心包安装
1 | # pi-durable 包 |
2.3 在 M6 上部署模型服务(M6 上做)
Pi 默认不包含任何本地模型。要使用本地 GGUF 模型,需要在 M6 上启动 llama-server(原文称 router)并指向 ~/models 目录。
启动模型服务(单模型模式)(在 M6 上,绑定雷雳网卡 IP)1
2
3
4
5
6
7
8llama-server \
--models-dir ~/models \
--no-models-autoload \
--jinja \
--host 10.0.0.2 \
--port 8080 \
-ngl 999 \
-c 32768
--models-dir ~/models:存放本地 GGUF 文件(如Qwen3-30B-A3B-Q4_K_M.gguf,30B 级 MoE,支持工具调用)--no-models-autoload:禁止自动加载未显式选择的模型--jinja:启用兼容的聊天模板和工具调用(对 pi-durable 代理有利)--host 10.0.0.2:M6 雷雳网卡 IP(示例)。需绑定雷雳 IP(或 0.0.0.0),M4 才能经雷雳访问;绑定0.0.0.0与雷雳 IP 的区别见 7.5--port 8080:默认端口,可自行调整-ngl 999:将几乎所有层移至 GPU(Apple Silicon 上的 Metal GPU / MPS)-c 32768:上下文窗口大小(按 1.3 选型预留 KV cache;可省略以使用模型原生上下文)
验证服务可达性(在 M4 上)1
2curl http://10.0.0.2:8080/health
curl http://10.0.0.2:8080/models
可选:Ollama / LM Studio(M6 上)
- Ollama:可通过环境变量指定监听地址(如
OLLAMA_HOST=10.0.0.2:11434,待核实)。 - LM Studio:在设置中指定监听地址为雷雳 IP(具体选项待核实)。
2.4 配置 models.json(M4 上做,本地模型注册)
M4 上的 pi 通过 models.json 指向 http://10.0.0.2:<端口>/v1(IP、端口均为示例)。Pi 使用 ~/.pi/agent/models.json 来注册本地模型;若使用 llama.cpp,请确保其模型已通过 /llama 命令加载(待核实:远程服务下的加载方式),然后在 M4 的 models.json 中添加对应条目。
示例 models.json(仅作参考,IP/端口为示例)1
2
3
4
5
6
7
8
9
10
11
12{
"providers": {
"llama": {
"baseUrl": "http://10.0.0.2:8080/v1",
"api": "openai-completions",
"apiKey": "ollama",
"models": [
{ "id": "Qwen3-30B-A3B-Q4_K_M" }
]
}
}
}
待核实:实际提供的
models.json格式可能因版本不同而略有差异,请根据pi list输出进行确认。
2.5 启动 pi-durable 命名代理(M4 上做)
1 | pi install npm:@earendil-works/pi-durable |
-c参数开启持久化会话,Pi 会在磁盘上保存状态(包括模型加载、工具调用历史、任务分支)。- 代理名称由
pi -c中的前缀决定(如agent-001),后续可通过pi -c agent-001继续运行。 (待核实:pi -c 原义是继续最近会话,命名续跑以官方文档为准)
3. 第一步:零代码 —— 安装 pi-agent 扩展并运行持久化子代理
3.1 安装 pi-agent CLI 扩展(M4 上)
1 | # 安装 pi-agent 扩展(真实包名待核实,以文档为准) |
3.2 启动持久化子代理
1 | pi -c agent-001 # 待核实 |
- 首次启动会自动加载
llama-server(如果未启动)并进入持久化模式。(待核实:llama-server现在运行在 M6 上,pi 能否自动加载远程服务待核实) - 代理名为
agent-001,后续续跑使用pi -c agent-001。 (待核实:pi -c 原义是继续最近会话,命名续跑以官方文档为准)
3.3 演示中断后续跑
1 | # 1. 启动代理 |
续跑机制(pi -c 续跑)1
2# 重新启动同名代理,Pi 会从上次保存的状态继续
pi -c agent-001 # 待核实
- Pi 会检测
~/.pi/agent/sessions/中的检查点,并恢复到上次保存的位置。 - 所有工具调用、对话历史、模型状态均保留。
注意:
pi -c续跑仅适用于同一进程的子代理。如果需要跨会话恢复,请使用pi -c <session-id>指定具体会话 ID。
3.5 Durable 注意事项(持久化操作安全性)
待核实:以下规则基于通用持久化系统设计,具体实现以
pi-durable官方文档为准。
- 只标可安全重跑的工具:只有幂等操作(查询数据库、读取文件、计算统计)才应标记为“可安全重跑”。如果工具执行后对外产生可见副作用(发消息、删文件、写库、创建订单),不要标成可安全重跑,避免中断恢复时重复执行导致数据不一致。
- 中断后交给模型判断:对于非幂等步骤(发消息、删文件、写库),在断点恢复时应由模型根据上下文判断是否已完成;若不确定,建议先查询恰当前状态再决定继续或回滚,而非盲目重跑。
- 外部来信去重:若外部渠道(如微信、邮件、API 回调)带有
requestId(或消息唯一 ID),应在进入 durable 代理前去重:先检查已处理列表,已存在则跳过,保证只处理一次;若无requestId,应由模型根据内容哈希或时间窗口判断是否已处理,避免重复回复。
4. 第二步:pi-durable 包写骨架 Node.js 常驻服务
4.1 项目结构
1 | manual/ |
注:该工程运行在 M4 上;
router.ts对应的llama-server实际部署在 M6 上。
4.2 核心实现思路
| 组件 | 职责 |
|---|---|
| router.ts | 启动 llama-server(或其他本地模型提供者),监听 /models 端点(在 M6 上) |
| index.ts | 启动 pi-durable 命名代理,绑定 /agent 命令(在 M4 上) |
| agent.ts | 持久化子代理:接收用户指令,通过 router 调用模型,记录任务状态 |
| models.json | 注册本地 GGUF 模型(通过 llama-server 或 Ollama) |
4.3 实现说明(待核实)
待核实:
piAgent、registerRouter、durableAgent.handle等 API 名称及参数疑似编造,需以@earendil-works/pi-durable官方文档为准。以下为参考思路:
- 通过
pi框架初始化实例(具体导出名待核实)。 - 创建持久化命名代理(替代
piAgent({ name: 'agent-001', persistence: true })的实际方法待查文档)。 - 配置本地模型路由(替代
registerRouter方法;通常通过环境变量或配置文件指定模型端点)。 - 实现消息处理循环(替代
durableAgent.handle方法;通常通过事件监听或命令接口获取用户输入)。 - 启动服务并保持运行(用于长期任务的典型方式)。
4.4 完整服务入口(待核实)
待核实:
pi.start()、agent.registerCommand、ctx.message等接口疑似编造,需以实际包文档为准。参考思路:
- 初始化
pi实例,注册/agent命令处理函数。 - 命令处理函数接收
ctx.message中的用户指令。 - 将指令转发到 durable 代理处理,返回结果。
- 启动
pi(替代pi.start()的实际方法待查文档)。
4.5 启动流程
1 | # 1. 在 M6 上启动模型服务(需先独立运行) |
5. 第三步:混合路由 —— 困难步骤退回 magpie 免费/套餐模型链
当某些任务难以由 pi-durable 代理单独完成时,可以通过 混合路由 将请求分流到外部模型链。pi-durable 代理本身可以调用 magpie 等免费/付费模型组合,以获得更强大的能力。
5.1 混合路由策略
| 任务类型 | 首选模型 | 备用模型 |
|---|---|---|
| 代码生成 | magpie(免费) |
gpt-4o(付费) |
| 复杂推理 | magpie(付费) |
本地 30B 级 MoE Q4(如 Qwen3-30B-A3B,70B 在 M6 32GB 上不可行) |
| 多模态处理 | magpie(图像) |
lm-studio(本地) |
| 长文本摘要 | magpie |
llama-3.2-1b |
5.2 实现示例(src/agent.ts 扩展)
1 | async function routeRequest(request: Request) { |
5.3 优缺点
- 优势:混合路由能结合免费模型的成本优势与本地模型的高效性能
- 劣势:增加路由逻辑复杂度,需管理模型切换状态
- 建议:优先使用混合路由处理“边界模糊”任务(如代码+分析复合),保持本地模型作为基准线
6. 最小原型场景
6.1 每晚整理知识库
场景:每天晚上将收到的文档、会议纪要、任务清单同步到本地知识库,供下班后快速检索。
实现(在 src/agent.ts 中)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21async function organizeKnowledge(basePath: string) {
const files = await fs.promises.readdir(basePath);
const organized = [];
for (const file of files) {
if (file.endsWith('.md')) {
const content = await fs.promises.readFile(file, 'utf-8');
organized.push({
id: file,
path: file,
content,
createdAt: new Date(),
});
}
}
// 写入索引文件
await fs.promises.writeFile(
basePath + '-index.json',
JSON.stringify(organized, null, 2)
);
return organized;
}
6.2 处理微信待办
场景:将微信待办列表导入本地,支持按日期/标签过滤、批量创建任务卡片。
实现(在 src/agent.ts 中)1
2
3
4
5
6
7
8
9
10
11async function syncWeChatTasks() {
const tasks = await fetchWeChatTasks(); // 模拟 API 调用
const processed = tasks.map(task => ({
id: task.id,
title: task.title,
priority: normalizePriority(task.priority),
dueDate: task.dueDate,
}));
await organizeKnowledge(processed);
return processed;
}
6.3 部署建议
| 步骤 | 操作 |
|---|---|
| 1 | 在 M6 上预装 Llama.cpp(llama-server)并下载本地 GGUF 模型 |
| 2 | 在 M4 上配置 ~/.pi/agent/models.json 注册模型,指向 M6 雷雳 IP |
| 3 | 在 M4 上启动 pi -c agent-001 启动持久化代理 (待核实:pi -c 原义是继续最近会话,命名续跑以官方文档为准) |
| 4 | 编写 src/agent.ts 实现核心逻辑 |
| 5 | 测试中断-续跑功能,确保状态持久化 |
| 6 | 逐步替换 magpie 等外部模型,实现混合路由 |
7. 常见问题排查
7.1 Pi 无法启动(M4)
- 检查
~/.pi/agent/sessions/是否存在 - 确认 M6 上
llama-server已启动、雷雳链路通(ping 10.0.0.2)且端口 8080 未被占用 - 运行
pi list查看已安装的包
7.2 模型未出现在 /model
- 确保 M6 上
llama-server已启动并通过/llama命令加载模型 - 检查 M4 的
models.json格式及baseUrl是否指向 M6 雷雳 IP - 尝试
pi -c agent-001 /llama列出可用模型 (待核实:pi -c 原义是继续最近会话,命名续跑以官方文档为准)
7.3 持久化失败(pi -c 续跑不恢复)
- 确认
~/.pi/agent/sessions/目录 - 检查
pi -c命令是否正确使用 - 确认代理名称与上次运行一致
7.4 雷雳链路不通(新增)
- 检查雷雳线两端是否插紧、接口是否正常
- 在 系统设置 → 网络 中确认出现“雷雳网桥”接口(中文名称待核实)
- 确认两台机器静态 IP 在同一网段、掩码正确(示例:M4
10.0.0.1、M610.0.0.2,均 /24) - 用
ping验证连通性,用iperf3(见 2.1)验证带宽 - 确认服务绑定的是雷雳 IP 而不是
127.0.0.1(见 7.5)
7.5 服务绑定 0.0.0.0 与雷雳 IP 的区别(新增)
0.0.0.0:监听所有网卡(Wi-Fi、以太网、雷雳等),M4 可经任一可达地址访问,但暴露面更大- 雷雳 IP(如
10.0.0.2,示例):仅监听雷雳网桥接口,更安全,直连场景推荐 127.0.0.1:仅本机回环,M4 无法访问- M4 的
models.json必须指向服务实际绑定的地址,否则连接失败
7.6 macOS 防火墙放行端口(新增)
- 系统设置 → 网络 → 防火墙,允许
llama-server(及测试用iperf3)的入站连接(具体选项名称与行为待核实) - 命令行方式:
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --add <可执行文件路径>等(待核实) - 排错时可临时关闭防火墙,仅限测试(待核实)