一个开发者每周花14小时盯着终端,只为确认AI代理没搞砸。这个数字来自OpenClaw作者的自述——他最终选择亲手解决这个问题。
为什么本地优先是唯一的出路
![]()
当前"AI代理控制面板"的主流做法很直白:把对话记录灌进云端数据库,再渲染一个仪表盘。Aerostack走了完全相反的路。
架构图很清晰:你的笔记本电脑运行开源网关(@aerostack/gateway),通过WebSocket连接到Cloudflare Durable Object,再连到iOS应用。Durable Object只存连接状态——哪台机器配哪部手机、最后活跃时间、当前会话ID。它不写入任何帧内容。
提示词、工具调用、模型输出、命令参数——这些全都不碰D1、R2或任何持久化存储。作者明确写道:"诱惑是'只存最近50帧用于调试',但你必须抵抗。一旦打破本地优先的契约,就无法挽回。"
唯一持久化的例外:审批决策元数据。时间戳、接受/拒绝、匹配了哪条glob规则、操作者是谁(团队工作场景用)。手机上的审计日志从这些元数据重建,从不从提示词本体重建。
LIVE面板的冻结之谜
第二阶段做的是实时视图:代理在笔记本上"思考"什么,手机实时可见。渠道接入(Slack消息、webhook、定时触发)→代理开始流式输出推理+工具调用→你在手机上看着它们落地。
单渠道运行时完美。第二个渠道在同一工作区激活时,手机上的LIVE面板冻结——不崩溃、不报错,只是停止流式更新。笔记本上的代理明显还在干活:守护进程日志狂飙,CPU跑满,但手机卡在90秒前的最后一个token。
作者的排查路径(全部错误):
WebSocket传输层→"帧被丢弃了"→实际全部送达
Durable Object→"状态损坏"→实际状态正常
iOS客户端→"SwiftUI列表渲染bug"→实际UI层无辜
真正的问题藏在网关层的流式处理逻辑。当第二个渠道激活时,网关错误地复用了同一个WebSocket连接标识,导致Durable Object把两个渠道的帧路由到同一个会话槽。手机端只订阅了"当前活跃会话",于是第二个渠道的流量被静默吞掉。
作者花了两天才定位到这里,因为症状太像网络层问题——冻结而非报错,且与渠道数量强相关。
glob策略与移动端审批的交互设计
审批流程的核心是glob匹配规则。用户可配置:所有rm -rf命令需审批、所有git push到main分支需审批、特定文件路径的写操作需审批。规则按优先级排序,首次匹配即触发审批弹窗。
移动端的关键交互:滑动审批+参数编辑。代理发起工具调用时,手机推送通知。用户左滑查看详情,可修改参数(比如把rm -rf /tmp/test改成rm -rf /tmp/test_backup),再右滑确认。glob规则在参数修改后重新评估——如果修改后的命令不再匹配高危规则,直接放行无需二次确认。
MCP(模型上下文协议)技能、插件、渠道的管理全部在手机端完成。作者的原话:"我不想开电脑只是为了加一个新的Slack webhook。"
这个架构选择意味着什么
Aerostack的本地优先不是技术洁癖,是对AI代理控制场景的深度理解。云端中转模式的隐含假设是"用户信任服务商",但代理的提示词包含代码库结构、内部API密钥、业务逻辑草稿——这些比聊天记录敏感得多。
Durable Object作为无状态透传层的设计,把信任边界划得很清楚:Cloudflare只看见加密连接的元数据,内容对他们是黑盒。如果哪天作者想加"最近50帧调试缓存",需要公开承诺且用户可见地撤销——这种设计让背叛成本极高。
冻结bug的两天排查,暴露的是分布式系统中"症状层"与"病因层"的经典错位。WebSocket连通、状态正常、UI无崩溃——所有可见层都健康,但业务语义已损坏。这种bug最难定位,因为它不留下痕迹。
作者最终修复的方式:为每个渠道分配独立会话命名空间,Durable Object维护"渠道ID→会话槽"的映射而非"连接ID→会话槽"。第二个渠道的流量不再覆盖第一个,LIVE面板恢复多路并行。
iOS应用上周已上线。作者没说下载量,但提了一个使用场景:睡前把手机放床头,代理处理长任务时推送进度,醒来查看结果——无需整夜开电脑。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.