跳过正文
  1. 博客文章/

把 Codex 会话放到远端 Mac mini:一次 Agent Deck 部署记录

·714 字·4 分钟·
AI DevOps Agent Deck Codex Tmux SSH
Zayn
作者
Zayn
专注 Kubernetes、CI/CD、可观测性等云原生技术栈,记录生产环境中的实战经验与踩坑复盘。
目录
AI 工程化实践 - 这篇文章属于一个选集。
3: 本文
2026 年 7 月 30 日,我把 Agent Deck v1.10.11 部署到一台长期在线的 Apple Silicon Mac mini。本机只负责看状态和切换会话,Codex 与 tmux 留在远端。安装很快,排查占了大部分时间:非交互 SSH 找不到命令,第一次 Codex 会话又卡在更新提示。等 smoke test 跑通,我还发现 15 个 MCP 正在拖慢初始化。下面按当天的顺序记录。

版本、耗时和资源数据只对应 2026-07-30 这次部署。后续版本可能改动命令参数和行为,使用前先看对应版本帮助。

我最后保留了这套配置
#

先把部署结果列在这里,后面再解释为什么这样配。

项目配置
控制端本机 Agent Deck TUI
执行端macmini,Apple Silicon,48 GiB 内存
Agent Deck本机与远端均为 v1.10.11
连接方式SSH federation
远端 profilefleet
并发限制fleet 组最多 3 个活动会话
默认 AgentCodex
Web 模式默认关闭
worktree / Docker按会话启用
flowchart LR
    Local["本机 Agent Deck TUI"] -->|SSH| Remote["macmini
profile: fleet"] Remote --> State["SQLite 状态库"] Remote --> Tmux["tmux 会话层"] Tmux --> Worker1["Codex: 实现"] Tmux --> Worker2["Codex: 审查"] Tmux --> Worker3["Codex: 验证"]

关闭本机 TUI 或断开 SSH 后,远端 tmux 会话继续运行。重新打开 TUI 后,可以从 macmini 分组进入原会话。

用 SSH,不开 Web
#

我先排除了常驻 Web 服务。Agent Deck 的 Web 模式能远程管理,但终端桥接会新增监听面。现有 SSH 已经配好主机密钥校验与认证,直接复用更省事:

  • 本机负责浏览、筛选和进入会话;
  • Mac mini 提供常驻运行环境和算力;
  • SSH 复用已有的主机密钥校验与认证配置;
  • 远端不需要新增长期监听端口。

这样做也把几个问题留在了远端:非交互 Shell 要单独补 PATH,Agent CLI 必须在 Mac mini 上登录,两端 Agent Deck 版本也得一致。

第一个坑:SSH 看到的 PATH 不一样
#

我先在同一台机器上对比非交互 SSH 和登录 Shell:

# 非交互 SSH,Agent Deck 的远端命令走这条路径
ssh macmini 'printf "%s\n" "$PATH"; command -v tmux; command -v codex'

# 登录 Shell,用来对照用户平时看到的环境
ssh macmini '/bin/zsh -lic "printf \"%s\\n\" \"\$PATH\"; command -v tmux; command -v codex"'

结果很直接:登录 Shell 能找到 Homebrew 和 ~/.local/bin 下的工具,非交互 SSH 只有系统目录。这解释了为什么 Agent Deck 可以安装成功,远端探测、会话启动和 remote update 却仍会失败。

同时检查基础资源:

ssh macmini 'uname -m; tmux -V; df -h "$HOME"; ulimit -n'

当时远端剩余约 50 GiB,文件描述符软限制为 256。这两个值后来分别影响了 worktree 策略和会话环境配置。

版本固定为 v1.10.11
#

我把本机和 Mac mini 都固定在 v1.10.11。下面的 SHA-256 只适用于这个版本的 install.sh

VERSION="v1.10.11"
INSTALLER="/tmp/agent-deck-install-${VERSION}.sh"
EXPECTED_SHA256="ea85297639d0c02ec61a89ac80d40f507a0c9096331c28b777c5ac0123001b11"

curl -fsSLo "${INSTALLER}" \
  "https://raw.githubusercontent.com/asheshgoplani/agent-deck/${VERSION}/install.sh"
printf '%s  %s\n' "${EXPECTED_SHA256}" "${INSTALLER}" | shasum -a 256 -c -
bash "${INSTALLER}" \
  --non-interactive \
  --version "${VERSION}" \
  --dir "${HOME}/.local/bin"

安装器还会使用 release 中的 checksums.txt 校验下载的二进制。本机和远端执行同一套安装步骤,然后确认版本一致:

agent-deck --version
ssh macmini '~/.local/bin/agent-deck --version'

我没有混用版本。远端协议、状态字段或命令参数一旦变化,排障时就会多出一个变量。

PATH 修一处还不够
#

SSH 命令和 Agent 会话读取不同位置,我分别处理。

让非交互 SSH 找到命令
#

在远端 ~/.zshenv 中加入一次:

# >>> agent-deck managed PATH >>>
export PATH="$HOME/.local/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:$PATH"
# <<< agent-deck managed PATH <<<

.zshenv 会被非交互 zsh 读取。这样 command -v agent-deck、远端版本检查和 agent-deck remote update 都能找到正确的二进制。

给 Agent 会话准备运行环境
#

创建远端 ~/.agent-deck.env

export PATH="$HOME/.local/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:$PATH"
ulimit -n 8192

然后限制文件权限:

chmod 600 ~/.agent-deck.env

这个文件由 Agent Deck 在启动会话时加载。ulimit -n 8192 只影响这些用户会话,无需修改 macOS 的系统级限制。

fleet 最多跑 3 个会话
#

远端配置如下:

default_tool = "codex"
group_sort = "actionable"

[shell]
env_files = ["~/.agent-deck.env"]

[group_defaults]
max_concurrent = 3

配置文件同样设为 0600

chmod 600 ~/.config/agent-deck/config.toml

group_defaults 只影响之后创建的组,因此显式创建 fleet

agent-deck -p fleet group create \
  --json \
  --max-concurrent=3 \
  fleet

v1.10.11 的部分子命令对空格分隔的 flag 值存在解析歧义。脚本中把 flag 放在位置参数之前,并优先写成 --flag=value,可以减少这类问题。

从本机注册远端节点
#

本机通过已有的 SSH alias macmini 注册执行端:

agent-deck remote add \
  --agent-deck-path=/Users/<remote-user>/.local/bin/agent-deck \
  --profile=fleet \
  macmini \
  macmini

第一个 macmini 是 Agent Deck 中显示的名称,第二个是 ~/.ssh/config 中的 Host alias。这里使用远端二进制的绝对路径,避免再次依赖远端命令解析。

注册后执行只读检查:

agent-deck remote list --json
agent-deck remote sessions macmini

日常创建远端会话时,我打开本机 agent-deck,选中 macmini 分组后按 n。v1.10.11 下,没有必要再用一套本地 profile 模拟远端会话。

第一次 smoke test 就卡住了
#

我建了一个临时 Codex 会话,只要求它回复固定文本,不读取也不修改项目文件:

Reply with exactly AGENT_DECK_REMOTE_OK. Do not read or modify files.

会话稳定后,输出只有一行:

AGENT_DECK_REMOTE_OK

看到这行输出后,整条链路算是跑通了。真正值得记的是前面卡住的地方。

首次启动被 Codex 更新提示挡住
#

第一个会话一创建就进入 error,pane 抓不到,tmux session 也消失了。我先怀疑 PATH 还没修完整。随后把 Codex 放进临时 tmux 直接启动,才看到交互式更新提示:

Update available! 0.145.0 -> 0.146.0
Press enter to continue

我选择跳过当前版本,再从 Agent Deck 重启会话,Codex 便能稳定运行。由于当时没有生成 spawn-failure 记录,更新提示不能算已经证明的唯一根因;它只是现场唯一能够重复看到的启动阻塞点。

无人值守节点上的 CLI 更新提示需要提前处理。升级本身应放进单独的维护窗口,完成后重新跑 smoke test。

--wait 不等于任务已经完成
#

我第一次发送消息时,session send --wait 返回 delivery=unverified。Codex 还在初始化 MCP,过了一会儿读取会话输出才看到 sentinel。

后来我把 --wait 只当作“会话可以收消息”。自动化任务仍要求 Agent 最后一行输出:

===AGENTDECK_DONE=== status=ok summary=<one-line result>

我现在会检查 sentinel,再核对文件、测试或外部状态。

15 个 MCP 拉长了启动时间
#

会话启动后,pane 里列出了 15 个 MCP。初始化超过 20 秒,Skill 描述也因上下文预算被压缩。我起初以为只是远端启动慢,看到这份列表后,问题才指向工具初始化和上下文占用。

Mac mini 的 CPU 和内存还有余量。我按下面的顺序处理:

  1. 全局只保留每个项目都需要的 MCP;
  2. 数据库、浏览器和内部平台 MCP 按任务附加;
  3. 修改 MCP 后重启对应会话;
  4. 多个会话长期复用同一批兼容 MCP 后,再评估 mcp_pool

我暂时没有启用 mcp_pool.pool_all

日常会话怎么组织
#

fleet 的上限虽然是 3,我平时只保持 1 到 2 个活动 Agent,第三个位置留给独立审查或紧急任务。

会话名称直接写工作结果,例如:

api-auth-impl
api-auth-review
infra-log-audit

我现在只守几条规矩:

  • 一个会话只负责一个可验收结果;
  • 同一工作区同时只允许一个写入者;
  • 并行开发使用不同 worktree 和分支;
  • 实现完成后,另开会话检查 diff 并重跑测试;
  • 完成的会话先归档,一次性探针才直接删除。

远端只剩约 50 GiB,这个选择很容易:不全局开启 worktree。功能开发、hotfix 和独立审查按会话启用,完成后运行 worktree finish。我会定期执行 worktree cleanup 的 dry-run,看过清单再清理。

本机常用命令只有几条:

agent-deck
agent-deck remote sessions macmini
agent-deck remote attach macmini <session-id>
agent-deck remote update macmini

Agent 忙碌时,用 --defer-if-busy 排队后续任务。刚创建的会话不使用 --no-wait,否则可能出现提示内容已经写入 pane、但 Enter 尚未提交的情况。

安全边界
#

为了避免一个误操作波及整台执行节点,我保留了这些设置:

  • 不全局启用 Codex YOLO 或其他免审批模式;
  • Docker sandbox 默认关闭,只给不可信仓库或脚本按会话启用;
  • sandbox 不挂载 SSH 凭据,资源上限从 4 CPU、8 GiB 内存开始;
  • Secret 放入权限为 0600 的 env 文件,不写入 prompt 或仓库;
  • Web 模式默认关闭。

需要 Web UI 时,服务只监听远端 127.0.0.1,设置高熵 token,再通过 SSH tunnel 或 Tailscale 访问。Agent Deck 的 Web 端包含终端桥接和会话创建接口,不能以无 token 的方式监听局域网或公网地址。

每周维护清单
#

我把每周检查压缩成下面几条命令:

# 查看远端会话
agent-deck remote sessions macmini

# 检查远端磁盘
ssh macmini 'df -h "$HOME"'

# 预览孤立 worktree,不直接强制清理
ssh macmini 'agent-deck -p fleet worktree cleanup'

# 先更新本机,再保持远端版本一致
agent-deck update
agent-deck remote update macmini

我还会看 Codex 启动了多少 MCP、花了多久。新增工具后如果每个会话都变慢,先缩减全局配置。

远端当时使用 tmux 3.6a,Agent Deck 提示该版本存在 control-mode 风险并带有缓解逻辑。tmux 升级应安排在没有活动会话的维护窗口,先确认上游修复版本,再通过 Homebrew 更新,避免临时替换运行中的二进制。

眼下的瓶颈是 MCP
#

这次让我改掉了一个直觉:机器还有余量时,不必立刻提高并发。眼下更值得做的是缩减全局 MCP,让单个会话更快进入可用状态。等真实队列经常碰到 3 个会话上限,再调整并发。

这仍是一套个人节点配置。多人共享、跨网络开放 Web UI 或大规模并发时,还要补账号隔离、访问审计、资源配额和备份。

参考资料
#

AI 工程化实践 - 这篇文章属于一个选集。
3: 本文

相关文章

Claude Code + GitLab pr-agent:AI 驱动的持续迭代开发实践
·1308 字·7 分钟
AI CI/CD DevOps Pr-Agent GitLab Claude Code
2026 AI Agent 工程架构与框架选型
·1005 字·5 分钟
AI 系统架构 Agent 架构 DevOps
EasyTier 私有共享节点部署
·617 字·3 分钟
VPN KCP QUIC DevOps Network +2