从自然语言到数据大屏:Forge Report Studio 的 AI 生成链路

业务人员说"我要一张经营监控大屏",AI 在 10 秒内给出完整可交互的可视化方案 ------ 这不是演示 Demo,是 Forge Report Studio 的日常。


一、问题场景:当业务人员需要大屏,但不懂代码

如果你在传统 BI 团队呆过,一定见过这样的流程:

复制代码
业务提需求 → BI 分析师出指标定义 → 前端开发画布局 → 
后端开发配数据源 → 联调数据接口 → UI 走查 → 上线

一张中等复杂度的大屏,从需求到上线通常需要 2-4 周,中间至少经历 3 个角色的反复沟通。任何一方排期紧张,大屏就搁置。

更残酷的真相是:80% 的数据大屏需求并没有那么复杂 。它们往往是"把 A 表的数据用柱状图展示、B 表的趋势画折线图、再加几个 KPI 卡片"。这类需求的问题不在技术难度,而在角色分工导致的流程成本

Forge Report Studio 的 AI 生成链路试图回答一个问题:能不能让业务人员直接说人话,AI 负责把话变成大屏?


二、解决方案:自然语言 → 大屏 JSON → 可视化组件

整体思路

我们选择了一条结构化生成路径,而不是让 AI 直接写前端代码:

javascript 复制代码
用户自然语言
  ↓
AI 模型(理解需求 + 组件选择 + 数据绑定)
  ↓
大屏 JSON Schema(标准化的组件配置)
  ↓
前端渲染引擎(组件库 + 布局算法 + 数据适配器)
  ↓
可交互的数据大屏

选择 JSON 而非代码生成,有三个核心考量:

  1. 可控性:JSON 可以在进入渲染引擎前被校验、修复、规范化,代码则几乎无法安全自动化
  2. 审计性:每一份 AI 生成的 JSON 都完整记录了"AI 选了什么组件、绑了什么数据集、做了什么取舍",出问题时可以精确定位
  3. 安全性:JSON 运行在已有的沙箱渲染引擎中,不会引入任意代码执行风险

端到端流程

从用户发送需求到看到大屏,整个链路大约 10-15 秒:

阶段 耗时 做什么
上下文准备 <1s 拉取组件目录、业务定义、数据集画像、运行时样例
AI 推理 5-10s 模型根据 prompt 生成大屏 JSON
流式解析 同步 SSE 逐块接收,前端实时展示进度
结果校验 <1s 数据集绑定验证、布局规范化、组件合法性检查
应用到画布 <1s 创建组件实例、合并默认配置、建立数据请求

三、数据结构:大屏的三个核心抽象

在进入实现细节前,先理解大屏的数据模型。一张大屏由三层结构组成:

3.1 画布层

typescript 复制代码
// 画布配置
interface CanvasConfig {
  width: number          // 画布宽度,默认 1920
  height: number         // 画布高度,默认 1080
  background: string     // 背景色或背景图
  chartThemeColor: string // 主题色,影响所有图表的配色
  projectName: string    // 项目名称
}

画布是整个大屏的容器,定义了尺寸和全局视觉基调。

3.2 组件层

typescript 复制代码
// AI 返回的组件 Schema
interface AIComponentSchema {
  key: string            // 组件类型,如 'BarCommon'、'PieCommon'
  x: number; y: number   // 左上角坐标
  w: number; h: number   // 宽高
  title?: string         // 自定义标题
  option?: Record<string, any>  // 图表配置(ECharts option 等)
  request?: {            // 动态数据绑定
    datasetId: number
    datasetName: string
    datasetFields: string[]
    datasetMapping: { ... }
  }
}

关键设计:request 字段是可选的数据绑定。AI 生成时如果知道该用哪个数据集,就会填 request.datasetId;如果无法确定,就只填 option.dataset 作为静态预览数据。这个设计打通了"AI 生成"和"动态数据查询"两个世界。

3.3 组件库:AI 的"调色盘"

AI 不是凭空创造组件,而是从已有的 100+ 组件库中选择。每个组件都有 AI 可见的描述信息:

yaml 复制代码
// 组件注册表中的 AI 描述
const aiComponentInfoMap = {
  BarCommon: { defaultW: 500, defaultH: 300, description: '柱状图,适合对比分类数据、月度/季度趋势' },
  PieCommon: { defaultW: 500, defaultH: 300, description: '饼图,适合展示占比分布' },
  LineCommon: { defaultW: 500, defaultH: 300, description: '折线图,适合展示时间趋势变化' },
  KpiCard: { defaultW: 300, defaultH: 120, description: 'KPI 指标卡片,适合展示核心数值' },
  TableScrollBoard: { defaultW: 500, defaultH: 250, description: '轮播表格,适合多行数据滚动展示' },
  MapBase: { defaultW: 800, defaultH: 600, description: '地图,适合地理数据展示' },
  // ... 共 100+ 个组件
}

AI prompt 中会注入组件目录文本,让模型知道有哪些"积木"可用。组件按类别组织:

  • 图表类:柱状图、折线图、饼图、散点图、热力图、雷达图、漏斗图、桑基图等 22 个 ECharts 组件 + 10 个 VChart 组件
  • 信息类:文本、渐变文字、图片、轮播图、视频、词云等 15 个
  • 表格类:普通表格、轮播表格、排行榜
  • 装饰类:13 种边框、6 种装饰元素
  • 指标类:KPI 卡片、指标组、仪表盘等 20 余个

这个组件目录是 AI 和渲染引擎之间的契约:AI 只负责选组件和给配置,渲染引擎负责把配置变成真实的 Vue 组件。


四、实现链路:AI 生成大屏的完整流程

4.1 整体架构

scss 复制代码
┌──────────────────────────────────────────────────┐
│              大屏设计器前端 (Vue 3)                 │
│  ┌──────────┐  ┌───────────┐  ┌────────────────┐ │
│  │ AIChatPanel │  │ aiEngine  │  │ componentRegistry│ │
│  │ (聊天UI)   │  │ (画布应用) │  │ (组件目录)       │ │
│  └─────┬────┘  └─────┬─────┘  └───────┬────────┘ │
│        │             │               │           │
│  ┌─────▼────┐  ┌─────▼─────┐  ┌─────▼─────────┐ │
│  │generateValidation│ │layoutAlgorithm│ │datasetAdapter│ │
│  │(结果校验) │  │(布局规整)  │  │(数据适配)     │ │
│  └──────────┘  └───────────┘  └───────────────┘ │
└────────────────────┬─────────────────────────────┘
                     │ POST /forge-report-api/ai/generate/stream
                     ▼
┌──────────────────────────────────────────────────┐
│               后端 AI 服务 (Spring Boot)           │
│  ┌──────────────┐  ┌───────────────────────────┐ │
│  │ AiChatService │  │ AiPromptTemplateRenderer │ │
│  │ (提示词构建)  │  │ (模板变量渲染)              │ │
│  └──────┬───────┘  └───────────────────────────┘ │
│         │                                         │
│  ┌──────▼──────────────────────────────────────┐ │
│  │         forge-plugin-ai                      │ │
│  │  AiClient → ChatClientCache → OpenAI API    │ │
│  │  (统一调用 + 供应商切换 + 流式输出)            │ │
│  └─────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────┘

4.2 提示词工程:让 AI 理解"做大屏"这个任务

AI 生成大屏的核心挑战不是"让模型输出 JSON"------任何现代模型都能做到。真正的挑战是让模型输出正确、可用、符合组件库约束的 JSON

我们的提示词设计分三层:

第一层:系统角色定义

javascript 复制代码
你是一个数据可视化大屏设计专家,请根据用户需求生成合法的 JSON 大屏配置。

第二层:约束注入(12 个模板变量)

后端 AiChatService 构建时注入以下上下文:

变量 内容 作用
componentCatalog 完整组件目录(100+ 组件,含 key、尺寸、描述) 告诉 AI 可选组件池
businessContext 业务定义 + 数据集画像(字段、用途、推荐用法) 告诉 AI 数据语义
runtimeDataPreview 数据集抽样查询结果(数据样例) 让 AI 理解字段取值和量级
canvasWidth / canvasHeight 画布尺寸 约束组件位置和大小
style 深色/浅色主题 约定视觉风格
projectName 项目名称 生成合理的标题
canvasContext 画布已有组件(增量修改时) 让 AI 在此基础上调整

第三层:数据绑定指令

这是最关键的一层。我们在 prompt 中明确要求:

diff 复制代码
- 所有图表、表格组件必须输出 request.datasetId 和 request.datasetFields
- request.datasetId 只能来自 datasets 列表中的值
- request.datasetFields 只能使用数据集 fields 内的 fieldName
- 优先使用 primary 数据集生成顶部 KPI 和主图
- 按 usageRemark 匹配趋势、排行、明细、地图等组件
- 允许保留 option.dataset 作为预览兜底,但动态组件必须带 request

前端在发送生成请求前,会将业务定义上下文和数据运行时样例进行压缩和格式化 ,构建为 businessContext JSON 字符串传给后端。

4.3 流式生成:让等待不再焦虑

AI 生成大屏通常需要 5-10 秒。如果这段时间用户只看到转圈,体验会很差。我们使用 SSE 流式输出,让用户实时看到进展:

csharp 复制代码
用户发送 "生成经营监控大屏"
  ↓
[connecting] 正在连接模型...
  ↓
[chunk × N] 流式推理文本逐块返回
  ↓
[complete] 生成完成

前端 consumeReasoningAwareChunk() 函数负责解析流式数据块,支持思考过程分离(DeepSeek-R1 等推理模型)。在生成模式下,前端不直接展示 AI 输出的 JSON 原文,而是显示进度步骤:

复制代码
● 理解需求 → ● 规划布局 → ○ 生成组件 → ○ 完善配置 → ○ 校验结果

进度步骤通过统计已接收文本长度来估算当前阶段,简单但有效。

4.4 JSON 解析与修复:容错是工程的第一要义

AI 输出的 JSON 不可能 100% 合法。llmClient.ts 中的解析管线做了两层容错

javascript 复制代码
export function parseStreamedResponse(fullText: string): AIGenerateResponse {
  // 第一层:提取 JSON(去除 markdown 代码块、前后文字)
  const jsonStr = normalizeJSONString(extractJSON(fullText))
  
  try {
    return JSON.parse(jsonStr)
  } catch {
    // 第二层:修复常见错误后重试
    const repaired = repairJSON(jsonStr)  // 补括号、去尾逗号、修引号
    return JSON.parse(repaired)
  }
}

normalizeJSONString() 处理了 6 类 AI 常见错误:

  • 对象碎片({ "0 } 类幻觉产物)
  • 引号错乱("xxx"option""xxx", "option"
  • 尾部多余逗号
  • 缺少引号的 key(key:"key":
  • 缺失的组件对象开头("key"{"key"
  • 括号不平衡(自动补齐 }]

这些修复让解析成功率从裸 JSON.parse 的约 85% 提升到 98%+。

4.5 结果校验:AI 输出不可信的,程序来兜底

这是整个链路中最重要的工程环节generateValidation.tsvalidateAIGenerateResponse() 函数对 AI 输出执行三层校验:

第一层:组件合法性检查

javascript 复制代码
const descriptor = componentRegistry.get(key)
if (!descriptor) {
  summary.skipped++  // 组件不在组件库中,直接跳过
  summary.warnings.push(`组件 ${key} 不在组件库中,已跳过`)
  return
}

第二层:布局规范化

javascript 复制代码
// 钳制越界:确保组件不超出画布范围
x = Math.max(0, Math.min(x, canvasWidth - w))
y = Math.max(0, Math.min(y, canvasHeight - h))
// 钳制尺寸:确保组件不小于最小尺寸
w = Math.min(Math.max(w, minW), canvasWidth)

第三层:数据集绑定验证与修复

这是最复杂的校验逻辑。核心函数 validateDatasetRequest() 实现了多级降级 + 自动修复策略:

arduino 复制代码
数据集绑定状态机:
  ┌──────────┐
  │ 有 datasetId │──→ 在当前业务定义中?──→ 是 ──→ 字段校验通过?──→✓ bound
  │              │                        │
  │              │                        └──→ 否 ──→ 自动修复字段 → repaired
  │              │
  │              │──→ 不在当前业务定义中?
  │              │      ├──→ 通过字段/编码/名称推断到其他数据集 → repaired(自动改绑)
  │              │      └──→ 无法推断 → staticFallback(删除 request,降级为静态)
  └──────┬───────┘
         │
  ┌──────▼───────┐
  │ 无 datasetId  │──→ 有业务定义?
  │              │      ├──→ 是 → 尝试推断数据集 → 成功 → repaired
  │              │      │                      └→ 失败 → static
  │              │      └──→ 否 → unverified(保留原始绑定但不校验)
  └──────────────┘

推断算法 :当 AI 没有指定 datasetId 或指定了无效的 datasetId 时,校验器会根据以下线索自动匹配数据集:

  • 组件 request 中的 datasetCode / datasetName 与数据集的精确/模糊匹配
  • 组件标题与数据集名称的语义匹配
  • request.datasetFields 与数据集 fields 的字段名交集
  • 数据集的 usageRemark(用途说明)与组件类型的匹配

这个推断算法让 AI 即使"记错"了 datasetId,只要在请求中表达了足够的数据意图,程序也能自动修正。

4.6 布局算法:模型管选择,程序管摆放

一个容易被忽略的设计决策是:布局由程序处理,AI 只管组件选择和数据

layoutAlgorithm.ts 中的 normalizeAILayout() 函数对 AI 返回的组件位置执行 9 步规整流程:

scss 复制代码
① 统一边框风格 (ensureConsistentPanelBorders)
② 去除冗余装饰 (removeRedundantDecorations)
③ 钳制越界尺寸 (normalizeSize)
④ 规范化标题 (normalizeHeader)
⑤ 装饰元素规范 (normalizeOverlayDecorations)
⑥ 指标组等宽排列 (normalizeTopMetrics)
⑦ 应用大屏布局模板 (applyLargeVisualTemplate)
    - 模板 A:左窄 + 中宽 + 右窄(经典监控布局)
    - 模板 B:左侧大图 + 右侧两列(地理大屏布局)
    - 模板 C:右侧大图 + 左侧两列(核心指标右置)
⑧ 大尺寸组件规范 (normalizeLargeVisuals)
⑨ 重叠检测 + 间距强制 (placeWithoutOverlap + enforceBorderGaps)

AI 返回的 x, y, w, h 只是一个"建议",布局算法会把它规整到合理的范围内。这意味着即使用户反复修改 prompt 重新生成,大屏的视觉效果始终保持一致。

4.7 应用到画布:AI 输出如何变成真实组件

aiEngine.tsapplyAIToCanvas() 函数是连接 AI 输出和渲染引擎的桥梁:

javascript 复制代码
export function applyAIToCanvas(response: AIGenerateResponse) {
  // 1. 应用画布配置:标题、背景色、主题色、尺寸
  // 2. 遍历 components,逐个 createAIComponent()
  // 3. 模式选择:replace(替换整个画布)或 append(增量添加)
}

function createAIComponent(schema: AIComponentSchema) {
  // 1. findConfigTypeByKey(schema.key) ------ 查找组件定义
  // 2. componentInstall(chartConfig) ------ 注册 Vue 组件
  // 3. createComponent(chartConfig) ------ 创建组件实例
  // 4. applyOption(schema.option, componentInstance) ------ 智能合并配置
  // 5. applyDatasetRequest(schema.request, componentInstance) ------ 建立数据绑定
}

智能配置合并applyOption() 的核心逻辑。由于 AI 只返回了部分 option 字段,前端需要把 AI 的输出合并到组件的默认配置上:

arduino 复制代码
// ECharts 组件的合并策略:
// 1. 覆盖 dataset(AI 给出的数据和维度)
// 2. 自动调整 series 数量匹配 dimensions 列数
// 3. 保留组件的样式、动画、交互等默认配置

这意味着 AI 只需要关心"数据长什么样",不需要关心"图表怎么渲染"。所有渲染细节(坐标轴、图例、tooltip、动画)都由组件的默认配置提供。


五、设计取舍:工程化 AI 生成的四个关键决策

5.1 为什么是 JSON Schema 而不是代码生成?

这是做 AI 大屏时要回答的第一个问题。市面上有不少方案让 AI 直接生成 React/Vue 组件代码,但我们的判断是:

维度 JSON Schema 生成 代码生成
输出可控性 高:结构化数据,可校验、可修复、可回滚 低:代码片段质量不稳定,难以自动化验证
安全性 高:运行在已有渲染引擎沙箱中 低:任意代码执行风险
上下文利用率 中:token 主要用于数据描述 低:大量 token 消耗在语法和模板代码
用户可编辑性 高:生成后用户可在设计器中拖拽调整 低:生成的代码用户无法修改
与已有组件库集成 高:组件 key 直接映射到注册表 低:需要额外的编译/转译步骤

结论:对于"业务人员使用"的场景,JSON Schema 是更合适的选择。代码生成更适合"开发者使用"的场景,那是另外的产品方向。

5.2 如何平衡 AI 的灵活性和系统的可控性?

我们的策略是分层解耦,各自负责擅长的部分

markdown 复制代码
AI 负责(灵活):
  - 理解用户需求
  - 选择合适的组件组合
  - 确定数据绑定关系
  - 提供布局建议

程序负责(可控):
  - 校验组件合法性
  - 修复无效的数据集绑定
  - 规范化布局位置
  - 合并组件默认配置
  - 统一视觉风格

这是一种**"放风筝"模式**:让 AI 自由发挥创意,但程序的校验和修复层像一根线,确保它不会飞出边界。

5.3 数据集绑定:为什么是最难的部分?

统计显示,数据集绑定错误占 AI 生成问题的 60% 以上。原因很简单:AI 模型不"认识"你的数据。

我们的解决方案分三个层次:

  1. Prompt 层面 :在 businessContext 中提供结构化的数据集画像(字段名、类型、用途、推荐用法),并明确要求 AI 按规则填写
  2. 数据预检层面:在生成前对绑定数据集做抽样查询,把真实数据样例注入 prompt,让 AI 看到字段的实际取值
  3. 校验修复层面 :生成后运行 validateDatasetRequest(),自动推断和修复错误的数据集绑定
yaml 复制代码
// 数据预检样例(注入到 prompt 中)
runtimeDataPreview: {
  items: [{
    datasetId: 42,
    datasetName: "销售明细表",
    status: "ready",
    sampleRows: [
      { month: "2024-01", revenue: 1280000, orders: 3420, category: "电子产品" },
      { month: "2024-02", revenue: 1150000, orders: 2980, category: "电子产品" }
    ]
  }]
}

这些样例行让 AI 能从"字段名"推断出"数据长什么样",从而做出更准确的组件类型选择和字段映射。

5.4 业务定义:AI 准备度评分

不是所有业务定义都适合 AI 直接生成。businessReadiness.ts 中的 evaluateBusinessReadiness() 函数对业务定义的 AI 准备度打分:

复制代码
评分维度(满分 100):
├── 语义字段(最多 60 分):业务描述、分析目标、指标口径、分析维度、使用建议
├── 数据集绑定(最多 21 分):数据集数量、主数据集标记
├── 字段上下文(最多 14 分):字段总数
└── 用途说明(最多 5 分):数据集 usageRemark 是否有值

≥80 分 → 准备充分 → 大概率生成高质量大屏
55-79 分 → 可生成 → 部分组件可能使用静态数据
<55 分 → 需补充 → 建议先完善业务定义再生成

这个评分在用户选择业务定义后实时展示,帮助用户在生成前评估预期的生成质量。


六、二开指南:三个最常用的扩展方向

6.1 扩展组件库

新增一个 AI 可选的组件需要两步:

第一步 :在 componentRegistry.tsaiComponentInfoMap 中添加组件描述:

yaml 复制代码
// 例如新增一个"实时监控指标卡"
RealTimeMonitor: {
  defaultW: 400,
  defaultH: 150,
  description: '实时监控指标卡,适合展示需要刷新的核心指标,支持阈值告警'
}

第二步 :确保组件在 packagesList 中正确注册,createComponent() 可以正常实例化。

无需修改任何 AI prompt 模板------组件描述会自动进入下一次请求的 componentCatalog

6.2 自定义主题

主题系统集中在 src/settings/chartThemes/ 目录下:

css 复制代码
// global.theme.json 控制全局 ECharts 样式
{
  "title": { "textStyle": { "color": "#BFBFBF" } },
  "xAxis": { "axisLine": { "lineStyle": { "color": "#B9B8CE" } } },
  "legend": { "textStyle": { "color": "#B9B8CE" } }
}

如果要新增主题方案(如"商务蓝"、"科技绿"),需要:

  1. chartThemes/ 下新建主题文件
  2. 在 AI 聊天面板的 style 选择器中添加对应选项
  3. AiChatService.buildDashboardContextVars() 中增加对应的模板变量

6.3 调整 AI 生成策略

AI 生成行为主要由两个后端配置控制:

系统提示词AiPromptTemplateRenderer 渲染):

arduino 复制代码
// 默认提示词
"你是一个数据可视化大屏设计专家,请根据用户需求生成合法的 JSON 大屏配置。"

业务定义上下文compactBusinessContext() 构建):

如果想让 AI 更激进地使用动态数据绑定,可以调整 generationPolicy

css 复制代码
// 例如:要求所有图表必须动态绑定
generationPolicy: {
  bindingPriority: [
    '所有图表、表格、指标组件必须输出 request.datasetId',
    '不允许生成不带 request 的图表组件'
  ]
}

如果希望 AI 更保守(优先使用静态数据预览),则反之。


七、体验预告

AI 生成大屏解决了"快速搭建"的问题,但大屏的真正价值在于数据是活的

下一篇 C07《大屏动态数据接入:从静态 Mock 到实时查询》 将深入 Forge Report Studio 的数据接入体系:

  • 数据源类型:HTTP API、JDBC 数据库直连、WebSocket 实时推送
  • 数据集定义:SQL 查询、动态参数(时间范围/下拉筛选/级联参数)
  • 数据安全:行级权限过滤、字段级脱敏、SQL 注入防护
  • 数据适配:数据集到 ECharts 的标准化映射
  • 外部 API 代理:跨域访问的安全方案设计

项目入口

模块 地址
后台管理 http://81.70.22.48:8084/forge/login
项目文档 http://81.70.22.48:8084/forge-docs/
大屏设计器 http://81.70.22.48:8084/forge-report/
Gitee gitee.com/ForgeLab/fo...
GitHub github.com/yaomindong1...
相关推荐
测试_AI_一辰1 小时前
AI模型评测不只看准确率-CV与Agent评测指标体系梳理
人工智能·机器学习·计算机视觉
她的男孩1 小时前
大屏动态数据接入:从静态 Mock 到真实业务 API
后端·架构
吴佳浩1 小时前
Vibe Coding 时代,研发经理为何越来越值钱?
算法·架构
sugar__salt1 小时前
Prompt工程实战指南:规范设计、LLM接口封装与避坑技巧
人工智能·python·prompt
QiLinkOS1 小时前
【用呼吸重构创造价值关系——QiLink生态】
c语言·数据结构·c++·人工智能·单片机·嵌入式硬件·算法
cxr8281 小时前
高分子复合材料AI逆向设计合成(PCARPS)流程研究
人工智能·智能体
canonical_entropy1 小时前
为什么 Attractor Guided Engineering 不能被降级为 AI Agent Skill
架构·agent·ai编程
weixin_468466851 小时前
图像处理特征提取新手实战指南
图像处理·人工智能·算法·ai·机器视觉·特征提取
我爱cope1 小时前
【Agent智能体13 | 工具使用-什么是工具?】
人工智能·语言模型·职场和发展