设计服务 赛事服务
哇蓝实验室 哇蓝知识库 EN
  1. 面向受限平台的 Agent Harness 改造方法论 —— 以 Android 为例
  2. 一、方法论总览
  3. 二、差异盘点:把"平台假设"写成表
  4. 三、抽象层:切在变化轴上
  5. 3.1 两条轴必须分开
  6. 3.2 四条设计纪律
  7. 四、契约化:把不变量写死
  8. 五、可靠性分层:状态 / 副作用 / 恢复
  9. 5.1 状态层:让每一步都可恢复
  10. 5.2 副作用层:定义崩溃窗口,然后堵住它
  11. 5.3 恢复层:判定与互斥
  12. 六、资源治理:分级预算与租约
  13. 七、可验证性:判定依据先于实现
  14. 八、反模式清单
  15. 九、迁移到下一个平台的检查清单
  16. 结语
作者:小檬 XMeng · 墨迹 Moji发布时间:2026-10-14约 17 分钟阅读

面向受限平台的 Agent Harness 改造方法论 —— 以 Android 为例

把一个在桌面或服务器上运转良好的 Agent 运行时搬到移动端,难点通常不在模型,而在平台假设的全面失效:
进程不再长驻、存储不再任意可写、运行时不再完整、后台不再自由、工具面不再由操作系统提供。

本文不展开某一款产品的实现细节,而是把一轮真实的 Android 化改造(把一个微内核 Agent 框架的
ReAct 核心抽成跨平台 Harness)沉淀成一套可复用的方法论:怎么盘点差异、怎么切抽象层、
怎么设计可靠性、怎么验证、以及哪些反模式一定会咬人。


一、方法论总览

平台化改造容易失败在"边写边发现":写到最后才发现某处假设在两个平台上不可能同时成立。
我们最终收敛成五步,顺序本身就是方法:

步骤 做什么 交付物
① 差异盘点 把运行时的隐含假设逐条写出来,而不是先写代码 平台能力矩阵(同时就是抽象层的需求清单)
② 抽象层切分 沿"变化轴"切开:平台能力 / 领域形态 一组零平台类型的接口 + 宿主适配器
③ 契约化 把"必须永远成立的事"写成契约与不变量 接口契约文档 + 安全默认值
④ 可靠性分层 按"状态 / 副作用 / 恢复"三层各自设计,而不是笼统地"加异常处理" 三层机制(快照、写前记录、分级恢复)
⑤ 可验证性设计 判定依据先于实现;门禁与它的边界一起写 纯函数判定、契约链测试、构建期门禁

贯穿五步的一条衡量标准:换一个宿主时,需要改的是适配器还是核心?
只需改适配器,说明抽象层切对了。


二、差异盘点:把"平台假设"写成表

不要从代码结构出发,而是把运行时依赖的隐含假设列出来,逐条追问:
这项假设还成立吗?不成立时的替代物是什么?替代物该由哪一层承担?

以 Android 相对桌面(PC/JVM)为例,通用维度是这九项:

假设维度 桌面上的默认 受限平台上的现实 替代物落在哪一层
进程寿命 长驻,崩溃是异常 被杀是日常(后台回收、内存压力、厂商省电策略) 可靠性层:可恢复的快照与副作用记账
存储语义 任意路径可读写、对外可见 私有目录 + 范围存储;对外不可见的目录等于"没交付" 平台层:逻辑路径 → 物理路径的映射与可见性策略
运行时完整性 完整标准库 缺少高版本 API,且发布构建会裁剪类 平台层:能力探测 + 降级 + 如实上报
后台执行 基本无限制 会被挂起、延迟、限制启动 宿主层:任务级租约、看门狗、用户授权引导
能力面 操作系统提供完整工具链 能力多为应用自定义(注册表实现或系统子集) 领域层:工具协议 + 能力缺口如实上报
人机交互 随时能问人 常常无人在场(锁屏、后台、无人值守) 平台层:确认门,且必须默认拒绝
资源预算 相对宽裕 电量、内存、流量都紧 全层:分级预算与截断策略
扩展机制 可跨平台 动态加载多为平台专有 领域层:只保留一个跨平台的扩展点
设备一致性 一个系统一种行为 同一安装包在不同 ROM 上行为可能不同 宿主层:真机巡检与灰度交付

这张表的用处不是"参考",而是需求清单:每一行的"替代物落点"都会变成抽象层里的一项能力。
盘点做得越早,后期返工越少 —— 我们踩到的多数问题,本质都是某一行假设从未被写下来。


