DoAnything
DoAnything 是 WebAgent 的开放型 API。你给一句自然语言 instruction,agent 自己挑工具、定路径、跑步骤,在浏览器环境里尽力达成目标。
和 DeepResearch / WebSearch / Track 不同:那 3 个是定型 API,产物形态固定、给质量契约。DoAnything 产物形态不定——连目标最终长什么样你可能都不完全知道,交给 agent 自由发挥。因此它不承诺质量契约;要契约就走对应的定型 API。4 个 API 并列,按你要什么直接选。
什么时候用
- 目标不定、复合,需要 agent 自主决定怎么做。
- 任务跨多步、跨页面,可能要登录态、人工确认、长程运行。
- 你接受「尽力而为」,而不是固定形态的结果契约。
需要固定形态的产物时,用 DeepResearch / WebSearch / Track。
资源模型
Session 是一个运行时容器——它持有浏览器、profile(cookies / 登录态)、workspace(文件系统)。Run 是跑在 session 里的一条 instruction。同一 session 可以接连跑多个 run,它们共享前一个 run 留下的状态。
project
└── session id: sess_…
├── browser, profile, workspace
└── run id: run_…
├── instructions "搜 Hacker News..."
├── status running | done | …
└── events (SSE) run.status_changed, run.message, …生命周期(7 态)
| 状态 | 含义 | 下一步 |
|---|---|---|
pending | 已接收,等 agent slot | → running、canceled |
running | agent 在跑 | → done、failed、awaiting_input、paused、canceled |
awaiting_input | agent 自己暂停了,等你回应 | → running(通过 intervene) |
paused | 你手动暂停 | → running(resume)、canceled |
done | 成功完成;output 已落 | 终态 |
failed | 报错;error.code error.detail 已落 | 终态 |
canceled | 你取消(或 max_duration 触发) | 终态 |
非终态都占 session 资源。用 max_duration_minutes 兜底。
提交一个 run
创建 session 时,第一条 run 随 CreateSessionRequest 一起提交。请求字段:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
instructions | string | 是 | 一段中文 / 英文。最多 10 000 字符 |
max_duration_minutes | int | 否 | 1–10 080(一周) |
recording | object | 否 | {enabled, quality, capture_during_take_control};不传 = 关 |
keep_alive | bool | 否 | run 结束后保持 session 热度,方便接 follow-up run |
allowed_actions | string[] | 否 | 允许 agent 调的工具白名单。空数组 = 全允许 |
profile_id | string | 否 | 复用 Profile 的 cookies / 登录态 |
完整 schema 见 OpenAPI spec。
示例:
from web_agent import Client
from web_agent.v1.types import CreateSessionRequest, RecordingConfigRequest
session = await client.sessions.create(CreateSessionRequest(
instructions="找出 24 小时内 Show HN 上 Top 5 的帖子。",
max_duration_minutes=10,
recording=RecordingConfigRequest(enabled=True),
keep_alive=True,
))
run = session.runs[0]Follow-up run
from web_agent.v1.types import CreateRunRequest
followup = await client.sessions.create_run(
session.id,
CreateRunRequest(instructions="点进第一篇帖子,总结一下评论区。"),
)跑在同一个浏览器里,cookies 与上一条 run 留下的 DOM 都在。
事件
每个 run 有一条 SSE 流:
GET /v1/projects/{pid}/do_anything/sessions/{sid}/runs/{rid}/events
Authorization: Bearer wa_…11 种事件类型(envelope 一致;data 内容不同):
| 类型 | 何时 emit |
|---|---|
run.status_changed | 状态切换 |
run.message | chat 里多一条(agent 或 user) |
run.action.started | agent 调用某工具 |
run.action.completed | 工具返回 |
run.action.failed | 工具抛错 |
run.screenshot | 浏览器新 frame(url 短期签名) |
run.input_request | agent 暂停,需要你回应 |
run.input_request_resolved | 你的 intervene 已采纳 |
run.cost_update | 一步的 cost 增量 |
run.completed | 终态;output 已落 |
stream.heartbeat | 约 15 s 一次;可忽略 |
断线干净续传:
GET …/events
Last-Event-ID: 142server 会重放 id > 142 的事件,不漏。
Input request(人在回路)
agent 撞到 captcha、2FA、判断不准的事,会发 run.input_request:
{
"type": "run.input_request",
"data": {
"input_request_id": "ir_01HXX…",
"prompt": "我看到一个 'Verify you're human' 挑战,你帮我点过去?",
"schema": { "type": "object", "properties": { "solved": { "type": "boolean" } } }
}
}你 POST /intervene 回:
await client.messages.intervene(
session.id, run.id,
input_request_id="ir_01HXX…",
response={"solved": True},
)run 切回 running。整轮一次往返,无 polling。
Take Control(接管浏览器)
有些步骤 agent 没法独自完成——captcha、2FA、要人亲手输的密码。这时可以把浏览器交给人操作:弹出一个可交互的实时浏览器,人点完、填完,再把控制权交还给 agent。
它和上面的 input request 不同:input request 是 agent 问一个问题、你用 JSON 回答;Take Control 是人直接操作 agent 那台浏览器。
接管走的是同一个 intervene 端点,用 kind 区分动作:
1. 发起接管
POST /v1/projects/{pid}/do_anything/sessions/{sid}/runs/{rid}/intervene
Content-Type: application/json
{ "kind": "take_control", "reason": "captcha" }常见触发点是 agent 先发了一条 run.input_request(captcha / 需要登录),你也可以在任意时刻主动发起。
2. 拿到控制 URL,交给人操作
后端签发一个 standalone 控制 URL,通过 run.take_control_pending 事件下发(data 带 standalone_control_url / exp / reason)。这是一个无品牌的实时浏览器页面——可以直接转发给你的终端用户。人在这个页面里直接对 agent 浏览器点击、输入:过验证码、登录、做需要人判断的事。
发起接管后 agent 不会立刻停——只有真的有人打开 URL、连上浏览器,run 才切到 paused、agent 才暂停(事件 run.user_paused)。没人连上,URL 约 5 分钟后失效,发 run.take_control_expired。
3. 交还控制
人操作完,再调一次 intervene:
{ "kind": "release_control", "trigger": "client_release" }run 切回 running,agent 重新观察当前页面、接着跑(事件 run.user_released)。控制页空闲约 30s 也会自动交还。URL 过期想重发,用 { "kind": "refresh_control_url" }。
相关事件
| 事件 | 含义 |
|---|---|
run.take_control_pending | 控制 URL 已签发、等人连接;data.standalone_control_url 是给人操作的页面 |
run.user_paused | 有人连上、开始操作,agent 已暂停 |
run.user_released | 控制已交还,agent 继续 |
run.take_control_expired | 没人连接,URL 超时作废 |
SDK 现状:Python / TypeScript SDK 的
intervene()目前只覆盖answer_input_request。Take Control 的take_control/release_control/refresh_control_url直接调 HTTP intervene 端点。完整字段见 OpenAPI spec。
Profiles
Profile = 一份整体的浏览器身份(一个用户一份,累积他在所有站点的登录态)。create session 时引用:
await client.sessions.create(CreateSessionRequest(
instructions="打开 LinkedIn 收件箱,回复最新一条消息。",
profile_id="prof_alice",
))第一次在 Console 里手动设置 Profile(把需要的站点逐个登录一遍)。后续 session 直接复用同一份。详见 Profiles。
Workspaces
Workspace = 持久文件系统。agent 可读可写;run 跑完后用签名 URL 取文件。"抓这个站,写 CSV,丢回来"这种场景常用。
接下来
- API 参考 —— 每个字段
- 鉴权 —— key、scope、轮换
- Vibecoding —— 怎么把这些提供给 IDE 里的 LLM