• 简体中文
  • 为 GUI Agent 补充应用知识

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

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

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

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

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

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

    页面截图操作步骤与模型可能出错的原因
    懂车帝首页的排行榜入口意图
    进入降价榜

    操作
    1. 点击首页上方快捷入口区域的“排行榜”。
    2. 在排行榜页面上方切换到“降价榜”。

    模型可能在哪里走错?
    仅凭“降价榜”这个名称,模型未必知道它位于“排行榜”页面内。部分模型可能先去其他页面寻找降价相关功能,或者尝试搜索“降价榜”。这里缺少的是应用的导航知识:在这个 App 中,需要先从首页进入排行榜,再切换榜单。
    已选择车型、上市时间和能源类型,价格范围尚未调整意图
    设置筛选条件和价格范围

    操作
    1. 打开车型筛选,选择“MPV”。
    2. 打开上市时间筛选,选择“近一年”。
    3. 打开能源类型筛选,选择“新能源”。
    4. 打开价格筛选面板。
    5. 点击上限的 40 万元刻度。
    6. 点击下限的 10 万元刻度。

    模型可能在哪里走错?
    模型可能选择拖动滑块来设置价格范围,拖动不准确时还需要反复调整。这个组件也支持点击刻度:对模型来说,目标值已有刻度时,点击刻度的操作效率和准确率更高。
    面板内已调为 10–40 万元,仍待确认意图
    确认并应用价格筛选

    操作
    1. 核对面板中的完整价格文案,确认范围为 10–40 万元。
    2. 点击面板底部的黄色结果按钮,应用筛选。

    模型可能在哪里走错?
    模型可能把面板内的数值变化当作筛选已经生效。但这个组件在调整后仍处于待确认状态,只有点击底部的黄色结果按钮,才会应用条件并更新列表。这里需要知道控件的交互规则。
    确认后,筛选栏回显并更新列表意图
    检查筛选是否生效

    操作
    1. 检查筛选栏是否回显 MPV、近一年、新能源和 10–40 万元。
    2. 检查结果列表是否正常更新。

    给 GUI Agent 补充缺失的知识

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

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

    需要补充的典型信息

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

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

    根据运行表现按需补充

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

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

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

    常见误区

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    如何传入上下文知识

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

    为所有 aiAct 调用配置全局 Context

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

    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。例如,已经进入降价榜后,为价格筛选单独提供交互规则:

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

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

    完整参数和优先级规则见 API Reference:单次调用上下文