# 让 AI Agent 自己把鸿蒙工程跑到真机上 # 智绘鸿蒙·如 7 而至#

## 前言

先说结论：在鸿蒙 7 这一代，"改代码 → 编译 → 装机 → 点开 → 看效果"这条链，已经可以完整交给一个不会看屏幕的 Agent 去跑。人手只需要出现在两个地方——第一次登录 AGC，和最后确认那两张真机截图。

我把这条链在 MatePad Pro 上重跑了一遍，每一步的真实输出都留了图。为什么要留图？因为这条链上有三个地方，退出码会骗人：

1. `create` 缺参数时不告诉你合法格式；
2. `build` 没签名照样 `BUILD SUCCESSFUL`；
3. `--hotreload-apply` 会静静地卡死。

如果你打算让 CI 或者 Agent 替你干活，这三处就是必须写断言的地方。下面按执行顺序走。

## 先做环境自检

三条命令，顺序无所谓，但必须都在。

| 命令 | 看什么 | 我这次的输出 |
| --- | --- | --- |
| `devecocli --version` | CLI 是否够新 | `1.3.2`（提示可升级到 1.3.4） |
| `devecocli device list` | 序列号 + Kind + Device Type | 平板 `device/tablet`、nova 12 `device/phone` |
| `devecocli auth team list` | 团队 ID | 四个团队，挑自己的那个 |

两个细节：

- `device list` 里 `Kind` 是 `device` 才算授权完成。出现 `unauthorized` 就是签名 Profile 里没有这台机器的 UDID，跟 USB 线没关系，别去插拔。
- `team list` 请自己看清楚，别把输出贴进文章或仓库。团队 ID 后面会用到，我这里一律写成 `30**********70`。

## 第 1 步：create，先在参数上摔两次

![create 的两次失败](assets/01-create-fail.png)

第一次失败：`Error: --app-name is required`。`--bundle-name` 可以省（自动推导成 `com.example.<app-name>`），`--app-name` 反而是必填的。这个反直觉，因为大家习惯先想包名。

第二次失败：`cli-demo` 不合法。App 名只允许字母开头 + 字母/数字/下划线，**连字符不行**。这条报错值得夸一句——它把正则用文字写全了，看一眼就能改对。

改成 `cli_demo` 之后一次过：

![create 成功](assets/02-create-ok.png)

生成的骨架只有 6 个顶层条目（`AppScope/`、`entry/`、`build-profile.json5`、`code-linter.json5`、`hvigor/`、`hvigorfile.ts`、`oh-package.json5`），没有 `.gitignore`。**Agent 在这里要主动补一个**，原因马上就会看到。

## 第 2 步：build 成功，但包是空的

先跑一次 `build`，故意不配签名。

![build 成功但没签名](assets/03-run-unsigned.png)

上半段全是绿的：`BUILD SUCCESSFUL in 3 s 167 ms`，`Build completed successfully.`。真正的判据在最后单独一行：

```
Target device is a real device, but the artifact for 'entry' is not signed.
Real devices cannot install unsigned packages.
```

所以写脚本时，**不要拿 `build` 的退出码当"能装机"的依据**。要判断的是 `run` 的最后一行有没有 `start ability successfully`。同理，`hvigor WARN: Will skip sign 'hos_hap'` 这条 WARN 是唯一的早期信号，值得在日志里 grep 一次。

## 第 3 步：signature generate，一次性的事

```
devecocli signature generate --product default --team-id 30**********70
```

输出是四行进度 + 两行结果：`p12 → csr → certificate → profile`，然后 `Signing config written to ...\build-profile.json5`。

![签名生成 + 真正跑通](assets/04-sign-run.png)

这里有三条硬约束，踩过一次就会记住：

- **Profile 绑 bundleName，也绑设备 UDID 列表。**换一台真机、或者换包名，都要 `--force` 重签。
- **它把密钥口令明文写进 `build-profile.json5`。**生成完立刻检查 `.gitignore` 是否覆盖到签名材料；仓库里绝对不要出现这个文件的 material 段。
- **材料落在 `C:\Users\<你>\.ohos\config\` 下，配置里是绝对路径。**换台机器、换个用户名，这套签名直接失效。别指望把 `build-profile.json5` 提交给同事就能跑。

补完签名再 `run`，这一次才真的落到屏幕上：

```
Installing artifacts to device 59HYD***2258...
App installed successfully
Launching com.example.hmos7series/EntryAbility...
Application 'com.example.hmos7series': start ability successfully.
```

## 第 4 步：让 Agent 自己看屏幕

`devecocli ui` 是这条链上真正的价值所在。它给的不是像素，是一棵带坐标的节点树：

```
devecocli ui layout --device 59HYD***2258
[0,0,1840,2800]
  Stack#ContainerModalStack [0,83,1840,2800]
    Text#HelloWorld [622,1349,1218,1474] "Hello World" clickable
