版本、耗时和资源数据只对应 2026-07-30 这次部署。后续版本可能改动命令参数和行为,使用前先看对应版本帮助。
我最后保留了这套配置#
先把部署结果列在这里,后面再解释为什么这样配。
| 项目 | 配置 |
|---|---|
| 控制端 | 本机 Agent Deck TUI |
| 执行端 | macmini,Apple Silicon,48 GiB 内存 |
| Agent Deck | 本机与远端均为 v1.10.11 |
| 连接方式 | SSH federation |
| 远端 profile | fleet |
| 并发限制 | fleet 组最多 3 个活动会话 |
| 默认 Agent | Codex |
| 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 和内存还有余量。我按下面的顺序处理:
- 全局只保留每个项目都需要的 MCP;
- 数据库、浏览器和内部平台 MCP 按任务附加;
- 修改 MCP 后重启对应会话;
- 多个会话长期复用同一批兼容 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 或大规模并发时,还要补账号隔离、访问审计、资源配额和备份。
