给 Agent Browser Workflow 加一层可观测性:Trace、Snapshot 和 Review Queue

Agent Browser Workflow 的一个典型问题是:任务跑完了,但你不知道它到底经历了什么。

传统脚本至少会在异常点抛错。Agent 不一样,它可能会基于页面内容继续判断、跳过某个步骤、得出一个看似合理但不符合预期的结果。

如果没有 Trace、环境快照和复核入口,最后排查时只剩一句话:这次结果不对。

这篇聊一个工程化做法:给 Agent 浏览器任务补一层可观测性。

1. 问题不是 Agent 不稳定,而是现场不可见

很多问题看起来像模型或脚本问题,实际是运行现场不可见。

text 复制代码
Agent
  -> Automation Interface
  -> Browser Profile
  -> Session State
  -> Proxy Policy
  -> Page State
  -> Result

任何一层变化,都会影响最终结果。

如果只保存最终输出,不保存中间证据,就无法回答:

  • Agent 当时看到了什么页面;
  • 浏览器是否处于登录态;
  • Profile 是否和预期一致;
  • Proxy、时区、语言是否变化;
  • 哪一步开始偏离预期;
  • 是否应该进入人工复核。

所以要做的不是单纯增加日志,而是记录能复盘的上下文。

2. 定义 RunTrace

先给每次任务运行一个统一对象。

ts 复制代码
type RunTrace = {
  runId: string;
  jobId: string;
  workspaceId: string;
  profileId: string;
  startedAt: string;
  finishedAt?: string;
  status: "running" | "success" | "failed" | "paused";
  environmentSnapshotId: string;
  stepEvidenceIds: string[];
  failureReason?: FailureReason;
  reviewQueueId?: string;
};

RunTrace 的作用是把环境、步骤、失败原因和复核记录串起来。

没有它,日志、截图和 Agent 输出会散落在不同地方,很难还原一次任务。

3. 运行前保存 EnvironmentSnapshot

Agent 打开浏览器前,先保存环境快照。

ts 复制代码
type EnvironmentSnapshot = {
  id: string;
  workspaceId: string;
  profileId: string;
  browser: {
    version: string;
    timezone: string;
    language: string;
    viewport: string;
  };
  session: {
    cookieStatus: "valid" | "expired" | "unknown";
    localStorageStatus: "ready" | "missing" | "unknown";
    indexedDbStatus: "ready" | "missing" | "unknown";
  };
  proxy: {
    policyId: string;
    region: string;
    exitType: "fixed" | "fallback";
  };
  createdAt: string;
};

任务启动前做一次校验:

ts 复制代码
function canStart(snapshot: EnvironmentSnapshot) {
  const reasons: string[] = [];

  if (snapshot.session.cookieStatus !== "valid") {
    reasons.push("cookie invalid");
  }

  if (snapshot.session.localStorageStatus !== "ready") {
    reasons.push("localStorage missing");
  }

  if (snapshot.session.indexedDbStatus !== "ready") {
    reasons.push("indexedDB missing");
  }

  return {
    ok: reasons.length === 0,
    reasons
  };
}

如果环境不满足条件,任务应该 paused,而不是继续跑。

4. 每个关键步骤保存 StepEvidence

不需要记录每一个鼠标移动,但关键步骤要保留证据。

ts 复制代码
type StepEvidence = {
  id: string;
  runId: string;
  stepIndex: number;
  action: "open" | "click" | "type" | "read" | "submit" | "wait" | "review";
  target?: string;
  url: string;
  pageTitle?: string;
  beforeScreenshot?: string;
  afterScreenshot?: string;
  assertion?: string;
  result: "passed" | "failed" | "skipped" | "uncertain";
  agentReason?: string;
  createdAt: string;
};

推荐保存截图的场景:

  • 进入关键页面后;
  • 执行提交、保存、删除等高影响操作前;
  • 断言失败时;
  • Agent 判断不确定时;
  • 进入人工复核队列前。

这样排查时不需要完全依赖日志文本,可以直接看到页面现场。

5. FailureReason 不要只有 failed

失败原因最好结构化。

ts 复制代码
type FailureReason =
  | { type: "session_invalid"; message: string }
  | { type: "env_mismatch"; message: string }
  | { type: "page_changed"; message: string }
  | { type: "action_timeout"; message: string }
  | { type: "agent_uncertain"; message: string }
  | { type: "permission_blocked"; message: string }
  | { type: "unknown"; message: string };

一个简单分类器可以这样写:

