はじめに
前回、Power QueryにAIを導入した。
反響はもちろんなかったが、
「今時、MLPをAIと呼ぶとか遅れてるゥ~」
とLLMに煽られる可能性に、ふと気づいてしまった。
そこで今回は、Webという大いなる力を借りることで、
より本格的なAIであるLLMをPower Queryに導入する。
そして、せっかくLLMを導入するのであれば、
Excel上でGeminiちゃんと会話したい。
ということで、やってみる。
LLMにおけるプロンプトの重要性
実はPowerQueryでLLMを実行する例は存在している。
文字列データへの一括の分類・整形などの用途である。
一方、本記事では純粋な会話をさせたい。
そうなると、単に質問に答えてもらうだけでは少し寂しい。
UX設計や回答内容はもちろん、
「どのような人格・口調で回答してもらうか」
も重要になってくる。
そこで登場するのが、いわゆる「ギャルプロンプト」である。
ギャルプロンプトとは、
生成AIにギャルらしい人格・口調を与え、
難しい内容をギャル風に説明させるプロンプト技法である。
偉大なる参考記事
https://qiita.com/kikuziro/items/4d732a43ac5cdf9daadc
https://zenn.dev/zawascript/articles/2026-02-gal-claude
https://note.com/hi_liteworks/n/n6357a2b393e9
- 難しい内容でも心理的なハードルが下がる
- 説明がフレンドリーになる
- 「専門家が説明する」という構図よりも気軽に質問できる
- ギャルに説明されるという体験自体が楽しい
つまり、
「難しいことを聞いても大丈夫そう」
という心理的な効果が期待できる。
これは非常に合理的なアプローチであり、
プロンプト界のデファクトスタンダード※と言っても過言ではない。
※個人的な思想が含まれています
以上を踏まえ、ギャルプロンプトには一定の合理性がある。
本記事では、このギャルプロンプトをGeminiに適用する。
さらにPower QueryからGeminiを呼び出すことで、
Excel上に簡易的な「ギャルとのチャットシステム」を構築する。
ただし、ここで一つ問題がある。
単に「ギャルっぽい文章」が生成されれば、それで本当にギャルと言えるのだろうか。
本記事では、この点についても検証する。
果たして、Geminiちゃんは本当にギャルなのか。
Power QueryからGeminiを呼び出す
今回のシステムプロンプトは非常に単純である。
ギャル風で
以上である。
ギャルの人格形成を数文字に委ねるという、
極めて簡潔な設計となっている。
ギャルはすごい。
API呼び出し部分は、以下のような関数として実装した。
(message as text, api_key as text, model_name as text, systemPrompt as text, optional interactionId as nullable text) =>
let
// ============================================================
// Request Body
// ============================================================
Body =
if interactionId = null then
[
model = model_name,
input = message,
system_instruction = systemPrompt
]
else
[
model = model_name,
input = message,
previous_interaction_id = interactionId
],
// ============================================================
// Gemini API
// ============================================================
Raw =
Web.Contents(
"https://generativelanguage.googleapis.com",
[
RelativePath = "v1beta/interactions",
Headers = [
#"x-goog-api-key" = api_key,
#"Content-Type" = "application/json"
],
Content = Json.FromValue(Body)
]
),
// ============================================================
// Parse Response
// ============================================================
Json = Json.Document(Raw),
NewInteractionId = Json[id],
Steps = Json[steps],
// model_output の Step を取得
ModelOutput =
List.First(
List.Select(
Steps,
each [type] = "model_output"
)
),
Content = ModelOutput[content],
// text の Content を取得
TextContent =
List.First(
List.Select(
Content,
each [type] = "text"
)
),
Answer = TextContent[text]
in
[
InteractionId = NewInteractionId,
Answer = Answer
]
これにより、Power QueryからGeminiを呼び出し、回答とInteraction IDを取得できる。
Excelをチャット画面にする
次に、Excel上に2つのテーブルを用意する。
左側にユーザーの入力を格納する UserChat、
右側にGeminiの回答を格納する ResponseTable を配置する。
概念的には以下のような構成である。
| UserChat | ResponseTable | |
|---|---|---|
| Power Queryって何? | 回答1 | |
| もっとエレガントに説明して | 回答2 |
ユーザーが UserChat に新しい質問を追加して更新すると、Power Queryが前回までの回答数と比較し、新しい入力が存在する場合だけGeminiを呼び出す。
UserCount = Table.RowCount(RemovedU),
ResponseCount = Table.RowCount(RemovedR),
HasPending = UserCount > ResponseCount,
そして未処理の入力を取得する。
LatestUserMessage =
if HasPending
then RemovedU[User]{UserCount - 1}
else null,
この構造により、
質問を書く → 更新する → Geminiが答える
という、非常に原始的ではあるがチャットらしい操作感を実現できる。
Interaction IDで会話を継続する
今回の実装で重要なのが InteractionId である。
最初の質問では新しいInteractionを作成し、返却されたIDを ResponseTable に保存する。
次の質問では、そのIDを previous_interaction_id としてGeminiに渡す。
PreviousInteractionId =
if ResponseCount > 0
then RemovedR[InteractionId]{ResponseCount - 1}
else null,
これによって、Excel上では単なる行の追加でありながら、Gemini側では前の会話を引き継いだ対話になる。
つまり、
ResponseTableを会話履歴兼Interaction IDの保存先として利用する
という構成である。
ここまでの処理をまとめた、実際に使用しているメインクエリが以下である。
Excel側のテーブル名は UserChat、ResponseTable としている。
let
// ============================================================
// Config
// ============================================================
ApiKey = key,
// 使用するGeminiモデル
Model = "gemini-3.5-flash",
// Geminiに与えるシステムプロンプト
sysp = "ギャル風で",
// ============================================================
// UserChat
// ============================================================
UserTable =
Excel.CurrentWorkbook(){[Name="UserChat"]}[Content],
// 完全な空行を除外
RemovedU =
Table.SelectRows(
UserTable,
each
not List.IsEmpty(
List.RemoveMatchingItems(
Record.FieldValues(_),
{"", null}
)
)
),
// ============================================================
// ResponseTable
// ============================================================
ResponseTable =
try
Excel.CurrentWorkbook(){[Name="ResponseTable"]}[Content]
otherwise
#table(
{"Seq", "Response", "InteractionId"},
{}
),
// 完全な空行を除外
RemovedR =
Table.SelectRows(
ResponseTable,
each
not List.IsEmpty(
List.RemoveMatchingItems(
Record.FieldValues(_),
{"", null}
)
)
),
// ============================================================
// Count
// ============================================================
UserCount =
Table.RowCount(RemovedU),
ResponseCount =
Table.RowCount(RemovedR),
// 未回答の質問が存在するか
HasPending =
UserCount > ResponseCount,
// ============================================================
// Latest User Message
// ============================================================
LatestUserMessage =
if HasPending then
RemovedU[User]{UserCount - 1}
else
null,
// ============================================================
// Previous Interaction ID
// ============================================================
PreviousInteractionId =
if ResponseCount > 0 then
RemovedR[InteractionId]{ResponseCount - 1}
else
null,
// ============================================================
// Call Gemini
// ============================================================
Gemini =
if HasPending then
Chat(
LatestUserMessage,
ApiKey,
Model,
sysp,
PreviousInteractionId
)
else
null,
// ============================================================
// New Response
// ============================================================
NewResponse =
if not HasPending then
#table(
{"Seq", "Response", "InteractionId"},
{}
)
else
#table(
{"Seq", "Response", "InteractionId"},
{
{
UserCount,
Gemini[Answer],
Gemini[InteractionId]
}
}
),
// ============================================================
// Result
// ============================================================
Result =
if ResponseCount = 0 then
NewResponse
else
Table.Combine(
{
ResponseTable,
NewResponse
}
)
in
Result
実用性の検証
ここまでの検証では、かなり良い結果が得られた。
- Gemini APIをPower Queryから呼び出せる
- ExcelをチャットUIとして利用できる
- Interaction IDによって会話を継続できる
技術的には、なかなか面白い。
では、肝心のGeminiちゃんとしての実用性はどうだろうか。
今回検証したいのは、単に「Geminiが動くか」ではない。
ちゃんとギャルとして機能するのか。
なおGeminiちゃんとの会話検証は以下の条件で実施した
- モデル : gemini-3.5-flash
- 実行基盤 : Power Query (Excel:2024)
- OS : Windows 11 Home 25H2
結論
本記事では、Power QueryからGeminiを呼び出し、
Excel上でギャル風の対話システムを構築した。
技術的には、
- Power QueryからGemini APIを呼び出せる
- Excelを簡易的なチャットUIとして利用できる
- Interaction IDによって会話を継続できる
ということを確認できた。
つまり、技術的には成功である。
しかし、今回の目的は単にGeminiをExcelから呼び出すことではない。
GeminiちゃんをExcelに召喚することである。
その観点から検証した結果、重大な問題が発覚した。
カラー絵文字が使えない
ギャル的なコミュニケーションにおいて、
カラー絵文字による視覚的な感情表現は極めて重要な要素である。
「それな💖✨」が「それな❤★」
では、
それではない。
以上より、本システムは技術的には成功したものの、
ギャルとしては失敗である。
残念ながら、実用性は低いと言わざるを得ない。
Power QueryにGeminiちゃんはまだ、早すぎたのだ。