```

`id`、边界框、文本、`clickable` 全在里面。Agent 拿到的是一句"把 (622,1349)-(1218,1474) 这个可点击节点的中心点敲一下"，也就是 `ui click 920 1411`。敲完再 dump 一次，文本变了、框也变窄了——闭环成立，全程不需要 OCR 一张图。

![layout → click → layout 闭环](assets/05-loop.png)

对应的两张真机截图，这是这条链唯一需要人眼确认的地方：

![点击前](assets/07-device-before.png)

![点击后](assets/08-device-after.png)

`ui` 家族完整能力：`layout` / `screenshot` / `window` / `click` / `doubleclick` / `longclick` / `swipe` / `fling` / `dircfling` / `drag` / `text`。三个坑：

1. `screenshot` 的参数是 `--path`，不是 `--output`；目标文件已存在会拒绝覆盖，脚本里先 `rm -f`。
2. 多台设备在线时 `--device` 是必填，不填它会自己猜。
3. `fling` 只吃 4 个坐标参数，速度得走 `hdc shell uitest uiInput fling ...`。

## 第 5 步：热重载，能用但别依赖它

CLI 有 `--hotreload` / `--hotreload-apply` 一对，看着是给 Agent 量身定做的：起一个 watch 会话，之后只推增量。

![热重载实测](assets/06-hotreload.png)

实测结果分两半。

`--hotreload` 起会话是好的：`BUILD SUCCESSFUL in 5 s 894 ms`，`HotReloadArkTS` 4 s 230 ms，最后明确打印"写 `.hvigor/<file>` 再到另一个终端 apply"。

`--hotreload-apply` 我没跑通。第一次报 `Hot reload requires --module <name>`——它前面刚打印完 `Auto-selected module: entry`，却又要求你显式传，这个自相矛盾值得留意。补上 `--module entry` 之后，卡在：

```
[DaemonClient] Compiling, waiting for hot compile to finish...
```

七分钟没有任何进展，最后我终止了守护进程。顺带在它的构建参数里看到 `requiredDeviceType=phone`——脚手架生成的 `module.json5` 里 `deviceTypes` 默认只有 `phone`，而目标是一台平板。这条线索我没有继续挖，不能断定是它导致的。

结论很实用主义：**Agent 批量迭代时直接重跑 `run`。**增量编译只占 3～7 秒，比一个会卡死的守护进程好维护得多。热重载留给"改一行文案反复看"这种人手场景。

## 把这条链写成断言

上面五步串起来，Agent 侧真正需要的其实只有四条判断。我把它们按"错了会怎样"排在一起，方便直接抄进脚本：

| 断言 | 通过条件 | 失败时的真实现象 |
| --- | --- | --- |
| 工程存在 | `create` 后 `oh-package.json5` 可见 | 报 `Error` 后目录根本不创建，`ls` 才是唯一可靠判断 |
| 包名合法 | `--app-name` 匹配 `[A-Za-z][A-Za-z0-9_]*` | 只打印一句 `Failed to create project.` |
| 产物已签名 | 日志里**不出现** `Will skip sign` | 装机时最后一行才告诉你没签名 |
| 应用已拉起 | `run` 输出含 `start ability successfully` | 退出码 1，但前面的 build 段全是绿的 |

第四条之后还可以再加一条业务断言：`ui layout` 里目标节点的文本等于预期值。这一步是整条链唯一能证明"功能真的对了"的判据，前面四条只证明"装上了"。

顺带一条经验：`ui layout` 输出的边界框是 `[left,top,right,bottom]`，不是 `x,y,w,h`。第一次拿到就把第三个数字当宽度用，点击坐标会飘到屏幕外，而且不报错——只会得到一棵纹丝不动的树，很容易误判成"应用卡死了"。

## 真机数据

| 环节 | 首次 | 后续（增量） |
| --- | --- | --- |
| `create` | 未计时，生成 6 项骨架 | — |
| `build` | `BUILD SUCCESSFUL in 20 s 805 ms`，33 tasks 全执行 | — |
| `CompileArkTS` | 11 s 433 ms | 424 ms |
| `PackageHap` | 1 s 785 ms | 993 ms |
| `run`（含装机 + 拉起） | 签名后那次编译 6 s 869 ms，26 executed / 7 up-to-date | 未签名那次 3 s 167 ms，18 executed / 15 up-to-date |
| `ui layout` | 未计时，人感"秒回" | 同左 |
| `--hotreload` 会话 | 5 s 894 ms，34 tasks（20 executed） | apply 未完成 |

测试机：MatePad Pro（API 26，1840×2800，序列号已打码）。对照机 nova 12 仅用于验证 `device list` 的多设备行为。

## 总结

这条链适合两类活：一是夜间批量回归——Agent 自己 create、自己签名、自己装机、自己点完一圈再截图；二是多设备适配验证，`ui layout` 的坐标树比人眼快得多。

不适合的是首次接手一台新机器：签名材料绑机器、绑包名、绑 UDID 列表，这一段必须有人登录一次 AGC。把这条规则写进流程，剩下的都可以交给命令。

> 💡 **一句话**：`build` 的退出码不代表能装机，`run` 最后一行的 `start ability successfully` 才是；剩下的判断交给 `ui layout` 的坐标树，而不是截图。

## 参考文献

1. DevEco Studio 工具概览：https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/ide-tools-overview
2. @deveco/deveco-cli（npm 包页）：https://www.npmjs.com/package/@deveco/deveco-cli
3. DevEcoCode 鸿蒙专属 AI 智能体：https://atomgit.com/openharmony-sig/deveco-code
