この動画は、ChatGPTをはじめとした生成AIツールの活用について学ぶ全10回のe-Learningです。前半は、記事、動画、企画書、Webページなどを様々な生成AIツールを利用して生成する方法を学ぶ内容です。後半は、GPT APIなどを利用してクローラー、AI-OCR、アプリケーション開発などを行う流れを紹介しており、難易度がグッと上がります。(後半は、プログラミングの知識が必要なため、事前にMicrosoft365、Google系ツールのコースを受講しておくことをオススメします。
各コンテンツは、20分程度の学習パートと事後課題で構成されています。
メンタルヘルスに課題を抱えて、離職、休職中の方の就労、復職をサポートするためのリワークプログラムの一環として作成していますが、実務に直結する内容かつかなり本格的な活用方法まで網羅していますので、純粋なリスキリングにもご活用いただけます。
動画コンテンツ
教材スライド
gpt_api_data_analysis_training_20250816065431PDFのダウンロードはこちら
演習ファイルはこちら
演習用手順書(Excel 操作手順+ API 呼び出し例)
0. 事前準備(3分)
- サンプル Excel を開く
「sample_gpt_data.xlsx」をダウンロードして Excel for the web で開きます。
シートには「顧客ID / 住所 / 商品名 / 顧客コメント」があります。 - Excel 環境
「自動化」タブ → 「コードエディター」(Office Scripts)を開けることを確認。 - API キーの保管
組織ポリシーに従い、キーはスクリプトに直書きしないのが原則。テスト用は一時的に直書き可ですが、実務では Power Automate の「環境変数」「接続参照」などを用いて安全に参照してください。
1. Excel 操作(表記統一の準備:2分)
- C列に「標準化住所」、D列に「カテゴリ」、E列に「カテゴリ理由(任意)」という見出しを追加。
- 1行目はヘッダーのため、2行目以降が処理対象です。
2A. Office Scripts で GPT API を直接呼ぶ(推奨:まずはこれを試す)
※ 一部テナントでは fetch が制限される場合があります。失敗したら 2B に進んでください。
2A-1. アドレス標準化スクリプト
- 「新しいスクリプト」を作成し、下記を貼り付けて保存(例:
StandardizeAddresses.ts)。 OPENAI_API_KEYは実際のキーに差し替え。- 実行すると「住所」列(B列)を読み取り、標準化した住所を C 列へ出力します。
- バッチ・リトライ・レート制御の最小実装を入れています(5MB 制限にも留意)。
// StandardizeAddresses.ts
// 住所の標準化:B列→C列
// 注意:学習用途の最小実装。実務ではキーの安全管理・監査ログ・エラー保護を強化してください。
async function main(workbook: ExcelScript.Workbook) {
const SHEET = workbook.getActiveWorksheet();
const range = SHEET.getUsedRange();
const values = range.getValues();
const header = values[0] as any[];
const COL_ADDR = header.indexOf("住所");
const COL_OUT = header.indexOf("標準化住所");
if (COL_ADDR < 0 || COL_OUT < 0) throw new Error("列見出しが見つかりません(住所/標準化住所)。");
const OPENAI_API_KEY = "YOUR_OPENAI_API_KEY"; // ←演習用。実務はPower Automate等で安全に注入
const ENDPOINT = "https://api.openai.com/v1/responses"; // Responses API
const MODEL = "gpt-4.1-mini"; // 手頃なモデル例。用途に応じて変更
// まとめて送りすぎると 5MB 制限に触れるため、小分け(例:20件)で処理
const chunkSize = 20;
for (let start = 1; start < values.length; start += chunkSize) {
const end = Math.min(start + chunkSize, values.length);
const batch = values.slice(start, end);
// 入力のみ抽出
const inputs = batch.map(row => (row[COL_ADDR] ?? "").toString());
// 空行スキップ用にインデックス保持
const idxMap = batch.map((_, i) => start + i);
// プロンプト(役割&出力フォーマット固定)
const system = "あなたは日本の住所表記の正規化を行うアシスタントです。丁目・番・号などの正規表現を用い、日本語の正式表記に統一してください。できない場合は元の文字列を返してください。";
const user = `次の住所を正式表記に統一し、各行をそのまま1行にして返してください。\n${inputs.map((s,i)=>`[${i}] ${s}`).join("\n")}`;
// API 呼び出し(簡易リトライ付き)
let text = "";
for (let attempt = 1; attempt <= 3; attempt++) {
try {
const res = await fetch(ENDPOINT, {
method: "POST",
headers: {
"Authorization": `Bearer ${OPENAI_API_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
model: MODEL,
input: [
{ role: "system", content: system },
{ role: "user", content: user }
]
})
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const json = await res.json();
// Responses API のテキスト抽出(最新仕様に合わせて適宜調整)
// 代表的には json.output[0].content[0].text など
text = json?.output?.[0]?.content?.[0]?.text ?? json?.output_text ?? "";
if (text) break;
else throw new Error("空のレスポンス");
} catch (e) {
await delay(400 * attempt); // バックオフ
if (attempt === 3) throw e;
}
}
// 行ごとに割り当て
const lines = text.split(/\r?\n/).map(s => s.replace(/^\[\d+\]\s*/, "").trim()).filter(Boolean);
lines.forEach((line, i) => {
const rowIdx = idxMap[i];
if (rowIdx !== undefined) SHEET.getCell(rowIdx, COL_OUT).setValue(line);
});
}
}
function delay(ms: number) {
return new Promise(r => setTimeout(r, ms));
}
2A-2. コメント分類スクリプト
- 「顧客コメント」(D列)を読み取り、E列に「カテゴリ理由(任意)」、D列の右隣(例:F列)に「カテゴリ」出力でも可。ここでは D→D(上書き)ではなく、D→Dそのまま、F にカテゴリ、E に理由という例を示します。
// ClassifyComments.ts
// 顧客コメントのカテゴリ付け:D列 → F列(カテゴリ), E列(理由)
// カテゴリ例:配送, サポート, デザイン, 価格, 品質, 情報, その他
async function main(workbook: ExcelScript.Workbook) {
const SHEET = workbook.getActiveWorksheet();
const range = SHEET.getUsedRange();
const values = range.getValues();
const header = values[0] as any[];
const COL_COMMENT = header.indexOf("顧客コメント");
const COL_REASON = header.indexOf("カテゴリ理由(任意)");
const COL_CAT = header.indexOf("カテゴリ");
if (COL_COMMENT < 0 || COL_REASON < 0 || COL_CAT < 0) {
throw new Error("列見出しが見つかりません(顧客コメント/カテゴリ理由(任意)/カテゴリ)。");
}
const OPENAI_API_KEY = "YOUR_OPENAI_API_KEY";
const ENDPOINT = "https://api.openai.com/v1/responses";
const MODEL = "gpt-4.1-mini";
const chunkSize = 30;
for (let start = 1; start < values.length; start += chunkSize) {
const end = Math.min(start + chunkSize, values.length);
const batch = values.slice(start, end);
const idxMap = batch.map((_, i) => start + i);
const comments = batch.map(row => (row[COL_COMMENT] ?? "").toString());
const system = "あなたは顧客コメントを日本語で分類するアシスタントです。カテゴリは「配送, サポート, デザイン, 価格, 品質, 情報, その他」のいずれかを選び、1件につき「カテゴリ」「簡単な理由」を返してください。";
const user = [
"次のコメントを行番号付きで与えます。",
"各行について、JSON Lines 形式で {index, category, reason} を返してください。",
"allowed_categories = [配送, サポート, デザイン, 価格, 品質, 情報, その他]",
comments.map((c,i)=>`[${i}] ${c}`).join("\n")
].join("\n");
let output = "";
for (let attempt = 1; attempt <= 3; attempt++) {
try {
const res = await fetch(ENDPOINT, {
method: "POST",
headers: {
"Authorization": `Bearer ${OPENAI_API_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
model: MODEL,
input: [
{ role: "system", content: system },
{ role: "user", content: user }
]
})
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const json = await res.json();
output = json?.output?.[0]?.content?.[0]?.text ?? json?.output_text ?? "";
if (output) break;
else throw new Error("空のレスポンス");
} catch (e) {
await delay(400 * attempt);
if (attempt === 3) throw e;
}
}
// JSON Lines をパース
const lines = output.split(/\r?\n/).filter(Boolean);
lines.forEach(line => {
try {
const obj = JSON.parse(line);
const rowIdx = idxMap[obj.index];
if (rowIdx !== undefined) {
SHEET.getCell(rowIdx, COL_CAT).setValue(obj.category ?? "その他");
SHEET.getCell(rowIdx, COL_REASON).setValue(obj.reason ?? "");
}
} catch { /* 無害化 */ }
});
}
}
function delay(ms: number) { return new Promise(r => setTimeout(r, ms)); }
メモ
- Office Scripts の外部呼び出しは環境によりブロックされることがあります。失敗時は 2B に切替。
- 1回のリクエストやレスポンスは 5MB 制限に注意。件数を小分けに。
- Responses API の構造は継続的に進化します。
output_textなど抽出箇所は最新版ドキュメントを確認してください。
2B. Power Automate で HTTP 経由(安定運用向け)
Office Scripts から外部 HTTP が制限される環境や、本番運用で鍵管理を厳密にしたい場合はこちら。Microsoft Learn
- フロー作成
- トリガー:「手動でフローをトリガーします」
- アクション:「Excel Online(Business)— Run script」で、対象ブックとスクリプト(例:
CollectChunk)を指定。スクリプトは処理対象のレコードを JSON で返すだけにしておく。 - アクション:「HTTP」または「HTTP with Microsoft Entra ID(preauthorized)」で OpenAI API を呼び出す(機密情報はフローの安全な入力/環境変数に格納)。
- アクション:「Excel Online(Business)— Update a row」等で結果を書き戻す。
- スクリプト分担の考え方
- Office Scripts:Excel I/O(範囲読み書き、チャンク化、インデックス保持)
- Power Automate:HTTP(OpenAI 呼び出し)、キー管理、監査ログ
- メリット
- 企業の DLP/監査ポリシーに沿いやすい
- 再実行や失敗時のリトライ、分岐、通知が GUI で設計しやすい
3. ピボットテーブルでの集計(3分)
- 「挿入」→「ピボットテーブル」
- フィールド設定
- 行:カテゴリ
- 値:顧客ID(件数)または 商品名(件数)
- グラフ化
- ピボットテーブル選択 → 「挿入」→「縦棒」
- 眺め方
- 件数が多いカテゴリは「声が多い領域」
- 改善余地や次の仮説検証に直結
4. ワーク(10分)
- Step1:
StandardizeAddresses.tsを実行し、住所の Before/After を確認 - Step2:
ClassifyComments.tsを実行し、カテゴリと理由を付与 - Step3:ピボットでカテゴリ別件数を可視化し、要点を 3 行メモ
- 例:「品質が最頻」「価格・情報に改善余地」「サポートは件数少だが重要度高い」
5. トラブルシューティング
- fetch が「保留/失敗」になる:テナントポリシーや CORS の影響の可能性 → 2B へ。
- 大量データでコケる:20~50件単位に分割、文字数を圧縮(プロンプトを短く)、5MB 制限に留意。
- レスポンス構造が合わない:Responses API ドキュメントの最新サンプルを確認して抽出箇所を調整。
6. セキュリティと運用の要点
- API キーは絶対に配布しない。実務は Power Automate 側に格納し、Office Scripts には渡さない設計が無難。Microsoft Learn
- ログ:呼び出し件数・トークン量・失敗時の本文(機微情報はマスキング)
- スケール:毎時や毎日で自動起動するフローに格上げし、Excel を「入出力用の境界」として設計
