你创建了一个配置任务,几毫秒内就拿到了一个pjb_任务 ID 和一个eng_引擎 ID。这个引擎已经可以使用了,但配置仍在继续完善:AI 代理会抓取你的源内容,并把品牌语气、术语表条目和指令写入其中。运行过程中,你大概会想把这些进展展示出来——像安装向导那样显示“正在抓取风格指南……正在配置引擎……已完成”,而不是只放一个什么都不说明的加载动画。
WebSocket 提供的正是这样一条进度流。连上任务后,服务器会先推送当前状态的快照,之后每当工作流进入新步骤,就发送一个provisioning.progress事件。而且因为服务器会在连接时先发当前状态,并在任务完成后立即关闭连接,你可以随时连接,哪怕是在任务结束之后——完全不用担心错过时机。
GET /jobs/provisioning/:jobId/ws这里的jobId就是create 调用返回的pjb_值。刚接触异步配置?建议先看概览,建立整体认知。
本页内容
消息类型#
Socket 上会传两类消息。第一类只会在连接建立时到达一次;第二类会随着任务推进持续到达。
| 类型 | 时机 | 关键字段 |
|---|---|---|
provisioning.snapshot | 初始连接时 | jobId、status、errorMessage |
provisioning.progress | 每个工作流步骤开始或完成时 | jobId、step、detail |
这是一条存活状态流,不是结果流:它告诉你任务进行到哪一步、有没有失败,但不会告诉你 AI 具体创建了哪些记录。所有已配置内容的汇总——包括品牌语气、术语表和指令的 ID——会通过完成 webhook单独送达,或者在任务完成后通过读取该任务获取。Socket 用来看进度条;要拿结果负载,用 webhook。
连接时快照#
你一连上,服务器就会从数据库读取任务当前状态并发给你。不需要先收到进度事件——快照本身就是完整成立的。
{
"type": "provisioning.snapshot",
"jobId": "pjb_A1b2C3d4E5f6G7h8",
"status": "in_progress",
"errorMessage": null
}| 字段 | 说明 |
|---|---|
status | in_progress、completed 或 failed。 |
errorMessage | 当status为failed时,为失败描述;否则为null。 |
快照是唯一一条你一定会收到的消息。如果任务还在运行,之后你会继续收到进度事件;如果任务已经结束,你会收到快照,然后就没有更多消息了(见下文)。
进度事件#
随着工作流运行,服务器每进入一个新步骤,都会广播一个provisioning.progress事件。每个事件都会标明step,并附带一条人类可读的detail。
{
"type": "provisioning.progress",
"jobId": "pjb_A1b2C3d4E5f6G7h8",
"step": "crawling",
"detail": "Crawling source URLs..."
}step | 时机 | 示例detail |
|---|---|---|
crawling | 正在抓取源 URL | "Crawling source URLs..." 或 "Retrying crawl (attempt 2)..." |
configuring | AI 代理正在分析内容并写入引擎配置 | "AI agent analyzing content and configuring engine..." 或 "Retrying configuration (attempt 2)..." |
completed | 任务已成功完成 | "Provisioning complete" |
failed | 任务失败 | 用于说明失败原因的错误消息 |
重试不等于失败
crawling 和 configuring 这两个步骤可能会触发多次——临时性的抓取或分析错误会自动重试,而重试会以进度事件的形式表现出来,其detail可能类似 "Retrying crawl (attempt 2)..."。这说明任务正在自我恢复,不代表任务失败。只有failed步骤才应视为终态;真正的失败原因在它的detail里。
处理你不认识的步骤
新的step值可能会随着时间不断增加。对你已知的步骤做分支处理,把completed和failed视为会关闭 socket 的两个步骤,其他值一律按信息性内容忽略——这样客户端即使不更新,也能保持前向兼容并继续正常工作。
任务结束后再连接#
任何进度 socket 最棘手的问题,都是如果你连得晚了会怎样——比如抓取已经结束、部署后标签页重新连上,或者任务早已失败。在这里,答案就内置在快照机制里。
如果任务已经到达completed或failed,服务器会发送一份带有最终status的快照(如果失败,还会带上errorMessage),然后立即关闭连接。不需要重放任何进度事件,因为最终状态本身就已经体现在这份快照里。仍在运行中的任务会保持连接开启并持续推送进度;已经结束的任务则会把结果交给你,然后挂断连接。
无论哪种情况,第一条消息都会告诉你当前处于什么状态。你可以随时连接,哪怕是在任务结束之后——不会连得太早,也不会连得太晚。
接入你的 UI#
针对pjb_任务 ID 打开 socket,先读取快照设置初始状态,再在每次收到进度事件时更新;当任务到达completed或failed时关闭连接:
import WebSocket from "ws";
const jobId = "pjb_A1b2C3d4E5f6G7h8";
const ws = new WebSocket(
`wss://api.lingo.dev/jobs/provisioning/${jobId}/ws`,
{ headers: { "X-API-Key": process.env.LINGO_API_KEY } }
);
ws.on("message", (raw) => {
const event = JSON.parse(raw);
switch (event.type) {
case "provisioning.snapshot":
console.log(`status: ${event.status}`);
break;
case "provisioning.progress":
console.log(`${event.step}: ${event.detail}`);
if (event.step === "completed" || event.step === "failed") {
ws.close();
}
break;
}
});对一个抓取过程顺利、并会逐步输出配置进展的任务运行:
status: in_progress
crawling: Crawling source URLs...
configuring: AI agent analyzing content and configuring engine...
completed: Provisioning complete这就是屏幕上完整的过程:任务以in_progress开始,你看着它先抓取、再配置,而completed会告诉你引擎已经完全配置完成。这个循环在延迟连接时同样适用——已完成的任务只会发送一条包含最终status的快照,然后 socket 关闭,所以处理实时运行的这套代码,也能直接处理这种“回放”,无需额外分支。
将 API 密钥保留在服务端#
这个 socket 使用你的 API 密钥进行鉴权——也就是 REST 端点使用的同一个组织级密钥。这把密钥可以访问你组织下的所有引擎,因此浏览器并不是建立连接的正确位置:任何能查看源码的人都可能看到它。
从后端连接,不要在浏览器里连
从你的服务器发起 WebSocket 连接,密钥本来就保存在那里;然后再通过你自己的通道把进度转发给浏览器——可以是你掌控的 WebSocket,或 server-sent events 流。前端负责展示引擎正在配置;密钥始终留在你的基础设施内。
这和webhook的模式是一致的:真正连接 Lingo.dev 的那一端运行在服务端,而最终传给用户什么内容,则由你自己的应用来决定。
它在整体流程中的位置#
WebSocket 提供的是实时视图——它绑定单个任务,并会在该任务完成时关闭。若要拿到一份持久的、服务端到服务端的结果记录,并且在标签页关闭或重新部署后依然保留结果,就把它和完成 webhook搭配使用:任务显示在屏幕上时,socket 驱动进度条;结果一落地,webhook 就会把 AI 创建内容的完整摘要送达。两者都从同一个create 调用接入。
