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
这里最重要的是两个关口:
- 环境不满足条件时,不要开始任务;
- 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 才更像工程系统,而不是一次性脚本。