最近终于抽时间继续折腾 Jetson Orin Nano。

一开始我的目标其实很简单:我想让它跑一个本地模型,然后慢慢变成一个可以长期运行的个人 AI 助手。

但实际做下来,这件事比"跑起来一个模型"复杂很多。因为一个真正可用的本地 AI 设备,不只是能回答一句话就结束了。它还需要:

  • 本地模型
  • 云端模型
  • 网络出口
  • 自动恢复
  • 服务管理
  • 模型切换
  • 代理配置
  • 断电恢复
  • 后续语音和视觉扩展

这次折腾下来,Jetson 基本已经从"刚装好的开发板",变成了一个初步可长期运行的 AI 小主机。


一、先从清理系统开始

刚开始检查磁盘的时候,发现系统空间占用比预期高很多。

最后排查到里面有不少历史遗留内容,包括:

  • 旧 Docker 镜像
  • 旧 llama.cpp Docker 环境
  • Docker overlay 数据
  • CUDA local repo
  • CUDNN local repo
  • TensorRT local repo

这些东西并不是全部没用,但很多属于"安装时留下来的本地缓存"或者"旧实验环境"。真正危险的是,模型文件一开始其实藏在 Docker overlay 里面。

如果直接清理 Docker,很可能会把本地模型一起删掉。

所以第一步并不是直接 prune,而是先确认:

  • 模型到底在哪里
  • 模型是不是唯一副本
  • CUDA / PyTorch / TensorRT 是否还依赖这些文件
  • Docker 镜像里有没有还需要保留的内容

最后成功把 Gemma 模型从 Docker 环境里迁移出来,保存到用户目录。

清理前后大概变化是:

  • 清理前:约 56GB used
  • 清理后:约 32GB used
  • 净释放:约 24GB

这一步的重点不是"删了多少",而是保住了真正重要的东西。


二、保住本地模型

这次保留下来的本地模型是 Gemma 4 E2B 的 GGUF 版本。

模型文件被保存到本机用户目录下,不再依赖 Docker overlay。这样以后 Docker、容器、旧镜像怎么删,都不会影响模型本体。

当前本地模型目录大致包含:

  • Gemma 4 E2B GGUF 主模型
  • mmproj 文件

总大小约 3.8GB。

这一步完成后,模型从"藏在 Docker 里面的不确定状态",变成了"明确、可管理、可备份的本地模型资产"。


三、修复 apt / dpkg

清理过程中还发现了一个隐藏问题:系统的 dpkg 状态数据库出过问题。

具体表现是:

  • apt 操作异常
  • dpkg -l 结果异常
  • /var/lib/dpkg/status 缺失或损坏

这类问题短期可能不影响已经运行的 AI 服务,但以后只要安装软件、更新依赖、装浏览器、装工具,就会出问题。

最后通过系统备份恢复了 dpkg status 数据库,并确认:

  • dpkg -l 正常
  • apt-get check 正常
  • 软件包数据库恢复

这一步算是本次项目里的隐藏大坑。如果不修,后面装 sing-box、浏览器、工具链都会很麻烦。


四、重新编译 llama.cpp

原来的 llama.cpp 是 Docker 里的旧环境。清理 Docker 后,本地只保留了模型,没有推理运行时。

所以接下来重新在 Jetson 上编译 llama.cpp,并启用 CUDA 支持。

最终生成了:

  • llama-server
  • llama-cli

并确认 CUDA / cuBLAS 能正常链接。

这个过程 CPU 一度满载,属于正常现象。因为它不是在"安装一个软件包",而是在 Jetson 上本地编译 C++ / CUDA 程序。

编译完成后,Jetson 拥有了原生运行 GGUF 模型的能力,不再依赖旧 Docker 镜像。


五、创建 local-gemma-agent

有了模型和 llama.cpp 后,下一步是把它包装成一个长期运行的本地服务。

本地 Gemma Agent 的目标是:

  • 在 Jetson 本机运行
  • 只监听 localhost
  • 提供 OpenAI-compatible API
  • 供 Hermes / Melody 或其他 Agent 调用
  • 不暴露到公网

服务地址:

http://127.0.0.1:18080/v1

健康检查:

http://127.0.0.1:18080/health

测试结果正常返回:

{"status":"ok"}

这意味着本地模型已经不只是"能跑",而是变成了一个本地 API 服务。


六、修复 GPU Offload,速度提升 6 倍

刚开始测试 Gemma 的时候,速度非常慢。

第一次测试大概只有:

3 token/s 左右

后来发现虽然 llama.cpp 识别到了 CUDA,但启动参数里没有真正把模型层 offload 到 GPU。

也就是说:

看起来有 CUDA
实际上主要在 CPU 跑

后来加上 GPU offload 参数后,速度提升到了:

20~26 token/s

大概是 6 倍提升

