阿里妹导读
全是 Web,没有 CLI,怎么行?Agent 都会写代码了,远程排障还要人肉点网页、复制命令、盯滚动条,这画面多少有点“地铁老人看手机.jpg”。本文记录一次围绕 StaRAGent/Drogo WebTerminal 的工具化实践:我们没有把 GPU hang、core dump 调试等场景固化成一个个“祖传脚本套件”,而是把 WebTerminal 抽象成稳定的 CLI 执行面,再用 Skill 描述操作方法。Agent 在任务中动态生成命令、读取结果、继续决策,最终完成远程 GPU hang 分析、文件上传下载、以及 Emacs + eshell + gdb 的交互式 coredump 调试验收。
插播:我对 Skill 的态度很朴素:Skill 不是法器,不是咒语,也不是“复制进去 Agent 就突然开悟”的玄学符纸。Skill 本质上就是说明书,是贴在工具箱盖子上的那张“先拧这个、再接那个、别把手伸进风扇里”的操作指南。真正能把活干成的,必须是 CLI:参数清楚、行为稳定、输出可解析、错误可复现、证据能落盘。 所以这套东西的核心不是“写一个很长的 Skill 让 Agent 背下来”,而是把能制度化的都制度化,把能流程化的都流程化,把以前靠老师傅手感、群里口口相传、网页上点点点的部分,全部压进 wt 这种可执行接口里。Skill 只负责告诉 Agent:什么时候该登录、什么时候该 run、什么时候该 interact、什么时候该停手问人。至于真正挥锤子的活, 必须交给 CLI。否则就是让 Agent 看着说明书徒手修发动机,场面很感人,结果很危险。(文章内容基于作者个人技术实践与独立思考,旨在分享经验,仅代表个人观点。)
WebTerminal 黑屏模式补充说明
号外号外,重大更新:
WebTerminal 终于可以像 SSH/SCP 一样用了
核心用法

$ ./bin/wt auth login --target-ip x.y.z.wwt: waiting for browser login cookies; finish login in the opened browserauthenticated: Trueuser: defaultsource: browser
$ ./bin/wsh x.y.z.wattached direct: x.y.z.w$ ./bin/wsh x.y.z.w -- 'hostname; pwd'hippo-033126067104.na175/home/admin/hippo/worker/slave/drogo-share_worker-h20-na175.worker-h20-na175_11_33/main
$ ./bin/wcp /tmp/wcp-demo.txt x.y.z.w:/tmp/wcp-demo.txt --forceuploaded: /tmp/wcp-demo.txt -> /tmp/wcp-demo.txt$ ./bin/wcp x.y.z.w:/tmp/wcp-demo.txt /tmp/wcp-demo.down.txt --forcedownloaded: /tmp/wcp-demo.txt -> /tmp/wcp-demo.down.txt
这次补上的关键体验
一点遗憾
以下是 V1 内容
前言:先把仓库拍你脸上
背景:Agent 要的不是另一个网页按钮,它要手脚
目标 IP、操作步骤都必须参数化,框架和具体案例隔离。 不做固定 suite,Agent 应该像工程师一样动态发送命令。 WebTerminal 浏览器会话要持续存在,不能每次操作都重新登录。 上传下载要走 WebTerminal 后端 API 或协议,不能靠 DOM 自动化点弹窗。 调试要是真交互:Agent 可以一条条给 gdb / emacs / eshell 发命令,拿到 response 后继续判断,而不是“一把梭,听天命”。