三、抽象层:切在变化轴上

3.1 两条轴必须分开

平台化最容易犯的错,是把两种变化混在一个抽象里:

混轴的后果很具体:换一种工具形态,就要改平台实现;换一个平台,又要重写工具层。
正确的切法是能力用聚合值对象注入,形态用执行协议隔离,核心只认接口。

3.2 四条设计纪律

  1. 能力注入,不用全局单例。全局可变状态会让同进程无法安全地跑两个独立实例
    (多租户、测试隔离全部失效)。新能力一律作为可替换的字段加进聚合根,并带默认值。
  2. 核心零平台类型,用构建期门禁强制。但门禁一定有边界:例如只匹配导入语句时,
    全限定调用与间接引用都抓不到。边界必须写进文档 —— 否则下一个人会把"门禁绿"
    读成"零平台依赖",而这恰恰是最危险的误解。
  3. 新增能力必须带安全默认:确认类能力默认拒绝、存储类能力默认内存实现、
    危险能力默认关闭。默认值的选择标准是"漏配时不出事",不是"跑 demo 方便"。
  4. 最小可用面尽量小:把必填项压到最低(例如只要求"文件系统 + 路径解析"两项),
    其余全部有安全默认。必填项越多,接入越容易出错。

四、契约化:把不变量写死

抽象层解决"能不能接",契约解决"接上以后会不会出事"。值得写成契约的有五类:

① 安全默认的方向性。 需要人来决策才能放行的操作,在无人在场时必须拒绝,
并且要区分"用户明确拒绝""无人可问""超时未决"三种结果 —— 它们的审计语义与对模型的措辞都不同。
把确认门改成"永远允许"能让测试全绿,但那是安全漏洞,不是捷径。

② 副作用协议的一致性。 工具执行器不用抛异常表达业务失败(用结果对象),
因为异常会把"工具失败"升级成"循环失败";但取消异常必须原样向上传播,否则"停止"语义失效。
这类规则要写进接口契约,而不是留给实现者猜。

③ 状态机的最小集。 例如"只有进行中的任务可以被接续,终态不可续"、
"终态恰好写一次(放在 finally 里收口)"。状态机越小,出错面越小。

④ 幂等与修复。 同一标识的重复写入必须保留既有状态(并发与重放都会触发);
追加式日志在写入前要确认上一条记录的完整性(崩在写一半时,新记录会粘在残行上,两条一起报废)。

⑤ 顺序即语义。 副作用记账必须"执行前写、执行后写",差一步就退化成事后记账,
崩溃窗口依然裸奔;循环里的终止分支若从"返回"改成"跳出",会继续执行循环之后的收口代码,
静默覆盖已得结论 —— 这类顺序问题只能靠契约与测试守住。

还有一条元契约:可靠性增强组件不得成为新的失败点。
快照写失败、日志写失败都不应该中断主任务,但必须留下可见的上报通道(默认静默是可接受的,
静默且不可观测不是)。


五、可靠性分层:状态 / 副作用 / 恢复

"进程随时会死"这一个事实,需要三层分别应对。三层缺一层都会留下真实的损失:

5.1 状态层:让每一步都可恢复

5.2 副作用层:定义崩溃窗口,然后堵住它

工具副作用不可回滚,因此**"执行后记录"永远不够**:

5.3 恢复层:判定与互斥


六、资源治理:分级预算与租约

移动端资源治理有两条通用做法:

① 预算是分层的,且每层的截断行为必须明确规定。
同一个"输出太大"的问题,在不同层有不同的上限与不同的处理(标注截断 / 静默截断 / 落盘留路径)。
不统一的后果是排查者永远说不清"输出到底丢在哪一层"。
一个实用的默认策略是:近期内容保完整、久远内容压摘要、超限内容离屏存放并给出可检索的位置。

② 保活不是"一直持锁",而是"活跃时才持锁"。
把保活建成任务状态驱动的租约,参数每一项都要有理由,而不是照抄经验值:

参数 取值思路
空闲释放 覆盖任务收尾与用户连续追问的间隔;固定长持锁的耗电可以差一个数量级
单次租期 到期必须重新判定,绝不无限持锁到电量耗尽
续租阈值 留出足够安全余量,保证看门狗周期一定小于它
看门狗 与系统既有唤醒同频(不额外增加唤醒次数),并在每次唤醒时确认"真的有活"
精确定时 无精确权限时回退免权限的模糊定时 —— 后者同样能穿透省电模式,只是精度放宽