这一步非常关键。因为 Jetson Orin Nano 的价值就在于它有 GPU,如果模型主要跑 CPU,就浪费了硬件能力。


七、把 local-gemma-agent 交给 systemd 管理

手动启动模型有一个问题:

  • 终端关了可能会停
  • 重启后不会自动恢复
  • 断电后需要手动 SSH 进去启动

所以后面给 local-gemma-agent 创建了 systemd service。

最终实现:

  • systemd 管理
  • 开机自动启动
  • 异常退出自动重启
  • 使用普通用户运行
  • 绑定 127.0.0.1:18080
  • 保留 GPU offload 参数

现在系统重启后,本地 Gemma 服务会自动恢复。

这是从"开发环境"进入"服务环境"的重要一步。


八、搭建 Codex Coding Agent

除了本地模型,我还希望有一个更强的云端 coding agent。

本地 Gemma 适合:

  • 快速响应
  • 离线使用
  • 简单问答
  • 本地控制
  • Agent 路由

但复杂代码任务、本地项目分析、debug、架构修改,还是需要更强的模型。

所以我搭了一个 Codex Coding Agent。

过程里遇到了几个坑:

1. Codex 登录问题

Jetson 是 headless / 半 headless 环境,OAuth 登录会跳转 localhost。如果在另一台电脑浏览器打开登录链接,localhost 指向的是那台电脑,不是 Jetson。

后来尝试 device code,也因为网络原因失败。

最终解决方式是:

  • 在 Mac 上完成 Codex 登录
  • 找到本机 Codex 的 auth 文件
  • 复制到 Jetson 的独立 Codex 目录

这样绕开了 Jetson 上 OAuth callback / token exchange 的问题。

2. 错误代理导致 Codex 超时

一开始 Codex 进程读到了一个错误的代理地址。这个代理地址来自另一个网段,Jetson 实际上访问不到。

结果就是:

  • 认证看起来成功
  • 进入 Codex 界面
  • 但是模型请求一直 Working
  • WebSocket / HTTPS 都超时

后来把错误 proxy 配置移除,Codex 正常访问 GPT-5.5。

现在 Codex 已经可以作为 coding agent 使用。


九、给主 Agent 取名 Melody

我不想每个模型都像不同的人。

所以设计了一个统一的人格层:Melody

底层可以是:

  • Hermes
  • Gemma
  • Codex
  • MiniMax
  • 未来的语音模型
  • 未来的视觉模型

但用户面对的统一身份是 Melody。

也就是说:

人格 = Melody
能力 = 不同模型 / 不同 Agent

这比"每个模型一个人格"更自然。

Melody 的设定是:

  • 温和
  • 好说话
  • 有耐心
  • 技术能力强
  • 不会让用户觉得自己问了蠢问题
  • 会优先保护数据
  • 做危险操作前先解释风险
  • 喜欢把复杂项目拆成一步一步

这个人格已经写进 Hermes 的 SOUL 文件。主 Agent 之后会以 Melody 的身份运行。

不过 subagent 默认不会自动继承 SOUL 文件,这是 Hermes 的隔离设计。所以额外创建了一个 Melody subagent context snippet,未来需要子 Agent 保持同一语气时,可以显式传入。


十、配置 sing-box,让 Jetson 拥有自己的网络出口

之前 Jetson 依赖其他设备的代理,这不适合长期运行。

因为一个真正的 AI 小主机应该自己能处理网络访问,而不是依赖 Mac、Windows 或路由器临时配置。

所以这次安装并配置了 sing-box。

一开始尝试 TUN 模式,但 Jetson 当前内核 / netfilter 对相关规则支持不完整,导致 TUN 无法正常工作。

后来决定放弃 TUN,改用更稳定的显式代理架构:

  • SOCKS5: 127.0.0.1:1080
  • HTTP: 127.0.0.1:1081

这个选择反而更适合当前项目。

因为 Jetson 上真正需要代理的主要是:

  • Codex
  • 未来云端 API
  • 未来联网 Agent

而本地 Gemma、localhost 服务、Tailscale 等并不需要代理。

所以最终架构是:

需要访问外部 AI API 的程序
↓
127.0.0.1:1080 / 1081
↓
sing-box
↓
加拿大 / 美国节点
↓
目标服务

这比全局透明代理更可控,也更容易排查问题。


十一、限制节点为加拿大 / 美国

订阅里节点很多,包括多个地区。

但对 AI 服务来说,我更希望使用加拿大 / 美国出口。

原因是部分 AI 服务、模型功能、账号风控和地区支持可能与地区有关。

所以最终配置为:

  • 加拿大节点优先
  • 美国节点备用
  • 香港、台湾、日本、韩国、新加坡等节点不参与自动选择

这样可以减少因为出口地区导致的奇怪兼容问题。


十二、sing-box systemd 化

