# 通过代理访问 SSH 智能体需要在远程机器上执行命令——部署容器、读日志、修配置。把裸的 SSH 私钥交给 LLM 是个坏主意:密钥会落进上下文窗口、日志和转录文本。SSH 代理解决了这个问题:凭据由它掌管,智能体只说 HTTP。 ## 模型 ``` LLM ──HTTPS──► SSH 代理 ──SSH──► 目标机器 (掌管密钥、 审计日志、 速率限制) ``` 代理通过简单的 REST 面提供持久会话: | 端点 | 用途 | |---|---| | `POST /connect?target=vps1` | 打开会话 → `session_id` | | `POST //exec` `{command, timeout}` | 执行命令(异步)→ `exec_id` | | `GET //exec/` | 轮询输出 | | SFTP 端点 | 文件上传/下载,`ls`,`mkdir`,`rm` | 认证是 Bearer 令牌——和你用的其他 API 相同的模式。 ## 执行模式 ```bash # 1. 连接一次,整个任务复用会话 SID=$(curl -s -H "Authorization: Bearer $KEY" \ "https://ssh-proxy.example/connect?target=vps1" | jq -r .session_id) # 2. 执行命令 EID=$(curl -s -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \ -X POST "https://ssh-proxy.example/$SID/exec" \ -d '{"command": "docker ps --format \"{{.Names}}\"", "timeout": 120}' | jq -r .exec_id) # 3. 轮询结果 curl -s -H "Authorization: Bearer $KEY" "https://ssh-proxy.example/$SID/exec/$EID" ``` ## 行为准则 1. **复用会话。** 每条命令都连接一次会浪费资源并触发速率限制。保存 `session_id`(如 `/tmp/vps1_sid`),复用前先心跳确认。 2. **给目标命名,而不是去发现它们。** 目标是预先注册的(`vps1`、`pi`……)。代理是唯一的事实来源——永远不要把 IP 地址硬编码进智能体代码。 3. **文件优先用 SFTP,而不是 shell 的 cat/heredoc。** 通过 `exec` 转义二进制数据会损坏载荷。使用专用的上传/下载端点。 4. **给你的命令加引号。** JSON 体会被解析两次(HTTP + shell)。远程命令用单引号包裹,并保持单行。 5. **假设审计存在。** 每次执行都会落入代理的审计日志——像人类在读一切那样行事(他确实可以)。 ## 什么时候不要用 exec - 读取拥有 API 的结构化状态(Docker、数据库、Synapse 的 Mind API)——永远优先用 API,而不是解析 CLI 输出。 - 长时间运行的交互式进程。`exec` 是带超时的即发即忘;改用目标机上的 `nohup` + 状态文件。 - 没有明确指令的任何破坏性操作。代理给你能力——人类给你许可。 ## 错误恢复 | 症状 | 处理 | |---|---| | 连接时 401 | 令牌过期——从 memory/settings 重新读取,绝不从转录文本 | | 会话 404 | 会话过期(24 小时上限)——重新连接 | | exec 超时 | 提高 `timeout` 或拆分命令;检查目标机负载 | | 文件内容损坏 | 你用了 `exec` 而不是 SFTP——改用 SFTP 端点 |