先看效果:别先讲架构,先看能不能干活
bin/wt run --capture captures/gpu-hang/env.raw.log -- \"hostname; date; uname -a; nvidia-smi -L || true; nvidia-smi || true"
bin/wt ls-files /home/adminbin/wt download /home/admin/remote.log captures/remote.logbin/wt upload ./local.txt /home/admin/local.txt
bin/wt interact \--start "cd /tmp && gdb -q ./app core.wtdebug" \--prompt-regex "\\(gdb\\)\\s*$" \--host 127.0.0.1 \--port 8765 \--capture captures/debug/gdb.raw.log \--summary-json captures/debug/gdb.summary.json
curl -s -X POST http://127.0.0.1:8765/command -d '{"send":"bt"}'curl -s -X POST http://127.0.0.1:8765/command -d '{"send":"frame 0"}'curl -s -X POST http://127.0.0.1:8765/command -d '{"send":"info locals"}'curl -s -X POST http://127.0.0.1:8765/command -d '{"send":"print item.id"}'1. 目标:把 WebTerminal 变成
Agent 友好的执行面
场景逻辑被写死后,Agent 只能“调用工具”,不能“分析现场”,智商被强行锁死。 每新增一个诊断路径,都要修改 CLI 代码。 远端状态复杂时,固定脚本很难处理分支,例如进程是否存在、gdb 是否可用、权限是否足够、core 文件是否生成成功。 安全边界不清晰,读操作、写操作、侵入式操作容易混在一起。
层次 | 职责 | 不做什么 |
WebTerminal 页面 | 登录、角色选择、审计、心跳、官方连接链路 | 不承载任务逻辑 |
| 会话复用、命令发送、输出捕获、文件 API、交互控制面 | 不内置具体排障 suite |
Skill | 描述操作方法、风险边界、推荐命令模板 | 不绑定某个 IP 或某个案例 |
Agent | 动态规划、执行、观察、复盘 | 不绕过授权链路 |
2. 会话设计:授权留在浏览器,执行交给 CLI
bin/wt session start --target-ipbin/wt session status
授权、审计、心跳仍走官方页面链路。 CLI 不保存 SSO token,也不绕过登录流程。 目标 IP 是参数,不会写死在实现里。 多轮排障可以共享同一个远端 shell 状态。
{"": {"type": "data","data": "pwd\r"}}3. 命令执行:不做 suite,做动态闭环
bin/wt run --capture captures/task/name.raw.log -- ""
printf '\n__WT_DONE___:%s\n' "$?"
*.raw.log:原始 ANSI 输出,适合保留完整现场。 *.plain:去 ANSI 后的文本,适合 Agent 解析。 *.snapshot.json:xterm buffer 快照,适合排查全屏程序或显示问题。
bin/wt run --capture captures/gpu-hang/env.raw.log -- \"hostname; date; uname -a; id; pwd; which nvidia-smi || true; nvidia-smi -L || true; nvidia-smi || true"bin/wt run --capture captures/gpu-hang/processes.raw.log -- \"ps -eo pid,ppid,stat,etime,%cpu,%mem,wchan:32,cmd --sort=-%cpu | head -200; GPU_PIDS=\"$(nvidia-smi --query-compute-apps=pid --format=csv,noheader 2>/dev/null | sort -u | tr '\n' ' ')\"; echo GPU_PIDS=$GPU_PIDS"bin/wt run --capture captures/gpu-hang/kernel.raw.log -- \"dmesg -T | egrep -i 'nvrm|xid|gpu|cuda|uvm|pcie|ecc|oom|hung|reset' | tail -300 || true"

4. 文件传输:从 DOM 自动化改成 API 调用
DOM 结构容易变化。 文件选择器和下载行为依赖浏览器设置。 很难做完整校验。 失败时只能看到 UI 结果,不容易拿到协议级错误。报错一句“上传失败”,人也失败了。
openFileSystem listFiles getDownloadFileHead downloadFile uploadFile heartbeat
bin/wt download /home/admin/remote.log captures/remote.logbin/wt upload ./local.txt /home/admin/local.txt

