上一篇里,我把 Jetson Orin Nano 从一块刚整理好的开发板,初步搭成了一个本地 AI 小主机。
当时已经完成了几个关键基础设施:
- 本地 Gemma 模型
- llama.cpp CUDA 编译
- 本地模型服务化
- 代理出口
- 远程访问
- Hermes / Melody 作为主 Agent
- Codex Coding Agent 初步接入
但是那时候严格来说,它还只是处在"基础设施搭起来了"的阶段。
能跑,不代表好用。
好用,还需要继续解决几个问题:
- 本地模型上下文是不是够用
- Hermes 能不能稳定切换到本地模型
- Codex 能不能真正作为 Jetson 上的 coding agent 使用
- 网络出口是不是稳定可靠
- 服务状态能不能被 Agent 正确检查
- 系统有没有留下可维护的 baseline 文档
这次继续折腾以后,我觉得 Jetson Local AI Agent 的 Phase 1 可以正式标记完成了。
一,先把 Gemma 的上下文问题解决掉
之前本地 Gemma 服务虽然已经可以跑,但有一个明显问题:
在 Hermes 里切到本地 Gemma 后,会发现上下文只有 4096 tokens。
这对一个本地 fallback 模型来说太小了。
简单聊天没问题,但只要涉及:
- 系统检查
- 多轮排查
- 日志分析
- 工具调用
- Hermes 自己的系统提示词
4096 tokens 很快就会不够。
后来继续排查发现,Gemma GGUF 模型本身并不是只能支持 4096。真正限制它的是本地模型服务启动时的 context 参数。
也就是说:
模型有更长上下文能力,但服务启动参数把它限制住了。
最后我把本地 Gemma 服务的日常上下文调到了:
65536 tokens
这个数字不是越大越好,而是一个平衡点。
我也测试过更大的上下文。它确实能跑,但在 Jetson Orin Nano 8GB 上会明显增加内存压力,响应也会变慢。
最后选择 65K,是因为它比较适合作为日常本地 fallback:
- 比 4K 宽裕很多
- 不会像更大上下文那样压力太大
- Hermes 切换后能正确识别上下文
- 本地系统检查和中短任务已经够用
现在切到本地 Gemma 后,Hermes 可以正确显示约 65K 的上下文。
这一步很重要。
因为 Gemma 不再只是"能跑一句 hello",而是真正可以成为 Hermes 里的本地模型选项。
二,Hermes 里的模型切换终于顺了
现在 Hermes / Melody 的模型结构基本变成了:
MiniMax = 默认云端主力模型
Gemma = 本地离线 / 低风险系统检查模型
Codex = Coding Agent
默认还是 MiniMax。
原因很简单:
MiniMax 上下文更大,适合长对话、复杂分析和日常主控。
Gemma 则更适合:
- 本地检查
- 离线兜底
- 简单系统状态判断
- 不想调用云端模型时使用
这次真正解决的是本地模型切换体验。
之前虽然本地 Gemma provider 已经注册进 Hermes,但 alias、context length、compression 配置之间有一些不一致。修完之后,Hermes 现在可以比较自然地在云端主力模型和本地模型之间切换。
对我来说,这个意义不只是"多了一个模型"。
更重要的是,Melody 开始变成一个统一入口:
同一个 Agent 人格
不同模型后端
不同任务分工
也就是说,我不用把每个模型都当成一个完全不同的助手。
Melody 是统一的交互层,底层能力可以根据任务切换。
三,Gemma 通过了基础系统检查测试
调好上下文之后,我让 Gemma 做了一些低风险测试。
一开始它也犯了一个很典型的错误:用简单的进程名匹配来判断服务是否存在。
这类方法容易误判,因为 systemd 服务名、实际进程名、启动脚本名不一定一致。
后来我明确要求它用更可靠的方式检查:
- systemd 服务状态
- 本地端口监听
- 关键进程摘要
- HTTP endpoint 健康检查
之后它能正确判断:
- 本地模型服务处于 active 状态
- 代理服务处于 active 状态
- 远程访问服务处于 active 状态
- 关键本地端口正常监听
这个测试让我对 Gemma 的定位更清晰了。
它不是用来完全替代云端模型的,也不是拿来做复杂代码改造的。
它更适合做:
本地、小范围、低风险、只读检查
只要 prompt 写清楚"不使用 sudo、不修改配置、不重启服务",它已经可以承担一部分本地运维辅助工作。
四,网络出口和 DNS 问题修复
这次另一个大坑是 Codex 的网络问题。
一开始 Codex wrapper 已经做好了,命令也能运行。
但是实际启动 Codex 时,会遇到 cloud config timeout 或者后端连接失败。
一开始看起来像是 Codex 自己的问题,后来继续查才发现,真正的问题在代理服务的 DNS 解析链路。
代理服务本身是 active,本地代理端口也在监听。
但是这不代表网络真的通。
问题出在部分海外域名的 DNS 请求会超时,于是应用层看起来就像是 Codex 连接不上后端。
最后采用的修复思路是:
国内 / 默认 DNS:使用更稳定的国内 DNS
国外 DNS:仍然使用海外 DNS,但通过代理出口访问
这样做的好处是:
- 国内解析不用绕远路
- 国外域名仍然可以使用更合适的海外 DNS
- 海外 DNS 不再直连容易超时的链路
- Codex 的后端连接恢复正常
- 不需要改节点
- 不需要改订阅
- 不需要动代理入口和出口结构
修改后重新检查:
- 配置检查通过
- 服务重启后 active
- 本地代理端口正常
- ChatGPT 后端接口可以连通
- Codex help 正常输出
这一步之后,Jetson 的网络出口才算真正稳定可用。
这次也提醒我一个很重要的点:
端口在监听,不代表网络真的通;服务 active,不代表应用一定能连上后端。
以后排查这类问题,需要同时看:
- systemd 状态
- 端口监听
- DNS 解析
- 代理链路
- 后端响应
- 实际应用行为
五,Codex wrapper 和登录问题最终解决
Codex 这条线也折腾了不少。
一开始的问题是 Jetson 上没有直接的 codex 命令。Codex CLI 实际安装在另一个 node 环境下面,所以后来我给它做了一个 wrapper。
这个 wrapper 做了几件事:
- 指定 Codex 的实际 CLI 路径
- 指定独立的 Codex 配置目录
- 注入代理环境变量
- 让 Jetson 上可以直接运行
codex
之后 Codex 的版本检查和帮助命令都正常了。
但登录又遇到 refresh token 问题。
这个问题本质上不是网络,也不是 wrapper,而是本地 auth token 失效。
最后还是用了比较实际的办法:
- 在另一台设备上完成 Codex 登录
- 将新的认证文件同步到 Jetson 的独立 Codex 配置目录
- Jetson 上继续通过 wrapper 使用 Codex
完成后,Codex 可以正常进入交互界面,也能读取系统信息。
这一步很关键。
因为从这里开始,Codex 不再只是"装好了",而是可以真正成为 Jetson 上的 coding agent。
六,Codex 通过了低风险验收测试
我没有一上来就让 Codex 改真实项目。先做了几个低风险测试。
第一个是只读系统总结
Codex 正确识别了:
- Ubuntu 22.04 LTS
- ARM64 / aarch64
- Tegra kernel
- ARM Cortex-A78AE CPU
- 约 8GB 级别内存
- NVMe root disk
- CUDA 12.6
- Python / Node / npm / git / ripgrep 等工具
它还指出 nvidia-smi 无法通信。
这个在 Jetson 上并不一定说明 GPU 不可用,因为 Jetson 不是普通桌面 NVIDIA GPU 环境。对 Jetson 来说,更常用的是 tegrastats 或 jtop。之前 llama.cpp 已经能 CUDA offload,Gemma 也能通过 GPU 跑起来,所以这里不算 blocker。
第二个是只读服务检查
Codex 正确确认:
- 本地模型服务 active / enabled
- 代理服务 active / enabled
- 远程访问服务 active / enabled
- 本地模型 API 端口正常监听
- 本地代理端口正常监听
并且它没有:
- 使用 sudo
- 重启服务
- 修改配置
- 写入系统文件
这说明 Codex 至少已经具备了基本的安全协作能力。
七,给 Codex 建立自己的工作区
我不想让 Codex 直接在 home 目录或者某个不确定的 current directory 里乱创建文件。
所以给它明确了一个自己的工作目录。
之后第一个正式输出是一份 Jetson baseline 文档。
这份 baseline 文档记录了当前 Jetson 的状态,包括:
- OS / kernel
- CPU / RAM / storage
- CUDA / Jetson GPU notes
- 本地模型服务状态
- 代理服务状态
- 远程访问状态
- Codex CLI 状态
- 已知注意事项
这个文件的意义不是内容多复杂,而是它给整个系统留下了一个"当前可用状态"的快照。
以后如果系统坏了、服务起不来、模型变慢、代理异常,就可以回头对比这个 baseline。
更重要的是,这次 Codex 严格遵守了边界:
只在指定目录创建文件
不使用 sudo
不重启服务
不修改系统配置
这让我可以更放心地把它用于后续代码和文档任务。
八,当前系统状态
现在 Jetson 上已经形成了比较清晰的分工:
Jetson Orin Nano
├── Remote Access
│ └── 远程访问
├── Proxy Service
│ └── 本地代理出口
├── Local Model Service
│ └── 本地 OpenAI-compatible API
├── Gemma
│ └── 本地模型 / 65K context
├── Hermes / Melody
│ └── 主 Agent / 模型切换 / 统一入口
├── MiniMax
│ └── 默认云端主力模型
└── Codex
└── Coding Agent / 文件与代码任务
systemd 管理的服务包括:
本地模型服务
代理服务
远程访问服务
暂时没有 systemd 化的是:
Hermes / Melody
Codex
这里还是保持之前的判断:Hermes / Melody 是主控制层,不急着自启。
等后续要做 systemd 化时,必须单独设计:
- dry-run
- 日志路径
- 环境变量
- 启动失败回滚
- 人工 SSH 救援方案
九,这次学到的几个经验
这次最大的感受是:Agent 系统不是"装几个模型"这么简单。
真正难的是服务之间的边界。
1. 模型能跑,不代表 Agent 好用
Gemma 一开始能回答问题,但上下文只有 4K。
在 Hermes 这种系统里,4K 很快就不够。只有把上下文、provider、alias、compression 等配置都理顺,它才真正可用。
2. 端口在监听,不代表网络真的通
代理服务的本地端口一直在监听,但 DNS 超时照样会让 Codex 失败。
所以排查网络不能只看 service active,还要看:
- DNS
- proxy detour
- backend response
- 实际应用行为
3. Codex 能启动,不代表 auth 没问题
Codex UI 能打开,但 token refresh 可能已经坏了。
这类问题和网络问题很像,但本质完全不同。最后还是要区分:
网络超时
代理错误
DNS 问题
OAuth token 问题
4. Agent 必须有工作边界
让 Codex "在当前目录创建文件"不够安全。
更好的方式是直接指定:
只允许修改某个专用工作区
这比依赖 current directory 稳很多。
5. 本地 AI 节点需要 baseline
很多 home lab 项目搭完之后,最大的问题不是当下不能用,而是过几天以后忘了自己改过什么。
这次 baseline 文档很重要,因为它记录了一个"此刻系统是好的"的状态。
以后排查问题时,至少知道应该回到什么基准线。
十,Phase 1 完成
到这里,我觉得 Jetson Local AI Agent Phase 1 可以正式标记完成。
当前已经完成:
- 本地 Gemma 服务可用
- Gemma context 调整到 65K
- Hermes 可以切换到 Gemma
- MiniMax 继续作为默认主力模型
- 代理 DNS 问题修复
- Codex wrapper 可用
- Codex auth 修复
- Codex 通过只读系统检查
- Codex 能在指定工作区创建 baseline 文档
- 核心后台服务已 systemd 化
- Jetson 具备独立网络出口和本地模型服务能力
现在它已经不只是:
一个能跑模型的 Jetson
而是更接近:
一个可以被 Agent 管理、检查、扩展的本地 AI 节点
下一阶段计划
接下来我准备从基础设施搭建,进入真正的应用层。
1. Home Lab 控制中心
把 Jetson、树莓派、NAS、服务状态、网络状态做成一个 dashboard。
目标是能一眼看到:
- 哪些服务在线
- 哪些端口正常
- 哪些设备可达
- 哪些容器 / systemd 服务异常
- 本地 AI 服务是否健康
这个方向最适合接在当前阶段后面,因为现在 Codex 和 Gemma 都已经具备基础检查能力。
2. Agent Inventory
让 Codex 只读扫描 agents 目录,生成一份 agent inventory 文档。
目标是把每个 agent 的:
- 启动脚本
- 配置路径
- 服务名
- 端口
- 模型依赖
- 注意事项
整理出来。
这个不是必须马上做,但对长期维护很有帮助。
3. 视觉识别
后面会接摄像头,让 Jetson 开始做视觉输入。
这可能会涉及:
- USB / CSI 摄像头
- 图像采集
- 轻量视觉模型
- 本地事件检测
- Home Lab / Smart Home 联动
4. 语音输入
另一个方向是接麦克风和 Whisper,让 Melody 可以通过语音输入工作。
目标是以后不用每次 SSH / 打字,而是可以更自然地和本地 Agent 交互。
总结
这次 Phase 1 最大的收获是:
本地 AI 小主机的核心不是模型,而是可维护的系统边界。
模型只是其中一部分。
真正让它变得可用的是:
- 服务化
- 网络出口
- 模型切换
- 上下文配置
- 只读检查能力
- Codex 工作区边界
- baseline 文档
- 回滚意识
现在 Jetson 已经具备了一个本地 AI 节点该有的基础能力。
下一步,就可以开始把它接入更实际的 home lab 场景了。