> For AI agents: the complete documentation index is available at /zh/llms.txt, the full documentation bundle is available at /zh/llms-full.txt.

# 为 GUI Agent 补充应用知识

使用 GUI Agent 时，开发者通常希望只描述目标，就能让它完成操作。但目标明确，不代表 Agent 已经掌握完成任务所需的应用知识。

本文通过实际案例介绍如何补充必要的应用知识、清晰地表达操作意图，帮助 GUI Agent 更稳定地执行任务。

## 案例：GUI Agent 会在哪些地方犯错

以懂车帝 Android App 为例，开发者希望用这样一句提示词完成任务：

> 进入降价榜，筛选近一年上市的新能源 MPV，将价格区间筛选设为 10–40 万元，并检查筛选是否生效。

上面的提示词只有一句话，但实际执行时需要经历多个操作。在实战测试中，我们观察到一些可能的薄弱点。

| 页面截图                                                                                                                 | 操作步骤与模型可能出错的原因                                                                                                                                                                                                                                                                                   |
| -------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| <img src="/images/test-runner-best-practices/ranking-entry-home.jpg" alt="懂车帝首页的排行榜入口" width="240" />                | **意图**<br />进入降价榜<br /><br />**操作**<br />1. 点击首页上方快捷入口区域的“排行榜”。<br />2. 在排行榜页面上方切换到“降价榜”。<br /><br />**模型可能在哪里走错？**<br />仅凭“降价榜”这个名称，模型未必知道它位于“排行榜”页面内。部分模型可能先去其他页面寻找降价相关功能，或者尝试搜索“降价榜”。这里缺少的是应用的导航知识：在这个 App 中，需要先从首页进入排行榜，再切换榜单。                                                               |
| <img src="/images/test-runner-best-practices/price-filter-initial.jpg" alt="已选择车型、上市时间和能源类型，价格范围尚未调整" width="240" /> | **意图**<br />设置筛选条件和价格范围<br /><br />**操作**<br />1. 打开车型筛选，选择“MPV”。<br />2. 打开上市时间筛选，选择“近一年”。<br />3. 打开能源类型筛选，选择“新能源”。<br />4. 打开价格筛选面板。<br />5. 点击上限的 40 万元刻度。<br />6. 点击下限的 10 万元刻度。<br /><br />**模型可能在哪里走错？**<br />模型可能选择拖动滑块来设置价格范围，拖动不准确时还需要反复调整。这个组件也支持点击刻度：对模型来说，目标值已有刻度时，点击刻度的操作效率和准确率更高。 |
| <img src="/images/test-runner-best-practices/price-filter-pending.jpg" alt="面板内已调为 10–40 万元，仍待确认" width="240" />     | **意图**<br />确认并应用价格筛选<br /><br />**操作**<br />1. 核对面板中的完整价格文案，确认范围为 10–40 万元。<br />2. 点击面板底部的黄色结果按钮，应用筛选。<br /><br />**模型可能在哪里走错？**<br />模型可能把面板内的数值变化当作筛选已经生效。但这个组件在调整后仍处于待确认状态，只有点击底部的黄色结果按钮，才会应用条件并更新列表。这里需要知道控件的交互规则。                                                                         |
| <img src="/images/test-runner-best-practices/price-filter-applied.jpg" alt="确认后，筛选栏回显并更新列表" width="240" />           | **意图**<br />检查筛选是否生效<br /><br />**操作**<br />1. 检查筛选栏是否回显 MPV、近一年、新能源和 10–40 万元。<br />2. 检查结果列表是否正常更新。                                                                                                                                                                                            |

## 给 GUI Agent 补充缺失的知识

前面的案例中，找到降价榜入口、通过刻度设置价格、调整后点击确认，都依赖具体的应用知识。将这些知识提供给 GUI Agent，可以帮助它更稳定地完成任务。

在 Midscene 中，可以通过 Context 为 `aiAct` 提供这些补充信息，让 Agent 在执行任务时参考应用的导航路径、交互规则和操作约束。

### 需要补充的典型信息

这些知识缺口通常集中在以下几类：

| 需要补充的信息                                              | 例子                                                                                                  |
| ---------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
| **导航知识**<br />当前屏幕尚未展示的功能入口和到达路径。                    | <ul><li>“降价榜”位于“排行榜”页面内，需要先从首页进入排行榜。</li><li>某些功能板块位于页面较下方，需要向下滚动较长距离才能看到。</li></ul>                |
| **交互规则与操作约束**<br />不常见或难以从界面直观判断的操作方式，以及需要避开的误触或副作用。 | <ul><li>价格可以通过点击刻度设置。</li><li>调整价格后，需要点击结果按钮才会应用。</li><li>拖动时应避开 Android 屏幕边缘，以免触发系统返回手势。</li></ul> |
| **相似内容的业务语义**<br />外观或文案相似的内容，在业务含义和作用上的区别。          | <ul><li>价格面板中的范围表示待确认的选择，筛选栏中的范围表示已应用的条件。</li></ul>                                                 |

