Skip to content

[Feature Request] 在对话中直接渲染 AI 生成的数据图表(vis 代码块) #6791

Description

@ninelcc336

🥰 需求描述

痛点

NextChat 已经很好地支持了 ```mermaid 代码块渲染为流程图。但当 LLM 需要展示数据时——销售趋势、用户分析、调查结果——只能输出 Markdown 表格或纯文本,用户需要把数据复制到别处才能可视化。

这是真实的需求。#5707 明确提出了数据可视化需求——"利用本地搭建的大模型做数据分析,并进行可视化展示",并提到希望使用 echarts 等可视化库。

缺少的是一个对 LLM 友好、像 mermaid 一样易生成的图表格式——不过 mermaid 画的是流程图,而用户需要的是数据图表。

为什么适合 NextChat

  1. 和 mermaid 一样的模式——用户已经理解"代码块语言 = 渲染的可视化结果"。vis 对数据图表来说是完全相同的思维模型,零学习成本。

  2. 响应真实需求——#5707 以及类似请求表明,用户希望在对话中通过自然语言驱动数据可视化。GPT-Vis 是这个等式的渲染端。

  3. 26 种图表,一种语法——统计图(折线/柱状/饼图/散点/雷达/漏斗等)+ 关系图(思维导图/流程图/网络图/组织架构图)+ 文本可视化(词云/表格)。

  4. 零破坏性变更——vis 检测与 mermaid/HTML 检测并存在 PreCode 中。没有 vis 代码块时静默跳过。

  5. 包体积可控——@antv/gpt-vis UMD 约 1MB gzipped,可通过动态 import() 加载,不阻塞首屏。

  6. 暗色模式兼容——内置 dark 主题,与 NextChat 暗色模式匹配。通过 new GPTVis({ container: el, theme: 'dark' }) 切换。

🧐 解决方案

方案

GPT-Vis@antv/gpt-vis,MIT 协议,AntV 出品)是专为 LLM 输出设计的可视化库。LLM 只需输出类 Markdown 的 vis 语法——就像输出 mermaid 一样简单:

```vis
vis line
title 月度销售额
axisXTitle 月份
axisYTitle 营收(万元)
data
  - month 1月
    value 120
  - month 2月
    value 200
  - month 3月
    value 150
```

这段 Markdown 直接渲染为一张可交互折线图。LLM 只写 Markdown,不需要 JavaScript、不需要 JSON、不需要 API 调用。

提案:在现有 mermaid 支持旁边增加 vis 代码块支持(约 40 行代码),完全复用已有的渲染模式。

概念验证

NextChat 已经有完美的集成模式。在 app/components/markdown.tsx 中,PreCode 组件检测 code.language-mermaid 并用 <Mermaid> 渲染。vis 可以用完全一样的方式:

// app/components/markdown.tsx — 在现有 Mermaid 组件旁边添加

import { GPTVis, isVisSyntax } from '@antv/gpt-vis';

function VisChart(props: { code: string }) {
  const ref = useRef<HTMLDivElement>(null);
  const visRef = useRef<GPTVis | null>(null);

  useEffect(() => {
    if (!ref.current) return;
    visRef.current = new GPTVis({ container: ref.current });
    return () => visRef.current?.destroy();
  }, []);

  useEffect(() => {
    if (visRef.current && isVisSyntax(props.code)) {
      visRef.current.render(props.code);
    }
  }, [props.code]);

  return <div ref={ref} style={{ minHeight: 300, margin: '12px 0' }} />;
}

// 在 PreCode 组件中,在 mermaid 检测旁边添加:
export function PreCode(props: { children: any }) {
  // ... 现有 state ...
  const [mermaidCode, setMermaidCode] = useState('');
  const [visCode, setVisCode] = useState(''); // 新增

  const renderArtifacts = useDebouncedCallback(() => {
    if (!ref.current) return;
    // ... 现有 mermaid 检测 ...
    const visDom = ref.current.querySelector('code.language-vis'); // 新增
    if (visDom) {
      setVisCode((visDom as HTMLElement).innerText); // 新增
    }
    // ... 其余不变 ...
  }, 600);

  return (
    <>
      {/* ... 现有 pre + 复制按钮 ... */}
      {mermaidCode.length > 0 && <Mermaid code={mermaidCode} key={mermaidCode} />}
      {visCode.length > 0 && ( // 新增
        <VisChart code={visCode} key={visCode} /> // 新增
      )}
    </>
  );
}

集成方式完全复制 mermaid 的现有模式——同样的检测逻辑、同样的组件结构、同样的生命周期。

流式渲染(附赠能力)

GPT-Vis 为流式输出而设计。在 LLM 逐 token 输出时,图表可以增量渲染:

// 流式场景:每次更新只渲染到最后一个完整行
useEffect(() => {
  if (!visRef.current || !isVisSyntax(code)) return;
  if (streaming) {
    const lastNewline = code.lastIndexOf('\n');
    if (lastNewline > 0) {
      try {
        visRef.current.render(code.slice(0, lastNewline));
      } catch {}
    }
  } else {
    visRef.current.render(code);
  }
}, [code, streaming]);

图表随 LLM token 实时生长,和文本、代码块的流式体验一致。这是 echarts、recharts、chart.js 完全不具备的能力。

📝 补充信息

相关资源

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions