鸿蒙 AI 自动化开发:让 AI 学会自己编译、自己调试
当前 AI 开发流程的真正瓶颈
用 AI 辅助编码半年后,大多数开发者都有一个感受:AI 写代码已经够用了,但围绕 AI 的人工配合太累了。
一个典型的场景:你让 AI 在首页加个下拉刷新,它几秒钟就写出了代码。然后——
- 你来编译,报错了。你把错误信息复制给 AI,等它改。
- 改完再编译,又报错。你再复制,再等。
- 终于编译通过了。你手动启动应用,导航到首页,操作一遍——发现 UI 不对。你截图,描述给 AI,再等它改。
- 改完再编译、再部署、再手动验证……
整个过程里,AI 只负责一件事:生成代码。而编译、部署、运行、看界面、判断对不对——全是你在做。你本质上在给 AI 当编译器、调试器和 QA。
你的日常工作流:
你 → 告诉 AI 需求
AI → 生成代码
你 → 编译 ← 人工
你 → 看报错 ← 人工
你 → 把报错复制给 AI ← 人工
AI → 修复代码
你 → 再编译 ← 人工
你 → 部署到设备 ← 人工
你 → 打开应用看界面 ← 人工
你 → 看日志排查问题 ← 人工
你 → 截图 + 复制日志给 AI ← 人工
AI → 再修复
...(重复直到你满意)问题很明显:AI 能写代码,但它看不见自己写的代码跑起来是什么样。它不知道编译有没有过,不知道界面长什么样,不知道日志里有没有报错,不知道功能对不对。每一次反馈都需要你手动介入——你就是 AI 的眼睛、耳朵和手。
换个思路:让 AI 自己编译、自己看界面
如果 AI 能自己编译呢?编译报错了,它自己读错误信息、自己改、自己重新编译——不需要你复制粘贴。
如果 AI 能自己部署运行呢?改完代码,它自己把应用装到设备上、自己启动。
如果 AI 能自己"看"界面呢?它能看到当前页面上有哪些元素、文字对不对、按钮在不在、布局是否符合预期——不需要你截图描述。
如果 AI 能自己读日志呢?它能自己抓取运行日志,看到崩溃堆栈、网络请求结果、业务流程走到了哪个分支——不需要你复制粘贴日志。
换句话说:不是让流水线替人做事,而是教会 AI 使用开发者每天都在用的那些工具——hvigor 编译、hdc 部署、uitest 查看 UI、hilog 读日志。AI 一旦拥有了这些能力,它就不再是一个只能输出代码片段的文本生成器,而是一个能独立完成「编码 → 编译 → 部署 → 验证 → 修复」循环的自主开发者。
新的工作流:
你 → 告诉 AI 需求
AI → 生成代码
AI → 自己编译 ← AI 自己做
AI → 编译报错?自己读错误、自己改、自己重编译 ← AI 自己做
AI → 编译通过 → 自己部署到设备 ← AI 自己做
AI → 自己查看 UI 是否符合预期 ← AI 自己做
AI → 自己读日志,检查有没有异常 ← AI 自己做
AI → 不对?自己分析 UI 树 + 日志、自己修复 ← AI 自己做
AI → 输出验证报告(含截图 + 关键日志)
你 → 看一眼报告,确认通过你的角色从「AI 的编译器和眼睛」变成了「最终审批者」。
给 AI 四种能力
要实现这个转变,需要给 AI 补上四种它目前不具备的能力:
能力一:自己编译、自己修
鸿蒙的构建工具 hvigor 可以通过命令行调用:
./hvigorw --mode project -p product=develop -p buildMode=debug assembleApp编译输出是结构化的——包含文件路径、行号、错误类型和详细描述。AI 完全可以自己跑这个命令,自己读输出,自己定位问题。
具体流程:
AI 执行 hvigorw 编译
↓
编译失败 → AI 解析错误输出
↓
AI 读取报错文件的对应代码
↓
AI 修改代码 → 重新编译
↓
循环直到编译通过(或达到上限,比如 5 轮)这里有一个关键细节:不需要把整个文件发给 AI。编译错误已经告诉你哪个文件哪一行出了什么问题,只需要取错误行上下 ±20 行源码就够了。一个 2000 行的文件只发 40 行,token 消耗降 98%。
另一个细节是错误去重。一个类型定义错误可能触发十几个下游报错(「找不到属性」「类型不匹配」……),全部喂给 AI 只会干扰判断。正确做法是按优先级排序,每轮只处理 Top 错误,修完重编译看残留。
能力二:自己部署、自己运行
编译通过后,AI 需要把应用装到设备上并启动。HarmonyOS 的 hdc(HarmonyOS Device Connector)提供了完整的命令行接口:
# 安装应用
hdc install entry/build/default/outputs/default/entry-default-signed.hap
# 启动应用
hdc shell aa start -a EntryAbility -b com.tjgd.mobilez.wisetv.hm这两条命令就是开发者每次手动「跑一下」的自动化版本。AI 执行完就能拿到一个正在运行的应用实例,接下来该「看」界面了。
能力三:自己"看"界面、自己判断
这是最关键、也最有价值的能力。AI 怎么"看"界面?
不是靠截图识别——视觉模型的 UI 理解能力不够稳定,容易误判。
而是靠 UI 树(可访问性树)。HarmonyOS 的 uitest 工具可以把当前页面的 UI 层级结构导出为 JSON:
uitest dumpLayout输出的 JSON 是一棵树,每个节点包含:
- 组件类型(Text、Image、Button、List 等)
- 文本内容(
text属性) - 元素 ID(
id属性) - 位置坐标(
bounds) - 是否可见、是否可点击等状态
AI 读这棵树,就像开发者用 DevTools 检查元素一样——它能看到页面上有什么文字、按钮在哪里、列表是否渲染了数据。这比截图对比稳定得多,因为文字断言不受字体、颜色、分辨率的影响。
除了"看",AI 还能"操作":
# 点击指定坐标
uitest uiInput click 540 1200
# 滑动
uitest uiInput swipe 540 1500 540 500 500
# 输入文字
uitest uiInput text "搜索内容"AI 可以模拟用户操作,然后重新 dump UI 树,验证操作后的页面状态是否符合预期。
能力四:自己读日志、自己分析
UI 树告诉 AI「界面上有什么」,但无法告诉 AI「程序内部发生了什么」。一个按钮在页面上确实存在,但点击后没有反应——是回调没触发?是网络请求失败了?还是数据解析出了异常?这些信息只存在于运行日志里。
开发者日常调试时,最常用的手段之一就是盯着 DevEco Studio 的 Log 面板看输出。AI 同样可以做到——HarmonyOS 的 hilog 提供了完整的命令行接口:
# 实时抓取日志,按应用进程过滤
hdc shell hilog | grep "com.tjgd.mobilez.wisetv.hm"
# 按日志级别过滤(只看 Error 和 Warn)
hdc shell hilog --level E
# 按 domain/tag 过滤
hdc shell hilog --tag "WSRequest"
# 导出日志到文件
hdc shell hilog -e > /tmp/app.logAI 读日志能覆盖的场景,是 UI 树和截图覆盖不了的:
| 问题类型 | UI 树能发现? | 日志能发现? |
|---|---|---|
| 按钮文字错误 | 能 | 不能 |
| 元素缺失/布局错乱 | 能 | 不能 |
| 点击后无响应(回调未触发) | 不能 | 能 |
| 网络请求失败/超时 | 不能 | 能 |
| 数据解析异常(JSON 字段缺失) | 不能 | 能 |
| 空指针/类型转换崩溃 | 不能 | 能 |
| 状态未更新(业务逻辑 bug) | 不一定 | 能 |
换句话说:UI 树是 AI 的"眼睛",日志是 AI 的"听诊器"。一个看外表,一个听内里。两者配合,AI 才能做出完整的诊断。
实际调试中,AI 的典型做法是:
1. 操作前:清空日志缓冲区(hilog -r)
2. 执行操作:click / swipe / 页面跳转
3. 操作后:抓取这段时间内的日志(按时间范围或 tag 过滤)
4. AI 分析日志:
- 有 ERROR/Exception → 定位崩溃原因,读对应源码,修复
- 有网络请求的 URL 和响应码 → 判断接口是否异常
- 有 xlog 输出的业务日志 → 判断业务流程是否走到预期分支
- 日志正常但 UI 不对 → 结合 UI 树分析渲染问题这里有一个实用技巧:让 AI 在生成业务代码时,在关键分支埋入带特定 tag 的日志。比如:
// AI 生成的代码中自动加入调试日志
xlog.info('PullRefresh', '开始刷新,当前页码: ${this.page}')
// ... 请求完成后
xlog.info('PullRefresh', '刷新完成,返回 ${data.length} 条数据')这样 AI 在验证阶段读日志时,就能直接看到业务流程是否按预期执行。相当于 AI 给自己装了"调试探针"——它知道自己在哪些关键位置留了日志,读日志时就能快速定位问题。
需要注意的是,日志量可能很大。一个应用启动后几秒内就可能产生上千条日志。全部发给 AI 会浪费大量 token。正确做法是预处理:按 tag 过滤只保留应用自身的日志,按级别过滤优先保留 Error 和 Warn,截取操作前后的时间窗口。通常经过这些过滤后,日志量可以从几千行压缩到几十行,足够 AI 做出准确判断。
实现方案:Python 封装层 + AI 调用
为什么用 Python
主项目是 ArkTS,但验证层我们选择了 Python。原因很实际:
hdc和uitest的 Python 封装生态更成熟,社区现成方案多- 验证脚本与业务代码解耦,独立维护,不会污染项目代码
- AI 可以在同一轮对话中同时生成 ArkTS 业务代码和 Python 验证脚本
封装层设计
Python 层封装一个 HdcHelper,把 hdc 和 uitest 的命令行调用包装成语义化的函数:
class HdcHelper:
def build(self) -> BuildResult:
"""执行 hvigorw 编译,返回结构化的编译结果"""
...
def install_and_launch(self, bundle_name: str):
"""安装 HAP 包并启动应用"""
...
def dump_layout(self) -> dict:
"""导出当前页面的 UI 树"""
...
def screenshot(self, name: str) -> str:
"""截图并返回本地路径"""
...
def click_text(self, text: str):
"""在 UI 树中找到指定文字并点击"""
...
def has_text(self, text: str) -> bool:
"""检查当前页面是否存在某文案"""
...
def swipe(self, x1, y1, x2, y2, ms: int):
"""模拟滑动手势"""
...
def clear_log(self):
"""清空日志缓冲区"""
...
def get_log(self, tag: str = None, level: str = None) -> str:
"""抓取日志,可按 tag/级别过滤"""
...
def get_errors(self) -> str:
"""只获取 Error 级别日志"""
...AI 的自主调试循环
有了这些能力,AI 的工作方式变成了:
1. AI 根据需求生成/修改代码(同时在关键分支埋入调试日志)
2. AI 调用 build() 编译
- 失败 → AI 读错误信息,读对应源码,修改代码,回到 2
- 成功 → 继续
3. AI 调用 install_and_launch() 部署并启动应用
4. AI 调用 clear_log() 清空日志缓冲区
5. AI 执行操作(click / swipe / 页面跳转)
6. AI 调用 dump_layout() 查看当前页面 UI 状态
7. AI 调用 get_log() / get_errors() 抓取运行日志
8. AI 综合诊断:
- UI 树异常 → 修改代码,回到 2
- 日志中有 ERROR/Exception → 定位崩溃或逻辑错误,修改代码,回到 2
- 日志中业务流程未走到预期分支 → 修改代码,回到 2
- UI 和日志均正常 → 继续
9. AI 调用 screenshot() 截图留档
10. AI 输出验证报告(含截图 + UI 树快照 + 关键日志片段)注意:这个循环里没有人。不是人编译了告诉 AI 结果,不是人看了界面告诉 AI 哪里不对,不是人翻了日志告诉你哪里报错。AI 自己编译、自己看 UI 树、自己读日志、自己综合诊断、自己修复。
验证脚本也可以 AI 生成
对于复杂场景,AI 在生成业务代码的同时,可以同步生成一个 Python 验证脚本:
def test_home_pull_to_refresh():
hdc = HdcHelper()
hdc.install_and_launch("com.tjgd.mobilez.wisetv.hm")
# 等待首页加载
assert hdc.has_text("推荐"), "首页未加载"
hdc.screenshot("01_home_loaded")
# 清空日志,准备捕获刷新操作的日志
hdc.clear_log()
# 执行下拉操作
hdc.swipe(540, 1500, 540, 500, 500)
# 检查日志:确认刷新流程走到了预期分支
log = hdc.get_log(tag="PullRefresh")
assert "刷新完成" in log, "刷新流程未完成,请检查日志"
# 验证 UI:刷新后数据更新
assert hdc.has_text("刚刚更新"), "下拉刷新未生效"
hdc.screenshot("02_after_refresh")这个脚本本身也是 AI 生成的——AI 知道需求是「下拉刷新」,所以它知道要验证「下拉后数据更新」。脚本运行后的结果(通过/失败、截图、UI 树快照)同样可以作为 AI 自我修复的输入。
整体架构
┌──────────────────────────────────────────────┐
│ 开发者 │
│ 输入需求描述 → 等待 → 审批最终结果 │
└─────────────────┬────────────────────────────┘
│
┌─────────────────▼────────────────────────────┐
│ AI 自主开发循环 │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 生成代码 │──→│ 自己编译 │──→│ 自己部署 │ │
│ │ +埋日志 │ └────┬─────┘ └────┬─────┘ │
│ └──────────┘ │ │ │
│ ↑ 编译失败? │ │
│ │ 自己读错误 │ │
│ │ 自己修改 │ │
│ │ │ │ │
│ │ └──→ 回到修改 ──┘ │
│ │ │ │
│ │ ┌────▼──────┐ │
│ │ │ 清空日志 │ │
│ │ │ 执行操作 │ │
│ │ └────┬──────┘ │
│ │ │ │
│ │ ┌──────────▼───────┐ │
│ ├──── 不对?自己改 ←│ 自己看 UI + 读日志 │ │
│ │ │ UI 树 + hilog │ │
│ │ │ + 截图留档 │ │
│ │ └──────────────────┘ │
│ ┌────▼──────────────────────────────────┐ │
│ │ 输出验证报告 │ │
│ │ 代码变更 + 截图 + UI 树快照 + 关键日志 │ │
│ └───────────────────────────────────────┘ │
└──────────────────────────────────────────────┘实践中踩过的坑
1. UI 树的时序问题
dumpLayout 拿到的是执行那一刻的 UI 快照。页面中的异步内容(网络请求、动画渲染)可能还没完成,AI 就会看到「元素缺失」并误判为 bug。
解法:引入轮询等待。对预期应出现的元素,在 5 秒超时窗口内周期性重试 dump,而非一次失败就判定为 bug。页面跳转后设置 1 秒固定延迟再首次 dump。
2. 编译错误的级联膨胀
一个类型定义错误可能触发十几个下游报错。如果 AI 试图一次性修所有报错,很容易改东改西、越改越乱。
解法:每轮只处理 Top-3 错误,按类型错误 > 语法错误 > 警告排序。修完重编译,很多下游报错会自然消失。
3. Token 消耗
AI 自己编译、自己看 UI 树,意味着每轮循环都要消耗 token。一个需求迭代 5-6 轮下来,token 消耗可能是纯代码生成的 3-5 倍。
解法:分层上下文 + 按需检索。核心规范固定注入(~500 token),组件文档按需求关键词动态检索(~1000 token),不要全量灌入。同时,编译错误只发错误行 ±20 行源码,UI 树只发相关子树而非整棵树。
4. AI 陷入死循环
某些情况下 AI 会反复修改同一段代码但始终修不对——比如对某个 ArkUI API 的理解本身就有误。
解法:设置熔断机制。编译最多重试 5 轮,UI 验证最多重试 3 轮。超过上限就终止循环,输出完整的错误报告(包含所有尝试过的修改和失败原因),交给人介入。
现在就能做的事
你不需要一次性搭完整个自动化系统。但有一件事今天就可以做,而且会立刻提升 AI 辅助编码的质量:
整理你的 AGENTS.md。
把代码规范、组件索引、架构约定、禁用手法写成 AI 可消费的格式。这份文档做两件事:
- 即使没有自动化流水线,你每次跟 AI 对话时把它作为上下文喂进去,AI 生成的代码质量会显著提升——因为它知道了你项目的规矩。
- 当你准备搭建自动化流水线时,这份文档就是 AI 的「项目知识库」,直接复用。
下一步,你可以尝试让 AI 自己跑编译命令、自己读编译输出。大多数 AI 编码助手已经支持执行终端命令——你只需要在 prompt 里告诉它:「写完代码后执行 ./hvigorw ... 编译,如果报错就自己读错误信息并修复,直到编译通过。」
再下一步,才是接入 hdc 和 uitest,让 AI 学会自己看界面。
流水线的终点是 AI 全自主,但起点是教会 AI 使用工具。