再补三条边界:冷启动不预持锁(无任务时零耗电);权限引导只做一次并保留手动入口;
所有策略抽成显式传时的纯函数,从而可以脱离负载测试。

③ 观测要回答"值不值",而不是"看起来快不快"。
指标里必须有历史最坏值与最近一次值 —— 平均值会被"大多数步都很快"稀释,
而真机上的卡顿恰恰来自最坏那一步。埋点本身守一条纪律:只自增、不改变控制流。


七、可验证性:判定依据先于实现

平台化改造的验证有几条与常规业务开发不同的要求:


八、反模式清单

方法论最实用的产出,是一份"不要这样做"的清单。以下是这一轮改造中真实遇到过的:

反模式 后果 正确做法
同一份抽象层存在两份副本,靠"记得同步" 静默语义分叉,测试不会变红 单一事实源;搬运是移动不是复制
危险能力默认打开,或提供"自助开启"的元工具 模型可自行提权,开关形同虚设 默认关闭 + 结构化开启指引 + 需用户显式开启
把确认门写成"永远允许"或默认放行 无人值守场景下高危操作静默执行 fail-closed,并区分拒绝 / 无人可问 / 超时
可靠性组件自身抛错 磁盘满、权限不足变成新的崩溃源 快照/日志失败不中断主任务,但可观测
只记录"执行后"的副作用 崩溃窗口内重复执行(重复写、重复发) 执行前记账 + 恢复时只提示核对、禁止自动重放
用文件系统时间戳反推身份 有损映射导致串档 / 串会话 内容为权威,文件名只做定位
路径安全只做字符串前缀匹配 .. 与相对路径可绕过 真实路径规范化 + 按工作目录解析 + 无基准不判定
同一个判定只挂在一条通路上 另一条通路完全绕过保护 单一判定入口,所有通路共用
高版本 API 直接静态引用 构建期裁剪报错、运行期缺类 能力探测 + 降级 + 如实上报
用平均值评估性能 最坏情况被掩盖 记历史最坏值与最近值
只做单元测试就宣称链路可用 跨组件的键/读法/默认值不一致 契约链集成测试
门禁覆盖不全且不写边界 门禁绿 ≠ 无平台依赖 覆盖全部子包 + 把盲区写进文档
全局单例承载可变状态 多实例 / 测试隔离失效 能力聚合根注入
把"进程长期存活"当默认前提 长任务一断就从头再来 每步可恢复 + 分级续跑

九、迁移到下一个平台的检查清单

把上面各节收敛成一份可以直接照着做的清单:

  1. 把运行时的隐含假设列成表,每行写清"不成立时的替代物落在哪一层";
  2. 抽象层沿"平台能力 / 领域形态"两条轴切开,核心保持零平台类型;
  3. 每条平台能力都带安全默认值;必填项压到最少;
  4. 把确认、副作用、状态机、幂等、顺序这五类不变量写成契约;
  5. 可靠性按状态 / 副作用 / 恢复三层实现,缺一层都不算完成;
  6. 快照按"最坏损失"定粒度,并做分级截断(协议要求的字段显式豁免);
  7. 恢复判定做成纯函数,跨进程互斥用"文件 + 回读 + 进程内监视器"组合;
  8. 保活的每个参数都有理由,精确定时准备好降级路径;
  9. 输出与上下文按层设预算,并规定截断行为(标注 / 静默 / 离屏);
  10. 观测必须包含最坏值,埋点不得改变控制流;
  11. 判定逻辑纯函数化、时钟显式化,边界靠假时钟而不是真实等待;
  12. 跨组件补契约链测试,关键判定做破坏验证;
  13. 构建期门禁覆盖全部子包,并把盲区写进文档;
  14. 给模型看的文本(提示词 / 文档 / 索引)与实现纳入静态一致性扫描;
  15. 明确声明真机验证的边界与替代手段(巡检 + 灰度 + 用户反馈)。

结语

把 Agent 搬上一个新的平台,考验的不是模型能力,而是工程上对平台现实的尊重程度:
进程会死,所以每一步都要可恢复;后台会被限,所以要用租约而不是长持锁;
权限要运行时申请,所以"声明"不等于"可用";能力面由应用自定义,所以给模型看的文本必须与实现同步;
路径、类库、进程间通信都与桌面不同,所以平台能力从第一天起就该走注入,而不是硬编码。

最终标准只有一条:换一个宿主时,你改的是适配器,还是核心。

AI