ts 复制代码
function classifyFailure(input: {
  snapshot: EnvironmentSnapshot;
  lastStep?: StepEvidence;
}): FailureReason | undefined {
  const { snapshot, lastStep } = input;

  if (snapshot.session.cookieStatus !== "valid") {
    return { type: "session_invalid", message: "cookie is not valid" };
  }

  if (snapshot.session.localStorageStatus !== "ready") {
    return { type: "env_mismatch", message: "localStorage is not ready" };
  }

  if (lastStep?.result === "uncertain") {
    return { type: "agent_uncertain", message: "agent needs review" };
  }

  if (lastStep?.result === "failed") {
    return { type: "page_changed", message: "step assertion failed" };
  }
}

这个分类不必一开始就特别精确,但它能让排查方向从"全都查一遍"变成"先查最可能的层"。

6. ReviewQueue 是 Agent Workflow 的安全阀

Agent workflow 里,人工复核不是拖慢效率,而是控制风险。

ts 复制代码
type ReviewItem = {
  id: string;
  runId: string;
  stepEvidenceId: string;
  reason: "high_impact_action" | "agent_uncertain" | "env_mismatch";
  status: "pending" | "approved" | "rejected";
  reviewer?: string;
  createdAt: string;
  resolvedAt?: string;
};

建议触发 review 的场景:

场景 动作
环境快照不一致 暂停任务
Agent 判断不确定 进入复核队列
高影响操作 操作前复核
连续失败 暂停并要求查看 Trace
失败原因 unknown 保存现场并人工分类

对团队来说,这比无限重试更可靠。

7. 一个推荐的数据流

text 复制代码
create RunTrace
  -> collect EnvironmentSnapshot
  -> canStart(snapshot)
  -> run Agent steps
  -> collect StepEvidence
  -> classify FailureReason
  -> push ReviewQueue if needed
  -> finalize RunTrace

这里最重要的是两个关口:

  1. 环境不满足条件时,不要开始任务;
  2. Agent 不确定或操作影响较大时,不要直接继续。

这两个关口能避免很多不可复现问题。

8. 工具层应该怎么配合

工具层需要承担的不是"替 Agent 做决定",而是给 Agent 提供稳定上下文。

它应该能管理:

  • Profile 与 workspace 的绑定;
  • SessionState 与本地状态检查;
  • ProxyPolicy、时区、语言;
  • RunTrace 与 StepEvidence;
  • FailureReason 分类;
  • ReviewQueue 与人工复核;
  • 团队可读的审计记录。

Web4Browser 这类Agent-ready browser environment 的一种实现方向可以作为观察样本:把浏览器环境、Agent workflow、Profile 管理和本地优先的数据控制放到同一套工作台里,而不是让每个脚本各自保存上下文。

9. Review checklist

检查项 通过标准
RunTrace 是否存在 每次任务有独立 runId
EnvironmentSnapshot 是否完整 Profile、Session、Proxy 信息齐全
StepEvidence 是否可读 关键步骤有截图或状态记录
FailureReason 是否分类 不只写 failed
ReviewQueue 是否接入 高影响操作和不确定判断可暂停
AuditLog 是否可追溯 能看到最近环境变更
团队成员是否能复盘 不依赖原作者口头解释

结尾

Agent 浏览器任务的稳定性,不只是模型能力问题。

如果没有 Trace、Snapshot 和 Review Queue,任务一旦出错,就会变成不可复现问题。

把现场留下来,把失败分类,把高影响操作交给复核,Agent workflow 才更像工程系统,而不是一次性脚本。

相关推荐
William_Xu7 小时前
Vue 2 与 Vue 3 生命周期对比
前端
Hilaku7 小时前
为什么你的大文件断点续传,一遇到弱网就崩溃?
前端·javascript·程序员
胡哈8 小时前
React Compiler:让开发者忘掉 memo 这件事
前端·react.js
xiyueyezibile8 小时前
“自迭代” Skill 的 team-pitfalls 的演进
前端·后端·ai编程
林恒smileZAZ8 小时前
`Array(100).map(() => 1)` 为什么全为空?
前端·javascript
小巧的砖头9 小时前
C#会重蹈覆辙吗?系列之2:反射及元数据的性能问题
开发语言·前端·c#
KaMeidebaby9 小时前
卡梅德生物技术快报|蛋白的叠氮基修饰:实操解析:核酸模板耦合蛋白的叠氮基修饰实现靶蛋白定点共价标记
前端·人工智能·物联网·算法·百度
濮水大叔9 小时前
在大型 Vue 项目中,如何有效管理复杂的状态?“散装版” vs “套装版”
前端·vue.js·typescript
玄星啊9 小时前
可修改:代码的生存底线
前端·ai编程
没落英雄10 小时前
3. DeepAgents 实战 - Memory Skills 与上下文工程
前端·人工智能·架构