### 根据运行表现按需补充

不同模型对应用的理解能力不同，需要补充的知识也会有所差异。建议先给出清晰的任务目标和预期结果，再根据实际运行表现补充必要的应用知识。

例如，Agent 如果能够自行找到降价榜，就无需额外说明入口；如果它在调整价格后遗漏确认操作，就补充“调整后需要点击结果按钮才会应用”这条交互规则。

随着模型能力提升，部分指导可能不再必要。可以定期结合运行结果检查 Context，精简 Agent 已经能够自行理解的内容。

## 常见误区

补充应用知识和编写操作指令时，可以参考以下建议。

### 不要只给出模糊的测试目标

只输入“测试一下排行榜”，没有说明具体目标、预期结果和必要的应用知识，Agent 只能猜测要做什么、怎样判断结果。

### 不要用固定坐标和像素距离描述操作

“点击 (424, 815)，再左拖 310 像素”绑定了当前设备和布局。屏幕尺寸或组件位置变化后，这些指令容易失效，也限制了 Agent 根据当前界面调整操作的空间。

### 不要为每条 Case 注入整个 App 的知识库

每条 Case 都加载整个 App 的超长知识库，会增加 Agent 理解无关信息的成本，也让当前任务需要的关键细节更容易被忽略。

### 不要脱离实际界面，凭空编写操作路径

脱离实际用户界面，凭想象补充操作路径，可能会把不存在的入口或操作写进指令，误导 GUI Agent。如果不确定具体路径，就保留清晰的任务目标，暂不指定操作路径，让 GUI Agent 根据执行时的页面情况决定如何完成任务。

### 不要把每次点击和等待都写成独立指令

将多个 `aiTap` 与固定时长的 `sleep` 依次串联，会让执行依赖预设的点击顺序和加载时间，难以适应页面状态的变化。例如：

```yaml
- aiTap: 排行榜
- sleep: 2000
- aiTap: 降价榜
- sleep: 2000
- aiTap: 车型筛选
- aiTap: MPV
```

建议在一条 `aiAct` 中串联相关业务操作，让 Agent 根据当前界面安排点击和等待：

```yaml
- aiAct: 从首页快捷入口进入排行榜，切换到降价榜，再打开车型筛选并选择 MPV。如果过程中出现弹框，将其关闭后继续操作。
```

这样，Agent 可以在遇到弹框时先关闭弹框，再继续完成筛选目标。相比固定串联点击指令，`aiAct` 能根据当前界面调整操作，应对这类临时干扰。

`aiAct` 提示词的长短与执行效果没有必然联系，关键是能否把意图说清楚。意图越清楚，模型需要自行推断的内容就越少，理解和规划的负担也越低；意图含糊时，模型需要补全更多信息，对模型能力的要求也更高。

## 如何传入上下文知识

整理好的知识可以通过两种方式传给 `aiAct`：为当前 Agent 的所有调用配置全局 Context，或者为某一次调用单独传入 Context。

### 为所有 aiAct 调用配置全局 Context

创建 Agent 时，通过 `aiContexts.aiAct` 设置默认上下文。它适合多次调用都会用到的应用知识。以 Android 为例，下面的 `deviceId` 为已连接的设备 ID：

```ts
import { agentFromAdbDevice } from '@midscene/android';

const appContext = `
  首页上方快捷入口区域有“排行榜”入口，进入后可以切换到“降价榜”。
  自定义价格支持点击刻度设置，调整后需要点击面板底部的结果按钮才会应用。
`;

const agent = await agentFromAdbDevice(deviceId, {
  aiContexts: {
    aiAct: appContext,
  },
});

await agent.aiAct('进入降价榜');
await agent.aiAct('筛选近一年上市的新能源 MPV，并应用 10–40 万元自定义价格');
```

这两次调用都会使用 `appContext`，无需在每条提示词中重复相同知识。这里的“全局”仅作用于当前 Agent 的 `aiAct` 调用。

### 为单次 aiAct 调用传入 Context

如果某段知识只适用于当前操作，可以在 `aiAct` 的第二个参数中设置 `context`。例如，已经进入降价榜后，为价格筛选单独提供交互规则：

```ts
await agent.aiAct('将价格范围设为 10–40 万元并应用筛选', {
  context: `
    自定义价格支持点击刻度设置，先设置上限，再设置下限。
    调整后需要点击面板底部的结果按钮才会应用。
    面板中的范围表示待确认的选择，筛选栏中的范围表示已应用的条件。
  `,
});
```

这份 Context 只用于本次调用，不会改变后续调用的默认上下文。如果同时配置了 `aiContexts.aiAct`，本次传入的 `context` 会覆盖它，不会自动合并。需要同时使用公共知识和本次操作的知识时，应先将两段文本拼接，再传给 `context`。

完整参数和优先级规则见 [API Reference：单次调用上下文](/zh/reference/index.md#单次调用上下文)。