5. 交互式调试:默认接口应该像普通 shell
bt 看到崩溃栈,才知道要进哪个 frame。 info locals 看到变量,才知道要 print 哪个字段。 gdb 缺符号、core 文件异常、路径不对,都需要临时调整。 对 emacs 这类全屏程序,需要发送 raw key,而不是 line command。否则你以为在控制 Emacs,Emacs 以为你在乱按键。
Endpoint | 用途 |
| 查看交互会话是否存活 |
| 查看完整步骤摘要 |
| 获取 xterm 屏幕快照 |
| 发送一行命令,按 prompt regex 等待结果 |
| 发送 raw keys,适合 emacs/vim/TUI |
| 读取当前缓冲输出 |
| 退出远端交互程序并关闭本地 server |
curl -s -X POST http://127.0.0.1:8765/command \-d '{"send":"print item.id"}'{"ok": true,"result": {"name": "command","send": "print item.id","matched": true,"timed_out": false,"plain_output": "(gdb) print item.id\r\n$1 = 42\r\n(gdb) "}}
6. 验收案例:在 Emacs 里写 crash.c,
再用 eshell + gdb 定位 core
wt interact启动远端emacs -nw -Q。 /send发送C-x C-f /tmp/wt-emacs-crash.c。 /send插入 C 源码并C-x C-s保存。 /send执行M-x eshell。 在 eshell 里编译并运行程序,触发 segmentation fault。 生成core.wtdebug。 在 eshell 里启动gdb -q ./wt-emacs-crash core.wtdebug。 分别发送bt、frame 0、info locals、print item.id、print item.name。
#includetypedef struct {int id;const char *name;} Item;static int crash(int n) {Item item = {42, "emacs-eshell-core"};int *ptr = 0;printf("about to crash: %s %d %d\n", item.name, item.id, n);return *ptr + item.id + n;}int main(int argc, char **argv) {int input = argc > 1 ? 7 : 3;return crash(input);}Program terminated with signal SIGSEGV, Segmentation fault.#0 0x0000000000401168 in crash (n=7) at /tmp/wt-emacs-crash.c:1212 return *ptr + item.id + n;
#0 crash (n=7) at /tmp/wt-emacs-crash.c:12#1 main (argc=2, argv=...) at /tmp/wt-emacs-crash.c:17
item = {id = 42, name = 0x402004 "emacs-eshell-core"}ptr = 0x0print item.id -> 42print item.name -> "emacs-eshell-core"7. GPU hang 分析:场景逻辑留给 Agent
机器:hippo-xyzw Kernel:5.10.134-010.ali5000.al8.x86_64 Driver:580.82.07 CUDA:13.0 GPU:8 x NVIDIA H20 采集时 GPU compute processes 为空。 采集时 8 张卡利用率均为 0%,显存占用约为基础占用。 当前没有看到 active GPU hang。
反复出现 NVRM: refcntRequestReference_IMPL: Failed to enter state 1 ... status: 0x00000056。 NVSwitch 曾出现 SXid 22013 非致命链路中断。 5 月 8 日有一次 Xid 43 ,进程名为 python。 另有一次看起来不直接相关的 memcg OOM。
8. Skill 的角色:
把经验写成操作方法,而不是写死代码
如何启动或复用 session。 如何确认用户已经登录并进入 WebTerminal。 什么时候用 run ,什么时候用 attach ,什么时候用 interact 。 文件上传下载必须走 API,不能操作 DOM。 侵入式命令需要用户批准。 GPU hang 分析的建议命令,但不把它变成固定 suite。 coredump demo 的推荐构造方式和清理方式。
9. 几个设计取舍
9.1 为什么不直接连 SSH
9.2 为什么不把 GPU hang 做成内置命令
9.3 为什么交互控制面用 HTTP
9.4 为什么保留 interact-script
wt interact:默认 live server,用于动态调试。 wt interact-script:固定脚本,用于已知步骤。
10. 可以复用的工程模式
结语
WebTerminal 继续负责官方授权和连接链路。 wt CLI 负责把远程 shell、文件传输、交互式程序变成可调用能力。 Skill 负责把排障经验写成可执行方法,让 Agent 根据现场动态闭环。
招聘:欢迎底层系统同学跳进 AI 推理这口锅
大模型推理系统工程:面向 LLM 推理链路做系统设计、稳定性、吞吐、延迟和工程化。 AI 高性能计算:面向 GPU/异构计算、通信、调度、算子和推理性能做深度优化。 AI 性能研发:把模型服务从“能跑”推进到“跑得快、跑得稳、跑得省”。 多模态推理引擎:面向文本、视觉、语音等多模态推理场景做系统和性能建设。
附录 A:当前 CLI 能力速览
wt session 管理持久 WebTerminal 浏览器会话wt status 查看当前终端状态wt run 执行一条 shell 命令并捕获输出wt attach 本地 raw TTY 直接接入 WebTerminalwt interact 启动 live HTTP 交互控制面wt interact-script 执行固定 expect 风格交互脚本wt snapshot 获取 xterm buffer 快照wt ls-files 通过 WebTerminal 文件 API 列目录wt download 通过 WebTerminal 文件 API 下载文件wt upload 通过 WebTerminal 文件 API 上传文件wt direct-info 输出脱敏后的直连协议材料,便于实验
