Holo4 与通用计算机操作智能体:从“会点鼠标”到可靠执行任务

2026-09-28 24 预计阅读时间: 1 分钟
来源: huggingface.co AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:10 分钟

从标题给出的定位看,Holo4 面向的是通用计算机操作智能体:模型不只生成文字,还需要理解屏幕、选择操作,并通过鼠标、键盘或工具调用完成跨应用任务。这类系统的难点并不是“能否点击按钮”,而是如何在不断变化的界面中持续判断、执行和验证,同时避免不可逆的误操作。

来源摘要没有提供 Holo4 的模型规模、接口、评测结果或部署方式,因此下面不假设其具体 API,而是围绕这类模型落地时真正需要的工程结构展开。等正式接口明确后,可以把 Holo4 接到相同的执行框架中。

通用性来自闭环,而不只是视觉识别

一个可工作的计算机操作智能体通常包含四个连续环节:

  1. 观察:获取截图、窗口信息、OCR 文本或可访问性树。
  2. 决策:结合用户目标与当前状态,生成下一步动作。
  3. 执行:完成点击、输入、滚动、快捷键或应用切换。
  4. 验证:重新观察界面,判断动作是否生效以及任务是否完成。

这个过程更像一个受约束的控制循环,而不是一次性的问答:

用户目标 -> 观察屏幕 -> 生成动作 -> 安全检查 -> 执行动作
              ^                              |
              |------------ 重新观察 --------|

如果缺少验证,智能体可能在页面尚未加载时连续点击;如果缺少安全检查,它可能把模型生成的坐标直接用于“删除”“支付”或“发送”等高风险按钮。模型能力再强,也不能代替执行层的边界控制。

动作协议应该比自然语言更严格

不要让执行器直接解析“点击右上角那个按钮”之类的文本。更稳妥的做法是定义结构化动作协议,例如:

{
  "action": "click",
  "x": 842,
  "y": 116,
  "reason": "打开搜索框",
  "risk": "low"
}

协议至少应覆盖以下动作:

  • click:点击指定坐标或元素;
  • type:输入文本;
  • scroll:滚动页面;
  • wait:等待页面或应用更新;
  • done:确认任务完成;
  • abort:发现异常后主动终止。

真正接入桌面环境时,还应附带当前窗口、截图编号、动作置信度和目标元素描述。这样可以拒绝基于旧截图生成的动作,也便于回放失败过程。

一个可运行的最小智能体循环

下面是一个仅使用 Python 标准库的模拟项目。它不会真的控制鼠标,而是在内存中模拟“打开浏览器并搜索文档”的过程,重点展示观察、决策、校验、执行和审计日志之间的边界。

保存为 agent_loop.py,然后运行 python agent_loop.py:

from dataclasses import asdict, dataclass
import json
from typing import Optional


@dataclass
class Action:
    kind: str
    x: Optional[int] = None
    y: Optional[int] = None
    text: Optional[str] = None
    reason: str = ""


class SimulatedDesktop:
    def __init__(self):
        self.state = "desktop"
        self.query = ""

    def observe(self) -> dict:
        screens = {
            "desktop": "Desktop is visible. Browser icon is at (120, 180).",
            "browser": "Browser is open. Search field is at (500, 120).",
            "typed": f"Search field contains: {self.query!r}. Submit button is at (820, 120).",
            "results": f"Results are visible for: {self.query!r}.",
        }
        return {
            "state": self.state,
            "screen": screens[self.state],
        }

    def execute(self, action: Action) -> None:
        if action.kind == "click" and self.state == "desktop":
            self.state = "browser"
        elif action.kind == "type" and self.state == "browser":
            self.query = action.text or ""
            self.state = "typed"
        elif action.kind == "click" and self.state == "typed":
            self.state = "results"
        elif action.kind in {"done", "abort", "wait"}:
            return
        else:
            raise RuntimeError(
                f"Action {action.kind!r} is invalid in state {self.state!r}"
            )


def mock_model(goal: str, observation: dict) -> Action:
    """用真实模型时,只替换这个函数,并要求其返回相同结构。"""
    state = observation["state"]
    if state == "desktop":
        return Action("click", x=120, y=180, reason="Open browser")
    if state == "browser":
        return Action("type", text=goal, reason="Enter search query")
    if state == "typed":
        return Action("click", x=820, y=120, reason="Submit search")
    if state == "results":
        return Action("done", reason="Search results are visible")
    return Action("abort", reason="Unknown state")


def validate(action: Action) -> None:
    allowed = {"click", "type", "scroll", "wait", "done", "abort"}
    if action.kind not in allowed:
        raise ValueError(f"Unsupported action: {action.kind}")

    if action.kind == "click":
        if action.x is None or action.y is None:
            raise ValueError("Click requires x and y")
        if not (0 <= action.x <= 1000 and 0 <= action.y <= 1000):
            raise ValueError("Click coordinates are outside the allowed area")

    if action.kind == "type" and not action.text:
        raise ValueError("Type requires non-empty text")


def run(goal: str, max_steps: int = 8) -> None:
    desktop = SimulatedDesktop()

    for step in range(1, max_steps + 1):
        observation = desktop.observe()
        action = mock_model(goal, observation)
        validate(action)

        print(json.dumps({
            "step": step,
            "observation": observation,
            "action": asdict(action),
        }, ensure_ascii=False))

        if action.kind == "done":
            print("Task completed")
            return
        if action.kind == "abort":
            raise RuntimeError(action.reason)

        desktop.execute(action)

    raise TimeoutError("Agent exceeded the maximum number of steps")


if __name__ == "__main__":
    run("查找团队休假政策")

接入真实模型时,可以保留 validate()、步数限制和日志结构,只替换 mock_model();接入真实桌面时,则替换 SimulatedDesktop.observe() 与 execute()。如果 Holo4 后续提供截图输入和结构化动作输出,这里就是适配器应连接的位置,而不需要让模型直接拥有不受限制的系统权限。

高风险动作不能依赖模型自觉

通用智能体越能跨应用工作,权限边界就越重要。生产系统至少应该设置三层保护:

  • 工具白名单:只开放任务需要的应用、网站和动作;
  • 提交前确认:发送消息、购买、删除、发布和权限变更必须由用户确认;
  • 敏感信息隔离:密码、令牌、支付信息不应出现在截图、提示词或普通日志中。

坐标动作还存在分辨率、缩放比例、弹窗遮挡和窗口移动等问题。相比盲目执行坐标,优先结合可访问性树、DOM 元素或窗口元数据;执行后再比较新旧截图,确认界面确实发生了预期变化。

评测也不应只统计最终成功率。更有诊断价值的指标包括平均步骤数、无效动作率、恢复成功率、人工接管率、高风险动作拦截率,以及完成任务所消耗的时间和模型调用成本。

如何开始采用

可以从可逆、低风险、结果容易验证的任务切入,例如查找资料、整理表格草稿或在测试环境填写表单。上线前建议逐项确认:

  • 每次模型动作都经过结构校验;
  • 执行器设置了应用、域名和坐标边界;
  • 循环具有最大步数、超时和主动终止机制;
  • 外部提交与不可逆操作需要人工批准;
  • 截图、动作、结果和错误可关联回放;
  • 使用脱敏测试账号评估提示注入与恶意页面。

Holo4 所代表的方向,是让智能体从“回答怎么做”走向“在计算机上完成它”。但真正决定系统能否投入使用的,不只是模型是否看得懂界面,而是执行框架能否把每一步限制在可观察、可验证、可撤销的范围内。


相关推荐