1. 背景
经常喜欢在服务器上开发,因为可以复用 linux 上的环境,所以想给 pi agent 也加上 ssh remote 功能,也就是可以让 AI 在本地远程操作,但是操作目录和环境是远程 linux, 目前成品已经在 pi-desk 上实现,下面记录下方案架构和原理
2. ssh remote 的多种方案实现原理
在开发前先学习下其他现成的方案,避免重复造轮子。下面我搜集了一些 pi agent 的 ssh remote 仓库
- https://github.com/petrichor20211/pi-ssh-remote
- https://github.com/cv/pi-ssh-remote
- https://github.com/zeflq/pi-bridge
- https://github.com/99percentpeople/pi-extensions/blob/master/extensions/ssh-remote/README.md
- https://github.com/oresk/pi-remote-tools
以及 vscode remote 和 codex remote 的实现原理
2.1 vscode remote
vscode 本身是开源的,参考 VS Code Remote Development using ssh、vscode-remote-release,核心是本地 UI + 远端 vscode server,本地窗口只负责界面,主要功能都在 server 上,编辑器向 server 请求文件增删改查、搜索、补全和调试,当连接到远程后,新安装的扩展实际上是安装到远程 server。其典型调用链可以简化为:
本地 vscode 窗口
│
├─ remote SSH 扩展: 解析配置、认证、建立通道
│
└─ vscode server
│
├─ 远端文件系统
├─ 远端 Terminal
├─ 远端语言服务和调试器
└─ 远端工作区扩展
这种设计的优点是完整度高,相当于在 linux 上启动了一个无头 vscode。代价是远端资源占用高,之前用 2H2G 的 vps 时,基本带不起来这个 server 服务,另外开发成本也比较高
2.2 codex
codex 核心也是开源的,可以参考文档 Codex Remote connections、codex-app-server-daemon README。
codex 的 ssh remote 更接近 vscode 的做法,在服务器上运行了一个完整的 Agent runtime,其抽象调用链是:
本地 codex Desktop
│
└─ ssh 连接和远端进程管理
│
└─ 远端 codex app-server
│
├─ 远端 cwd 和文件
├─ 远端 shell / tools
├─ 远端 Codex 配置
└─ 远端线程运行时
ssh 连接建立后,连接流程会在远端主机启动
codex app-server客户端通过 app-server 自定义协议与远端 Codex 交互
远端 app-server 所在主机拥有 cwd 和工具执行能力
这么做的缺点也很明显,部署代价更重,远端安装了完整的 codex runtime,在性能和开发成本上不占优势
2.3 petrichor20211/pi-ssh-remote
项目说明文档在 https://github.com/petrichor20211/pi-ssh-remote/blob/main/README.zh-CN.md,这是一个 pi 的 ssh remote 的插件,当连接远程 ssh 时,扩展会替换 pi 原生的 read/write/edit/bash 工具实现,换成自己实现的 read/write /edit/bash,这些命令会在远程服务器上执行。
插件还会通过 before_agent_start 修改 system prompt,告诉模型当前远端 cwd、ssh endpoint 以及工具路由状态
连接后的调用链大致是:
本地 pi
│ 调用 read / write / edit / bash
▼
pi-ssh-remote 扩展
├─ read / write / edit:SFTP
└─ bash / user shell:ssh exec channel
│
▼
远端 sshd 和工作区
实现方式:
- 解析
ssh user@host -p PORT [-i KEY] - 使用
ssh2建立持久 ssh 连接 - 并替换 pi 自带的命令,文件通过 SFTP 读写,命令通过 ssh exec channel 执行,替换 pi 原生的
read/write/edit/bash工具实现
优点是实现成本低,缺点是 ssh2 只支持有限 ssh 参数:-p、-l、-i,不读取 ~/.ssh/config,不支持 ProxyJump,同时 agent 还是会有操作本地环境的风险,ssh 会话管理、复用等复杂的功能无法实现
2.4 cv/pi-ssh-remote
这是一个 pi 的插件,项目地址为 https://github.com/cv/pi-ssh-remote,主要实现方式是:
- 启动时通过 sshFS 把远端目录挂到本地临时目录,让 pi 的内置
read/write/edit/grep/find/ls直接访问这个本地临时目录。 - 对 pi 的命令修改只覆盖
bash工具,bash 命令通过系统 ssh 在远端执行。
远端 /home/user/project
│ sshFS
▼
本地 /tmp/pi-sshfs/...
│
└─ pi 把它当作 cwd,普通文件工具访问挂载目录
优点很明显:改动小,方案简单,缺点是 windows 客户端不带 sshFS 功能,也无法处理复杂的 ssh 会话状态管理等功能,容错性很低,比如 mount 失败就 gg 了
2.5 zeflq/pi-bridge
这是一个 pi 的插件,项目地址为 https://github.com/zeflq/pi-bridge。他的实现方式为
- 通过 ssh 上传一个远端 Node HTTP server,远端 server 只监听
127.0.0.1,再通过 ssh port forwarding 暴露给本地。 - 本地创建一个假的目录树,例如
/tmp/pi-bridge/root/project,然后process.chdir()到假的 cwd。 - 通过 monkey-patch Node 的
fs、fs.promises、fs/promises和部分child_processApi,这个更底层 - 对 fake root 下的路径转成 HTTP 请求;对其他路径继续走本地文件系统
pi 的 fs.readFileSync(remotePath)
│
└─ preload 拦截 → ssh port forwarding → 远端 HTTP server
pi 的 spawn("git", ...)
│
└─ preload 拦截 → ssh 执行远端 Git
这个方案的优点是可以在复用远端的 AGENTS.md、skills、项目上下文和 Git 状态,不依赖 sshfs,只需要远端有 Node.js 环境。缺点
是运行时 patch Api 的面积太大,感觉很不可控,而且服务器上的 HTTP server 用 node 实现也不太靠谱
2.6 99percentpeople/pi-extensions
这也是一个 pi 的插件,项目地址 https://github.com/99percentpeople/pi-extensions/blob/master/extensions/ssh-remote/README.md,实现原理为:
- 在 pi 中注册远程版本的
read/write/edit/bash/grep/find/ls - Linux/macOS 使用 Openssh ControlMaster(多次工具调用复用连接),Windows 使用持久
ssh2连接,ssh2模式通过本地ssh -G解析~/.ssh/config,并自己实现 ProxyJump、known_hosts 和多跳认证 - 使用逻辑路径命名空间,例如把远端 Unix 路径编码成
/__pi_ssh_remote_unix__/...,避免 pi 本地 mutation queue 把远端路径误判成本地路径 - 远端工作区不可用时 fail closed,不静默回退到本地
这个项目实现的比较完整,workspace 路由和失败状态设计、对抗测试案例比较多,支持 Openssh alias、ProxyJump、非标准端口和多平台远端。代价是实现比较复杂
2.7 oresk/pi-remote-tools
这也是一个 pi 的插件,项目地址为 https://github.com/oresk/pi-remote-tools。此插件采用的是最简单的方案。新增 ssh-read、ssh-write、ssh-edit 和 ssh-bash,每个工具参数都带 host;本地标准 read、write、edit、bash 不被替换,也没有全局的本地/远端路由状态。
模型调用 ssh-read(host, path)
│
└─ 在该次调用中建立或使用 ssh,读取远端文件
优点是简单高效,适合简单的操作。缺点是模型必须区分两套工具,cwd、Git 工作区、文件搜索和多步编辑不会自然形成一个统一的远端 workspace,我觉得用这个插件还不如直接在对话中告诉 AI 帮我连接ssh xxx,执行xxx操作
2.8 几种方案的共同结论
从这些实现可以提炼出四个架构选择:
- 远端服务型。 vscode 和 codex 把工作区或 Agent runtime 放到远端,语义完整,但需要远端安装服务并管理服务生命周期
- 工具路由型。
petrichor20211/pi-ssh-remote和99percentpeople/pi-extensions保留本地 pi,替换工具执行后端,体验接近本地,但需要维护路由和生命周期 - 文件系统桥接型。
cv/pi-ssh-remote和pi-bridge把远端文件系统投影或拦截成看似本地的 Api,兼容面广,但更依赖操作系统、运行时 patch 和边界完整性 - 显式工具型。
pi-remote-tools不改变全局状态,安全直观,但多步 Agent 工作流的上下文一致性较弱
3. 在 pi-desk 实现 ssh remote
首先介绍下 pi-desk,项目地址在 https://github.com/saucer-man/pi-desk,用 golang(wails+vue)实现了一个 pi 的 gui 页面,底层还是 pi
3.1 实现方案
实现 ssh remote 的设计原则是简洁+完整,简洁指的是不依赖服务端和客户端环境,尽量用 go 跨平台编译实现,完整是指功能完整,能处理 ssh 会话管理、复杂网络环境
pi-desk 采用 本地 pi + Go host remote control plane + 远端受限 helper 的架构:
远端 helper :这是利用 golang 编写的一个独立的远端运行的工具,当 pidesk 连接服务器时,会自动上传。这个工具负责在远端处理文件读写、搜索、Git、Bash 和 Terminal 任务。
Go host:结合在 pi-desk wails 后端代码中,维护 ssh 连接管理、helper runtime、路径处理、命令转发等
pi 插件: pi-desk 启动 pi rpc 时会给 pi 增加一个插件,通过 before_agent_start 获取当前远端上下文,并把远端根目录、远端项目上下文写入本轮 system prompt。并且屏蔽原有工具,并注册工具: read、write、edit、find、grep、ls、bash,插件会把命令和参数发送到 Go host
假设用户在 pi-desk 的「新建任务」窗口中选择 ssh,填写:
ssh alias:devbox
远端根目录:/srv/project
文件请求:创建 a.txt,内容为 hello remote
一次完整流程如下。
第一步:UI 提交结构化连接请求:UI 会展示解析配置
第二步:Go host 校验 Openssh 配置,建立连接、探测服务器平台、安装 helper、完成 handshake。其中 helper 固定位置如下:
$HOME/.cache/pi-desk/remote-helper/<protocol>/<sha>/helper
第三步:调用远端 helper,获取当前 task 的 root 目录,对 /srv/project 做规范处理 ,并返回:
canonicalPath = /srv/project
device = ...
inode = ...
rootHandle =
Go host 将远端根目录写入 catalog
WorkspaceID
TargetID = devbox 对应的 target
requestedRoot = /srv/project
canonicalRoot = /srv/project
device / inode
trust = approve
第四步:创建 pi 进程,加载插件 pi-desk-remote.ts
--no-builtin-tools
--no-extensions
--no-context-files
--extension <pi-desk-remote.ts>
第五步:模型看到请求并调用 write:远端 adapter 的 before_agent_start 会把下面几类信息追加到 pi 的 system prompt:
实际远端 workspace root:/srv/project
pi 本地进程 cwd:<local ssh anchor>
文件和命令必须使用 registered remote tools
远端项目上下文:AGENTS.override.md 或 AGENTS.md 的内容
用户要求创建 a.txt 后,模型调用的仍然是普通工具:
{
"name": "write",
"path": "a.txt",
"content": "hello remote"
}
adapter 先把 a.txt 规范化为 root-relative a.txt,拒绝 ../a.txt、绝对路径越界、反斜杠和控制字符。adapter 再调用 Go broker,broker 将请求转发给远端 helper,执行完毕后返回结果
第六步:pi 拿到结果后,将结果写入 JSONL,pi-desk 通过 Wails event 更新消息和 Repository 状态。用户看到的是任务结果:已在远端 workspace 创建a.txt
3.2 QA
对模型来说,他看到的是什么?:模型看到的是 pi 的标准 Agent 上下文和标准工具名称:
工具仍是
read、write、edit、find、grep、ls、bash;system prompt 追加远端 root、pi 本地 anchor cwd 的说明和远端项目上下文;
对 pi 来说,他看到的是什么?:pi 看到的是一次普通的 pi --mode rpc session:
- cwd 是本地 ssh anchor,而不是远端
/srv/project的真实映射; - 会话记录仍保存在本地 pi JSONL 保存;
- 通过加载插件修改了 system_prompt 和注册工具
服务器上的 skill、AGENTS.md 是否生效?:AGENTS.md 可用,skill 不可用。 AGENTS.md 会在 before_agent_start 中被追加到 system prompt
安全性保证?:主要是对文件目录做了限制,文件读写都限定在用户提供的 path 目录下
对 token 开销有无增加?:基本忽略不计,除了服务器上的 agent.md 和注册工具外,无其他上下文,ssh 通道对于模型和 pi 来说,都是透明的
4. 总结
本文对市面上 agent 的几种 ssh remote 方案做了简单介绍,并在 pi-desk 中开发了此功能,欢迎使用和反馈