SpreadJSの「AI関数」でAI対応のスプレッドシートを実現する

2026年3月26日にリリースされた「SpreadJS(スプレッドJS)」の最新バージョン「V19J」では、AIプラットフォームのAPIを利用したAIアシスタント機能の1つとして「AI関数」機能が追加されました。多言語翻訳や感情分析、自然言語によるクエリ処理といった機能を提供します。

本機能をアプリケーションに導入することで、データ分析を行う業務担当者や、ExcelライクなUIを使った業務アプリを開発するエンジニアは、データ処理や分析にかかる工数の削減が期待できます。
また、海外拠点と連携する業務を担当するユーザーや、多言語対応が求められる業務アプリの開発者にとっても、翻訳作業を効率化できる点が大きなメリットです。

本記事では「AI関数」の特長や導入メリット、具体的な使用方法についてご紹介します。
※ AIアシスタント機能の使用には、あらかじめAIプラットフォームのAPIキーを準備・設定する必要があります。

SpreadJSのAI関数

AI関数とは

SpreadJSのAI関数は、スプレッドシートの操作感をそのままに、AIによるデータ処理や分析を表計算関数として利用できる機能です。「V19J」で追加されたSpreadJSのAIアシスタント機能の一部として提供され、以下の3種類のAI関数が利用可能です。

関数名説明
SJS.AI.QUERY連携したAIモデルに自然言語で任意のクエリを送信し、各種のデータ処理や分析を可能にします。
SJS.AI.TRANSLATE対象のコンテンツを指定した言語に翻訳します。
SJS.AI.TEXTSENTIMENT対象のテキストの感情を分析し、肯定的、否定的、中立的、の結果を返却します。

AI関数の特長

SpreadJSのAI関数には以下のような特長があります。

コンテキスト認識

AI関数を含めたSpreadJSのAIアシスタント機能は単にAIプラットフォームのAPIを呼び出すだけではなく、ワークシートのデータ(テーブル構造、名前付き範囲、セル内容など)を自動的に抽出、理解し、AIモデルに適切なコンテキスト(文脈情報)として渡します。これにより、AIはシートの実際の構造に基づいた精度の高い応答を生成できます。

AI機能を後付けできるプラガブルな設計

AI関数を含めたSpreadJSのAIアシスタント機能は、既存のSpreadJS環境に後付けで導入できる設計となっています。本機能はAIアドオンのモジュール参照を追加することで利用が可能となっており、参照方式は用途や開発環境に応じて以下から選択できます。

scriptタグによる参照
<script src="gc.spread.sheets.ai.x.x.x.min.js"></script>
ESモジュール(import)による参照
import '@mescius/spread-sheets-ai-addon';

いずれの方式でも、既存のSpreadJSアプリケーションの構成を大きく変更することなく、AI 機能を組み込むことが可能です。

注記:

本番環境でAIアシスタント機能を利用する場合は、APIキーの保護などセキュリティの観点から、AIサービスへのリクエストはサーバーを経由して送信する構成が推奨されます。これは一般的なAI連携と同様の構成であり、フロントエンド側の実装やUI構成を大きく作り替える必要はありません。

さまざまなAIプラットフォームと連携できる

AI関数を含めたSpreadJSのAIアシスタント機能は、OpenAI形式のAPIと互換性のあるリクエスト/レスポンス設計を採用しておりますが、異なるAIプラットフォームを使用する場合であっても、リクエストやレスポンス形式の差分を適切な形式に整合させるカスタムアダプターを独自で用意することで連携が実現可能となっています。

本記事後半では一例として「Gemini API」と連携する方法をご紹介します。

AI関数のメリット

「SJS.AI.QUERY」関数や「SJS.AI.TEXTSENTIMENT」関数をアプリケーションに導入することで、データ分析を行う業務担当者やExcelライクなUIを使う業務アプリ開発者は、以下のような関数を伴う複雑な数式が必要なデータ処理や、大量のデータからのデータ分析などを自然言語による指示で手軽に実行できます。

  • 「売上データから前月比20%以上増加した拠点を抽出する」
  • 「ECサイトの顧客レビュー1000件を肯定/否定/中立に自動分類する」

また、「SJS.AI.TRANSLATE」関数を使用すれば、海外拠点と連携する業務を担当するユーザーや、業務アプリに多言語対応を求められる開発者の以下のような翻訳業務にかかる負担の軽減が期待できます。

  • 「海外拠点からの英文レポートをセル上で一括日本語翻訳する」
  • 「UI文言や帳票データを多言語化する」

