平台为企业提供 AI 智能对话能力,对话窗口(左侧)已定义清晰的输入形态(文本/文件/图片/视频/压缩包)。目前 AI 回复仅以纯文本/Markdown 呈现,无法承载结构化业务输出(表格、图表、表单、审批流等),导致用户需要离开对话界面去业务系统操作,体验断裂。
量化指标:
| 阶段 | 关键动作 | 数据形态 |
|---|---|---|
| ① 请求 | 用户发送消息 → 前端组装 → 网关转发 | { session_id, message, files[] } |
| ② 处理 | 意图识别 → 路由场景 → 调用能力 → 格式化输出 | 内部结构化数据 → Artifact JSON |
| ③ 响应 | SSE 返回 → 提取 artifacts → Registry 路由 → 渲染 | { artifacts: [{ type, title, data, actions }] } |
| ④ 回流 | 用户操作 → 事件注入对话上下文 → 新一轮请求 | { action, params, artifact_id } |
右侧面板的渲染能力由四个解耦组件协作完成,新增组件类型只需向 Renderer Registry 注册,不改动其余三者:
| 组件 | 职责 | 关键接口 / 输入 |
|---|---|---|
| Chat Container | 承载对话流,解析 SSE 文本,识别消息体内的 artifact 引用并触发面板加载 | onMessage(chunk) / onArtifact(ref) |
| Artifact Panel | 右侧画布容器,管理多 artifact 的标签页/堆叠、展开收起、全屏,向下分发渲染请求 | render(artifact) / focus(id) |
| Renderer Registry | 按 type 字段查表,把 Artifact JSON 路由到对应渲染器组件;未注册类型降级为 JSON 折叠视图 | register(type, Comp) / resolve(type) |
| State Manager | 维护 artifact 与会话的双向绑定,缓存历史 artifact,捕获组件内交互事件并回注对话上下文 | dispatch(action) / getContext() |
从用户发送消息到 artifact 可交互,前端走如下时序,关键点是文本与 artifact 分离投递:
artifact 不是一次性渲染的死图,而是与会话绑定的活动状态。State Manager 维护三类状态,保证用户操作能反馈给 AI:
每个 artifact 以 artifact_id 为主键缓存其 data 与 actions,支持历史消息回溯时重新挂载,刷新页面后可由会话快照恢复。
组件内的用户操作统一抽象为 { action, params, artifact_id } 事件,入队后既更新本地视图(乐观更新),又异步注入对话上下文,作为下一轮请求的结构化输入。
维护 artifact 与所属 message 的双向引用,AI 在后续轮次可引用"上一张表格的第 3 行"等指代,无需用户重新描述,支撑多轮连续操作。
| 层 | 选型 | 说明 |
|---|---|---|
| 前端渲染 | React + 组件注册表 | 渲染器按 type 动态加载,复用现有对话窗口技术栈 |
| 传输协议 | SSE(文本流)+ JSON(artifacts) | 文本走流式、artifacts 流末整体投递,互不阻塞 |
| 编排引擎 | Dify Workflow | 意图识别、条件路由、格式化输出节点产出 Artifact JSON |
| 能力接入 | Skill 执行器 / API 工具 / RAG | 由各产品线提供,编排层只消费其结构化结果 |
{
"message": "AI 的文本回复(展示在左侧对话)",
"artifacts": [
{
"id": "artifact_001",
"type": "table | chart | kpi | form | checklist | markdown | ...",
"title": "费用明细",
"data": { /* type-specific payload */ },
"actions": [
{ "label": "导出Excel", "action": "download", "params": {} },
{ "label": "筛选", "action": "filter", "params": {} }
],
"meta": {
"source": "finance_query_skill",
"timestamp": "2024-05-15T10:30:00Z",
"ttl": 3600
}
}
]
}
| type | data 结构 | 说明 |
|---|---|---|
| table | { columns: [{key, label, type}], rows: [{}] } | 可排序、分页、导出 |
| chart | { chartType, xAxis, yAxis, series: [] } | ECharts 兼容格式 |
| kpi | { items: [{label, value, delta, unit}] } | 指标卡片组 |
| form | { fields: [{name, type, label, rules}], submitAction } | 动态表单,含校验规则 |
| checklist | { items: [{text, required, checked}], progress } | 可勾选,含进度 |
| markdown | { content: "..." } | 富文本渲染 |
| approval | { steps: [{name, status, actor}], currentStep } | 审批流程可视化 |
| file_preview | { url, mimeType, name, size } | 文件预览/下载 |
| realtime | { wsEndpoint, dataPath, refreshMs } | 实时数据订阅 |
| composite | { layout: "grid|tabs|steps", children: [] } | 容器,嵌套子组件 |
// 用户在右侧面板点击按钮/提交表单时,前端生成:
{
"role": "user",
"type": "artifact_action",
"artifact_id": "artifact_001",
"action": "submit",
"content": "[用户操作] 在「报销申请」中提交了表单",
"metadata": {
"type": "artifact_action",
"artifact_id": "artifact_001",
"action": "submit",
"params": {
"expense_type": "差旅费",
"amount": 4560,
"date": "2024-05-08"
},
"request_id": "req_8f3a2b"
}
}
// → 和用户手动打字走同一条 sendMessage 通道
// → 对网关和 Dify 来说,这就是一条新的用户消息
右侧面板的用户操作能否可靠传递到模型/智能体并触发下一步,是方案成立的核心前提。保障分三层:
// 所有面板交互统一通过 onAction 收口
function handleArtifactAction(artifact, action, params) {
const actionMessage = {
role: "user",
content: `[用户操作] 在「${artifact.title}」中执行了「${action.label}」`,
metadata: {
type: "artifact_action",
artifact_id: artifact.id,
action: action.action,
params: params,
request_id: generateUUID() // 幂等键
}
};
// 和普通对话消息走完全相同的 sendMessage 函数
sendMessage(actionMessage);
}
# 工作流入口的 Code 节点 — 判断消息来源
def route_input(message):
metadata = message.get("metadata", {})
if metadata.get("type") == "artifact_action":
# 结构化操作 → 直接路由到对应 Skill,跳过意图识别
return {
"route": "skill_direct",
"skill": resolve_skill_by_artifact(metadata["artifact_id"]),
"action": metadata["action"],
"params": metadata["params"]
}
else:
# 普通文本 → 正常走意图识别 LLM 节点
return {"route": "intent_classify"}
关键:artifact_action 类型的消息绕过 LLM 意图识别直接路由到 Skill,既省 token 又保证确定性。
| 机制 | 作用 | 实现方式 |
|---|---|---|
| artifact_id 关联 | 操作能追溯到哪个输出产生的 | Skill 据此恢复上下文(如上一次查询的 session state) |
| 幂等去重 | 重复提交不会重复执行 | request_id 在 Skill 侧做去重,10min TTL |
| 超时兜底 | 防止用户等太久 | 前端发出 action 后 10s 无响应 → 显示"处理中"+ 重试按钮 |
| 降级文本 | metadata 解析失败时仍能工作 | content 字段的自然语言走正常意图识别兜底 |
| Action 白名单 | 防止非法操作 | 每个 artifact 声明允许的 actions,前端只渲染声明过的按钮 |
| 操作确认 | 高风险操作防误触 | Skill 在 output_schema 中标记 requireConfirm 的 action,前端弹二次确认 |
| 分类 | 组件 | 典型场景 | 优先级 |
|---|---|---|---|
| 静态展示 | 数据表格 Table | 费用明细、订单列表 | P1 |
| KPI 指标卡 | 经营日报、设备概览 | P1 | |
| Markdown 文档 | 会议纪要、报告 | P1 | |
| 折线/柱状图 | 销售趋势、能耗分析 | P1 | |
| 饼图/环形图 | 成本构成、用户分布 | P2 | |
| 时间轴 Timeline | 项目进展、工单记录 | P2 | |
| 树形结构 Tree | 组织架构、分类目录 | P3 | |
| 地图热力图 | 门店分布、设备位置 | P3 | |
| 交互操作 | 动态表单 Form | 报销申请、工单填报 | P1 |
| 检查清单 Checklist | 质检、巡检、审计 | P1 | |
| 审批流 Approval | 请假、采购审批 | P2 | |
| 筛选器 Filter Panel | 多条件数据筛选 | P2 | |
| 评分卡 Scorecard | 供应商评估、绩效 | P2 | |
| 排程 Scheduler | 会议安排、排班 | P3 | |
| 拖拽看板 Kanban | 项目管理、工单状态 | P3 | |
| 实时数据 | 实时监控面板 | 设备状态、生产线 | P2 |
| 日志流 Log Stream | 系统日志、操作审计 | P2 | |
| 进度追踪 Progress | 任务执行、数据处理 | P1 | |
| 告警卡片 Alert | 异常通知、阈值预警 | P2 | |
| 仪表盘 Gauge | 实时指标可视化 | P3 | |
| 文件与动作 | 文件预览 | PDF/图片/Office 预览 | P1 |
| 代码高亮 Code | 代码生成、配置展示 | P1 | |
| 文件下载 | 导出报表、生成合同 | P1 | |
| 对比视图 Diff | 版本比较、修改审核 | P2 | |
| 签名板 Signature | 电子签名 | P3 | |
| 二维码 QRCode | 分享链接、设备绑定 | P3 | |
| 复合容器 | Tab 分组 | 多视图切换 | P1 |
| Grid 布局 | 仪表盘式多卡片 | P1 | |
| Step 步骤条 | 向导式操作 | P2 | |
| 对比列 Compare | 方案对比、产品比较 | P2 | |
| 折叠面板 Collapse | FAQ、详情收纳 | P2 | |
| 弹窗 Modal | 确认操作、详情展开 | P2 | |
| 侧抽屉 Drawer | 关联信息展开 | P3 |
// 前端注册一个新渲染器
rendererRegistry.register('table', {
component: TableRenderer,
validator: (data) => data.columns && data.rows,
fallback: 'markdown' // 验证失败时降级渲染
});
// 收到 artifact 后的调度逻辑
function renderArtifact(artifact) {
const renderer = rendererRegistry.get(artifact.type);
if (!renderer) return rendererRegistry.get('markdown'); // 兜底
if (!renderer.validator(artifact.data)) {
return rendererRegistry.get(renderer.fallback);
}
return renderer.component(artifact);
}
平台目前已有多个独立智能体,每个智能体是一个垂直领域的"专家"——接收用户输入后,自主理解意图并自动跑完它所负责的那条业务流程(多步骤编排),最终产出业务结果。但智能体之间当前是隔离孤岛:
| 隔离维度 | 现状表现 |
|---|---|
| 入口隔离 | 用户需自行判断该进哪个智能体,办不同的事要切换不同入口 |
| 上下文隔离 | A 智能体产出的结果,B 智能体看不到、无法引用,跨域协作需用户手动搬运 |
| 能力隔离 | 各智能体的 Skill 各自维护,相似能力(如"导出报表")重复建设 |
| 输出隔离 | 各智能体输出形态不统一,前端难以用一套渲染层承接 |
无论用户当前在哪个智能体对话,AI 的结构化产出都遵循同一套 Artifact JSON Schema(见第 3 章),渲染到同一个右侧组件面板。这让"多智能体隔离"与"体验一致"两者并不矛盾:
| 层 | 是否隔离 | 说明 |
|---|---|---|
| 智能体编排(理解+流程) | 隔离 | 每个智能体独立维护意图识别、流程步骤、领域 Skill |
| 输出协议(Artifact Schema) | 统一 | 所有智能体产出同一套 JSON 协议,前端无需为某个智能体定制 |
| 渲染层(组件面板) | 统一 | 一套 Renderer Registry 承接全部智能体的输出,体验一致 |
| 交互回流 | 统一 | 组件内操作按同一 Action 协议回流到"当前所在智能体"的对话 |
统一输出层只是第一步。在 Artifact 协议落地后,多智能体可沿如下三阶段渐进打通,每一阶段都不破坏前一阶段的隔离保证:
Dify 工作流适合 AI 编排逻辑,但在以下场景存在局限:
| 痛点 | Dify 工作流 | Skill 方案 |
|---|---|---|
| 复杂业务逻辑 | Code 节点有执行时间/依赖限制 | 独立运行时,无限制 |
| 输出 Schema 管理 | 散落在各工作流的 Code 节点中 | 集中声明在 skill.yaml |
| 版本控制 | 工作流整体版本,粒度粗 | 每个 Skill 独立版本 |
| 复用性 | 跨智能体复用困难 | Skill 可被多个智能体引用 |
| 测试 | 依赖完整工作流运行 | Skill 可独立单测 |
# skill.yaml
name: finance_query
version: 1.2.0
description: 财务数据查询与报表生成
input_schema:
type: object
properties:
query_type: { type: string, enum: [expense, revenue, budget] }
date_range: { type: object, properties: { start: {type: string}, end: {type: string} } }
department: { type: string }
output_schema:
artifacts:
- type: table
data_schema:
columns: { type: array, items: { properties: { key: {type: string}, label: {type: string} } } }
rows: { type: array }
actions: [download, filter, sort]
- type: kpi
data_schema:
items: { type: array, items: { properties: { label: {}, value: {}, delta: {} } } }
triggers:
- intent: ["查费用", "报销记录", "预算"]
- entity: ["expense", "budget", "revenue"]
| 适合 Dify 工作流 | 适合 Skill |
|---|---|
| 轻量数据查询(单表/单接口) | 复杂业务逻辑(多步事务) |
| AI 生成类(文案/总结/翻译) | 精确计算类(报表/统计) |
| 简单条件分支 | 需要外部 SDK/依赖的场景 |
| 需频繁调整 prompt 的场景 | 输出 Schema 稳定的场景 |
周期:2-4 周 / 场景
周期:1-3 天 / 场景
| 平台功能 | 与 Artifact 方案的关系 |
|---|---|
| Skill 注册中心 | 管理所有 Skill 的 output_schema,前端据此预加载 renderer |
| Dify 工作流编辑器 | 提供「Skill 调用」节点模板,自动填充 input/output 映射 |
| 组件预览沙盒 | 开发者可上传 mock data,实时预览组件渲染效果 |
| Schema 校验服务 | Skill 注册时校验 output_schema 是否符合 Artifact 协议 |
| 灰度/版本管理 | Skill 新版本可灰度切流,旧版 renderer 保持兼容 |
// 1. 创建渲染器文件
// src/renderers/ApprovalRenderer.tsx
// 2. 实现组件(接收标准 data + actions)
function ApprovalRenderer({ data, actions, onAction }) {
return (
<div className="approval-flow">
{data.steps.map(step => (
<StepCard key={step.name} {...step} />
))}
{actions.map(act => (
<Button onClick={() => onAction(act)}>{act.label}</Button>
))}
</div>
);
}
// 3. 注册到 Registry
rendererRegistry.register('approval', {
component: ApprovalRenderer,
validator: (data) => Array.isArray(data.steps),
fallback: 'markdown'
});
| 时间 | 里程碑 | 验收标准 |
|---|---|---|
| 第 2 周 | 框架 Demo | 左右分栏 + 1 个组件端到端跑通 |
| 第 4 周 | Phase 1 交付 | 3 个标杆场景可演示,交互回流工作 |
| 第 7 周 | Skill 平台 Alpha | 开发者可自助注册 Skill + 预览输出 |
| 第 10 周 | Phase 2 交付 | 10+ 场景上线,移动端可用 |
| 第 14 周 | Phase 3 Beta | 开放 API 文档完成,外部产品线试接入 |
| 第 18 周 | 全量上线 | 33 组件就绪,性能达标 |
| 依赖 | 提供方 | 当前状态 | 风险等级 |
|---|---|---|---|
| Dify 工作流 HTTP 节点支持调用 Skill | 平台组 | 已支持 | 低 |
| 推理网关 SSE 代理稳定性 | 基础设施 | 偶发 502 | 中 |
| 产品线提供标准 API(ERP/MES/财务) | 各产品线 | 部分就绪 | 中 |
| 前端组件库技术选型 | 前端组 | 待定 | 高 |
| Skill 执行器运行环境 | 平台组 | 方案中 | 中 |
| 风险 | 影响 | 应对策略 |
|---|---|---|
| 推理网关不稳定(502) | 用户对话中断 | 前端增加重试 + 优雅降级提示;网关做健康检查自动摘除 |
| Artifact JSON 格式不一致 | 渲染异常/白屏 | Registry 强制 validator 校验 + fallback 降级为 Markdown |
| 产品线 API 返回格式不统一 | Skill 开发成本增加 | 提供 Adapter SDK,封装常见数据源的格式转换 |
| 组件数量膨胀导致前端包体积大 | 首屏加载慢 | 按 type 动态 import,只加载当前会话需要的 renderer |
| 交互回流导致对话上下文过长 | LLM Token 超限 | 设计 context window 管理策略:只保留最近 N 次交互摘要 |
| 项目 | 参考价值 | 地址 |
|---|---|---|
| assistant-ui | Generative UI 规范、组件协议设计 | github.com/assistant-ui/assistant-ui |
| Vercel AI SDK | 流式 UI + React Server Components | sdk.vercel.ai |
| Ant Design X | AI 对话组件库、Thought Chain | x.ant.design |
| shadcn AI Artifacts Block | Artifact Canvas 交互模式 | github.com/shadcn-ui/ui |