サムネイル

flint-chart 入門 — AIエージェントが崩れないグラフを描く中間言語

  • 0

こんにちは。もるふぉです。

flint-chartを知ったのは、Claudeに「このCSV、いい感じにグラフにして」と頼んで、Vega-LiteのJSONが返ってきたのに軸ラベルは見切れ、色は3色ぶつかり合い、凡例は謎の位置で重なっていた、という経験がきっかけです。

「プロンプトをもっと丁寧に書いたら?」と思いますよね。

私も何度もそう試みました。

でも根本原因はそこじゃなくて、AIにとってVega-Liteを「正しく」書くことが構造的に難しい、という話です。

そのイライラをMicrosoft Researchが正面から解こうとしたのが、2026-07-08にShow HNで公開されたflint-chartです。

可視化ライブラリではなく、AIエージェント向けの「可視化中間言語」というアプローチが面白い。

ちなみに名前が似ているFlutterのfl_chartとは無関係で、こちらはWeb向けのTypeScriptライブラリです。

この記事では、崩れる理由から解決の仕組みまで、AIエージェントに図を描かせて困った経験のあるエンジニア向けに整理していきます。

flint-chartが必要になる背景:AIエージェントがVega-Liteを直書きすると崩れる理由

記事の画像

まず問題を解剖します。

AIエージェントが吐きがちなVega-Lite JSONを見てみましょう。

{
  "data": { "values": [...] },
  "mark": "bar",
  "encoding": {
    "x": { "field": "product", "type": "nominal" },
    "y": { "field": "sales_2025_q1", "type": "quantitative" }
  }
}

一見それらしいのですが、この記述からVega-Liteが読み取れるのは「棒グラフで、X軸に商品名、Y軸に数量」というだけです。

sales_2025_q1が売上金額なのか個数なのか」「通貨単位は円なのかドルなのか」「商品名は10個なのか100個なのか」といった情報は一切入っていません。

だから軸ラベルはsales_2025_q1という生の列名がそのまま出るし、カテゴリ数に対して幅も自動決めなのでラベルは平気で重なる。

しかもVega-Liteのフル機能を使うには、configencodingtransformresolveなど何十ものプロパティを組み合わせる必要があります。

LLMは頑張って出しますが、微妙にプロパティ名を間違えたり、必須フィールドを落としたりする。

Vega-Lite・ECharts・Chart.jsは仕様が全部違うので、「今回はChart.jsで」と言われるとまた別の書き方を思い出さないといけません。

「プロンプトを工夫すれば」という話もよく出ますが、根本原因は列名とデータ意味の乖離にあります。

priceという列名があっても、それが商品の値段なのか株価なのかガソリン単価なのかは、モデルが文脈から推測するしかない。

この推測の当たり外れがそのままグラフの品質に出てしまいます。

ここがプロンプト改善だけでは埋められない溝です。

この構造的な問題を、flint-chartはどう解いたのか。

そこから見ていきます。

flint-chartとは何か:Microsoft Researchが公開した可視化中間言語

flint-chartはMicrosoft Researchが公開したTypeScript製のライブラリで、「データとチャート仕様を分離した中間表現」を提供します。

ライブラリそのものはグラフを描画しません。

書いたFlint仕様をVega-LiteEChartsChart.jsのいずれかの形式にコンパイルして出力する、いわばグラフ版のトランスパイラみたいなものです。

なぜ「中間言語」なのか。

Microsoft Researchのブログには「短い仕様だと見栄えの悪いグラフになり、綺麗なグラフには詳細な仕様が必要という基本的なジレンマがある」と書かれています。

AIにVega-Liteを直接書かせると、この「詳細な仕様」の部分で必ず事故が起きる。

だから中間層を挟んで、詳細なレイアウトはコンパイラに任せる、という設計です。

つまり「AIが書きやすい言語」と「描画ライブラリが読む仕様」を完全に分離した。

ライセンスはMITで、リポジトリはGitHubで公開されています。

