人工介入
让智能体向你提问、在提交前等待你批准、遇到 CAPTCHA 时把浏览器交给你,并允许你随时接管。
有些步骤需要人来完成:发到你手机上的一次性验证码、购买前的决定、CAPTCHA。智能体会暂停运行,告诉你它需要什么,并在你回复后继续。你的代码(或使用它的人)通过同一个端点 POST /v1/browser/runs/{id}/reply 进行回复。
| 运行需要 | needs.kind | 你的回复 |
|---|---|---|
| 它没有的信息 | question | {"answer": "…"} 或 {"secret": "…"} |
| 提交前得到你的同意 | approval | {"approve": true} 或 {"approve": false, "reason": "…"} |
| 有人在浏览器中操作 | takeover | 在实时画面(Live View)中完成操作,然后回复 {"done": true} 或 {"failed": true} |
暂停的工作方式#
当智能体需要你时,运行的 status 会变为 waiting_for_human,needs 描述它需要什么(节选):
{
"id": "brun_5b7d9f1a3c5e7a9b1d3f5a7c",
"object": "browser_run",
"status": "waiting_for_human",
"steps": 6,
"current_url": "https://bank.example.com/verify",
"needs": {
"kind": "question",
"prompt": "What is the 6-digit code sent to your phone ending 42?",
"expects_secret": true,
"screenshot": "/v1/browser/runs/brun_5b7d9f1a3c5e7a9b1d3f5a7c/screenshot",
"since": "2026-10-11T09:31:12.000Z",
"reply": "/v1/browser/runs/brun_5b7d9f1a3c5e7a9b1d3f5a7c/reply"
}
}| 字段 | 含义 |
|---|---|
needs.kind | question、approval 或 takeover |
needs.prompt | 用一句话说明智能体需要什么,可直接展示给人看 |
needs.screenshot | 暂停开始时页面 JPEG 截图的路径。用你的 API 密钥获取。 |
needs.since | 暂停开始的时间 |
needs.reply | 回复的路径 |
运行等待期间:
- 浏览器保持打开并停留在同一页面,因此智能体会从停下的地方继续。
- 带
?wait=的状态请求会立即返回,而不会挂起。请直接回复,或按你自己的间隔轮询。 - 如果你设置了 Webhook,它会收到一个
run.waiting事件。 - 每次暂停包含一分钟。超出部分的等待按每分钟 $0.005 计费(价格)。
- 如果在
human_timeout_minutes(默认 30)内无人回复,运行以timed_out结束,错误为no human response within 30 minutes。
在代码中处理暂停#
下面的循环运行一个可能请求批准或验证码的任务,并在终端中询问操作者:
import os
import requests
API = "https://lovelyrouter.com/v1/browser"
H = {"Authorization": f"Bearer {os.environ['LOVELY_API_KEY']}"}
run = requests.post(f"{API}/runs", headers=H, json={
"goal": "In the rewards shop, redeem 500 points for the Blue Mug and tell me the order number.",
"start_url": "https://shop.example.com/rewards",
"profile": "shop-login",
"approve": "risky",
}).json()
while run["status"] in ("running", "waiting_for_human"):
if run["status"] == "waiting_for_human":
needs = run["needs"]
if needs["kind"] == "approval":
print(needs["prompt"], *needs["fields"], sep="\n ")
reply = {"approve": input("Approve? [y/N] ").strip().lower() == "y"}
elif needs["kind"] == "question":
value = input(needs["prompt"] + " ")
reply = {"secret": value} if needs["expects_secret"] else {"answer": value}
else: # takeover
print(needs["prompt"], "\nOpen:", needs["live_view_url"])
input("Press Enter when you are done. ")
reply = {"done": True}
# A 409 here means the run already moved on; the next status request shows where it is.
requests.post(f"{API}/runs/{run['id']}/reply", headers=H, json=reply)
run = requests.get(f"{API}/runs/{run['id']}", headers=H, params={"wait": 30}).json()
print(run["status"], run["answer"])如果对方不在终端前,请通过你自己的应用、聊天工具或邮件把 needs.prompt(以及截图或实时画面链接)发给他们,收到回复后再提交。Webhook 会告诉你何时该这样做。
回答问题#
当页面需要目标中没有的信息时,智能体就会提问:发到你手机或邮箱的验证码、缺失的细节,或只有你能做的选择。有两种回复方式:
{"answer": "…"}:智能体会读取并使用的文本,最多 2,000 个字符。用于细节和选择:{"answer": "Use the billing address, not the shipping one."}{"secret": "…"}:智能体会输入到页面中、但自己看不到的值,最多 4,096 个字符。用于一次性验证码:{"secret": "481923"}
当问题要求提供验证码、密码、PIN 或一次性验证码时,needs.expects_secret 为 true。密钥回复只能在提问时运行所在的网站上输入,可以输入到该网站上任何文本类字段,包括密码和 PIN 字段。与其他密钥一样,六个字符及以上的值无论出现在运行输出的什么位置,都会被替换为 [secret:<name>],并且该值会在运行结束时删除。对于每次运行都要用到的密码,请改为存入保险库,这样智能体就不必询问。
curl https://lovelyrouter.com/v1/browser/runs/brun_5b7d9f1a3c5e7a9b1d3f5a7c/reply \
-H "Authorization: Bearer $LOVELY_API_KEY" \
-H "content-type: application/json" \
-d '{"secret": "481923"}'对于身份验证器应用生成的验证码,请改为把 TOTP 种子存入保险库。这样智能体会自己输入当前的验证码,无需询问你。参见两步验证码。
提交前批准#
approve 决定智能体在执行会提交内容的操作之前,何时停下来等你同意。这类操作包括:提交表单的按钮;标签表明是购买、预订、付款、发送消息或删除的按钮(“Buy now”、“Book”、“Pay”、“Send”、“Delete” 等);以及在不是搜索框的表单字段中按 Enter,这会提交该表单。链接永远不会触发暂停:打开链接只会打开一个页面。
approve | 智能体会在以下操作前暂停 |
|---|---|
risky(默认) | 它判断为不可撤销或后果重大的上述操作:付款或购买、下单或预订、发送消息或邮件、删除数据、提交申请。登录、搜索、页面跳转和表单的中间步骤会直接进行。 |
submit | 上述每一个操作。 |
none | 不暂停。智能体自行提交。 |
其他任何值都会被拒绝,并返回 400。批准暂停如下所示:
{
"status": "waiting_for_human",
"needs": {
"kind": "approval",
"prompt": "Approve: click button \"Redeem 500 points\" on Blue Mug – Rewards?",
"fields": ["Quantity: \"1\"", "Ship to: \"Ada Lovelace, 12 Main St\"", "Gift wrap: unchecked"],
"action": "Redeem 500 points",
"screenshot": "/v1/browser/runs/brun_5b7d9f1a3c5e7a9b1d3f5a7c/screenshot",
"since": "2026-10-11T09:33:40.000Z",
"reply": "/v1/browser/runs/brun_5b7d9f1a3c5e7a9b1d3f5a7c/reply"
}
}fields 列出表单当前的值,action 是它要点击的按钮的标签。回复:
{"approve": true}:智能体在下一步点击该按钮。{"approve": false, "reason": "Wrong address"}:智能体不提交。它会读取你的理由(最多 500 个字符),然后尝试其他方式,或结束并报告原因。{"deny": true}的效果相同。
curl https://lovelyrouter.com/v1/browser/runs/brun_5b7d9f1a3c5e7a9b1d3f5a7c/reply \
-H "Authorization: Bearer $LOVELY_API_KEY" \
-H "content-type: application/json" \
-d '{"approve": true}'遇到 CAPTCHA 时移交#
智能体无法完成 CAPTCHA 或“验证你是真人”的检查。当页面出现这类检查,或遇到只有人能完成的步骤时,它会把浏览器交给你,并将 needs.kind 设为 takeover:
{
"status": "waiting_for_human",
"needs": {
"kind": "takeover",
"prompt": "Please solve the CAPTCHA and press Continue.",
"live_view_url": "https://…",
"live_view_expires_at": "2026-10-11T10:35:02.000Z",
"screenshot": "/v1/browser/runs/brun_5b7d9f1a3c5e7a9b1d3f5a7c/screenshot",
"since": "2026-10-11T09:35:02.000Z",
"reply": "/v1/browser/runs/brun_5b7d9f1a3c5e7a9b1d3f5a7c/reply"
}
}- 在浏览器中打开
live_view_url。它会实时显示智能体的浏览器标签页,人可以在其中点击和输入。该链接有效期为一小时。 - 由人按
prompt的说明操作。 - 回复
{"done": true},可以附带一条智能体会读取的note(最多 500 个字符),例如"Solved the CAPTCHA, now on the search results"。如果对方没能完成,回复{"failed": true, "note": "…"}。
如果运行自己发现移交已经结束,它会不等你回复就继续,之后的回复会返回 409,可以忽略。智能体在卡住时也会移交:连续三步失败后,或重复同一操作三次仍无进展时。
每次运行中,CAPTCHA 或“验证你是真人”页面最多移交两次。如果之后它仍然存在,或者对方回复了 {"failed": true},运行会以 failed 结束,错误为 blocked by the website。直接屏蔽浏览器的页面(拒绝访问、403)也是如此,因为人同样无法通过;以 "human": false 启动的运行遇到任何真人检查时也是如此。
自行接管运行#
你可以随时接管浏览器,例如给智能体指路,或手动完成某一步:
curl https://lovelyrouter.com/v1/browser/runs/brun_5b7d9f1a3c5e7a9b1d3f5a7c/takeover \
-H "Authorization: Bearer $LOVELY_API_KEY" \
-H "content-type: application/json" \
-d '{"instructions": "I will pick the delivery slot myself."}'请求会立即返回 202。运行会在当前步骤完成后暂停,下一次查询状态时会显示 needs.kind 为 takeover,并带有 live_view_url。在实时画面中完成你需要的操作,然后用 {"done": true, "note": "Picked Saturday 10–12"} 交还。智能体会从你离开时的页面继续。
instructions(可选,最多 500 个字符)会成为该次暂停的 prompt。如果运行已经在等待某个问题或批准的回复,接管会在你回复之后开始。已结束的运行会返回 409,错误为 This run has finished。
设置超时#
human_timeout_minutes 设置每次暂停等待回复的时长:1 到 1,440 分钟,默认 30(由云端 Agent 启动的运行默认 5,因为通常无人值守)。云端 Agent 的会话结束时,其未完成的浏览器运行会被停止。每次暂停都会重新计时。超时的运行以 timed_out 结束,并按其步数和计费等待时长扣费。
超时越长,留给人回复的时间越多,但会预留更多余额:预留金额包含一次等待到超时为止的费用,按每分钟 $0.005 计。参见预留机制。
无人值守运行#
对于无人值守的任务,关闭暂停:
{
"goal": "Get the current mortgage rates table.",
"start_url": "https://bank.example.com/rates",
"human": false,
"approve": "none"
}"human": false:智能体从不提问,也不移交。如果缺少信息或卡住,它会尽力继续,运行可能以failed结束。"approve": "none":它提交表单时不等待批准。
两者都设置后,运行不会为等待时长预留任何金额。
回复参考#
POST /v1/browser/runs/{id}/reply 只接受以下请求体中的一种:
| 请求体 | 适用的 needs.kind | 效果 |
|---|---|---|
{"answer": "…"} | question | 智能体读取你的文本(最多 2,000 个字符)。 |
{"secret": "…"} | question | 智能体在当前网站上输入该值,但自己看不到它(最多 4,096 个字符)。 |
{"approve": true} | approval | 智能体在下一步点击该按钮。 |
{"approve": false, "reason": "…"} 或 {"deny": true, "reason": "…"} | approval | 智能体不提交,并读取你的理由。 |
{"done": true, "note": "…"} | takeover | 智能体从当前页面继续。 |
{"failed": true, "note": "…"} | takeover | 智能体继续,并知道对方没能完成。 |
它返回运行对象,其 status 已恢复为 running。错误:
| 状态码 | 错误信息 | 原因 |
|---|---|---|
400 | Reply with one of: … | 请求体不符合上面任何一种形式。 |
404 | No such run | id 错误,或该运行属于其他账户。 |
409 | This run is not waiting for a reply | 运行正在进行或已经结束。 |
409 | This run is waiting for a reply of kind "question" | 回复与 needs.kind 不匹配(错误信息会写明期望的类型)。 |