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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions