GAS:GoogleAppsScript / GoogleAppSheetさくらインターネットAI(人工知能/ディープラーニング)

【GAS活用術】さくらAIとGoogleWorkspaceで「自分専用AIチャット」のWEBアプリができます

この記事は約21分で読めます。

さくらAIとGoogleWorkspaceで「自分専用AIチャット」2024年、さくらインターネットが提供する生成AIサービス「高火力」、通称「さくらAI」が注目を集めています。
実は、Google Workspace(旧 G Suite)に標準搭載されている Google Apps Script (GAS) を使えば、プログラミング経験がなくても、さくらAIに接続する自分専用のWebアプリケーションを驚くほど簡単に自作できます。
この記事では、GASを使って「さくらAI」のオリジナルAIアプリを作成する価値から、具体的な設定方法、そして「社内データと連携させる」RAG化の可能性までを、ステップバイステップでご紹介します。

さくらAIとGoogleWorkspace

完成したWEB画面はこんな感じです。さくらAIの「gpt-oss-120b」を使って自分専用のWEBアプリとして動作しています。「gpt-oss-120b」はOpenAIが出しているオープンソースのChatGPTの最高峰の賢さがあり、現行のChatGPT4程度以上の能力があると言われています。これが無料で使えるのはすごいですね。

これまで、さくらAIについて以下の解説ページで紹介しています。

さくらインターネットの高性能AIが無料で使える!「さくらのAI Engine」使い方ガイド
2025年9月24日、さくらインターネットが、生成AI向けの新サービス「さくらのAI Engine」の一般提供を開始しました。このニュースはX(旧Twitter)で同社の田中社長が告知、特に「月3,000回まで無料で使える無償プラン」があることで大きな話題を呼んでいます。自社のサービスにAIを組み込みたい開発者から、最新のAIを手軽に試してみたい初心者まで、すべての人にチャンス到来です。
エンジニアでなくても大丈夫!さくらの生成AI「高火力」を自分のPCで快適に使う方法はLobeChat活用です
最近話題のさくらインターネットの生成AIサービス「高火力」、皆さんはもう試しましたか?「なんだか専門的で難しそう…」と思っている方も多いかもしれません。実は私自身もエンジニアではなく、Pythonなどのコードは全く分かりません。しかし、試行錯誤の末、まるで自分のPCに高性能なオープンソースのAIがインストールされているかのような非常に快適な環境を無料で構築することができました。いわゆるノーコード開...

今回はあらたにGoogleWorkspace内で、さくらAIとAPI接続するというチャレンジをしてみました。

この制作については、ほぼGeminiとのやりとりでできています。その意味でもGoogleWorkspace内で開発から運用までできてしまったということです。我ながらすごい(エンジニアではないのに、という意味です)

なぜGASで「さくらAI」を使うのか? その圧倒的価値

「わざわざGASを使わなくても…」と思うかもしれませんが、GASで自作することには、既存のツールにはない大きなメリットがあります。

  1. Google Workspaceと完全連携できるこれが最大の価値です。GASは、スプレッドシート、Gmail、Googleドキュメント、Googleドライブといった全てのGoogleサービスを操作できます。
    • スプレッドシートのデータを読み込ませて、さくらAIに分析・要約させる。
    • Gmailの画面から、さくらAIにメールの返信文を作成させる。
    • Googleドライブ内のテキストファイルを読み込ませて、自動で議事録を作成する。GASをハブ(中継地点)にすることで、「さくらAI」があなたの業務フローにシームレスに溶け込みます。
  2. 低コストで「専用アプリ」が持てるさくらAIのAPI利用料(無料枠もあり)と、Google Workspaceの契約(Standardプランなどで十分)だけで動作します。通常、Webアプリを動かすために必要なサーバーの契約やインフラ管理は一切不要です。
  3. UI(操作画面)を自由に作れるLobeChatは素晴らしいツールですが、機能が多すぎて迷うことも。GASならHTMLとCSSを使って、業務に必要なボタンだけを配置した「自分(自社)専用の操作画面」を作れます。今回作成したシンプルなチャット画面もその一例です。
  4. 安心の国内完結(心理的安全性)API通信は、Googleのインフラと、さくらインターネットの国内サーバーとの間で完結します。機密性の高い情報を扱う際も、海外のサービスを経由しないという点で、心理的な安心感が得られます。

3ステップで完成!さくらAI専用チャットアプリの設定方法

必要なのは、LobeChatなどで既に使っている「さくらAIの接続情報」だけです。

ステップ1:さくらAIの接続情報を準備

以下の2つをメモ帳などにコピーしておきます。

  • トークンID: 24xxxxfa-xxxx-.... のような形式の文字列
  • 接続先URL: https://api.ai.sakura.ad.jp/v1/chat/completions

ステップ2:サーバーサイド(コード.gs)の作成

Googleドライブで「新規」>「その他」>「Google Apps Script」を選び、新しいプロジェクトを開きます。

コード.gs というファイルに、以下のコードを貼り付けます。これは「GASからさくらAIのAPIを呼び出す」ための心臓部です。

GAS(JavaScript)の「コード.js」内容は以下のとおりです

/**
 * Webアプリとしてアクセスされたときにindex.htmlを表示する関数
 */
function doGet() {
  return HtmlService.createHtmlOutputFromFile(‘index’)
    .setTitle(“さくらのAI カスタムチャット”);
}
/**
 * さくらのAI APIに会話履歴を送信し、応答を返す関数
 * @param {Array<Object>} messages ユーザーとアシスタントの会話履歴の配列
 * @returns {string} さくらのAIからの応答メッセージ、またはエラーメッセージ
 */
function callSakuraAI(messages) {
  // — 設定項目 —
  const SAKURA_API_URL = “https://api.ai.sakura.ad.jp/v1/chat/completions”;
  const API_TOKEN = “YOUR_SAKURA_API_TOKEN“; // 自身のsakuraAIトークンIDに書き換えてください
  const MODEL_NAME = “gpt-oss-120b”; // 利用したいモデルを指定
  // —————
  const headers = {
    “Authorization”: “Bearer ” + API_TOKEN,
    “Content-Type”: “application/json”
  };
  // ペイロードに会話履歴の配列をそのまま設定
  const payload = {
    “model”: MODEL_NAME,
    “messages”: messages
  };
  const options = {
    “method”: “post”,
    “headers”: headers,
    “payload”: JSON.stringify(payload),
    “muteHttpExceptions”: true
  };
  try {
    const response = UrlFetchApp.fetch(SAKURA_API_URL, options);
    const responseCode = response.getResponseCode();
    const responseBody = response.getContentText();
    if (responseCode >= 200 && responseCode < 300) {
      const responseData = JSON.parse(responseBody);
      if (responseData.choices && responseData.choices[0] && responseData.choices[0].message && responseData.choices[0].message.content) {
        return responseData.choices[0].message.content;
      } else {
        console.error(“APIからの予期せぬレスポンス形式:”, responseBody);
        return “エラー:APIから予期せぬ形式の応答がありました。”;
      }
    } else {
      console.error(“APIエラー:”, `ステータスコード: ${responseCode}, レスポンス: ${responseBody}`);
      return `エラー:APIサーバーからエラーが返されました(コード: ${responseCode})。`;
    }
  } catch (e) {
    console.error(“リクエストエラー:”, e);
    return “エラー:APIへの接続中に問題が発生しました。”;
  }
}

【重要】 YOUR_SAKURA_API_TOKEN の部分を、ステップ1でメモしたご自身の「トークンID」に必ず書き換えてください。

ステップ3:フロントエンド(index.html)の作成

次に、ユーザーが操作する画面を作ります。スクリプトエディタの「ファイル」>「新規作成」>「HTMLファイル」を選択し、ファイル名を index にします。

HTMLの内容は以下のとおりです

<!DOCTYPE html>
<html lang=”ja”>
<head>
  <meta charset=”utf-8″>
  <!– スマホファースト:全面レスポンシブ対応 –>
  <meta name=”viewport” content=”width=device-width, initial-scale=1.0, viewport-fit=cover”>
  <base target=”_top”>
  <script src=”https://cdn.jsdelivr.net/npm/marked/marked.min.js”></script>
  <style>
    :root {
      /* 最低20ptを保証しつつ、viewport幅に応じて可変 */
      –base-font-size: clamp(20pt, 4.2vw, 24pt);
      –background-color: #f0f2f5;
      –border-color: #e0e0e0;
      –user-bubble-color: #007bff;
      –ai-bubble-color: #fff;
      –radius: 22px;
      –gap: 14px;
    }
    html {
      -webkit-text-size-adjust: 100%;
    }
    body {
      margin: 0;
      padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
      font-family: -apple-system, BlinkMacSystemFont, “Segoe UI”, Roboto, “Helvetica Neue”, Arial, sans-serif;
      background: var(–background-color);
      font-size: var(–base-font-size);
      line-height: 1.7;
      color: #1d1d1f;
      display: flex;
      justify-content: center;
    }
    /* レスポンシブ:左右固定なし */
    #app-container {
      width: 100%;
      background: #fff;
      display: flex;
      flex-direction: column;
      min-height: 100vh;
      margin: 0;
    }
    h1 {
      text-align: center;
      margin: 0;
      padding: 16px 10px;
      font-size: 1.25em;
      border-bottom: 1px solid var(–border-color);
      background: #fff;
    }
    #chat-container {
      flex: 1;
      overflow-y: auto;
      padding: 16px;
      -webkit-overflow-scrolling: touch;
      scroll-behavior: smooth;
    }
    .message {
      max-width: 95%;
      margin-bottom: var(–gap);
      padding: 12px 16px;
      border-radius: var(–radius);
      word-wrap: break-word;
      overflow-wrap: anywhere;
      line-height: 1.65;
      font-size: 1rem;
    }
    .user-message {
      background: var(–user-bubble-color);
      color: #fff;
      margin-left: auto;
      border-bottom-right-radius: 6px;
    }
    .ai-message {
      background: var(–ai-bubble-color);
      color: #212529;
      margin-right: auto;
      border: 1px solid #e5e5e5;
      border-bottom-left-radius: 6px;
    }
    /* Markdown整形 */
    .ai-message h1 { font-size: 1.4em; margin: 1em 0 .5em; padding-bottom: .3em; border-bottom: 1px solid #ddd; }
    .ai-message h2 { font-size: 1.25em; margin: 1em 0 .5em; padding-bottom: .25em; border-bottom: 1px solid #eee; }
    .ai-message p:last-child { margin-bottom: 0; }
    .ai-message img, .ai-message table { max-width: 100%; height: auto; display: block; margin: .5em 0; }
    .ai-message table { overflow-x: auto; border-collapse: collapse; }
    .ai-message th, .ai-message td { padding: 10px 14px; border: 1px solid #ccc; white-space: nowrap; }
    .ai-message th { background: #f7f7f7; }
    /* 入力エリア */
    #input-area {
      display: flex;
      align-items: center;
      gap: 10px;
      padding: 10px 12px;
      padding-bottom: calc(env(safe-area-inset-bottom, 0) + 10px);
      border-top: 1px solid var(–border-color);
      background: #f8f9fa;
      position: sticky;
      bottom: 0;
    }
    #prompt {
      flex: 1;
      min-height: 48px;
      max-height: 40vh;
      padding: 12px 14px;
      border: 1px solid #ccc;
      border-radius: 24px;
      font-size: 1rem;
      line-height: 1.6;
      resize: none;
      overflow-y: auto;
      background: #fff;
    }
    #send-button {
      padding: 0 36px;
      min-height: 96px;
      border: none;
      background: var(–user-bubble-color);
      color: #fff;
      border-radius: 24px;
      font-size: 2rem;
      font-weight: 700;
      cursor: pointer;
      touch-action: manipulation;
    }
    #send-button:disabled { background: #aaa; cursor: not-allowed; }
    .blinking-cursor { animation: blink 1s step-end infinite; }
    @keyframes blink { 50% { opacity: 0; } }
  </style>
</head>
<body>
  <div id=”app-container”>
    <h1>さくらのAI(gpt-oss-120b)</h1>
    <div id=”chat-container”></div>
    <div id=”input-area”>
      <textarea id=”prompt” rows=”1″ placeholder=”メッセージを入力…”></textarea>
      <button id=”send-button” onclick=”sendMessage()”>送信</button>
    </div>
  </div>
  <script>
    const chatContainer = document.getElementById(‘chat-container’);
    const promptInput   = document.getElementById(‘prompt’);
    const sendButton    = document.getElementById(‘send-button’);
    let conversationHistory = [];
    function autosizeTextarea(el) {
      el.style.height = ‘0’;
      el.style.height = Math.min(el.scrollHeight, window.innerHeight * 0.4) + ‘px’;
    }
    promptInput.addEventListener(‘input’, () => autosizeTextarea(promptInput));
    autosizeTextarea(promptInput);
    promptInput.addEventListener(‘keydown’, event => {
      if (event.key === ‘Enter’ && !event.shiftKey) {
        event.preventDefault(); sendMessage();
      }
    });
    function sendMessage() {
      const prompt = promptInput.value.trim(); if (!prompt || sendButton.disabled) return;
      addMessageToHistory(‘user’, prompt);
      displayMessage(‘user’, prompt);
      promptInput.value = ”;
      autosizeTextarea(promptInput);
      sendButton.disabled = true;
      const aiDiv = document.createElement(‘div’); aiDiv.className = ‘message ai-message’; aiDiv.innerHTML = ‘<span class=”blinking-cursor”>▋</span>’;
      chatContainer.appendChild(aiDiv); scrollToBottom();
      google.script.run
        .withSuccessHandler(res => onAiResponse(res, aiDiv))
        .withFailureHandler(err => onAiError(err, aiDiv))
        .callSakuraAI(conversationHistory);
    }
    function onAiResponse(text, div) { addMessageToHistory(‘assistant’, text); typewriter(div, text); }
    function onAiError(err, div) { div.textContent = ‘エラー: ‘ + err.message; sendButton.disabled = false; }
    function addMessageToHistory(r, c) { conversationHistory.push({ role: r, content: c }); }
    function displayMessage(s, t) { const d = document.createElement(‘div’); d.className = ‘message ‘ + s + ‘-message’; d.innerText = t; chatContainer.appendChild(d); scrollToBottom(); }
    function typewriter(el, txt) { el.innerHTML = ”; let i=0; const interval = setInterval(() => { if(i<txt.length) { el.innerHTML+=txt[i++]; scrollToBottom(); } else { clearInterval(interval); el.innerHTML = marked.parse(txt); sendButton.disabled=false; promptInput.focus(); } }, 20); }
    function scrollToBottom() { chatContainer.scrollTop = chatContainer.scrollHeight; }
    window.addEventListener(‘resize’, () => autosizeTextarea(promptInput));
    document.addEventListener(‘visibilitychange’, () => { if (!document.hidden) autosizeTextarea(promptInput); });
  </script>
</body>
</html>

なお、htmlについてはGeminiを使ってどんどん自分向けに修正して使いやすいように変更していけばよいです。

ステップ4:「ウェブアプリ」としてデプロイ

コードを保存したら、エディタ右上の「デプロイ」>「新しいデプロイ」をクリックします。

  1. 「種類の選択」で「ウェブアプリ」を選択します。
  2. 「アクセスできるユーザー」を「自分のみ」(または共有したい範囲)に設定します。
  3. 「デプロイ」ボタンを押します。

これで、表示された「ウェブアプリURL」にアクセスするだけで、あなたが作った専用チャットアプリが利用できます。このURLは、ブラウザにブックマークしたり、メモしたりしてすぐに呼び出せるようにしておきましょう。

さらなる拡張性:GASで作る「社内データRAG」の可能性

このアプリが完成したことは、ゴールではなくスタートです。GASの真価は、ここからの拡張性にあります。

現在、LobeChatなどでは「RAG(Retrieval-Augmented Generation)」という、AIに外部の知識(PDFやテキスト)を読み込ませて回答させる機能が注目されています。

これをGASで自作できるのです。

GASでRAGを実現するアイデア

  1. 知識源: Googleドライブに「社内マニュアル」や「議事録」のフォルダを用意し、そこにGoogleドキュメントやテキストファイルを入れておきます。
  2. 検索: ユーザーがチャットで質問(例:「A案件の決定事項は?」)をしたら、GASの DriveApp.searchFiles() 機能を使って、ドライブ内の関連ドキュメントを検索します。
  3. プロンプト生成: GASが、見つかったドキュメントのテキストを読み込み、「以下の資料を参考にして、A案件の決定事項を要約してください。[ここに資料のテキスト]」というプロンプトを自動で組み立てます。
  4. AIに送信: そのプロンプトを、さくらAIのAPI(callSakuraAI関数)に送信します。

たったこれだけで、Googleドライブを知識データベースにした「さくらAI搭載の社内アシスタント」が完成します。(まだ実装していないので、するのではないかと考えています、これからのチャレンジです)

まとめ

GASは、さくらAIという強力なエンジンを、私たちの日常業務に最適化するための「最高の接着剤」です。

スプレッドシートで=SUM()関数を覚えるのと同じ感覚で、google.script.runの使い方を一つ覚えるだけで、あなたのGoogle Workspaceは、ただのオフィスソフトから「強力なAI開発プラットフォーム」へと変貌します。

まずはこの記事のコードをコピーして、自分専用のAIチャットが動く感動を体験してみてください。ちなみに、私はエンジニアではないのですが、ここまではできますということです。