このように、AI関数を利用することで、従来はロジック実装や手作業が必要だった処理を、スプレッドシート操作の延長として実行できるようになります。

AI関数を使ってみよう

ここからは実際にSpreadJSでAI関数を使用する方法を解説していきます。

事前準備

開発環境

この記事では以下の開発環境を使用します。

  • Visual Studio Code
  • Express 5.2.1
  • dotenv 17.4.2
  • Google Gen AI SDK 1.50.0
  • SpreadJS 19.0.6

Gemini APIのAPIキーの入手

このプロジェクトでは、AIプラットフォームとして無料でも利用できる「Gemini API」を使用します。Gemini APIについてはこちらの記事もご参考にしていただければと思います。

Gemini APIを使うために必要なAPIキーを入手するには、Gemini APIのページを開いて上部にある[APIキーを取得する]ボタンをクリックします。

APIキーを取得する
出典:Gemini API  |  Google AI for Developers(2026年4月13日閲覧)

続いて、表示された「APIキー」ページで右上の[APIキーを作成]ボタンをクリックすると、キーが発行されます。リンク表示になっているキー部分をクリックするとキーの内容を確認できます。

なお、Gemini APIにはレート制限があります。無料のキーにはかなり厳しい制限があるので、最初にレート制限を確認することをお勧めします。

SpreadJSでAI連携する仕組み

今回作成するプロジェクトは、下図のような構造になります。

SpreadJSでAI連携する仕組み

前述の通り、SpreadJSのAI機能はOpenAI形式のリクエストとレスポンスを処理するように設計されています。そのため、OpenAI以外のAIプラットフォームを利用する場合は、バックエンドでリクエストとレスポンスのフォーマットを変換する必要があります。

また、AIプラットフォームを利用するには何らかの方法でAPIキー(認証情報)を設定する必要がありますが、こちらも前述の通り、セキュアな構成にするために、バックエンドを経由することでAPIキーを秘匿することが推奨されています。

SpreadJSをAIプラットフォームと連携する方法やその際に注意すべき点については、ヘルプでも詳しく解説しているので、ぜひご一読ください。

プロジェクトのファイル構成

今回作成するプロジェクトのファイル構成は次のとおりです。バックエンドを構成するファイルはプロジェクトのルートに置き、フロントエンドを構成するものは「public」フォルダ内に配置します。

project/
├── server.js
├── package.json
├── .env
└── public/
    ├── index.html
    ├── css/
    |  ├── gc.spread.sheets.excel2013white.19.0.6.css
    |  └── styles.css
    └── scripts/
        ├── app.js
        ├── plugins/
        |  └── gc.spread.sheets.ai.19.0.6.min.js
        └── resources/
            └── gc.spread.sheets.resources.ja.19.0.6.min.js

バックエンドの作成

このプロジェクトでは、SpreadJSコントロールをバックエンドのサーバーを経由してGemini APIと連携します。

まずは以下のコマンドでプロジェクトに「package.json」を作成します。

npm init -y

続いて、今回使用するフレームワーク「Express」、環境変数を使用するための「dotenv」、Gemini APIを使用するための「Google Gen AI SDK」をそれぞれインストールします。

npm install express dotenv @google/genai

インストールが完了したらES Modulesを使用するため、「package.json」の中で"type": "module"を指定します。

{
・・・(中略)・・・
  "author": "",
  "license": "ISC",
  "type": "module",
・・・(中略)・・・

次に「server.js」をプロジェクトの直下に作成し、以下のように記述します。

import express from "express";
import { GoogleGenAI } from "@google/genai";
import dotenv from "dotenv";

// .env ファイルから環境変数を読み込む
dotenv.config();

const app = express();
const port = process.env.PORT || 3000;

// JSON リクエストボディをパース
app.use(express.json());
// 静的ファイルを public フォルダから配信
app.use(express.static("public"));

// Google GenAI クライアントを初期化
const ai = new GoogleGenAI({ apiKey: process.env.AI_API_KEY });
const defaultModel = process.env.AI_MODEL || "gemini-2.5-flash";

// OpenAIのメッセージ形式をGoogleGenAIのcontents形式に変換
const convertMessages = (messages) => {
  const systemParts = [];
  const contents = [];

  for (const msg of messages) {
    if (msg.role === "system") {
      // systemメッセージは最初のuserメッセージに結合する
      systemParts.push(msg.content);
    } else {
      contents.push({
        role: msg.role === "assistant" ? "model" : "user",
        parts: [{ text: msg.content }],
      });
    }
  }

  if (systemParts.length > 0 && contents.length > 0 && contents[0].role === "user") {
    contents[0].parts[0].text = systemParts.join("\n\n") + "\n\n" + contents[0].parts[0].text;
  }

  return { contents };
};

// AI連携用APIエンドポイント
app.post("/api/queryAI", async (req, res) => {
  try {
    const body = req.body;
    const model = body.model || defaultModel;
    const { contents } = convertMessages(body.messages || []);

    const params = {
      model,
      contents,
      config: {
        temperature: body.temperature ?? 0.7,
        maxOutputTokens: body.max_tokens ?? 4096,
      },
    };

    if (body.stream) {
      await handleStream(params, res);
    } else {
      await handleNonStream(params, model, res);
    }
  } catch (error) {
    handleError(error, res, req.body.stream);
  }
});

// ストリーミング応答をSSE形式で返す
const handleStream = async (params, res) => {
  res.setHeader("Content-Type", "text/event-stream");
  res.setHeader("Cache-Control", "no-cache");
  res.setHeader("Connection", "keep-alive");

  try {
    const response = await ai.models.generateContentStream(params);
    const created = Math.floor(Date.now() / 1000);
    const requestId = "gemini-" + Date.now();

    for await (const chunk of response) {
      const text = chunk.text || "";
      if (!text) continue;

      const openaiChunk = {
        id: requestId,
        object: "chat.completion.chunk",
        created,
        model: params.model,
        choices: [{
          index: 0,
          delta: { content: text },
          finish_reason: null,
        }],
      };

      res.write(`data: ${JSON.stringify(openaiChunk)}\n\n`);
    }

    // ストリームの終了を通知
    res.write("data: [DONE]\n\n");
    res.end();
  } catch (error) {
    res.write(`data: ${JSON.stringify({ error: error.message })}\n\n`);
    res.end();
  }
};

// 非ストリーミング応答をJSONで返す
const handleNonStream = async (params, model, res) => {
  const response = await ai.models.generateContent(params);
  const text = response.text || "";

  res.setHeader("Content-Type", "application/json");
  res.setHeader("Cache-Control", "no-store");
  res.status(200).json({
    id: "gemini-" + Date.now(),
    object: "chat.completion",
    created: Math.floor(Date.now() / 1000),
    model,
    choices: [{
      index: 0,
      message: { role: "assistant", content: text },
      finish_reason: "stop",
    }],
    usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },
  });
};

// エラー処理:ストリーミングか通常レスポンスかで返し方を切り替える
const handleError = (error, res, isStream) => {
  console.error("Error:", error);
  if (isStream) {
    res.write(`data: ${JSON.stringify({ error: error.message })}\n\n`);
    res.end();
  } else {
    res.status(500).json({ error: error.message });
  }
};

// サーバーを起動
app.listen(port, () => {
  console.log(`Server running at http://localhost:${port}`);
});

「server.js」では、次の処理を行います。

  • リクエストをOpenAI形式からGemini形式にマップ
  • Gemini APIの呼び出し
  • レスポンスをGemini形式からOpenAI形式にマップ

さらにプロジェクト直下に「 .env」ファイルを作成し、あらかじめ入手したGemini APIのAPIキーと使用するモデル、ポート番号を定義します。

AI_API_KEY=入手したgemini apiキーをここに記述します
AI_MODEL=gemini-2.5-flash
PORT=3000

フロントエンドの作成

フロントエンドは次のように実装します。

プロジェクト直下に「public」フォルダを作成し、以下の3つのファイルを作成します。

index.htmlページ本体。ページの要素としてSpreadJSコントロールを配置します
scripts/app.jsSpreadJSコントロールを作成するコードを記載します
css/styles.cssSpreadJSコントロールのスタイル定義を記載します
<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8">
  <script src="scripts/gc.spread.sheets.all.19.0.6.min.js"></script>
  <script src="scripts/plugins/gc.spread.sheets.ai.19.0.6.min.js"></script>
  <script src="scripts/resources/gc.spread.sheets.resources.ja.19.0.6.min.js"></script>
  <link href="css/gc.spread.sheets.excel2013white.19.0.6.css" rel="stylesheet" />
  <link href="css/styles.css" rel="stylesheet" />
  <script src="scripts/app.js"></script>