npmからflint-chartを入れれば数行のTypeScriptで動かせます。

加えてflint-chart-mcpというMCPサーバーもセットで提供されていて、Claude DesktopやCursorから直接呼び出せます。

具体的に何ができるのかは、次の仕組みで見えてきます。

flint-chartが解決する仕組み:70+のsemantic typeとマルチライブラリコンパイル

記事の画像

Flintのアプローチはシンプルで、「フィールドに意味を付ける」ことです。

70以上のsemantic type、たとえばPriceTemperatureCountryRankPercentageといった型を用意していて、これを列に紐付けます。

「70以上」と聞くとピンとこないかもしれません。

でもこれが意味するのは、「モデルが推測しなくて済む文脈が70種類以上、コンパイラ側に組み込まれている」ということです。

TypeScriptで書くとこんな感じです。

import { assembleVegaLite } from 'flint-chart';

const spec = assembleVegaLite({
  data: { values: myData },
  semantic_types: {
    weight: 'Quantity',
    mpg: 'Quantity',
    origin: 'Country'
  },
  chart_spec: {
    chartType: 'Scatter Plot',
    encodings: { x: { field: 'weight' }, y: { field: 'mpg' } },
    baseSize: { width: 400, height: 300 }
  }
});

originCountryだと分かれば、Flintコンパイラは「国名は文字数がバラつくから凡例は縦並びにする」「地図型のカラーパレットを使う」といった判断ができます。

Priceなら通貨記号を軸に付けるし、Percentageなら軸範囲を0-100にスナップする。

この「意味からレイアウトを導く」ロジックがコンパイラに詰まっているので、AIエージェントは「これは価格です」「これは国名です」とだけ伝えればよくなります。

もう1つ面白いのが、同じ入力から複数のバックエンドに出せることです。

assembleVegaLite()をやめてassembleECharts()assembleChartjs()を呼ぶだけで、出力形式が切り替わります。

Vega-Liteは静的レポート向き、EChartsはインタラクティブなダッシュボード向き、Chart.jsはWebに軽く埋め込みたいときに向いている。

1つのFlint記述がこの3ライブラリへコンパイルできるので、用途に応じた切り替えを1か所で管理できます。

対応チャートタイプは30種類以上あり、バー・線・散布図・ヒートマップ・ドーナツ・レーダー・ストリームグラフ・Sankey・treemapあたりを一通りカバーしています。

この仕組みをClaudeやCursorから直接呼べるようにしたのが、次のMCP連携です。

flint-chart-mcpでClaude/Cursorから呼び出す

ここが個人的には一番アツいポイントです。

Flintはflint-chart-mcpというMCPサーバーが用意されていて、Model Context Protocol経由でClaudeやCursorからそのまま呼び出せます。

起動は1行です。

npx -y flint-chart-mcp

Claude DesktopのMCP設定ファイルにサーバーとして登録しておくと、Claudeとの会話中に「このデータをグラフにして」と頼めば、内部的にFlint仕様を組み立ててコンパイル、Vega-LiteなりEChartsなりのJSONを返してくれます。

エージェントはFlintの仕様さえ知っていればよく、各ライブラリの細かい書き方を覚える必要がなくなります。

MCPサーバー1つで複数バックエンドをカバーするので、エージェントの手数が減ります。

ユーザーが「Vega-Liteで欲しい」と言えばassembleVegaLiteが呼ばれ、「軽くWebに埋め込みたい」と言えばassembleChartjsが呼ばれる。

ライブラリの切り替えがLLM側の判断で自然にできる、というのが実際に使ってみて一番助かった点です。

既存のClaude/Cursor環境にコマンド1行で乗れるので、試し始めのハードルは低いです。

flint-chartとMermaidの住み分け:フロー図 vs データグラフ

記事の画像

「じゃあMermaidは要らなくなるのか?」と聞かれそうですが、これは完全に住み分けです。

Mermaidが得意なのは、フローチャート・シーケンス図・状態遷移・ER図・ガントチャートといった「構造を線と箱で表す図」です。

