任务 API 与接口
无头 HTTP 任务 API——提交任务、后续轮次、逐轮用量、取消——以及哪个接口在哪个端口上、会话与固定脚本模式,和完整的命令行标志与环境变量参考。
在服务模式下,守护进程在 --http-addr 上提供一个小巧的 HTTP 任务 API。所有端点均为
JSON。同一端口还承载 OpenAI / Anthropic 与 A2A 前端;MCP、ACP 与管理 API 各有自己的端口
——见接口与端口。
端点
| 方法 | 路径 | 用途 |
|---|---|---|
POST | /tasks | 提交任务 → {"id":"task-N"} |
GET | /tasks/:id | 状态 / 结果快照(未知 id 返回 404) |
GET | /tasks/:id/events | SSE —— 直到终态的 status 帧 |
DELETE | /tasks/:id | 取消 → 202 Accepted,未知 id 返回 404 |
GET | /metrics | Prometheus 计数器 |
POST | /session/close | 会话模式:拆除被复用的浏览器会话 |
GET | /tasks/:id/artifacts/:name | 下载任务留在其工作区中的文件(属于 A2A) |
没有认证。 该端口上没有任何东西校验凭据——
/tasks、/session/close、OpenAI / Anthropic 端点都不校验。任何能访问它的人,都能用容器的服务商密钥和浏览器运行任务。请把它 放在私有网络中,或放在带认证的代理之后。
提交任务
curl -X POST localhost:8080/tasks -H 'Content-Type: application/json' \
-d '{"task":"open example.com and report the title","mode":"browser"}'
# → {"id":"task-0"}请求体即任务契约:
| 字段 | 类型 | 默认值 | 含义 |
|---|---|---|---|
task | string | ——(必填) | 给 agent 的指令。 |
followups | object[] | [] | 同一对话的后续轮次,在 task 之后运行——见后续轮次。(v0.3.16) |
mode | string | "browser" | agent 模式。 |
profile | string | 无 | 要克隆的 base 配置名(登录状态);省略则用空白 base。 |
policy.allow_shell | bool | false | 放行 shell 工具(run_command、bash)。 |
policy.allow_fs_write | bool | false | 放行文件写入工具。 |
policy.allow_upload | bool | false | 放行 uploadFile。 |
policy.domain_allowlist | string[] | [] | 将 navigate / web_fetch 限制到这些域名(空 = 任意)。 |
wall_clock_secs | int | 无 | 每任务挂钟截止时间。 |
token_budget | int | 无 | 每任务 token 消耗上限。 |
idempotent | bool | false | 即使已运行过写操作也重试(调用方声明幂等)。 |
no_retry | bool | false | 完全禁用自动重试。 |
end_session | bool | false | 仅会话模式——在此任务后拆除共享浏览器。 |
save_profile | bool | false | 仅会话模式——拆除时把克隆写回某个 base 配置(隐含结束会话)。 |
save_profile_as | string | 所克隆的 base | 可选的另存为 base 名。 |
backend | string | 无 | 用来代替 agent 循环运行的脚本后端路径——见按任务选择后端。 |
策略默认完全锁死。 不提供
policy时,任务获得无 shell、无文件写入、 无上传、任意域名。请按任务、按需授予能力。
一个完整请求:
{ "task": "open example.com and report the title",
"mode": "browser",
"profile": "base1",
"policy": { "allow_shell": false, "allow_fs_write": false,
"allow_upload": false, "domain_allowlist": ["example.com"] },
"wall_clock_secs": 300, "token_budget": 200000,
"idempotent": false, "no_retry": false }失败的尝试会自动重试——总共最多 3 次尝试,且仅当失败的那次没有派发过任何写操作工具时才
重试,除非设置了 idempotent。no_retry 关闭重试。
状态与结果
curl -s localhost:8080/tasks/task-0{ "id": "task-0",
"status": "succeeded", // queued | running | succeeded | failed | canceled
"attempts": 1,
"output": "The title is Example Domain.",
"artifacts": [], // 留在任务工作区中的文件名
"session_id": "automation-task-0-1",
"usage": [ { "main": { "input": 5120, "output": 210, "calls": 2, "cache_read": 4096 },
"last_input": 2860, "first_token_ms": 820, "total_ms": 9400,
"model": "claude-..." } ] }canceled自 v0.3.16 起成为独立状态。被取消的任务以前报告为failed并带error: "cancelled";现在报告为canceled,error字符串保持不变。对status做穷举 匹配的客户端需要更新。error在失败时出现;output与error为空时省略。session_id(v0.3.16) 是最后一次尝试的会话,想要完整事件日志时把它交给nevoflux-agent session export。usage(v0.3.16) 为每个调用了 LLM 的 agent 轮次给出一项:main(输入 / 输出 token、调用次数,以及服务商报告时input中来自提示缓存的cache_read/cache_write部分——自 v0.3.16 起 Anthropic 流量使用提示缓存)、运行了子 agent 时的subagent、last_input(最后一次调用的上下文大小)、计时(first_token_ms、decode_ms、total_ms)与model。没有发生 LLM 调用时省略——例如固定脚本模式。estimated: true表示数字是估算的,而非服务商报告。turn_outputs(v0.3.16) 在任务带有后续轮次时出现:按顺序列出每轮的最终文本。output是其中最后一个。
任务还会把 result.json 与 debug-bundle/ 写进它的工作区,即
NEVOFLUX_PROFILE_WORK 下的 ws-<task-id>。镜像没有设置该变量,所以工作区落在
/tmp/nevoflux-profiles/——在 tmpfs 上,不在 /work 挂载中。其中的文件可以通过
GET /tasks/:id/artifacts/:name 取回。如果你把 NEVOFLUX_PROFILE_WORK 指向 /work 以便在
宿主机上收集结果,请注意每个任务的配置克隆(含 cookie)在任务运行期间也会创建在同一
目录中。
实时事件(SSE)
curl -N localhost:8080/tasks/task-0/events
# event: status
# data: {"id":"task-0","status":"running","attempts":0,...}
# event: status
# data: {"id":"task-0","status":"succeeded","output":"...Example Domain","attempts":1,...}每次状态变化(以及终态)都会发出一个 status 帧,其间夹杂保活注释。任务到达
succeeded、failed 或 canceled 时流结束。
取消与指标
curl -i -X DELETE localhost:8080/tasks/task-0 # → 202 Accepted(未知则 404)
curl localhost:8080/metrics
# nevoflux_tasks_total / nevoflux_tasks_failed / nevoflux_browser_restartsDELETE 在队列层面把排队中或运行中的任务标记为 canceled。它不会中断已在运行的尝试:
该尝试会跑到自己结束(受 wall_clock_secs 限制),其最终结果可能覆盖 canceled 状态。
如果需要靠取消来控制成本,请给任务设置挂钟。
后续轮次
v0.3.16 新增。 followups 在一个任务中运行同一段对话的后续轮次——相同的会话 id、相同
的浏览器,并像聊天那样把之前各轮的纯文本作为历史带上:
curl -X POST localhost:8080/tasks -H 'Content-Type: application/json' -d '{
"task": "open example.com and report the title",
"followups": [
{"message": "now summarise the first paragraph"},
{"message": "and list every link on the page", "delay_secs": 30}
]}'delay_secs在发送该轮之前等待(例如在测量提示缓存时让缓存过期)。- 轮次在第一个失败的轮次处停止——后面的轮次会是在回答一段已经出错的对话。
turn_outputs保存每轮的回答,usage每轮一项。- 后续轮次适用于 agent 循环;脚本后端只运行一次。
按任务选择后端
backend 为当前这一个任务指定一个脚本后端文件,
用来代替 agent 循环(或进程级的 NEVOFLUX_HEADLESS_SCRIPT)。典型用途是给脚本后端登录:
以任务形式运行一个等待脚本并给足挂钟,在它等待时通过 VNC
登录,然后把克隆另存为 base 配置。
curl -s localhost:8080/tasks -H 'content-type: application/json' -d '{
"task": "sign in",
"backend": "/opt/nevoflux/scripts/login-wait.py",
"profile": "gemini",
"wall_clock_secs": 900,
"save_profile": true,
"save_profile_as": "gemini"
}'
backend可以是容器内任何可读的.py路径,而任务 API 没有认证——所以任何能访问/tasks的人,都能运行镜像或其挂载中已有的任何脚本。脚本运行在沙箱解释器里,但它们驱动 浏览器时不受任务策略的限制。不要挂载你不愿让调用方运行的脚本。
接口与端口
所有前端都把请求归约到同一个任务队列。各端口提供什么:
| 接口 | 端点 | 在 --http-addr 上 | 专用标志 | 文档 |
|---|---|---|---|---|
| 任务 API | /tasks…、/metrics、/session/close | 是 | —— | 本页 |
| OpenAI Chat Completions、Responses、模型列表 | /v1/chat/completions、/v1/responses、/v1/models | 是 | --openai-addr | OpenAI 与 Anthropic API |
| Anthropic Messages | /v1/messages | 是 | --anthropic-addr | OpenAI 与 Anthropic API |
| A2A | /.well-known/agent-card.json、/a2a、/a2a/v1 | 是 | --a2a-addr | A2A |
| MCP(Streamable HTTP) | /mcp | 否 | --mcp-addr | MCP 端点与管理 API |
| ACP(JSON-RPC 2.0) | /acp | 否 | --acp-addr | 见下文 |
| 管理 API | /admin/… | 否 | --admin-addr(+ NEVOFLUX_ADMIN_TOKEN) | MCP 端点与管理 API |
# 每个接口各占一个端口
nevoflux-agent --daemon --headless \
--http-addr 0.0.0.0:8080 --openai-addr 0.0.0.0:8081 \
--mcp-addr 0.0.0.0:8082 --acp-addr 0.0.0.0:8083 \
--a2a-addr 0.0.0.0:8084 --anthropic-addr 0.0.0.0:8085 \
--admin-addr 127.0.0.1:8086专用标志只是新增一个监听端口,并不会把端点从 --http-addr 上移除。特别是,主端口始终
响应 A2A 以及 OpenAI / Anthropic 格式。
ACP
一个最小化的 ACP 端点:initialize、session/new 与 session/prompt。提示中的文本块会被
拼接成一个任务;答案作为一个文本块返回,不发送流式 session/update 通知。
curl -X POST localhost:8083/acp -H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"session/prompt",
"params":{"prompt":[{"type":"text","text":"open example.com, report title"}]}}'
# → {"result":{"stopReason":"end_turn","content":[{"type":"text","text":"...Example Domain"}]}}仅含提示词的接口的环境变量默认值
OpenAI / Anthropic、MCP run_browser_task、ACP 与 A2A 前端只携带提示词,因此
mode / profile / policy / 各种上限都来自环境变量。POST /tasks 仍然按请求接受所有
这些字段——环境变量只是那些无法携带它们的接口的默认值。
| 环境变量 | 字段 | 默认值 |
|---|---|---|
NEVOFLUX_TASK_MODE | mode | browser |
NEVOFLUX_TASK_PROFILE | profile | 无 |
NEVOFLUX_POLICY_ALLOW_SHELL | policy.allow_shell | false |
NEVOFLUX_POLICY_ALLOW_FS_WRITE | policy.allow_fs_write | false |
NEVOFLUX_POLICY_ALLOW_UPLOAD | policy.allow_upload | false |
NEVOFLUX_POLICY_DOMAIN_ALLOWLIST | policy.domain_allowlist | 空(逗号分隔) |
NEVOFLUX_WALL_CLOCK_SECS | wall_clock_secs | 无(A2A 流式:900) |
NEVOFLUX_TOKEN_BUDGET | token_budget | 无 |
NEVOFLUX_IDEMPOTENT | idempotent | false |
NEVOFLUX_NO_RETRY | no_retry | false |
NEVOFLUX_SAVE_PROFILE | save_profile | false |
NEVOFLUX_SAVE_PROFILE_AS | save_profile_as | 无 |
布尔值接受 1 / true / TRUE / yes。
会话模式
默认情况下每个任务完全隔离:克隆一份全新配置 → 启动浏览器 → 运行 → 关闭浏览器并删除克隆。
设置 NEVOFLUX_SESSION_MODE=1 则改为运行一个 task-flow:各任务共享一个浏览器 +
配置克隆(任务之间活动标签页会被软重置为 about:blank),直到你结束这个流程。
# 环境中 NEVOFLUX_SESSION_MODE=1
curl -X POST localhost:8080/tasks -d '{"task":"log in to example.com"}' # 使用共享浏览器
curl -X POST localhost:8080/tasks -d '{"task":"go to the dashboard"}' # 复用它(共享登录)
curl -X POST localhost:8080/tasks -d '{"task":"export the report","end_session":true}' # 运行,然后拆除
# 或随时带外结束:
curl -X POST localhost:8080/session/close # → {"closed":true|false,"saved_to":...,"save_error":...}- 预热浏览器 (v0.3.14)。开启会话模式后,守护进程在启动时就拉起共享浏览器,因此第一个
任务(或第一次 MCP
browser_*调用)不必承担冷启动、也不会因此失败。预热失败会被记录, 并由下一个任务重试。(由进程内引擎提供浏览器工具 时跳过。) - 按顺序驱动任务——提交,等它结束,再提交下一个。共享浏览器由锁串行化,但任务顺序 由调用方负责。
- 流程内共享状态——任务共享 cookie / localStorage / 登录。被提示注入的任务可能影响同一 流程中后续的任务,所以会话模式只用于可信、顺序的流程。跨流程隔离依然保留(下一个流程 会克隆全新的 base 配置)。
- 崩溃恢复——共享浏览器在流程中途崩溃时,下一个任务会基于同一克隆重新拉起它(cookie 持久化在磁盘上,登录状态得以保留)。
- 软重置——脚本后端任务会跳过软重置,因为它们自己管理标签页(把脚本复用的标签页清空会
迫使每个请求都冷加载一次)。
NEVOFLUX_SESSION_SOFT_RESET=0对所有任务关闭软重置。
把登录状态写回 base。 在任务上设置 save_profile: true(或调用 POST /session/close
并传 {"save": true, "as": "<name>"}),即可在拆除时把会话的克隆写回某个 base 配置
(save_profile_as / as 指定不同的 base 名)。这要求 base-profiles 挂载可写(去掉
:ro)。
A2A 上下文无论是否设置 NEVOFLUX_SESSION_MODE 都使用
同一套 task-flow 机制,POST /session/close 也会释放已绑定的 A2A 上下文。
固定脚本模式
对于不需要 LLM 服务商的确定性浏览器流水线,把守护进程指向一个 Python 脚本:
NEVOFLUX_HEADLESS_SCRIPT=/opt/nevoflux/fixed-flow.py设置后,每个无头任务都运行该脚本,而不是 LLM agent 循环(除非请求另选了后端——见
模型路由与 backend)。
脚本定义 def run(task): ...;守护进程以接口传入的任务字符串调用它。run 返回(或
打印)的内容成为 output;抛出异常 → status: "failed" 并附错误信息。它使用与 agent 相同
的浏览器工具,运行在沙箱化的 Monty 解释器中,驱动所绑定的无头浏览器——但零 LLM
调用、无需 API 密钥(也不需要 GBrain)。脚本也可以改为定义 def chat(request) 来接收
完整的脚本后端契约。
def run(task):
nav = browser_navigate(url="https://example.com/search")
tab = nav["tab_id"] # navigate 会打开一个新标签页
browser_fill(selector="#q", value=task, tab_id=tab)
browser_click(selector="button[type=submit]", tab_id=tab)
browser_wait_for(selector="#results", tab_id=tab, timeout_ms=15000)
return browser_get_markdown(tab_id=tab)["markdown"]注意事项:
browser_navigate会打开一个新的、非活动的标签页并返回{"tab_id": N}——要把这个tab_id传给之后的每次调用,否则工具会报 "No active web tab found"。- 工具结果是结构化的 dict,而不是字符串——例如
browser_get_markdown(...)→{"markdown","title","url","success"}。请按字段取值。 - Monty 是 Python 的子集,在 v0.3.14 中升级(能运行的普通 Python 比以前多得多)。不支持 的写法会明确报错——例如装饰器会被拒绝,而不是被悄悄忽略。
- 脚本的时间预算是任务挂钟减去 10 秒余量(在
chat()请求中为metadata.budget_secs)。 Monty 没有时钟,所以请按轮数限制轮询循环。
挂载脚本并设置环境变量(Compose):
environment:
NEVOFLUX_HEADLESS_SCRIPT: /opt/nevoflux/fixed-flow.py
volumes:
- ./fixed-flow.py:/opt/nevoflux/fixed-flow.py:ro模板脚本位于 deploy/headless/examples/fixed-flow.py(基础的 导航 → 填写 → 点击 → 读取)与
fixed-flow-advanced.py(多步分页 + try/except,始终返回结构化的 {ok, ...} dict)。
命令行标志参考
nevoflux-agent 二进制上与无头部署相关的标志:
| 标志 | 含义 |
|---|---|
--daemon | 作为核心守护进程运行。本页所有模式都必需。 |
--headless | 无头自动化模式:每个任务启动一个浏览器,并提供下列接口。 |
--remote-control | 运行一个由手机驱动、没有 HTTP 接口的端——见远程控制(容器)。(v0.3.14) |
--http-addr <addr> | 任务 API;也提供 OpenAI、Anthropic 与 A2A 路由。 |
--openai-addr <addr> | 在专用端口上提供 OpenAI Chat Completions、Responses 与 /v1/models。 |
--anthropic-addr <addr> | 在专用端口上提供 Anthropic Messages。(v0.3.16) |
--a2a-addr <addr> | 在专用端口上提供 A2A card 与 JSON-RPC 路径。(v0.3.16) |
--mcp-addr <addr> | 基于 Streamable HTTP 的 MCP(/mcp)。仅在此处提供。 |
--acp-addr <addr> | 基于 HTTP 的 ACP(POST /acp)。仅在此处提供。 |
--admin-addr <addr> | 受令牌保护的管理 API;没有 NEVOFLUX_ADMIN_TOKEN 时不挂载。请绑定到私有接口。(v0.3.14) |
要使用不在 ~/.config/nevoflux/ 下的 config.toml,请设置 NEVOFLUX_CONFIG=<path>
(v0.3.16);它优先于默认位置,空值视为未设置。(二进制也接受 --config 标志,但截至
v0.3.16 守护进程并不读取它——请使用环境变量。)
不要与
--mcp(不带--daemon)混淆,后者运行的是用于 Claude Code 集成的独立 stdio MCP 服务器——与上面的无头 MCP-over-HTTP 接口是两种不同模式。