Artifact 入门
Artifact 是 Agent 推给客户端的结构化数据块。客户端用对应的 renderer 把它 渲染成可交互的卡片:表单、进度条、审批、卡片、媒体。本页讲 Artifact 入门: subtype 列表、payload 结构、最简示例。双向交互(artifact_update + 用户回流)见 Artifact 高级。
subtype 列表
| subtype | 客户端 renderer | 典型用途 |
|---|---|---|
info | InfoCard | 信息提示卡 |
reply | ReplyCard | 结构化回复 |
app | AppRenderer(按 payload.ui 路由) | 小程序 / 小游戏 / 自定义 UI |
form | FormCard | 收集用户输入 |
response | ResponseCard | 用户对 form 的反馈 |
tool_call | (内部)触发 tool 调用 UI | 详见 Tool Call |
tool_response | (内部)tool 执行结果 | 详见 Tool Call |
approval | ApprovalCard | 危险操作确认 |
progress | ProgressCard | 长任务进度 |
media | MediaCard | 图 / 视频 / 音频聚合展示 |
最简示例:信息卡
await agent.sendArtifact(conversationId, { artifact: { subtype: "info", title: "部署成功", payload: { body: "你的 Cloudflare Worker 已部署到 us-east-1", icon: "✅", actions: [ { id: "open", label: "查看 logs", url: "https://dash.cloudflare.com/..." }, ], }, },});进度卡
const artifactId = ulid();
await agent.sendArtifact(conversationId, { artifact: { artifact_id: artifactId, subtype: "progress", title: "导出 Q1 数据", payload: { percent: 0, status: "starting", eta_s: 120 }, },});
// 长任务更新进度for (let pct = 10; pct <= 100; pct += 10) { await new Promise((r) => setTimeout(r, 5000)); await agent.updateArtifact(conversationId, { ref_artifact: artifactId, based_on_revision: (pct / 10) - 1, artifact: { payload: { percent: pct, status: pct < 100 ? "running" : "done", eta_s: Math.round((100 - pct) * 1.2), }, }, });}详见 Artifact 高级 — artifact_update 与 revision。
表单卡
await agent.sendArtifact(conversationId, { artifact: { artifact_id: ulid(), subtype: "form", title: "新建项目", payload: { fields: [ { id: "name", label: "项目名", type: "text", required: true }, { id: "description", label: "描述", type: "textarea" }, { id: "private", label: "私有", type: "checkbox", default: false }, { id: "visibility", label: "可见性", type: "radio", options: [ { id: "public", label: "公开" }, { id: "private", label: "私有" }, ], }, ], submit_label: "创建", cancel_label: "取消", }, },});
agent.addEventHandler(async (event) => { if (event.type !== "artifact_response") return; if (event.payload.action !== "submit") return; const { name, description, private: isPrivate, visibility } = event.payload.payload; await createProject({ name, description, private: isPrivate, visibility });});审批卡
await agent.sendArtifact(conversationId, { artifact: { artifact_id: ulid(), subtype: "approval", title: "确认部署到生产环境", payload: { request: { environment: "production", version: "v2.4.1", changes: ["3 commits, 12 files"], }, approve_label: "批准并部署", reject_label: "取消", timeout_s: 300, }, },});approval 是 form 的一种特殊 UI(内置 approve / reject 双按钮 + 倒计时)。
SDK 在 timeout_s 内未收到响应自动 timeout,给你发 artifact_response status=denied
reason=timeout。
媒体卡
// 先上传文件得到 attachment_idconst { attachment_id: imageId } = await agent.uploadFile(conversationId, { filename: "chart.png", mime: "image/png", data: chartPng, send_as: null, // 不直接发;只为 artifact 用});
await agent.sendArtifact(conversationId, { artifact: { subtype: "media", title: "Q1 数据图表", payload: { items: [ { type: "image", attachment_id: imageId, caption: "趋势" }, ], }, },});应用卡 (subtype: “app”)
最灵活——任意 React 组件渲染。客户端按 payload.ui 路由 renderer:
await agent.sendArtifact(conversationId, { artifact: { artifact_id: ulid(), subtype: "app", title: "今日午餐投票", payload: { ui: "vote_card", // 客户端按 ui 选 renderer question: "今天午餐吃什么?", options: [ { id: "a", label: "拉面", votes: 0 }, { id: "b", label: "披萨", votes: 0 }, { id: "c", label: "沙拉", votes: 0 }, ], total: 0, }, capability_flags: { allows_response: true }, },});客户端内置 renderer 支持 vote_card / tic_tac_toe / 2048 / form / progress,
社区自定义 UI 走 fallback。
详见 小程序与小游戏。
完整 ArtifactSendPayload 字段
interface ArtifactSendPayload { artifact: { artifact_id?: string; // 可选;不传 SDK 会生成 ULID(强烈建议自己传以便后续 update) subtype: // 必填 | "info" | "reply" | "app" | "form" | "response" | "tool_call" | "tool_response" | "approval" | "progress" | "media"; title?: string; payload: object; // 任意 JSON,由客户端 renderer 解释 capability_flags?: { allows_response?: boolean; ttl_s?: number; }; expires_at?: string; // ISO 8601;过期后客户端自动 disable };}限制
| 项 | 上限 |
|---|---|
| 单 artifact JSON 大小 | 64 KB(含 wrap) |
| 同对话同时存活的 artifact 数 | 100(超过后老的自动 expire) |
| artifact 默认存活 | 30 天(除非 expires_at 显式覆盖) |
| 单 artifact update 次数 | 100(防止状态机暴走) |
大数据建议放 R2 → artifact payload 里只放引用 url;客户端按需 GET。
客户端 renderer 速读
| subtype | 客户端组件 | payload 关键字段 |
|---|---|---|
info | InfoCard | body, icon, actions[] |
form | FormCard | fields[], submit_label, cancel_label |
progress | ProgressCard | percent, status, eta_s |
approval | ApprovalCard | request, approve_label, reject_label, timeout_s |
app | AppRenderer (按 ui) | 自定义 |
media | MediaCard | items[] (type=image/video/audio + attachment_id) |
未匹配的 subtype 走 “Unknown artifact” fallback —— 显示原始 JSON + “Open in browser” 按钮。
接收 artifact_response 概要
agent.addEventHandler(async (event) => { if (event.type !== "artifact_response") return; const { ref_artifact, // 你之前发的 artifact_id action, // "submit" / "approve" / "reject" / "vote" / 自定义 payload, // 用户提交的数据 sender_id, } = event.payload; console.log(`user ${sender_id} did ${action} on artifact ${ref_artifact}`);});详细处理见 Artifact 高级。
下一步
- Artifact 高级 — 双向交互 — artifact_update / revision / 冲突处理
- 小程序入门 — 用 artifact 做”小程序”
- 小游戏 2048 — 状态化游戏的实战
- Tool Call —
subtype: "tool_call"的特殊用法