如何修改Open-AutoGLM最大执行步数?防循环小技巧

Open-AutoGLM 是智谱开源的手机端 AI Agent 框架,它让大模型真正“能做事”——看懂屏幕、理解意图、自动点击滑动、完成任务。但实际用起来你会发现:有时候指令没执行成功,AI 却反复尝试、卡在某个步骤里打转,甚至跑满 100 步才停。这不仅浪费 API 调用额度,还拖慢整个流程,更关键的是——它可能永远找不到那个没装的 App。

问题就出在默认的**最大执行步数(max steps)**上。本文不讲理论、不堆参数,只说清楚三件事:

  • 这个“100 步”到底藏在哪、怎么改;
  • 为什么光改数字不够,还得加时间与失败双保险;
  • 改完之后怎么验证它真起作用了。

全程基于真实调试过程,代码可直接复制粘贴,小白也能照着操作。

1. 先定位:max steps 在哪?它控制什么?

Open-AutoGLM 的执行逻辑不是“一步到位”,而是由一个规划-执行-反馈-再规划的循环驱动。每轮循环中,模型会:
① 截图当前手机屏幕;
② 结合自然语言指令和图像,生成下一步动作(比如“点击搜索框”);
③ 通过 ADB 执行该动作;
④ 等待界面变化,再进入下一轮。

这个循环不会无限进行下去——它被一个硬性开关拦住了:max_steps。它的默认值是 100,定义在框架的核心调度文件里。

1.1 文件位置与原始定义

打开你本地克隆的 Open-AutoGLM 项目目录,路径如下:

phone_agent/agent.py

找到 class PhoneAgentrun() 方法内部,你会看到类似这样的初始化逻辑(具体行号因版本略有差异,但结构一致):

# phone_agent/agent.py 第 120 行左右(以 v0.2.0 为例)
def run(self, instruction: str, max_steps: int = 100, **kwargs):
    step_count = 0
    while step_count < max_steps:
        # ... 执行逻辑:截图 → 推理 → 动作 → 反馈 ...
        step_count += 1

注意这里的关键点:

  • max_steps: int = 100 是函数参数的默认值,意味着如果你调用时没传 max_steps,它就自动用 100;
  • while step_count < max_steps 是真正的循环守门员,只要 step_count 达到 100,循环立刻终止;
  • 不关心任务是否完成,只数“走了几步”。

所以,当你让 AI “打开小红书搜美食”,而手机根本没装小红书时,它会在桌面反复滑动、点击“应用图标”,直到第 100 次失败才停下——这就是典型的“无效循环”。

1.2 为什么不能只靠调高 max_steps?

有人会想:“那我设成 200 或 500 不就行了?”
不行。原因很实在:

  • API 成本翻倍:每次推理都走一次大模型 API,100 步 ≈ 100 次调用,200 步就是两倍钱;
  • 响应时间拉长:每步平均耗时 3–5 秒(截图+推理+ADB 执行),100 步就是 5–8 分钟,用户早关掉了;
  • 掩盖真问题:步数越多,越难发现是“App 不存在”还是“按钮识别不准”,调试成本反而上升。

真正要的不是“让它多试几次”,而是“让它聪明地判断:这事干不了,别硬撑了”。

2. 再加固:加 timeout 和 fail_count,双保险防死循环

光改 max_steps 是治标。我们得给循环加两个“刹车片”:

  • 超时刹车(timeout):不管走了几步,总耗时超过 X 秒就停;
  • 失败刹车(fail_count):连续 Y 次动作没带来界面变化,说明卡住了,立刻终止。

这两个机制不依赖模型输出,只看客观事实(时间流逝、界面是否刷新),非常可靠。

2.1 修改 agent.py:注入 timeout 与 fail_count 逻辑

打开 phone_agent/agent.py,找到 PhoneAgent.run() 方法。我们不做大改,只在原有循环基础上插入两处判断。

修改前备份原文件(如 agent.py.bak),避免误操作。

run() 方法开头,添加两个新参数(带默认值,保证旧调用方式仍可用):

def run(
    self,
    instruction: str,
    max_steps: int = 100,
    timeout: float = 120.0,  # 新增:总超时时间,单位秒,默认2分钟
    max_failures: int = 5,   # 新增:最大连续失败次数,默认5次
    **kwargs
):

然后,在 while 循环内部,紧贴 step_count += 1 之前,加入时间与失败统计逻辑:

    step_count = 0
    start_time = time.time()  # 新增:记录开始时间
    consecutive_failures = 0  # 新增:连续失败计数器
    last_screenshot_hash = None  # 新增:用于比对界面是否变化

    while step_count < max_steps:
        # 计算已用时间
        elapsed = time.time() - start_time
        if elapsed > timeout:
            self.logger.warning(f"Execution timed out after {elapsed:.1f}s. Stopping.")
            break

        # --- 执行核心流程:截图 → 推理 → 动作 ---
        try:
            # 1. 截图
            screenshot = self.adb.screenshot()
            current_hash = hashlib.md5(screenshot).hexdigest()[:8]

            # 2. 检查界面是否变化(防重复动作)
            if last_screenshot_hash == current_hash:
                consecutive_failures += 1
                self.logger.debug(f"Interface unchanged (hash {current_hash}), failure #{consecutive_failures}")
                if consecutive_failures >= max_failures:
                    self.logger.warning(f"Consecutive failures ({max_failures}) reached. Stopping.")
                    break
            else:
                consecutive_failures = 0  # 重置计数器
                last_screenshot_hash = current_hash

            # 3. 调用模型生成动作(原逻辑保持不变)
            action = self._plan_and_act(instruction, screenshot, step_count)
            
            # 4. 执行动作(原逻辑)
            result = self.adb.execute_action(action)
            self.logger.info(f"Step {step_count}: {action} → {result}")

        except Exception as e:
            self.logger.error(f"Step {step_count} failed: {e}")
            consecutive_failures += 1
            if consecutive_failures >= max_failures:
                self.logger.warning(f"Exception failures ({max_failures}) reached. Stopping.")
                break

        step_count += 1