</head>

<body>
  <div id="ss"></div>
</body>

</html>

上記で参照しているSpreadJSのモジュールはnpmなどから入手する方法もありますが、今回は環境に直接SpreadJSのモジュールを配置していきます。あらかじめSpreadJSの製品版かトライアル版をご用意ください。トライアル版は以下より無償で入手可能です。

製品版、またはトライアル版をダウンロードしたら、ZIPファイルを解凍し、以下のファイルを環境にコピーします。CSSファイルは以下に記載のもの含め7種類あるのでお好みのテーマのものを選択してください。

  • scripts/gc.spread.sheets.all.19.0.6.min.js
  • scripts/plugins/gc.spread.sheets.ai.19.0.6.min.js
  • scripts/resources/gc.spread.sheets.resources.ja.19.0.6.min.js
  • css/gc.spread.sheets.excel2013white.19.0.6.css

「app.js」では、SpreadJSの初期化処理や、バックエンドとの連携処理を行います。
※ ライセンスキーを設定しない場合、トライアル版を示すメッセージが表示されます。ライセンスキーの設定方法についてはこちらをご覧ください。

// 名前空間のエイリアス名
const spreadNS = GC.Spread.Sheets;

// ライセンスキーとカルチャの設定
//spreadNS.LicenseKey = 'ここにSpreadJSのライセンスキーを設定します';
GC.Spread.Common.CultureManager.culture("ja-jp");

document.addEventListener("DOMContentLoaded", () => {
  // SpreadJSの生成
  const spread = new spreadNS.Workbook("ss");
  spread.options.allowDynamicArray = true;
  injectAI(spread);
  initSheet(spread);
});

// Sheet1の設定
const initSheet = (spread) => {
  // データの設定
  const data = [
    ['英語の原文'],
    ['Do in Rome as the Romans do.'],
    ['All is well that ends well.'],
    ['The early bird catches the worm.'],
    ['Misfortunes never come singly.'],
  ];

  // シートの基本設定
  const sheet = spread.getSheet(0);
  sheet.defaults.colWidth = 20;
  sheet.setColumnWidth(1, 270);
  sheet.setColumnWidth(3, 350);

  // 項目ヘッダのスタイル
  const headerStyle = new spreadNS.Style();
  headerStyle.backColor = "royalblue";
  headerStyle.foreColor = "white";
  headerStyle.hAlign = spreadNS.HorizontalAlign.center;

  // 英語の原文
  sheet.setArray(1, 1, data);
  sheet.setStyle(1, 1, headerStyle);
  sheet.getRange(1, 1, 5, 1).cellPadding("0 0 0 5");
  sheet.getRange(1, 1, 5, 1).setBorder(
    new GC.Spread.Sheets.LineBorder("royalblue", spreadNS.LineStyle.medium),
    { outline: true }
  );

  // 日本語訳(SJS.AI.TRANSLATE関数の動作確認用)
  const watermark1 = '=SJS.AI.TRANSLATE(B3:B6,"日本語")';
  sheet.setValue(7, 1, "日本語訳");
  sheet.setStyle(7, 1, headerStyle);
  sheet.getRange(7, 1, 5, 1).setBorder(
    new GC.Spread.Sheets.LineBorder("royalblue", spreadNS.LineStyle.medium),
    { outline: true }
  );
  sheet.getCell(8, 1).watermark(watermark1);

  // 感情分析(SJS.AI.TEXTSENTIMENT関数の動作確認用)
  const watermark2 = '=SJS.AI.TEXTSENTIMENT(B9:B12,"良い","悪い","普通")';
  sheet.setValue(7, 3, "感情分析");
  sheet.setStyle(7, 3, headerStyle);
  sheet.getRange(7, 3, 5, 1).setBorder(
    new GC.Spread.Sheets.LineBorder("royalblue", spreadNS.LineStyle.medium),
    { outline: true }
  );
  sheet.getRange(8, 3, 4, 1).hAlign(spreadNS.HorizontalAlign.center);
  sheet.getCell(8, 3).watermark(watermark2);

  // クエリの設定(SJS.AI.QUERY関数の動作確認用)
  const watermark3 = '=SJS.AI.QUERY("このことわざの意味を簡潔に教えてください",B9:B12)';
  sheet.setValue(13, 1, "任意のクエリ");
  sheet.getCell(13, 1).setStyle(headerStyle);
  sheet.getRange(13, 1, 5, 3).setBorder(
    new GC.Spread.Sheets.LineBorder("royalblue", spreadNS.LineStyle.medium),
    { outline: true }
  );
  sheet.addSpan(13, 1, 1, 3, spreadNS.SheetArea.viewport);
  sheet.getCell(14, 1).watermark(watermark3);
}

