【GUI-Agent】阶跃星辰 GUI-MCP 解读---(6)---HITL(Human In The Loop)

【GUI-Agent】阶跃星辰 GUI-MCP 解读---(6)---HITL(Human In The Loop)

目录

  • [【GUI-Agent】阶跃星辰 GUI-MCP 解读---(6)---HITL(Human In The Loop)](#【GUI-Agent】阶跃星辰 GUI-MCP 解读---(6)---HITL(Human In The Loop))
    • [0x00 摘要](#0x00 摘要)
    • [0x01 HITL](#0x01 HITL)
      • [1.1 HITL的意义](#1.1 HITL的意义)
      • [1.2 Step-GUI HITL](#1.2 Step-GUI HITL)
    • [0x02 MCP流程](#0x02 MCP流程)
      • [2.1 流程图](#2.1 流程图)
      • [2.2 时序图](#2.2 时序图)
        • [阶段 1:任务初始化](#阶段 1:任务初始化)
        • [阶段 2:Agent 动作循环(核心)](#阶段 2:Agent 动作循环(核心))
        • [阶段 3:任务终止](#阶段 3:任务终止)
      • [2.3 MCP 工具区别](#2.3 MCP 工具区别)
        • [2.3.1 详细业务逻辑对比](#2.3.1 详细业务逻辑对比)
        • [2.3.2 使用场景对比](#2.3.2 使用场景对比)
        • [2.3.3 业务逻辑实现差异](#2.3.3 业务逻辑实现差异)
          • [在 execute_task 中的处理](#在 execute_task 中的处理)
          • [在 gui_agent_loop 中的体现](#在 gui_agent_loop 中的体现)
          • [Human-in-the-Loop 场景应用](#Human-in-the-Loop 场景应用)
        • [2.3.4 核心区别总结](#2.3.4 核心区别总结)
      • [2.4 代码](#2.4 代码)
    • [0x03 INFO 操作](#0x03 INFO 操作)
      • [3.1 INFO 操作的核心特性](#3.1 INFO 操作的核心特性)
      • [3.2 处理策略](#3.2 处理策略)
      • [3.3 流程控制机制](#3.3 流程控制机制)
      • [3.4 INFO 操作的实现细节](#3.4 INFO 操作的实现细节)
      • [3.5 INFO 操作的应用场景](#3.5 INFO 操作的应用场景)
      • [3.6 代码](#3.6 代码)
    • [0x04 auto_reply 函数](#0x04 auto_reply 函数)
      • [4.1 作用](#4.1 作用)
      • [4.2 协作关系](#4.2 协作关系)
      • [4.3 代码](#4.3 代码)
    • [0x05 特殊分析](#0x05 特殊分析)
      • [5.1 GUI 操作扩展机制](#5.1 GUI 操作扩展机制)
        • [5.1.1 动作定义层扩展](#5.1.1 动作定义层扩展)
        • [5.1.2 前端执行器扩展](#5.1.2 前端执行器扩展)
        • [5.1.3 移动端辅助功能扩展](#5.1.3 移动端辅助功能扩展)
        • [5.1.4 扩展步骤详解](#5.1.4 扩展步骤详解)
        • [5.1.5 实际扩展示例](#5.1.5 实际扩展示例)
      • [5.2 CopilotClientRolloutRunner](#5.2 CopilotClientRolloutRunner)
        • [5.2.1 功能](#5.2.1 功能)
        • [5.2.2 具体实现](#5.2.2 具体实现)
      • [5.3 GUI-MCP 反馈数据处理与闭环机制](#5.3 GUI-MCP 反馈数据处理与闭环机制)
        • [5.3.1 反馈数据处理机制](#5.3.1 反馈数据处理机制)
        • [5.3.21 反馈处理流程](#5.3.21 反馈处理流程)
        • [5.3.3 闭环控制机制](#5.3.3 闭环控制机制)
        • [5.3.4 信息反馈闭环](#5.3.4 信息反馈闭环)
        • [5.3.5 数据记录与追踪](#5.3.5 数据记录与追踪)
    • [0xFF 参考](#0xFF 参考)

0x00 摘要

25年底,阶跃星辰升级发布了全新的AI Agent系列模型Step-GUI,包括云端模型Step-GUI、首个面向GUI Agent的MCP协议:GUI-MCP(Graphical User Interface - Model Context Protocol),这是首个专为图形用户界面自动化而设计的 MCP 实现,兼顾标准化与隐私保护。

因此,我们就来解读这个MCP协议,顺便看看端侧Agent的实现架构。本文是第六篇,主要是介绍Step-GUI的HITL,以及其他特殊之处。

因为是反推解读,而且时间有限,所以可能会有各种错误,还请大家不吝指出。

0x01 HITL

1.1 HITL的意义

Human-in-the-loop(简称HITL)是一种重新划分人类认知与机器能力边界、放大双方优势的系统设计理念。它的存在价值,可从三个核心维度展开:

  • 突破技术天花板。再强大的模型,认知边界也局限于训练数据覆盖的范围------在这个范围内,它能展现出稳定的"自信";可一旦遭遇罕见场景、长尾问题或是对抗性样本,其判断的可靠性便会急剧下降。而HITL的设计巧思正在于此:当机器的置信度低于预设阈值时,会自动将决策权移交人类。这就像给系统装上了一张"安全网",稳稳接住机器力所不及的漏洞。
  • 守住伦理与合规底线。算法的决策责任,永远无法转嫁到冰冷的硅片上。HITL特意保留了"人类确认"的关键环节,让整个算法决策链条中,始终存在一个可追溯、可追责的"自然人"主体,这是技术落地必须守住的伦理根基。
  • 优化经济成本结构。HITL不是"全程人工介入",而是一种"稀疏化参与"------用极少的人类工时投入,换取系统安全性的大幅提升,其ROI远高于全人工操作或纯机器自主决策的方案。说到底,HITL就是用"人类注意力"撬动"系统鲁棒性"的最优杠杆。

因此,若要让人类有效掌控任务走向,落实HITL理念的核心在于两点:

  • 优化交互设计。通过合理的交互逻辑,让人类能够顺畅地参与到任务补充与推进的过程中,实现"需要时介入,介入时高效"。
  • 保障对话连续性。在人类介入任务的过程中,不得中断当前对话链路,应采用"挂起等待"的模式,确保人机协同的连贯性与信息完整性。

1.2 Step-GUI HITL

Step-GUI中,HITL 信息获取能力如下:

  • 上下文感知:

    • INFO 操作能够根据当前上下文提出具体问题;
    • auto_reply 函数利用当前截图和任务信息生成澄清问题;
  • 信息类型多样化:

    • 可以获取文本输入、确认信息、选择项等不同类型信息
    • 可以通过 value 字段传递具体问题内容
    • 支持多轮对话以获取复杂信息

0x02 MCP流程

2.1 流程图

流程图如下,该流程图展示了任务从启动到结束的完整流程

  1. 任务启动后先判断会话 ID,新建 / 续领会话并初始化设备;
  2. 捕获截图→Agent 处理→生成动作,根据动作类型分 3 类处理(设备执行 / 人工介入 / 任务结束);
  3. 人工介入支持自动回复、手动回复、转交客户端等模式,最终回到 Agent 循环;
  4. 任务结束 / 达到最大步数时,记录包含会话、设备、任务状态的日志。

2.2 时序图

时序图如下。该时序图清晰展示了「客户端 - MCP 服务器 - Agent 服务器 - 设备 - 人类」的协同交互流程,核心逻辑分为三阶段:

阶段 1:任务初始化

  • 客户端向 MCP 服务器发起「启动新任务」请求(携带任务信息、设备 ID);
  • MCP 服务器向 Agent 服务器申请会话 ID,Agent 服务器返回会话标识;
  • MCP 服务器指令设备重置环境(按 Home 键),并捕获设备初始截图。

阶段 2:Agent 动作循环(核心)

进入循环执行逻辑,直到任务结束 / 达到步数上限:

  • MCP 服务器将「会话 ID + 截图」传给 Agent 服务器,调用 automate_step 接口获取 Agent 动作;
  • 分支处理:
    • ✅ 若动作是INFO(需要人类介入):
      • MCP 服务器通知客户端「任务暂停,需人工回复」,并返回会话 ID;
      • 人类向客户端提供回复内容;
      • 客户端携带「会话 ID + 人工回复」请求 MCP 服务器续跑任务;
      • MCP 服务器将「截图 + 人工回复」传给 Agent 服务器,继续执行动作;
    • ❌ 若动作非INFO(如点击、输入等):
      • MCP 服务器指令设备执行对应动作;
      • 等待指定延迟后,捕获设备新截图,进入下一轮循环。

阶段 3:任务终止

  • ✅ 任务完成:Agent 服务器返回 COMPLETE 动作,MCP 服务器向客户端返回「任务成功完成」结果;
  • ❌ 达到最大步数:MCP 服务器直接向客户端返回「步数超限」结果。

2.3 MCP 工具区别

ask_agent_continue 和 ask_agent_start_new_task 的业务逻辑区别如下:

特性 ask_agent_start_new_task ask_agent_continue
环境重置 reset_environment = True reset_environment = False
会话状态 创建新会话 继续现有会话
目标 开始新任务 继续已有任务
设备状态 重置到初始状态 保持当前状态

2.3.1 详细业务逻辑对比

ask_agent_start_new_task

python 复制代码
@mcp.tool
def ask_agent_start_new_task(
    # ...参数
):
    # 启动新任务,重置环境
    # 重置设备到初始状态(按 HOME 键)
    # 创建全新的会话
    # 适用于独立的、全新的任务
    reset_environment = True  # 重置环境
    return_log = execute_task(
        device_id=device_id,
        task=task,
        reset_environment=reset_environment,  # 重置环境
    )
    return return_log

ask_agent_continue

python 复制代码
@mcp.tool
def ask_agent_continue(
    # ... 参数
):
    # 继续任务,不重置环境
    # 保持设备当前状态
    # 基于之前的上下文继续执行
    # 适用于需要连续性的任务
    reset_environment = False  # 不重置环境
    return_log = execute_task(
        device_id=device_id,
        task=task,
        reset_environment=reset_environment,  # 不重置环境
    )
    return return_log

2.3.2 使用场景对比

ask_agent_start_new_task 使用场景

python 复制代码
graph TD
A[用户发起新任务] --> B[是否与之前任务相关?]
B --> |无关/新任务| C[使用 ask_agent_start_new_task]
C --> D[重置环境到初始状态]
D --> E[启动全新任务]

适用场景:

  • 完全独立的新任务
  • 不同 App 的任务(如:从淘宝切换到微信)
  • 需要干净环境的任务
  • 错误后的重新开始

ask_agent_continue 使用场景

python 复制代码
graph TD
A[用户继续任务] --> B{是否与之前任务相关?}
B -->|相关/继续| C[使用 ask_agent_continue]
C --> D[保持当前环境状态]
D --> E[基于上下文继续任务]

适用场景:

  • 同一个任务的继续执行
  • 需要保持当前应用状态
  • Human-in-the-Loop 后的继续
  • 多步骤任务的后续步骤

2.3.3 业务逻辑实现差异

在 execute_task 中的处理
python 复制代码
def execute_task(
# ...
 reset_environment: bool,  # 关键参数
# ...
):
 if reset_environment and session_id is None and task is not None:
     press_home_key(device_id, print_command=True)  # 重置设备
        
# session_id 为 None 时创建新会话

# session_id 存在时继续现有会话
在 gui_agent_loop 中的体现
python 复制代码
def gui_agent_loop(
# ...
 reset_environment: bool = True,
 session_id: str = None,
# ...
):
 if reset_environment and session_id is None and task is not None:
     press_home_key(device_id, print_command=True)  # 重置环境
 if session_id is None:
    # 创建新会话
    session_id = agent_server.get_session({...})
 else:
    # 继续现有会话
    print(f"Continue Session ID: {session_id}")
Human-in-the-Loop 场景应用

任务中断后继续

python 复制代码
# 第一步:开始任务,遇到INFO action
result = ask_agent_start_new_task(
    device_id=device_id,
    task="去淘宝帮我选一个生日礼物",
    # ...
)
# 返回:stop_reason="INFO_ACTION_NEEDS_REPLY", session_id="xxx"

# 第二步:用户提供回复后继续

result = ask_agent_continue(
    device_id=device_id,
    task=None,  # 不需要重新指定任务
    session_id="xxx",  # 使用之前的会话ID
    reply_from_client="铜苹果",  # 用户的回复
    # ...
)

多任务切换

python 复制代码
# 开始任务A
result_a = ask_agent_start_new_task(
    device_id=device_id,
    task="打开微信并发送消息",
    # ...
)
# 完成后开始不相关的任务B
result_b = ask_agent_start_new_task(  # 使用 start_new_task 重置环境
    device_id=device_id,
    task="打开高德地图导航到公司",
    # ...
)

2.3.4 核心区别总结

环境状态:

  • ask_agent_start_new_task:重置设备环境到初始状态
  • ask_agent_continue:保持设备当前环境状态

会话管理:

  • ask_agent_start_new_task:创建新会话
  • ask_agent_continue:继续现有会话

使用时机:

  • ask_agent_start_new_task:新任务、独立任务、需要干净环境
  • ask_agent_continue:任务继续、保持上下文、Human-in-the-Loop

上下文连续性:

  • ask_agent_start_new_task:无上下文连续性
  • ask_agent_continue:保持任务上下文和应用状态

这种设计使得系统既能处理独立的离散任务,又能处理需要连续性的复杂任务,提高了任务执行的灵活性和效率。

2.4 代码

ask_agent_start_new_task 代码如下:

python 复制代码
@mcp.tool
def ask_agent_start_new_task(

    device_id: Annotated[str, Field(description="ID of the device to perform the task on. listed by list_connected_devices tool.")],

    task: Annotated[str | None, Field(description="The task that the agent needs to perform on the mobile device. if this is not None, the agent will try to perform this task. if None, the session_id must be provided to continue the previous session.")],
    
    # reset_environment: Annotated[bool, Field(description="Whether to reset the environment before executing the task, close current app, and back to home screen. If you want to execute a independent task, set this to True will make it easy to execute. If you want to continue the previous session, set this to False.")] = False,

    max_steps: Annotated[int, Field(description="Maximum number of steps the agent can take to complete the task.")] = 20,

    # session_id: Annotated[str | None, Field(description="Optional, session ID must provide when the last task endwith INFO action and you want to reply, the session id and device id and the reply from client must be provided.")] = None,

    # When the INFO action is called, how to handle it.
    # 1. "auto_reply": the INFO action will be handled automatically by calling the caption model to generate image captions.
    # 2. "no_reply": the INFO action will be ignored. THE AGENT MAY GET STUCK IF THE INFO ACTION IS IGNORED.
    # 3. "manual_reply": the INFO action will cause an interruption, and the user needs to provide the reply manually by input things in server's console.
    # 4. "pass_to_client": the INFO action will be returned to the MCP client to handle it. 
#     reply_mode: Annotated[str, Field(description='''
#         How to handle the INFO action during task execution.
        
#         Options:
#             - "auto_reply": Automatically generate image captions for INFO actions.
#             - "no_reply": Ignore INFO actions (may cause the agent to get stuck).
#             - "manual_reply": Interrupt and require user input for INFO actions.
#             - "pass_to_client": Pass INFO actions to the MCP client for handling.
# ''')] = "auto_reply",

    # reply_from_client: Annotated[str | None, Field(description="If the last task is ended with INFO action, and you want to give GUI agent a reply, provide the reply here. If you do so, you must provide last session id and last device id.")] = None,
) -> dict:

    """
# Ask GUI Agent to start performing a new task on a connected device.

Ask the GUI agent to perform the specified task on a connected device.
The GUI Agent can be able to understand natural language instructions and interact with the device accordingly.
The agent will be able to execute a high-level task description,if you have any additional requirements, write them down in detail at tast string.
This function will reset the environment before executing the task, close current app, and back to home screen.

if you have 

## The agent has the below limited capabilities:

1. The task must be related to an app that is already installed on the device. for example, "打开微信,帮我发一条消息给张三,说今天下午三点开会"; "帮我在淘宝上搜索一款性价比高的手机,并加入购物车"; "to purchase an ea on Amazon".

2. The task must be simple and specific. for example, "do yyy in xxx app"; "find xxx information in xxx app". ONE THING AT ONE APP AT A TIME.

3. The agent may not be able to handle complex tasks that require multi-step reasoning or planning. for example. You may need to break down complex tasks into simpler sub-tasks and ask the agent to perform them sequentially. For example, instead of asking the agent to "plan a trip to Paris for xxx", you can ask it to "search for flights to Paris on xxx app", "find hotels in Paris on xxx app", make the plan yourself and ask agent to "sent the plan to xxx via IM app like wechat".

4. The agent connot accept multimodal inputs now. if you want to provide additional information like screenshot captions, please include them in the task description.

## Usage guidance:

1. you should never directly ask an Agent to pay or order anything. If user want to make a purchase, you should ask agent to stop brfore ordering/paying, and let user to order/pay.

2. tell the agent, if human verification is appeared during the task execution, the agent should ask Client. when the you see the INFO, you should ask user to handle the verification manually. after user says "done", you can continue the task with the session_id and device_id and ask the agent to continue in reply_from_client.

3. IF the last agentic call is failed or you want to perform a new task in different app, you should always use this function to start a new task, so that the environment will be reset before executing the task.

Returns:
    dict: Execution log containing details of the task execution.
    with keys including
        - device_info: Information about the device used for task execution.
        - final_action: The final action taken by the agent to complete the task.
        - global_step_idx: The total number of steps taken during the task execution.
        - local_step_idx: The number of steps taken in the current session.
        - session_id: The session ID for maintaining context across multiple tasks.
        - stop_reason: The reason for stopping the task execution (e.g., TASK_COMPLETED_SUCCESSFULLY).
        - task: The original task description provided to the agent.
    """

    reply_mode = "pass_to_client"

    # if task is not None:
    #     assert session_id is None, "If task is provided, session_id must be None."
    #     # New task, so reset_environment is True
    #     reset_environment = True
    # else:
    #     assert session_id is not None, "If task is None, session_id must be provided to continue the previous session."
    #     # Continuing previous session, so reset_environment is False
    #     reset_environment = False

    reset_environment = True
    

    return_log = execute_task(
        device_id=device_id,

        task=task,

        reset_environment=reset_environment,
        max_steps=max_steps,

        # enable_intermediate_logs=False,
        # enable_intermediate_image_caption=False,
# 
        enable_intermediate_logs=True,
        # enable_intermediate_image_caption=False,
        enable_intermediate_image_caption=True,

        enable_intermediate_screenshots=False,

        enable_final_screenshot=False,
        # enable_final_image_caption=False,
        enable_final_image_caption=True,

        reply_mode=reply_mode,

        session_id=None,
        # session_id=session_id,
        reply_from_client=None,
        # reply_from_client=reply_from_client,


    )

    return return_log

ask_agent_continue 代码如下:

python 复制代码
@mcp.tool
def ask_agent_continue(
    device_id: Annotated[str, Field(description="ID of the device to perform the task on. listed by list_connected_devices tool.")],

    task: Annotated[str | None, Field(description="The task that the agent needs to perform on the mobile device. if this is not None, the agent will try to perform this task. if None, the session_id must be provided to continue the previous session.")],
    
    # reset_environment: Annotated[bool, Field(description="Whether to reset the environment before executing the task, close current app, and back to home screen. If you want to execute a independent task, set this to True will make it easy to execute. If you want to continue the previous session, set this to False.")] = False,

    max_steps: Annotated[int, Field(description="Maximum number of steps the agent can take to complete the task.")] = 20,

    # session_id: Annotated[str | None, Field(description="Optional, session ID must provide when the last task endwith INFO action and you want to reply, the session id and device id and the reply from client must be provided.")] = None,

    # When the INFO action is called, how to handle it.
    # 1. "auto_reply": the INFO action will be handled automatically by calling the caption model to generate image captions.
    # 2. "no_reply": the INFO action will be ignored. THE AGENT MAY GET STUCK IF THE INFO ACTION IS IGNORED.
    # 3. "manual_reply": the INFO action will cause an interruption, and the user needs to provide the reply manually by input things in server's console.
    # 4. "pass_to_client": the INFO action will be returned to the MCP client to handle it. 
#     reply_mode: Annotated[str, Field(description='''
#         How to handle the INFO action during task execution.
        
#         Options:
#             - "auto_reply": Automatically generate image captions for INFO actions.
#             - "no_reply": Ignore INFO actions (may cause the agent to get stuck).
#             - "manual_reply": Interrupt and require user input for INFO actions.
#             - "pass_to_client": Pass INFO actions to the MCP client for handling.
# ''')] = "auto_reply",

    # reply_from_client: Annotated[str | None, Field(description="If the last task is ended with INFO action, and you want to give GUI agent a reply, provide the reply here. If you do so, you must provide last session id and last device id.")] = None,
) -> dict:

    """
# Ask GUI Agent to continue performing a task on a connected device, using previous context.

Ask the GUI agent to perform the specified task on a connected device.
The GUI Agent can be able to understand natural language instructions and interact with the device accordingly.
The agent will be able to execute a high-level task description,if you have any additional requirements, write them down in detail at tast string.
This function will **NOT** reset the environment before executing the task, so that the agent can continue the previous session.

if you have 

## The agent has the below limited capabilities:

1. The task must be related to an app that is already installed on the device. for example, "打开微信,帮我发一条消息给张三,说今天下午三点开会"; "帮我在淘宝上搜索一款性价比高的手机,并加入购物车"; "to purchase an ea on Amazon".

2. The task must be simple and specific. for example, "do yyy in xxx app"; "find xxx information in xxx app". ONE THING AT ONE APP AT A TIME.

3. The agent may not be able to handle complex tasks that require multi-step reasoning or planning. for example. You may need to break down complex tasks into simpler sub-tasks and ask the agent to perform them sequentially. For example, instead of asking the agent to "plan a trip to Paris for xxx", you can ask it to "search for flights to Paris on xxx app", "find hotels in Paris on xxx app", make the plan yourself and ask agent to "sent the plan to xxx via IM app like wechat".

4. The agent connot accept multimodal inputs now. if you want to provide additional information like screenshot captions, please include them in the task description.

## Usage guidance:

1. you should never directly ask an Agent to pay or order anything. If user want to make a purchase, you should ask agent to stop brfore ordering/paying, and let user to order/pay.

2. tell the agent, if human verification is appeared during the task execution, the agent should ask Client. when the you see the INFO, you should ask user to handle the verification manually. after user says "done", you can continue the task with the session_id and device_id and ask the agent to continue in reply_from_client.

3. IF the last agentic call is successful or the last action is INFO or the new task is related to the previous task, you can use this function to continue the task, so that the agent can finish the task faster by leveraging the previous context.
    dict: Execution log containing details of the task execution.
    with keys including
        - device_info: Information about the device used for task execution.
        - final_action: The final action taken by the agent to complete the task.
        - global_step_idx: The total number of steps taken during the task execution.
        - local_step_idx: The number of steps taken in the current session.
        - session_id: The session ID for maintaining context across multiple tasks.
        - stop_reason: The reason for stopping the task execution (e.g., TASK_COMPLETED_SUCCESSFULLY).
        - task: The original task description provided to the agent.
    """

    reply_mode = "pass_to_client"

    # if task is not None:
    #     assert session_id is None, "If task is provided, session_id must be None."
    #     # New task, so reset_environment is True
    #     reset_environment = True
    # else:
    #     assert session_id is not None, "If task is None, session_id must be provided to continue the previous session."
    #     # Continuing previous session, so reset_environment is False
    #     reset_environment = False

    reset_environment = False    

    return_log = execute_task(
        device_id=device_id,

        task=task,

        reset_environment=reset_environment,
        max_steps=max_steps,

        # enable_intermediate_logs=False,
        # enable_intermediate_image_caption=False,
# 
        enable_intermediate_logs=True,
        enable_intermediate_image_caption=True,

        enable_intermediate_screenshots=False,

        enable_final_screenshot=False,
        # enable_final_image_caption=False,
        enable_final_image_caption=True,

        reply_mode=reply_mode,

        session_id=None,
        # session_id=session_id,
        reply_from_client=None,
        # reply_from_client=reply_from_client,
    )

    return return_log

0x03 INFO 操作

3.1 INFO 操作的核心特性

INFO交互模式特殊性如下:

  • 用户输入请求:INFO 操作是唯一需要用户主动输入的交互模式,与 CLICK、TYPE、AWAKE 等自动执行操作不同,INFO 需要中断自动化流程以获取用户反馈。

  • 任务暂停机制:当执行 INFO 操作时,自动化流程暂停,系统会等待用户提供必要信息后继续执行,防止因缺少关键信息导致的错误操作

3.2 处理策略

INFO 操作有多种处理策略,具体在 reply_mode 中设置:

  • auto_reply:自动调用模型生成回复

  • no_reply:忽略 INFO 操作,可能导致代理卡住

  • manual_reply:手动输入回复

  • pass_to_client:将 INFO 操作传递给 MCP 客户端处理

何处设置 reply_mode?具体如下:

  • execute_task 函数中定义处理模式
  • gui_agent_loop 函数根据 reply_mode 执行相应逻辑
  • 支持动态调整 INFO 操作处理方式

自动回复机制的细节如下:

  • auto_reply 函数结合当前任务、截图和 INFO 操作内容
  • 使用 LLM 生成合适的回复内容
  • 减少对用户手动输入的依赖

人工回复处理的细节如下:

  • manual_reply 模式下,程序暂停并等待用户输入
  • 提供中英文提示信息来帮助用户理解需要回复的内容
  • 验证用户输入的有效性

3.3 流程控制机制

INFO 的流程控制机制如下:

  • 会话中断与恢复:

    • INFO 操作触发时,stop_reason 设置为 INFO_ACTION_NEEDS_REPLY
    • 保存当前会话状态,包括 session_id
    • 支持后续使用相同 session_id 继续执行
  • 回复传递机制:

    • 用户回复通过 reply_from_client 参数传递
    • 在 payload 中作为 query 字段传递给代理
    • 代理将用户回复作为下一步操作的输入

3.4 INFO 操作的实现细节

INFO 操作的信息传递流程如下:

  • 从代理到用户:

    • 代理生成 INFO 操作并包含 value(问题内容)
    • action['value'] 被显示给用户
    • 用户输入回复内容
  • 从用户到代理:

    • 用户输入通过 reply_from_client 参数传递
    • reply_info 变量存储用户回复
    • 作为 query 字段传递给下一次 automate_step 调用

3.5 INFO 操作的应用场景

INFO 操作的应用场景可能如下:

人机协作场景

  • 验证码处理:

    • 当遇到图形验证码或短信验证码时触发 INFO 操作
    • 代理请求用户提供验证码
    • 用户输入验证码后代理继续执行
  • 敏感操作确认:

    • 在执行支付、删除等敏感操作前,代理可能通过 INFO 操作请求用户确认
    • 避免自动化操作导致的意外后果

信息补充场景

  • 个性化信息获取:

    • 代理需要获取用户的个人信息如姓名、地址等
    • 通过 INFO 操作请求用户提供特定信息
    • 完成表单填写等任务
  • 决策支持:

    • 当面临多个选项需要用户选择时
    • 代理通过 INFO 操作询问用户偏好
    • 根据用户选择继续执行相应路径

3.6 代码

INFO的相关代码如下:

python 复制代码
def gui_agent_loop( # 省略代码
        ):
    """
    Evaluate a task on a device using the provided frontend action converter and action function.    
    """
	
    # 省略代码
    
        action = uiTars_to_frontend_action(action)

        if action['action_type'].upper() == "INFO":
            if reply_mode == "auto_reply":
                print(f"AUTO REPLY INFO FROM MODEL!")
                reply_info = auto_reply(image_b64_url, task, action, model_provider=agent_loop_config['model_config']['model_provider'], model_name=agent_loop_config['model_config']['model_name'])
                print(f"info: {reply_info}")

            elif reply_mode == "no_reply":
                print(f"INFO action ignored as per reply_mode=no_reply. Agent may get stuck.")
                reply_info = "Please follow the task and continue. Don't ask further questions."
                # do nothing, agent may get stuck
            elif reply_mode == "manual_reply":
                print(f"EN: Agent asks: {action['value']} Please Reply: ")
                print(f"ZH: Agent 问你: {action['value']} 回复一下:")
                reply_info = input("Your reply:")
                print(f"Replied info action: {reply_info}")
            elif reply_mode == "pass_to_client":
                print(f"Passing INFO action to client for reply.")
                # break the loop and return to client for handling
                stop_reason = "INFO_ACTION_NEEDS_REPLY"
                break
            else:
                raise ValueError(f"Unknown reply_mode: {reply_mode}")
    # 省略代码
    
        act_on_device(action, device_id, device_wm_size, print_command=True, reflush_app=reflush_app)

        history_actions.append(action)        
        
    # 省略代码

    if stop_reason in ['MANUAL_STOP_SCREEN_OFF', 'INFO_ACTION_NEEDS_REPLY', "NOT_STARTED"]:
        pass
    elif  action['action_type'].upper() == 'COMPLETE':
        stop_reason = "TASK_COMPLETED_SUCCESSFULLY"
    elif action['action_type'].upper() == 'ABORT':
        stop_reason = "TASK_ABORTED_BY_AGENT"
    elif step_idx == max_steps - 1:
        stop_reason = "MAX_STEPS_REACHED"

    return return_log

0x04 auto_reply 函数

4.1 作用

auto_reply 函数的作用如下:

  • 信息处理功能
    • 处理由 GUI Agent 发起的 INFO 操作
    • 通过大语言模型自动生成对用户问题的回复
    • 模拟用户角色,根据当前任务和页面内容提供简洁直接的答案
  • 输入处理
    • 接收当前页面截图的 URL
    • 接收任务描述
    • 接收 Agent 的询问内容
    • 接收模型提供商和模型名称
  • 输出生成
    • 生成简洁明确的回复内容
    • 避免多余的解释或礼貌用语

4.2 协作关系

auto_reply 函数与其他组件的关系如下:

  • 与 gui_agent_loop 的关系

    • auto_reply 被 gui_agent_loop 在特定条件下调用
    • 当 reply_mode 设置为 "auto_reply" 时触发
    • 处理 Agent 发出的 INFO 动作
  • 与 execute_task 的关系

    • execute_task 函数通过 reply_mode 参数控制是否使用自动回复功能
    • 传递配置信息给 gui_agent_loop,间接影响 auto_reply 的执行
  • 与 ask_llm_anything 的关系

    • auto_reply 依赖 ask_llm_anything 函数来获取大语言模型的回复
    • 通过 ask_llm_anything 与模型提供商交互
  • 与 MCP 服务器的关系

    • 在 MCP 服务器中,可以通过设置 reply_mode 为 "auto_reply" 来启用此功能
    • 提供了自动处理 Agent 询问的能力,无需人工干预

工作流程

  • 触发条件

    • Agent 执行 INFO 动作
    • reply_mode 设置为 "auto_reply"
  • 处理过程

    • 构建包含任务目标、Agent 问题和当前页面的提示
    • 通过 ask_llm_anything 获取模型回复
    • 清理回复内容(移除思考部分)
  • 输出结果

    • 返回简洁明确的回复内容
    • 用于继续 Agent 的执行流程
  • 作用意义

    • 实现了自动化的 Agent 交互处理
    • 避免了在需要用户澄清时的人工干预
    • 保持了任务执行的连续性
    • 提高了 GUI Agent 的自主执行能力

4.3 代码

auto_reply 代码如下:

python 复制代码
def auto_reply(current_image_url, task, info_action, model_provider, model_name):
    """
    Reply with information action.
    """
    messages_to_ask = [
        {
            "role": "user",
            "content": [
                {
                    "type": "text",
                    "text":  f"""# 角色
你将扮演一个正在使用GUI Agent完成任务的用户。

# 任务
阅读下方提供的所有背景信息,针对[Agent的澄清问题],生成一个提供关键信息的、简短直接的回答。

# 背景信息
- **任务目标:** {task}
- **agent 问的问题:** {json.dumps(info_action, ensure_ascii=False)}

# 输出要求
- 你的回答必须极其简短和明确。
- 你的回答应直接命中问题的核心,解决Agent的疑惑。
- 不要进行任何额外的解释、对话或使用礼貌用语。
- 只输出回答本身,不要添加任何引号或其他修饰。

以下是当前页面内容:
                """,
                },
                {
                    'type': "image_url",
                    'image_url': {
                        'url': current_image_url
                    }
                },
                {
                    "type": "text",
                    "text": '请基于以上信息,简洁直接地回答Agent的问题。'
                }
            ]
        }
    ]

    response = ask_llm_anything(
        model_provider=model_provider,
        model_name=model_name,
        messages=messages_to_ask,
        args={
            "max_tokens": 1024,
            "temperature": 0.5,
            "top_p": 1.0,
            "frequency_penalty": 0.0,
        }
    )

    if "</think>" in response:
        response = response.split("</think>")[-1].strip()

    return response

0x05 特殊分析

我们接下来就系统的一些特殊之处进行分析。

5.1 GUI 操作扩展机制

  • 插件化架构

    • 工具扩展:通过添加新工具扩展功能
    • 解析器扩展:支持不同动作解析器
    • 设备扩展:支持不同类型设备
  • 配置化管理

    • 服务配置:mcp_server_config.yaml管理服务参数
    • 模型配置:model_config.yaml管理模型参数
    • 运行配置:灵活的运行时配置

5.1.1 动作定义层扩展

  • action 类型枚举
    • action_tools.py:定义_ACTION_TYPE_ENUM,包含现有动作类型
    • 新增动作:在枚举中添加新动作类型,如SCROLL、DOUBLE_CLICK等
  • 动作验证机制
    • action_assertion:验证动作格式和参数完整性
    • 参数检查:针对不同动作类型验证特定参数

5.1.2 前端执行器扩展

  • 动作转换映射
    • action_type_map:定义前端动作类型映射关系
    • step_api_to_frontend_action:处理 API 动作到前端动作的转换
  • 动作实现
    • CLICK:实现点击操作,处理坐标点转换
    • TYPE:实现文本输入,支持键盘存在性检测
    • SLIDE:实现滑动操作,处理路径坐标
    • 新增动作:需要在act_on_device函数中添加新的动作处理逻辑

5.1.3 移动端辅助功能扩展

  • 设备操作层
    • mobile_action_helper.py:提供底层设备操作接口
    • ADB 命令:通过 ADB 实现设备控制
    • 坐标系统:支持归一化坐标到实际坐标的转换
  • 坐标处理
    • convert_point_to_realworld_point:坐标转换函数
    • 屏幕方向检测:自动处理不同屏幕方向的坐标映射

5.1.4 扩展步骤详解

  • 定义新动作
    • 添加动作类型:在action_tools.py_ACTION_TYPE_ENUM中添加新动作
    • 更新验证逻辑:在action_assertion中添加新动作的参数验证
  • 实现动作转换
    • 更新映射表:在pu_frontend_executor.pyaction_type_map中添加新动作映射
    • 添加转换逻辑:在step_api_to_frontend_action中添加新动作的转换处理
  • 实现设备操作
    • 添加操作实现:在act_on_device函数中添加新动作的 ADB 命令实现
    • 参数验证:确保新动作的参数验证和错误处理
  • 解析器支持
    • 更新解析器:在parser_0920_summary.py中添加新动作的解析支持
    • 动作格式:定义新动作的输入输出格式

5.1.5 实际扩展示例

  • 添加 SCROLL 动作

    • 定义动作:在_ACTION_TYPE_ENUM中添加 "SCROLL"
    • 验证逻辑:在action_assertion中添加 SCROLL 参数验证
    • 转换实现:在step_api_to_frontend_action中添加转换逻辑
    • 设备操作:在act_on_device中实现 SCROLL ADB 命令
  • 扩展参数支持

    • 方向参数:支持 UP/DOWN/LEFT/RIGHT 方向
    • 距离参数:支持自定义滑动距离
    • 时间参数:支持自定义滑动时间

5.2 CopilotClientRolloutRunner

CopilotClientRolloutRunner 存在于代码中,但是没有调用实例,所以我们直接反推。

5.2.1 功能

CopilotClientRolloutRunner 中的 "rollout" 概念确实来源于强化学习领域,在强化学习中,rollout 指从当前状态开始执行一系列动作直到终止状态的完整轨迹。

CopilotClientRolloutRunner 实际上是一个多设备任务并行执行框架,主要功能是管理多个设备上的 GUI 代理任务执行, 支持同时在多个设备上运行 GUI 代理任务,提高执行效率。CopilotClientRolloutRunner 与强化学习有概念上的联系,但不是强化学习算法的实现。

主要用途:

  • 任务分发:将任务分发到多个设备并行执行
  • 结果收集:收集所有任务的执行结果
  • 执行监控:跟踪每个设备的任务执行状态

当然,GUI 代理任务的执行轨迹可以用于训练或评估智能体,因此,CopilotClientRolloutRunner 类可能用于强化学习训练数据的收集,执行的轨迹数据可用于后续的强化学习算法训练。

5.2.2 具体实现

使用 multiprocessing 模块实现并行处理,包含四个主要进程:

  • logger_runner - 日志记录进程
  • reader_runner - 任务读取和分发进程
  • work_runner - 每个设备对应一个工作进程
  • writer_runner - 结果写入进程

任务队列与负载均衡

CopilotClientRolloutRunner 为每个设备维护独立的任务队列 task_queue

任务分发时会检查结果文件,避免重复执行已完成的任务

支持任务失败重试机制,失败任务会重新放回队列

结果收集与持久化

  • 通过 done_queue 收集所有设备的执行结果
  • writer_runner 进程将结果写入指定的输出文件
  • 支持断点续传,会跳过已存在的任务结果

设备管理与监控

  • 支持对多个设备进行任务分配和管理
  • 记录每个设备的任务执行统计信息(总任务数、成功数、错误数)
  • 通过 device_name_map 支持设备命名

与 GUI 代理系统集成

  • 调用 evaluate_task_on_device 函数执行具体的 GUI 代理任务
  • LocalServer 集成,使用指定的配置执行任务
  • 保留原始元数据,便于后续分析和追踪

5.3 GUI-MCP 反馈数据处理与闭环机制

5.3.1 反馈数据处理机制

  • 数据收集与处理

    • 设备状态反馈:通过 capture_screenshot 函数获取设备屏幕截图
    • 环境感知:捕获当前设备状态作为模型决策依据
    • 用户交互反馈:处理 INFO 动作中的用户输入
  • 反馈数据类型

    • 视觉反馈:屏幕截图转换为 base64 编码格式
    • 状态反馈:设备运行状态、屏幕方向等信息
    • 交互反馈:用户对 INFO 动作的回复

5.3.21 反馈处理流程

  • 反馈数据转换

    • 图像编码:使用 make_b64_url 函数将截图转换为 base64 编码
    • 数据封装:将反馈信息封装到 observation payload中
    • 模型输入准备:构建包含图像和文本的多模态输入
  • 反馈数据传递

    • automate_step接口:将反馈数据传递给 AI 模型
    • 会话管理:通过session_id维护上下文连续性
    • 历史记录:保存历史动作和环境状态

5.3.3 闭环控制机制

  • 任务执行闭环

    • 函数:实现完整的执行 - 反馈 - 决策循环
    • 步骤控制:在 max_steps 限制内执行循环
    • 状态监控:持续监测设备状态和任务进展
  • 反馈驱动决策

    • 模型推理:使用ask_llm_anything处理反馈数据并生成下一步动作
    • 动作解析:通过parser.str2action将模型输出转换为可执行动作
    • 动作执行:通过act_on_device执行解析后的动作

5.3.4 信息反馈闭环

用户交互闭环

  • 用户输入获取:通过命令行或客户端获取用户回复
  • 回复注入:将用户回复作为查询注入到下一步的观察中
  • 会话恢复:支持基于用户回复继续任务执行

5.3.5 数据记录与追踪

  • 执行日志记录

    • intermediate_logs:记录中间执行步骤
    • final_action:记录最终执行结果
    • LocalServerLogger:持久化保存执行日志
  • 会话状态追踪

    • session_id管理:维护会话唯一标识
    • 状态同步:确保服务端与设备状态一致
    • 上下文保存:保留历史动作和环境状态
  • 异常处理与终止条件

    • 闭环终止条件

      • stop_reason机制:定义多种终止条件
        • TASK_COMPLETED_SUCCESSFULLY:任务成功完成
        • TASK_ABORTED_BY_AGENT:代理主动中止
        • MAX_STEPS_REACHED:达到最大步数限制
        • INFO_ACTION_NEEDS_REPLY:需要用户回复
    • 异常处理机制

      • 屏幕状态检查:detect_screen_on确保设备处于可用状态
      • 错误恢复:当设备不可用时终止任务执行
      • 安全退出:确保资源正确释放

0xFF 参考

从HITL(Human In The Loop) 实践出发看Agent与设计模式的对跖点