sing-box 配置完成后,也交给 systemd 管理。

现在 sing-box 已经实现:

  • systemd 管理
  • 开机自启
  • 断电恢复
  • SOCKS5 / HTTP 代理端口可用
  • 加拿大 / 美国节点池
  • 与 Tailscale 不冲突
  • 与 local-gemma-agent 不冲突

这样 Jetson 重启后,不需要手动启动代理服务。


十三、模型切换

当前默认模型仍然是 MiniMax-M2.7

因为它是订阅制,不是按 token 付费,所以没有必要为了省钱强行默认切本地模型。

但我希望 Gemma 能作为可切换模型存在。

现在已经把 local_gemma 注册到了 Hermes 配置里。

目标是实现:

  • 默认继续用 MiniMax
  • 需要离线 / 本地测试时切到 Gemma
  • 复杂 coding 任务用 Codex
  • 未来再接入更多模型

当前模型结构大概是:

Melody + MiniMax = 默认云端强脑
Melody + Gemma = 本地离线脑
Melody + Codex = 代码专家

十四、当前系统架构

目前 Jetson Orin Nano 上已经有:

Jetson Orin Nano
├── Tailscale
├── sing-box
│   ├── SOCKS5: 127.0.0.1:1080
│   └── HTTP: 127.0.0.1:1081
├── llama.cpp
├── Gemma 4 E2B
├── local-gemma-agent
│   └── http://127.0.0.1:18080/v1
├── Hermes / Melody
├── Codex Coding Agent
└── MiniMax-M2.7

已经 systemd 化并开机自启的服务:

  • Tailscale
  • sing-box
  • local-gemma-agent

暂时没有 systemd 化的:

  • Hermes / Melody

原因是 Melody 本身是控制者。如果现在让她修改自己的启动方式,风险比较高。所以 Hermes/Melody 服务化会放到后面单独处理,并且要有备份、dry-run 和回滚方案。


十五、为什么这次不急着让 Hermes 自启

这是一个很重要的决定。

sing-box、Gemma、Tailscale 都是"外部服务"。它们坏了,可以由 Melody 帮忙排查。

但 Hermes / Melody 是当前的控制者。如果让它自己修改自己的 systemd service,万一写错:

Melody 启动失败

控制者消失

只能人工 SSH 救火

所以更稳妥的顺序是:

  1. 先把网络、代理、本地模型服务化
  2. 确认断电恢复没问题
  3. 最后再单独设计 Hermes / Melody 自启
  4. 必须有备份和回滚方案

目前这个决定让系统更安全。


十六、目前完成度

已经完成:

  • 系统清理
  • 模型迁移
  • apt / dpkg 修复
  • llama.cpp CUDA 编译
  • 本地 Gemma 服务
  • GPU 加速
  • local-gemma-agent systemd 化
  • Codex 登录与验证
  • Melody SOUL 文件
  • sing-box 安装与配置
  • 加拿大 / 美国节点筛选
  • sing-box systemd 化
  • Tailscale 保持自启

当前整体状态:

系统层:稳定
网络层:稳定
代理层:稳定
本地模型:稳定
Codex:可用
Melody:可用
Hermes 自启:暂缓

下一阶段计划

接下来会进入更有趣的阶段。

1. 摄像头接入

目标:

  • 接 USB / CSI 摄像头
  • 验证 Linux 能否识别
  • 测试图像采集
  • 后续接入视觉模型

2. Whisper 本地语音识别

目标:

  • 接麦克风
  • 本地语音转文字
  • 中英文识别
  • 低延迟输入

3. 语音唤醒

目标:

  • 不需要一直打字
  • 可以用唤醒词启动 Melody
  • 后续配合 TTS 输出

4. Agent Router

目标:

让 Melody 自动判断:

  • 简单问题 → Gemma
  • 复杂分析 → MiniMax
  • 编程任务 → Codex
  • 视觉任务 → Vision Agent

5. Hermes / Melody 服务化

最后再做 Melody 自启。

这一步必须谨慎,因为它涉及主控制 Agent 本身。


总结

这次折腾下来,我最大的感觉是:

搭建本地 AI 助手不是"跑一个模型"这么简单。

真正麻烦的是:

  • 模型在哪里
  • 服务怎么启动
  • 网络怎么走
  • 断电后怎么恢复
  • 哪些东西应该本地
  • 哪些东西应该云端
  • 哪些服务该自启
  • 哪些服务不该急着自启

现在 Jetson 已经不再只是一个开发板,而是一个初步成型的本地 AI 小主机。

它已经具备:

  • 本地模型
  • 云端 coding agent
  • 独立代理出口
  • 开机自启服务
  • 统一人格 Melody
  • 后续语音 / 视觉扩展基础

接下来终于可以从"基础设施搭建"进入"真正好玩的 AI 交互"阶段了。