Blog

2025.11.17

Engineering

Optuna DashboardがLLMと連携:自然言語によるデータフィルタやグラフの生成が可能に

Masashi Shibata

はじめに

Optuna Dashboard (GitHub: https://github.com/optuna/optuna-dashboard) は、Optunaの最適化履歴を手軽に確認できるWebダッシュボードです。Optuna v5ロードマップに記載の通り、Optunaでは次のメジャーリリースに向けて大規模言語モデル(LLM)との連携を強化しており、今回紹介するOptuna DashboardのLLM連携機能はその取り組みの1つです。

Optuna Dashboardの最新リリース v0.20.0は、LLM連携機能をサポートするはじめてのバージョンです。本記事ではLLM連携機能の概要、設計や実装面での工夫についてご紹介します。

LLM連携機能の概要

LLM連携機能を有効化することで、現時点では下記の2つの機能が利用可能になります。

  1. 自然言語によるTrialの絞り込み機能
  2. グラフ自動生成機能

自然言語によるTrialの絞り込み機能は図1に示すように、グラフや表に表示されるTrialを自然言語で絞り込む機能です。従来のOptuna Dashboardでも簡単な絞り込みは可能でしたが、絞り込める条件は限定的でした。様々なフィルター条件に対応しようとするとUIが複雑化してしまい、使いづらくなってしまうためです。本機能では図1に示すようにユーザーが自然言語で条件を指定すると、LLMが対応するフィルター関数を生成することで、シンプルなUIで柔軟な絞り込みを可能にします。

図1: 自然言語によるTrial絞り込み機能 (この例では、レイヤー数が2以上かつオプティマイザーとしてAdamが選択されたTrialのみを可視化)

図1: 自然言語によるTrial絞り込み機能 (この例では、レイヤー数が2以上かつオプティマイザーとしてAdamが選択されたTrialのみを可視化)

次に紹介するのはグラフ自動生成機能です。Optuna Dashboardでは、評価値の推移やパレートフロント解の可視化、等高線図など、最適化タスクによらず汎用的に使えるグラフをサポートしてきました。一方で特定の最適化タスクに特化した可視化機能などのサポートには課題がありました。本機能では、図2に示すように、ユーザーが見たいグラフを自然言語で記述するとLLMがPlotly.jsを使った可視化用関数を生成することで、これまでサポートできなかったタスク特化の可視化を可能にします。

図2: グラフの自動生成機能の使用例。使用したLLMは、OpenAIのGPT-5 Mini。入力プロンプトは 「Plot intermediate values of all trials in a study.」。

図2: グラフの自動生成機能の使用例。使用したLLMは、OpenAIのGPT-5 Mini。入力プロンプトは「Plot intermediate values of all trials in a study.」。

LLM連携機能の使い方に関する詳細は、公式ドキュメントのチュートリアル Tutorial: LLM Integration – Optuna Dashboard Documentation をご参照ください。 

 

内部での処理の流れ

前述した2つの機能(Trialの絞り込み機能とグラフの自動生成)は、設計や実装の観点では非常によく似ています。LLM連携機能の内部の流れをシーケンス図にまとめると図3のようになります。

図3: Optuna DashboardのLLM連携機能の実行の流れ

図3: Optuna DashboardのLLM連携機能の実行の流れ

まず最初にユーザーが指示を入力し送信すると、その文字列をJSON APIサーバーが受け取ります。JSON APIサーバーは、事前に用意されたプロンプトテンプレートをもとにプロンプトを生成し、LLM Provider (OpenAI APIPLaMo Prime等) に問い合わせます。LLM Providerが返すのは eval 関数により実行可能なJavaScript関数の文字列です。JavaScript関数文字列をユーザーに表示し実行確認をとり、問題がなければeval関数を呼び出し、評価結果をpostMessageで親ページに送り表示します。

プロンプトテンプレートの内容は、機能により異なりますが基本的には次の4つの要素で構成されます。

  1. フィルター関数の生成指示(例:”Please write a filtering function for …”)
  2. TypeScriptの型定義 (例: “type Trial = {trial_id: number;  …. }” )
  3. Few-shot examples (詳細はソースコードを参照)
  4. (リトライ時のみ) 前回評価に失敗した関数定義とそのエラーメッセージ

図3の理解を簡単にするために省略しましたが、LLMが生成したJavaScript関数の評価に失敗した場合は実行した関数とエラーメッセージをプロンプトテンプレートに埋め込みリトライします。リトライによって成功するケースも多く、実用的には非常に重要な処理となっています。

このように全体の処理の流れを見てみると、設計観点ではあまり特殊な点はありません。しかし素直な実装では、LLMが生成した関数をevalすることには一定のリスクや懸念が考えられます。そこでOptuna Dashboardではいくつかの対策を取り入れました。以降は実装面での工夫について紹介します。

確認ダイアログの表示

前提としてOptuna Dashboardは不特定多数に公開するような使われ方は想定しておらず、各個人のローカルPCや信頼のできる社内などの限られたメンバー間でのみ共有して利用されます。それでもなお、LLMが生成するコードを完全に信頼することは難しく、ユーザーの意図しない動作が引き起こされるリスクは一定数存在します。

一般的な対策の1つとして、Claude Desktopや各種コーディングエージェントが行っているように、確認ダイアログが挙げられます。Optuna Dashboardにおいても図4に示すような確認ダイアログを表示し、実行前にユーザーがJavaScript関数の中身を確認し実行を拒否できるようにしました。

図4: JavaScriptコードの実行確認ダイアログ

図4: JavaScriptコードの実行確認ダイアログ

実行確認ダイアログでは利便性をできるだけ損なわないように “Never Ask Again” ボタンを合わせて提供しています。このボタンをクリックするとWebブラウザのsessionStorageにその情報を記録し、次回以降の実行確認ダイアログの表示を省略します。sessionStorageはWebブラウザのタブに紐づくため、localStorageとは異なり別タブで許可したとしても引き継がれることはなく、タブを閉じるとクリアされます(参考: Web Storage API)。

iframeタグを用いた隔離環境でのeval実行

Optuna Dashboardの利用者全員がJavaScriptに詳しいわけではないと思いますし、人目での確認には限界があります。そこで確認ダイアログとあわせて行った対策が、 <iframe sandbox=”allow-scripts”></iframe> タグを用いた隔離環境内での eval実行です。

MDNのドキュメントでも注意書きがあるように、evalは任意の JavaScript を実行できてしまうため、悪意あるコードが渡された場合には、アプリケーション内部の変数を読んだり、DOMを直接書き換えたりすることが可能になります。iframeタグを使うと、メインのページとは分離された独立した実行コンテキストを作ることができます。この中でevalを使っても親ページのDOMやJavaScriptの変数には直接アクセスできません。さらにiframeタグのsandbox属性を指定し、 “allow-scripts” 以外をすべて制限することにより、ポップアップの表示やフォーム送信、localStorageやsessionStorage へのアクセスといった問題を防ぐこともできます。

具体的な実装のイメージを説明するためにOptuna Dashboardのソースコードを一部簡略化して抜粋したものを以下に示します(ソースコード全体はこちらから確認できます)。invisibleな要素としてiframeタグをページ内に埋め込み、評価前のJavaScript関数(文字列)と引数として与えるべきデータをpostMessageで送信、eval実行後の結果のみを親ページにpostMessageで返却します。

const iframeSrcDoc = `
    <script>
      window.fetch = () => { throw new Error("Unexpected") }
      window.XMLHttpRequest = () => { throw new Error("Unexpected") }
      window.addEventListener('message', (event) => {
        const { data } = event;
        if (typeof data !== 'object' || data.type !== 'filter') return;
        const { trials, filterFuncStr } = data;
        try {
          const filterFunc = eval('(' + filterFuncStr + ')');
          const result = trials.filter(filterFunc);
          parent.postMessage({ type: 'result', filteredTrials: result }, '*');
        } catch (e) {
          parent.postMessage({ type: 'result', filteredTrials: [], error: String(e) }, '*');
        }
      });
    </script>
  `
return (
  <iframe
    ref={iframeRef}
    sandbox="allow-scripts"
    style={{ display: "none" }}
    srcDoc={iframeSrcDoc}
  />
)

 

注意点としてiframeタグによる隔離環境はすべてのセキュリティ上の懸念を排除できるわけではありません。例えばfetch API等を利用して外部通信することをiframeタグだけでは制限することができず、CSP(Content Security Policy)など別の仕組みを併用してネットワークアクセスを制限するなどの対策が必要になります。

おわりに

本記事ではLLM連携機能の概要とその内部の設計や実装上の工夫について紹介しました。LLMを用いたTrialの絞り込み機能によって、非常にシンプルなUIながら柔軟なフィルタリングが可能になりました。またユーザーの要望に応じてPlotlyのグラフを自動生成する機能は、LLMの活用なしでは実現困難な機能でした。LLMとの連携機能はまだまだ発展の余地が残されており、Optuna v5にむけてさらにブラッシュアップを続けたいと考えています。

ぜひ本記事を読んでご興味をお持ちいただけた方は、こちらのチュートリアルを参考にLLM連携機能を触っていただき、フィードバックをいただけますと幸いです。

  • Twitter
  • Facebook