1. 这个项目到底在解决什么问题
1.1 从“守着电脑等编译”说起
但凡用 Claude Code 或者 Codex 这类命令行 AI 编程助手干过活的人,大概都经历过这种场景:你在终端里敲下一段提示词,让它帮你重构一个模块、跑一遍测试、生成几张数据图表,然后你就只能坐在电脑前干等。它跑得快还好,几十秒出结果;要是碰上大项目,编译加测试跑个三五分钟很正常,你就被钉在椅子上了,想倒杯水、上个厕所、去阳台透口气都得惦记着屏幕。
更别扭的是,这类工具的输出往往是纯文本的。它说“我已经生成了图表”,你还得自己切到文件管理器里去找那张 PNG;它说“模拟器已经启动”,你还得把模拟器窗口从一堆窗口里翻出来。整个交互链路是割裂的:指挥在终端,产物在别处,人被困在电脑前。
我做的这个 App,核心就一件事:把“指挥 AI 编程助手”和“查看它产出的可视化结果”这两件事,从电脑前解放到手机上。你人可以在客厅、在通勤路上、在咖啡馆,掏出手机发一条指令,电脑那头的 Claude Code 或 Codex 就开始干活,干完把图表、模拟器截图、运行日志直接推到你手机屏幕上。这不是远程桌面那种笨重的方案,而是专门为“AI 编程助手工作流”定制的一条轻量通道。
1.2 它适合谁,不适合谁
先说清楚定位,免得有人装完发现不是自己想要的。
适合的人:日常用 Claude Code / Codex 做开发、跑数据脚本、做算法验证的开发者;经常需要让 AI 跑长时间任务(比如批量生成图表、跑仿真、训练小模型)的人;手头有一台常开的开发机或者家里有台性能还行的台式机的人。
不太适合的人:完全不用命令行 AI 助手的人;期望的是完整 IDE 体验、要在手机上改代码的人;对延迟极度敏感、要求毫秒级响应的人。这个 App 的定位是“指挥 + 监看”,不是“在手机上写代码”。
理解了这个边界,后面的技术选型和实现思路就顺理成章了。
2. 整体架构设计:为什么这么搭
2.1 三层结构:手机端、中转层、执行端
整个系统我拆成了三层,这个划分不是拍脑袋定的,而是被实际需求逼出来的。
第一层是手机端 App,负责展示会话列表、输入指令、渲染图表和截图。它不直接跟电脑通信,因为手机网络环境太复杂,直连电脑会碰到一堆问题:家里路由器没做端口映射、公司网络限制入站、电脑 IP 动态变化等等。所以手机端只跟中转层打交道。
第二层是中转层,这是整个架构的关键。它承担三个职责:消息队列(把手机指令排队发给电脑)、状态同步(电脑的执行进度回传给手机)、文件中转(图表和截图通过它转发)。中转层可以是一台云服务器,也可以是一个内网穿透服务。我实测下来,用一台最低配的云主机跑中转层完全够用,因为传输的都是文本指令和压缩后的图片,带宽压力很小。
第三层是执行端,也就是你电脑上跑的一个常驻小进程。它监听中转层的指令,收到后调用本地的 Claude Code 或 Codex 命令行,把输出捕获下来,遇到图片文件就读取并压缩上传,遇到模拟器就截屏上传。
为什么不让手机直连电脑?我试过,坑太多。手机切到 4G/5G 网络后,跟电脑不在同一个局域网,直连基本没戏;就算在同一 WiFi 下,电脑休眠、IP 变动、防火墙拦截都会让连接断掉。中转层虽然多了一跳,但换来的是稳定性——只要电脑能上网,手机能上网,这条链路就通。
2.2 为什么选“命令包装”而不是“API 对接”
有人可能会问:Claude Code 和 Codex 不是有 API 吗,为什么不直接调 API,非要在电脑上跑命令行?
这个问题我认真权衡过。直接调 API 看起来更干净,但会丢掉一大堆本地能力。Claude Code 和 Codex 的价值恰恰在于它们能操作你本地的文件系统、能跑你本地的编译器和测试、能访问你本地的 GPU 和模拟器。如果改成调 API,那 AI 就变成了一个纯文本生成器,它没法真正帮你跑代码、生成图表、启动模拟器。而这些“本地副作用”正是我想要在手机上看到的东西。
所以我的方案是:手机端只负责发指令和收结果,真正的执行还是在你电脑上的原生环境里跑。App 做的事情是给这个命令行进程套一层“遥控外壳”,把 stdin/stdout 和文件系统变化桥接到手机上。这样既保留了本地执行的全部能力,又获得了远程指挥的便利。
2.3 通信协议的选择:WebSocket 为主,HTTP 为辅
通信这块我用的是 WebSocket 长连接加 HTTP 短请求的混合模式。
WebSocket 负责实时性要求高的部分:指令下发、执行日志流式回传、进度百分比更新。这些数据的特点是“持续、小包、要求低延迟”,用 WebSocket 最合适。我实测下来,一条指令从手机发出到电脑开始执行,端到端延迟大概在 200 到 500 毫秒之间,取决于中转服务器的地理位置。
HTTP 负责大文件传输:图表 PNG、模拟器截图、较长的日志文件。这些数据的特点是“一次性、大包、可以容忍几秒延迟”。用 HTTP 的好处是可以利用标准的断点续传和缓存机制,图片加载失败还能重试。
这里有个细节值得说:图片上传前一定要压缩。我一开始偷懒直接传原图,一张 4K 截图好几兆,手机端加载慢得要命。后来改成上传前统一压到长边 1280 像素、JPEG 质量 80%,体积降到一两百 KB,加载体验立刻顺畅了。这个压缩在执行端做,不占用手机流量。
3. 核心功能拆解与实操要点
3.1 指令下发:怎么把一句话变成电脑上的执行
指令下发看起来简单,其实里面有不少讲究。手机端输入框里敲的内容,不能直接原样丢给命令行,得先做一层处理。
首先是会话隔离。你可能有多个项目同时在跑,每个项目对应一个独立的 Claude Code 会话。App 里用“会话”这个概念来管理,每个会话绑定电脑上的一个工作目录。你切到哪个会话,指令就发到哪个目录下执行。这个设计避免了指令串台——总不能让重构 A 项目的指令跑到 B 项目目录里去执行。
其次是指令模板。常用的操作我做了快捷模板,比如“跑一遍测试”“生成数据图表”“启动模拟器并截图”。这些模板本质上就是预置的提示词,点一下直接发送,省得每次手打。模板内容可以在设置里改,我自己的习惯是把项目里最常用的几条固定下来。
第三是执行确认。有些指令是有破坏性的,比如让 AI 删文件、改配置。App 里对这类指令做了二次确认,弹个框让你确认一下再发。这个功能救过我好几次,有一次手滑差点让 AI 把整个 build 目录清了。
实操上,指令下发的完整链路是这样的:手机端把指令文本加上会话 ID、时间戳,通过 WebSocket 发给中转层;中转层根据会话 ID 找到对应的执行端连接,转发过去;执行端收到后,把指令写入对应工作目录下的 Claude Code 进程的 stdin,同时在本地记录一条执行日志。
注意:执行端要处理好“指令排队”。如果上一条指令还在跑,你又发了一条,不能直接覆盖,得排队。我的做法是维护一个先进先出的队列,当前指令执行完再取下一个。界面上会显示队列长度,让你心里有数。
3.2 图表回传:让 AI 产出的图直接出现在手机上
这是整个项目里我觉得最实用的功能。以前让 Claude Code 生成一张图表,它跑完 Python 脚本,图存在本地某个目录,我还得自己去翻。现在执行端会自动监控指定的输出目录,一旦发现有新的图片文件生成,立刻读取、压缩、上传。
监控目录这块我用的是文件系统事件监听,不是轮询。轮询虽然实现简单,但要么频率高了费 CPU,要么频率低了有延迟。文件系统事件监听能在文件写入完成的瞬间触发,响应快而且几乎不占资源。不过要注意,有些程序写文件是“先写临时文件再重命名”,监听的时候要处理这种情况,不然会读到不完整的文件。我的做法是监听“重命名”和“关闭写入”这两个事件,而不是“创建”事件。
图片上传后,手机端按时间倒序排列展示。每张图下面标注生成时间、对应的指令、文件大小。点开可以看大图,支持双指缩放。对于数据图表这种需要看细节的,缩放功能很重要。
这里有个经验:图片的命名和归档要规范。我让执行端在上传时自动按“日期/会话名/时间戳.png”的规则重命名,这样手机端展示的时候天然就是有序的,找起来也方便。如果直接用原始文件名,一堆output.png、figure1.png混在一起,根本分不清哪张是哪次跑的。
3.3 模拟器画面:截屏 + 推流两种模式
模拟器画面的回传比分发图表复杂一些,因为它是动态的。我做了两种模式,适应不同场景。
截屏模式适合“我就想看一眼当前状态”的场景。执行端调用系统的截屏命令,把模拟器窗口截下来,压缩上传。这个模式延迟低、流量小,但只能看到某一瞬间的画面。
推流模式适合“我要持续观察模拟器运行”的场景。执行端以每秒 2 到 5 帧的频率连续截屏并上传,手机端按顺序播放,形成类似视频的效果。帧率可以调,网络好的时候调到 5 帧,看起来比较流畅;网络差的时候降到 2 帧,至少能看个大概。
推流模式有个坑:连续截屏很吃 CPU。我一开始用系统截屏命令,每秒截 5 次,电脑风扇直接起飞。后来改成用模拟器自带的截图接口,性能好很多。如果你的模拟器没有提供接口,那就只能降低帧率,或者只在需要的时候开推流,平时用截屏模式。
提示:推流模式下,手机端要做帧缓冲。因为网络抖动,帧到达的时间不均匀,直接播放会卡顿。我的做法是缓冲 3 到 5 帧再开始播放,这样即使某一帧晚到了,播放也能保持流畅。
3.4 执行日志的流式回传
日志这块我纠结过要不要做,因为 Claude Code 的输出有时候很长,全传过来手机上也看不过来。但后来发现,日志对于排查问题太重要了——AI 说“执行失败”,你得知道失败在哪一步。所以最后还是做了,但做了取舍。
日志分两级:摘要级和详细级。摘要级只传关键节点,比如“开始执行”“编译完成”“测试通过”“生成图表 3 张”“执行结束”。详细级传完整的 stdout 和 stderr。默认显示摘要级,你想看细节的时候点一下展开,再拉取详细日志。
这样设计的好处是,日常使用的时候手机端很清爽,不会被刷屏;出问题的时候又能拿到完整信息。详细日志在执行端会保留最近若干次,超过的自动清理,避免占满磁盘。
日志回传用 WebSocket 流式发送,每条日志带一个序号,手机端按序号拼接。如果中间有丢包,手机端能发现序号不连续,主动请求补发。这个机制保证了日志的完整性,不会出现“中间少了一段”的情况。
4. 实操过程:从零把这条链路跑通
4.1 执行端的部署与配置
执行端是整个系统的“手脚”,部署在你电脑上。我用的是常驻进程的方式,开机自启,后台运行。
部署步骤大致是这样:先把执行端程序放到一个固定目录,然后配置一个配置文件,里面填中转服务器的地址、认证令牌、工作目录列表。工作目录列表就是你想让 App 能操作的那些项目目录,每个目录配一个显示名称。配置好之后启动进程,它会主动连接中转服务器,建立 WebSocket 长连接。
这里有个关键点:认证令牌一定要设。中转服务器如果暴露在公网上,没有认证的话任何人都能连上来发指令,那你的电脑就成别人的了。令牌用一长串随机字符,配置在执行端和手机端,两边对上才放行。我还会定期轮换令牌,虽然麻烦点,但安全第一。
执行端启动后,会在日志里打印连接状态。看到“已连接中转服务器”就说明通了。如果一直连不上,先检查网络,再检查令牌,最后检查中转服务器是否正常运行。
4.2 中转层的搭建
中转层我用的是一台最低配的云主机,1 核 1G 内存,跑一个轻量的 WebSocket 服务加一个静态文件服务。这个配置应付个人使用绰绰有余,因为同时只有一个执行端和一个手机端连接,并发压力很小。
中转层的核心逻辑是“按会话 ID 路由”。每个执行端连接上来的时候,会注册自己支持的会话列表;手机端连接上来的时候,会订阅某个会话。中转层维护一张路由表,把手机端的指令转发到对应的执行端,把执行端的结果转发回对应的手机端。
文件传输走 HTTP,中转层提供一个上传接口和一个下载接口。执行端上传图片后拿到一个 URL,把这个 URL 通过 WebSocket 发给手机端,手机端再用这个 URL 去下载图片。这样图片不占用 WebSocket 通道,不会阻塞指令和日志的传输。
注意:中转层的磁盘空间要留意。图片文件会越积越多,得定期清理。我的做法是设置一个保留期限,比如 7 天,超过的自动删除。反正图片在执行端本地还有备份,中转层只是临时中转。
4.3 手机端的界面与交互
手机端我做得比较克制,没有堆太多功能,核心就是三个页面:会话列表、指令输入、结果展示。
会话列表页显示所有配置的会话,每个会话显示名称、当前状态(空闲/执行中)、最近一次执行的时间。点进去就是指令输入页,上面是历史指令记录,下面是输入框和快捷模板按钮。执行结果页分两个标签:图表和日志。图表标签展示图片,日志标签展示执行日志。
交互上我特别在意一点:状态要实时。执行中的时候,会话列表页那个会话要显示一个转圈的进度指示;执行完了要变成“完成”并显示耗时。这些状态通过 WebSocket 实时推送,不需要手动刷新。我试过手动刷新的版本,用起来很别扭,总担心自己看到的是不是最新状态。
4.4 一次完整的执行流程实录
拿一个真实场景走一遍:我在手机上想让电脑跑一个数据可视化脚本,生成三张图表。
第一步,打开 App,进入“数据分析”会话,点“生成图表”快捷模板。指令内容是预置好的提示词,大意是“运行 visualize.py,生成三张图表到 output 目录”。
第二步,指令通过 WebSocket 发到中转层,中转层转发到执行端。执行端把指令写入 Claude Code 进程,同时回传一条“开始执行”的日志。
第三步,Claude Code 开始跑脚本。执行端监控 output 目录,发现第一张图生成,立刻读取、压缩、上传。手机端收到图片 URL,下载并展示。接着第二张、第三张陆续到达。
第四步,脚本跑完,Claude Code 输出“完成”。执行端回传“执行结束”日志和总耗时。手机端把会话状态改回“空闲”。
整个过程从点按钮到三张图全部显示,大概十几秒。这十几秒里我人可以不在电脑前,手机揣兜里,图生成完了会有通知提醒。
5. 踩过的坑与排查技巧
5.1 连接不稳定:先查这三处
连接问题是反馈最多的。手机端显示“连接中”转半天,或者用着用着突然断了。排查顺序我总结成三步。
第一步查中转服务器。登录云主机,看服务进程还在不在,看端口有没有被占用。有时候服务莫名其妙挂了,重启一下就好。我后来加了个守护脚本,进程挂了自动拉起来。
第二步查执行端。看执行端的日志,有没有报错。常见的是网络波动导致 WebSocket 断开,执行端要能自动重连。我一开始没做重连,断了就得手动重启,很烦。后来加了指数退避重连,断了之后隔 1 秒、2 秒、4 秒、8 秒依次重试,基本能自动恢复。
第三步查手机网络。手机切到弱网环境,WebSocket 容易断。App 里做了断线提示,断了会显示“连接已断开,正在重连”,重连成功自动恢复。用户不用做任何操作。
5.2 图片显示不出来:多半是这三个原因
图片上传了但手机上看不到,我遇到过几次,原因基本就三种。
一是压缩失败。执行端的图片压缩依赖一个图像处理库,如果库没装好或者版本不对,压缩会报错,图片就传不上去。解决办法是检查依赖,确保图像库正常。我在执行端启动的时候加了个自检,压缩库有问题直接日志报错,省得后面抓瞎。
二是 URL 拼错。中转层返回的图片 URL 如果拼接有误,手机端下载会 404。这个排查简单,把 URL 复制到浏览器里访问一下就知道。我后来在中转层加了 URL 校验,生成的时候确保格式正确。
三是手机端缓存。有时候图片其实下载成功了,但手机端缓存了旧的失败状态,一直显示占位图。清一下 App 缓存就好。这个体验不好,后来我改成下载失败自动重试两次,还失败才显示错误。
5.3 模拟器截屏黑屏:权限和时机问题
模拟器截屏截出来是黑的,这个坑我踩得比较深。
权限问题:有些系统对截屏有权限限制,执行端进程如果没有相应权限,截出来就是黑的。解决办法是把执行端加到截屏权限白名单里,或者用模拟器自带的截图接口绕过系统截屏。
时机问题:模拟器刚启动的时候,画面还没渲染出来,这时候截屏就是黑的。我的做法是截屏前先检测模拟器窗口是否已经渲染完成,没完成就等一会儿再截。检测方法因模拟器而异,有的是查窗口句柄,有的是查进程状态。
多显示器问题:如果电脑接了多个显示器,截屏的时候要指定截哪个屏,不然可能截到别的屏幕上。这个在执行端配置里指定显示器编号就行。
5.4 常见问题速查表
| 现象 | 可能原因 | 排查方法 | 解决方式 |
|---|---|---|---|
| 手机端一直显示连接中 | 中转服务挂了 | 登录云主机看进程 | 重启服务,加守护脚本 |
| 指令发出后电脑没反应 | 执行端断连 | 看执行端日志 | 等待自动重连或手动重启 |
| 图片上传失败 | 压缩库异常 | 看执行端报错 | 检查图像库依赖 |
| 图片显示 404 | URL 拼接错误 | 浏览器访问 URL | 修正中转层 URL 生成逻辑 |
| 模拟器截屏黑屏 | 权限或时机问题 | 换截屏方式测试 | 加权限或用模拟器接口 |
| 日志中间缺失 | WebSocket 丢包 | 看日志序号是否连续 | 手机端请求补发 |
| 推流卡顿 | 帧率过高或网络差 | 看帧率和网络 | 降帧率或改截屏模式 |
6. 几个提升体验的细节设计
6.1 通知机制:干完活主动告诉你
这个功能看起来小,但用起来幸福感提升很大。执行端在任务完成或者失败的时候,会通过中转层给手机端推一条通知。手机端收到后弹一个系统通知,你锁屏状态下也能看到。
通知内容我做了精简,只包含会话名、状态、耗时。比如“数据分析:完成,耗时 12 秒”。点通知直接跳到对应的结果页。失败的通知会带上错误摘要,方便你判断要不要立刻处理。
这里要注意通知的频率。如果任务很多,通知刷屏会很烦。我加了个设置,可以按会话开关通知,也可以设置“只在失败时通知”。默认是全部通知,你可以按自己的习惯调。
6.2 历史记录:找回上次的结果
有时候你想看昨天跑的那张图,或者想对比两次执行的结果。App 里做了历史记录,按时间倒序保存每次执行的指令、日志摘要、生成的图片。
历史记录存在手机本地,不占中转层空间。图片是懒加载的,你翻到哪张才下载哪张,不会一打开就下载几百张图。历史记录可以按会话筛选,也可以搜索指令关键词。
我自己的用法是,每次跑完重要的任务,如果结果不错,就手动标记一下,加个星标。这样以后要找的时候,直接看星标记录就行,不用在几百条历史里翻。
6.3 多电脑支持:一台手机管多台机器
如果你有多台电脑,比如一台台式机跑重任务,一台笔记本随身带,App 支持配置多个执行端。每个执行端注册到中转层的时候带一个设备名,手机端可以按设备筛选会话。
这个功能实现上不难,就是路由表里多一个设备维度。但用起来很实用。我在台式机上跑训练任务,在笔记本上跑日常开发,手机上切换一下就能分别指挥,不用装两个 App。
提示:多设备的时候,设备名要起得清楚,比如“台式机-训练”“笔记本-开发”。不然时间长了你自己都忘了哪台是哪台。
7. 后续可以扩展的方向
这套东西跑通之后,我发现能扩展的地方还挺多。
一是支持更多 AI 助手。目前接的是 Claude Code 和 Codex,但架构上是通用的——只要一个命令行工具能从 stdin 读指令、往 stdout 写输出,就能接进来。后面想试试接其他的命令行 AI 工具,把选择权交给用户。
二是增加语音指令。手机上打字毕竟不如说话快,尤其是长指令。可以加个语音输入,转成文字后发送。这个技术上不难,主要是要处理好语音识别的准确率,尤其是技术术语的识别。
三是做执行结果的自动分析。现在图片传回来是给人看的,但有些场景下可以让 AI 自己分析结果。比如跑完测试,让 AI 读一下测试报告,直接告诉你“通过了 95%,有 3 个失败用例,分别是……”。这样你连图都不用看,直接看结论。
四是支持定时任务。有些任务你想让它每天固定时间跑,比如每天早上跑一遍回归测试。可以在执行端加个定时调度,到点自动执行,结果推送到手机。这个功能我还在琢磨,主要是要处理好任务失败的重试和告警。
这套方案我从有想法到跑通大概花了两三个周末,中间踩的坑基本都写在上面了。核心思路其实不复杂:用中转层解决网络连通性,用命令包装保留本地能力,用 WebSocket 加 HTTP 的混合模式兼顾实时性和大文件传输。真正花时间的是各种边界情况的处理——断线重连、图片压缩、日志补发、权限问题,这些才是决定好不好用的关键。
如果你也在用命令行 AI 助手,又经常被绑在电脑前,可以试试这个思路。不一定非要照搬我的实现,理解了这个架构,用你熟悉的技术栈搭一套自己的版本,可能更顺手。