NevoFlux
无头部署(Docker)

任务 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/eventsSSE —— 直到终态的 status 帧
DELETE/tasks/:id取消 → 202 Accepted,未知 id 返回 404
GET/metricsPrometheus 计数器
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"}

请求体即任务契约:

字段类型默认值含义
taskstring——(必填)给 agent 的指令。
followupsobject[][]同一对话的后续轮次,在 task 之后运行——见后续轮次。(v0.3.16)
modestring"browser"agent 模式。
profilestring无要克隆的 base 配置名(登录状态);省略则用空白 base。
policy.allow_shellboolfalse放行 shell 工具(run_command、bash)。
policy.allow_fs_writeboolfalse放行文件写入工具。
policy.allow_uploadboolfalse放行 uploadFile。
policy.domain_allowliststring[][]将 navigate / web_fetch 限制到这些域名(空 = 任意)。
wall_clock_secsint无每任务挂钟截止时间。
token_budgetint无每任务 token 消耗上限。
idempotentboolfalse即使已运行过写操作也重试(调用方声明幂等)。
no_retryboolfalse完全禁用自动重试。
end_sessionboolfalse仅会话模式——在此任务后拆除共享浏览器。
save_profileboolfalse仅会话模式——拆除时把克隆写回某个 base 配置(隐含结束会话)。
save_profile_asstring所克隆的 base可选的另存为 base 名。
backendstring无用来代替 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_restarts

DELETE 在队列层面把排队中或运行中的任务标记为 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-addrOpenAI 与 Anthropic API
Anthropic Messages/v1/messages是--anthropic-addrOpenAI 与 Anthropic API
A2A/.well-known/agent-card.json、/a2a、/a2a/v1是--a2a-addrA2A
MCP(Streamable HTTP)/mcp否--mcp-addrMCP 端点与管理 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_MODEmodebrowser
NEVOFLUX_TASK_PROFILEprofile无
NEVOFLUX_POLICY_ALLOW_SHELLpolicy.allow_shellfalse
NEVOFLUX_POLICY_ALLOW_FS_WRITEpolicy.allow_fs_writefalse
NEVOFLUX_POLICY_ALLOW_UPLOADpolicy.allow_uploadfalse
NEVOFLUX_POLICY_DOMAIN_ALLOWLISTpolicy.domain_allowlist空(逗号分隔)
NEVOFLUX_WALL_CLOCK_SECSwall_clock_secs无(A2A 流式:900)
NEVOFLUX_TOKEN_BUDGETtoken_budget无
NEVOFLUX_IDEMPOTENTidempotentfalse
NEVOFLUX_NO_RETRYno_retryfalse
NEVOFLUX_SAVE_PROFILEsave_profilefalse
NEVOFLUX_SAVE_PROFILE_ASsave_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"]

注意事项:

  1. browser_navigate 会打开一个新的、非活动的标签页并返回 {"tab_id": N}——要把这个 tab_id 传给之后的每次调用,否则工具会报 "No active web tab found"。
  2. 工具结果是结构化的 dict,而不是字符串——例如 browser_get_markdown(...) → {"markdown","title","url","success"}。请按字段取值。
  3. Monty 是 Python 的子集,在 v0.3.14 中升级(能运行的普通 Python 比以前多得多)。不支持 的写法会明确报错——例如装饰器会被拒绝,而不是被悄悄忽略。
  4. 脚本的时间预算是任务挂钟减去 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 接口是两种不同模式。

本页目录