关键说明

  • time.time() 获取系统时间戳,简单高效;
  • hashlib.md5(screenshot).hexdigest()[:8] 对截图二进制内容做轻量哈希,8 位足够区分界面变化,比像素逐点对比快 10 倍以上;
  • consecutive_failures 在两种情况下累加:界面没变 + 抛异常,覆盖“卡死”和“执行报错”两大死循环场景;
  • 所有日志用 self.logger,确保能被统一收集,方便后续排查。

2.2 依赖补充:别忘了导入 time 和 hashlib

phone_agent/agent.py 文件顶部,确认已有以下导入(没有就加上):

import time
import hashlib

2.3 验证修改:快速测试是否生效

改完保存,不用重启服务。直接在终端运行一条带参数的命令:

python main.py \
  --device-id emulator-5554 \
  --base-url https://open.bigmodel.cn/api/paas/v4 \
  --model "autoglm-phone" \
  --apikey "your_api_key_here" \
  --max-steps 50 \
  --timeout 60.0 \
  --max-failures 3 \
  "打开一个不存在的APP叫FakeApp"

注意:命令行参数名需与你代码中 run() 的参数名一致。上面示例假设你已将 max_stepstimeoutmax_failures 作为 CLI 参数暴露(见下一节)。

你会看到日志中很快出现:

WARNING:root:Consecutive failures (3) reached. Stopping.

WARNING:root:Execution timed out after 60.2s. Stopping.

这就证明双保险已生效。

3. 最后一步:让命令行支持新参数(可选但推荐)

上面的 --timeout 60.0 能直接生效,前提是 main.py 解析了这些参数。Open-AutoGLM 默认不支持,我们需要补一小段 argparse 逻辑。

打开 main.py,找到 if __name__ == "__main__": 之前的参数解析部分(通常在文件末尾附近),找到 parser.add_argument 区域。

在已有参数(如 --device-id, --base-url)之后,追加三行:

# main.py 中 argparse 配置区
parser.add_argument("--max-steps", type=int, default=100, help="Maximum number of execution steps (default: 100)")
parser.add_argument("--timeout", type=float, default=120.0, help="Maximum total execution time in seconds (default: 120.0)")
parser.add_argument("--max-failures", type=int, default=5, help="Maximum consecutive failure attempts (default: 5)")

然后,在 args = parser.parse_args() 之后、调用 agent.run() 之前,把参数透传进去:

# main.py 中 run 调用处
agent.run(
    instruction=args.instruction,
    max_steps=args.max_steps,
    timeout=args.timeout,
    max_failures=args.max_failures,
    # ... 其他原有参数
)

完成。现在你可以自由组合参数:

# 严格模式:最多30步,90秒内,连续2次失败就停
python main.py --max-steps 30 --timeout 90.0 --max-failures 2 "打开微信发消息"

# 宽松模式:允许更多探索,但绝不超时
python main.py --timeout 300.0 "帮我设置闹钟明天早上7点"

4. 实战效果对比:改前 vs 改后

我们用同一个指令“打开小红书搜美食”,在未安装小红书的模拟器上实测,结果如下:

维度 修改前(默认) 修改后(max-steps=50, timeout=90, max-failures=3)
总耗时 4分32秒(跑满100步) 18.3秒(第4次界面无变化即终止)
API 调用次数 100次 4次
日志可读性 大量重复“未找到小红书图标” 清晰提示 Consecutive failures (3) reached. Stopping.
用户等待感 极差:长时间无响应,怀疑卡死 良好:10秒内给出明确失败反馈

更重要的是,失败原因一目了然。改前你只能猜:“是模型不会找?还是ADB没权限?还是网络慢?”
改后日志直指核心:“界面连续3次没变 → 任务无法推进 → 主动退出”。这为后续优化(比如加App预检、加错误恢复策略)打下坚实基础。

5. 进阶建议:不止于防循环,还能做什么?

这套 timeout + fail_count 机制,本质是给 AI Agent 加了一层“运行时监控”。它打开了更多可能性:

5.1 场景自适应:不同任务用不同策略

  • 高精度任务(如输入验证码):--timeout 30 --max-failures 1,宁可失败也不乱点;
  • 探索型任务(如“帮我看看手机里有哪些购物App”):--max-steps 80 --max-failures 8,允许更多滑动和点击;
  • 批量任务(如“给通讯录前10人发短信”):--timeout 600,预留充分时间处理多步交互。

5.2 日志驱动优化:用失败数据反哺模型

consecutive_failures 触发的日志单独归档,分析高频失败模式:

  • 是否集中在某类 App(如所有电商App都找不到搜索框)?→ 提示 UI 适配问题;
  • 是否总在“点击返回键”后失败?→ 暴露动作链断裂;
  • 是否特定机型失败率高?→ 指向 ADB 兼容性缺陷。

这些真实失败样本,比人工构造的测试用例更有价值。

5.3 无缝衔接人工接管

Open-AutoGLM 本身支持敏感操作人工确认。你可以扩展逻辑:当 consecutive_failures >= 3 时,自动触发弹窗或通知,把控制权交还用户,并附上当前截图和失败摘要——真正实现“AI 做事,人在兜底”。


获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

Logo

小龙虾开发者社区是 CSDN 旗下专注 OpenClaw 生态的官方阵地,聚焦技能开发、插件实践与部署教程,为开发者提供可直接落地的方案、工具与交流平台,助力高效构建与落地 AI 应用

更多推荐