Progress Bar — 长任务进度条
LLM 调用、文件处理、数据导出等长任务前发个 progress 卡,让用户看到 “还在跑” + “ETA”,胜过沉默 30 秒后突然吐结果。
前提
- 同 发送文本
代码
import { HasheeAgent } from "@hasheeai/agent-sdk-ts";import { ulid } from "ulid";
const agent = await HasheeAgent.init({ agentId: process.env.HASHEE_AGENT_ID!, token: process.env.HASHEE_AGENT_TOKEN!, baseUrl: "https://api.hashee.ai", connectionMode: "websocket",});
async function withProgress<T>( convId: string, title: string, totalSteps: number, task: (step: (n: number, msg?: string) => Promise<void>) => Promise<T>,): Promise<T> { const artifactId = ulid(); let revision = 0;
await agent.sendArtifact(convId, { artifact: { artifact_id: artifactId, subtype: "progress", title, payload: { percent: 0, status: "starting", eta_s: null }, }, });
const startedAt = Date.now();
const updateStep = async (current: number, message?: string) => { const percent = Math.min(100, Math.round((current / totalSteps) * 100)); const elapsed = (Date.now() - startedAt) / 1000; const eta = current > 0 ? Math.round((elapsed / current) * (totalSteps - current)) : null; revision++; try { await agent.updateArtifact(convId, { ref_artifact: artifactId, based_on_revision: revision - 1, artifact: { payload: { percent, status: message ?? (percent < 100 ? "running" : "done"), eta_s: eta, }, }, }); } catch (e: any) { if (e.code !== "ARTIFACT_REVISION_CONFLICT") throw e; } };
const result = await task(updateStep);
// 完成 revision++; await agent.updateArtifact(convId, { ref_artifact: artifactId, based_on_revision: revision - 1, artifact: { payload: { percent: 100, status: "done", eta_s: 0 } }, });
return result;}
// 用法:处理 100 个文件agent.addMessageHandler(async (msg) => { if (msg.payload?.type !== "text") return; if (msg.payload.text !== "/process") return;
const files = Array.from({ length: 50 }, (_, i) => `file-${i}.csv`);
const summary = await withProgress( msg.conversation_id, `处理 ${files.length} 个文件`, files.length, async (step) => { const results = []; for (let i = 0; i < files.length; i++) { // 模拟处理 await new Promise((r) => setTimeout(r, 100)); results.push({ file: files[i], ok: true }); if (i % 5 === 0 || i === files.length - 1) { await step(i + 1, `处理中: ${files[i]}`); } } return results; }, );
await agent.send(msg.conversation_id, { type: "text", text: `✓ 全部 ${summary.length} 个文件处理完成`, });});
console.log("[progress-bot] up");UI 渲染
┌─────────────────────────────────────┐│ 处理 50 个文件 │├─────────────────────────────────────┤│ ████████░░░░░░░ 60% (30/50) ││ 处理中: file-30.csv ││ ETA: 8 秒 │└─────────────────────────────────────┘性能建议
- 不要每个 step 都 update — 5-10% 节奏即可(每秒 update 不超过 5 次)
- 加 throttle / debounce 防止短任务洪水
- 用
try/catch容忍ARTIFACT_REVISION_CONFLICT(并发场景)