ノードとエッジの関係性を書けば、レイアウトエンジンがいい感じに配置してくれる。

ソフトウェア設計やドキュメント整備では圧倒的にMermaidが速いです。

一方flint-chartは、数値データを軸に載せる「データグラフ」の領域です。

売上推移・気温分布・スコアランキングみたいに、データセットから統計的な可視化を作る用途。

これはMermaidのシンタックスではそもそも表現できません。

用途の切り分けとしては、「システム構成や業務フローを書きたい→Mermaid」「CSVやAPIから取ったデータを可視化したい→flint-chart」で覚えておけば迷いません。

Claudeにドキュメントを書かせるときも、この2つを使い分けさせるMCP構成にしておくと、出力の破綻がだいぶ減ります。

現時点でMermaidとFlintのMCPを両方入れておくのが、AIエージェントに図を書かせる構成として実用的です。

今使うべきか:flint-chartの限界と向いているユースケース

正直に言うと、いま全ての可視化をFlintに寄せるべきかというと、そこまでではありません。

向き不向きがはっきりしているツールです。

向いているのは、「AIエージェントが定型的なデータ可視化を出す」ユースケースです。

社内のダッシュボード自動生成、レポート添付用のグラフ、チャットUIでのアドホック可視化といった「毎回違うデータで同じような種類のグラフを出したい」場面。

崩れないことが最優先になるので、Flintの設計と噛み合います。

まだ向いていないのは、細部までデザインをコントロールしたい高度なカスタム可視化。

D3.jsで手書きするような凝った可視化、独自のアニメーション、特殊なマーク形状といった領域はFlintの中間表現ではカバーしきれません。

ここはVega-LiteやD3を直接書いた方が早いです。

社外に公開する本番プロダクトへの組み込みは、少し様子見でもいいと思います。

公開から日が浅く、breaking changeが入る可能性はある。

とはいえMicrosoft Research発でMITライセンス、GitHubで開発が活発なので、まずは社内ツールやプロトタイプで試して、感触を掴んでおく価値は十分あります。

flint-chartを使い始めるには:MCPサーバー連携までの流れ

向き不向きを確認したうえで試すなら、導入はコマンド2つで済みます。

ライブラリとして使うならnpm install flint-chart、MCPサーバーとして使うならnpx -y flint-chart-mcp

前者はTypeScriptプロジェクトに組み込んで自前でチャート生成ロジックを書く用途、後者はClaude Desktop等に接続してAIエージェントに使わせる用途です。

Claude Desktop側の設定は、claude_desktop_config.jsonmcpServersセクションにflint-chart-mcpをサーバーとして登録するだけで、あとはClaudeの会話から呼び出せます。

詳しい設定例は公式サイトのMCPページに載っています。

TypeScriptから直接叩く場合は、ChartAssemblyInput型にdatasemantic_typeschart_specの3要素を渡してassembleVegaLite()(またはassembleECharts()/assembleChartjs())を呼ぶだけです。

返ってきた仕様をそのままVega-LiteランタイムやEChartsのインスタンスに渡せば描画されます。

既存のBIツールに組み込む場合は、この形が扱いやすいです。

まとめ:flint-chartの中間言語という設計判断が、崩れないグラフを生む

Flintが面白いのは、「AIにVega-Liteを上手く書かせる」という発想ではなく、「AIに書かせやすい別の言語を挟む」という発想を取ったところです。

プロンプトチューニングやモデルの改良ではなく、言語設計で問題を解いた。

「レイヤーを1枚挟んで抽象度を上げる」解法はやっぱりきれいだと思います。

AIエージェントに図を描かせて崩れた経験がある人は、npx -y flint-chart-mcpを1行打ってClaudeに繋ぎ、いつも使うデータで試してみてください。

崩れなかったことが確認できます。

会員登録して機能を使おう

この機能を利用するには、無料の会員登録が必要です。
お気に入りの記事を保存して、あとで読み返しましょう!