Lovely Router文档
获取密钥
浏览器智能体

人工介入

让智能体向你提问、在提交前等待你批准、遇到 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 描述它需要什么(节选):

JSON
{
  "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.kindquestion、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
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。批准暂停如下所示:

JSON
{
  "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
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:

JSON
{
  "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"
  }
}
  1. 在浏览器中打开 live_view_url。它会实时显示智能体的浏览器标签页,人可以在其中点击和输入。该链接有效期为一小时。
  2. 由人按 prompt 的说明操作。
  3. 回复 {"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
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 计。参见预留机制。

无人值守运行#

对于无人值守的任务,关闭暂停:

JSON
{
  "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。错误:

状态码错误信息原因
400Reply with one of: …请求体不符合上面任何一种形式。
404No such runid 错误,或该运行属于其他账户。
409This run is not waiting for a reply运行正在进行或已经结束。
409This run is waiting for a reply of kind "question"回复与 needs.kind 不匹配(错误信息会写明期望的类型)。