预览流
预览流使请求方能在提供方仍在撰写回复时看到回复的文本。本页说明提供方如何启用预览、请求方如何请求预览、预览适用哪些限制,以及预览为什么不是结果。
概述
以语言模型回答文本任务的提供方,可能需要很长时间才能完成。没有预览时,请求方在结果到达之前什么也看不到。有了预览,提供方每隔一段时间发送一次回复的快照。请求方可以看到目前文本的末尾部分,并据此判断任务是否在推进。
预览不属于任务。它不被存储,不出现在任务历史中,也不作为结果签名。结束任务的结果取代所有预览,且回执所涵盖的文本只有该结果。
预览默认关闭。双方都需参与:
提供方在其节点上启用预览,且只有文本任务可以预览;
请求方为每个任务请求预览。未提出请求的请求方不会收到预览。
在提供方启用预览
设置项 stream_preview 有三个取值。
off默认值。节点不发送预览。
on节点向提出请求的请求方发送预览。
with_reasoning同
on,并且节点还向提出请求的请求方发送模型的推理文本。
anet preview 命令显示并修改该设置。
$ anet preview
$ anet preview on
$ anet preview off
设置 with_reasoning 需要在终端上确认,因为模型的推理可能包含系统提示词和内部信息。其他取值无需确认。在 off 与其他取值之间切换,会使节点重新发布其卡片,使请求方能够看到提供方是否发送预览(参见 能力与 Agent 卡片)。
预览文本的来源
只有在有来源提供目前已生成的文本时,节点才会发送预览。来源有两种。
openai自动回复后端任务请求预览时,守护进程以流式模式调用模型服务,在文本到达时收集,并交给预览发送器。最终回复与非流式调用的回复逐字节相同。如果流式调用失败,节点不会改用非流式调用重试,因为那样会被计费两次。
- 控制接口
运营人员运行的程序可以将目前已写出的文本推送到本地控制接口的
POST /tasks/preview。请求体为{"task_id": "...", "text": "...", "reasoning": "..."},其中text最大 1 MiB,reasoning可选。该调用需要控制令牌。调用成功时返回{"sent": true},表示文本已交给发送器,而不表示已经到达。
POST /tasks/preview 的拒绝应答如下:
preview_off(409)此节点上预览已关闭。
task_not_open(409)任务已结束,或不是发给本节点的文本任务。
not_requested(200,带"sent": false)请求方在本轮对话中未请求预览。
下面的命令为默认身份推送一段文本。它从 config.json 读取控制地址,从数据目录中的 control_token.txt 读取令牌。函数 auth 通过文件描述符把头交给 curl,使令牌不出现在参数列表中(bash 或 zsh)。
$ DATA=~/.anet
$ auth() { printf 'Authorization: Bearer %s\n' "$(cat $DATA/control_token.txt)"; }
$ curl -sS -X POST "http://$(jq -r .control_addr $DATA/config.json)/tasks/preview" \
-H @<(auth) \
-H "Content-Type: application/json" \
-d '{"task_id": "<task-id>", "text": "The first part of the reply"}'
{"sent":true}
自动回复的 exec 后端不提供预览文本。
请求方请求预览
请求方针对单个任务请求预览。
- 命令行
anet delegate <aid> "text" --watch发送任务、请求预览,并继续执行anet task watch。anet task watch <task-id>跟踪已存在的任务,输出预览、送达状态变化和最终结果。提供方发送推理文本时,选项--reasoning会将其输出。- 本地 A2A 接口
客户端使用
SendStreamingMessage以流式发送消息,或使用SubscribeToTask跟踪任务,且已激活扩展https://agentnetwork.org.cn/a2a/ext/anet-preview/v1时,会把每个预览作为产物anet.reply的更新收到。该更新取代该产物此前的文本,并在元数据中标记为anet.preview: true。预览省略文本开头时,以省略号开头。未激活该扩展的客户端不会收到任何预览事件。- 控制接口
POST /tasks/events,请求体为{"task_id": "..."},为一个任务打开服务器发送事件流。事件task是任务当前的样子(截取到约 24 KB),最先发送,并在每次状态变化时再次发送。事件preview含seq、at、text与omitted,启用推理时还有reasoning与reasoning_omitted;在已有预览时加入的订阅者,先收到当前的预览。事件delivery含delivery、detail、wait和句子text。空闲的流每 20 秒写一行注释。流随任务的最终状态结束。已收到答复的任务在请求方结束它(POST /end)之前保持input-required,只需要答复的客户端在收到携带答复的task事件时即可停止读取。该路由需要控制令牌,MCP 与控制台不提供它。节点上已没有的任务,以no_local_task应答。- MCP
MCP 不提供预览。
wait_task在任务状态变化时返回该任务。
$ anet delegate <provider-aid> "Explain the difference between a hub and a node." --watch
delivery: Sent; waiting for the other side's node to confirm it.
[delivered] Delivered: the other side's node confirmed it.
[working]
preview (14:05:20, first 0 bytes left out):
A hub carries encrypted messages between nodes. ...
[completed]
A hub carries encrypted messages between nodes. ...
提供方声明了预览时,anet card <aid> 会显示相应的一行。
限制
这些值固定在代码中,没有对应的设置。
限制 |
取值 |
|---|---|
同一任务两次预览的间隔 |
至少 5 秒 |
最后一次变化的延迟 |
5 秒内发出 |
单个节点发出的预览 |
每秒最多 20 个;其余的丢弃并计数 |
预览文本 |
最后 8 KiB,在字符边界处截断 |
预览在传输中的生存期 |
60 秒 |
同一任务接受的预览 |
每 2 秒一个 |
单个对端接受的预览 |
每秒 10 个 |
请求方保留最新预览的任务数 |
1024 |
只有在任务处于打开状态,且提供方尚未发出当前一轮的答复时,才会发送预览。真正的答复或结果入队时,节点取消仍在等待的预览,此后不再发送该轮的预览。请求方只接受同时满足以下条件的预览:属于其发出的任务、比已见过的预览更新、并且回应的是请求预览的那条消息。未通过这些检查的预览被丢弃,不作回复。
性质
预览作为普通的加密消息经由 Hub 发送,由提供方签名。Hub 无法读取。消息的生存期很短,为 60 秒,生存期结束后 Hub 将其丢弃。
预览只保存在内存中。它不写入任务数据库、出站队列或证据链,其文本也不写入日志。
请求方对每个任务只保留最新的预览。新消息、任务结果以及任务结束都会将其删除。
提供方重启后会忘记正在预览的那一轮。该轮不再发送预览,其结果照常交付。下一轮可以再次预览。
GetTask、任务历史和每个 MCP 工具都不包含预览。推理文本只出现在本地控制接口上,从不出现在本地 A2A 投影中。
说明
预览可能与最终回复不同。模型可能写出一段文本后又将其替换。任何显示预览的程序都必须将其标注为预览,且不得依据预览采取行动。
推理预览可能泄露提供方的指令和内部数据。它默认关闭,并需要单独确认。
持有提供方较早密钥、且尚未得知密钥轮换的对端,可以为进行中的任务伪造预览。预览依据为该对端存储的身份记录进行验证,不要求新的密钥确认。伪造的预览不会被存储,并会被结果取代。
节点已开启预览但没有
openai后端的提供方,只通过POST /tasks/preview发送预览。anet doctor将此作为信息报告。