// AIリクエストをサーバーを経由してAIサービスに送信
const serverCallback = async (requestBody) => {
  const response = await fetch("/api/queryAI", {
    method: 'POST',
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(requestBody),
  });
  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  }
  return response;
};

const injectAI = (spread) => {
  spread.injectAI(serverCallback);
}

SJS.AI.TRANSLATE関数、SJS.AI.TEXTSENTIMENT関数、およびSJS.AI.QUERY関数は動的配列数式なので、Workbookクラスのoptions.allowDynamicArrayプロパティをtrueに設定しておく必要があります。

また、コードの最後の部分では、serverCallback関数式とWorkbookクラスのinjectAIメソッドを使ってバックエンドとやりとりを行っています。

「styles.css」ではSpreadJSのスタイルを定義します。

/* SpreadJSのスタイル */
#ss {
  inline-size: 740px;
  block-size: 430px;
  border: 1px solid silver;
}

AI関数の動作確認

以上ですべての準備が完了したので、以下のコマンドを実行してサーバーを起動します。

node server.js

ブラウザで「http://localhost:3000」にアクセスすると、次のようにSpreadJSが表示されます。

SpreadJSの表示

SJS.AI.TRANSLATE関数

SJS.AI.TRANSLATE​​は、セルに設定された文字列を指定した言語に翻訳します。

SpreadJSのB9セルに次の数式を入力すると、「英語の原文」の各文を日本語に翻訳したものが「日本語訳」の欄に表示されます。

=SJS.AI.TRANSLATE(B3:B6,"日本語")

GC.Spread.Common.CultureManagerクラスのcultureメソッドを使ってカルチャを設定している場合は、そのカルチャがAIの応答言語として使用されます。このプロジェクトの「app.js」では次のように日本語のカルチャを設定しているので、SJS.AI.TRANSLATE関数の第2引数を空文字にした場合でも自動的に日本語に翻訳されます。

SJS.AI.TEXTSENTIMENT関数

SJS.AI.TEXTSENTIMENTは、セルに設定された文字列を分析し、その内容が肯定的/否定的/中立的のどれに区分されるかを返します。

D9セルに次の数式を入力すると、「日本語訳」の各文の内容を「良い、悪い、普通」の3段階で評価した結果が「感情分析」の欄に表示されます。

=SJS.AI.TEXTSENTIMENT(B9:B12,"良い","悪い","普通")

SJS.AI.QUERY関数

SJS.AI.QUERYは、連携しているAIプラットフォームに任意のリクエストを送信します。第1引数にAIに実行させたい処理を記述し、第2引数にその処理の対象となるデータ、または処理対象のデータを含むセル参照を記述します。

B15セルに次のように入力すると、SJS.AI.TRANSLATE関数で翻訳(B9:B12に出力)したことわざの意味の解説を出力できます。

=SJS.AI.QUERY("このことわざの意味を簡潔に教えてください",B9:B12)

なお、ヘルプの「AIアシスタント」の末尾にある「AI生成機能に関する注意」に記載しているように、AI生成結果には不正確な情報や不完全な表現、または誤解を招く内容が含まれる可能性があるので、ご留意ください。

今回作成したサンプルは以下よりダウンロード可能です。

さいごに

今回の記事では、SpreadJSのAI関数の特長や導入メリット、具体的な利用方法についてご紹介しました。AI関数を使用することでスプレッドシート上のさまざまなデータ処理や分析をAIで支援することができるので、AIの業務活用に興味のある方はぜひお試しください。

また、次回の記事では、数式エディタで使えるAIアシスタント機能ついて詳しく解説する予定ですので、こちらもぜひご期待ください。

製品サイトでは、SpreadJSの機能を手軽に体験できるデモアプリケーションやトライアル版も公開しておりますので、こちらもご確認ください。

また、ご導入前の製品に関するご相談、ご導入後の各種サービスに関するご質問など、お気軽にお問合せください。

\  この記事をシェアする  /