TL;DR
- 検証済み(Python CLI PoC): 問い合わせ 分類 → FAQベクトル検索 → 返信下書き生成 まで動作。顧客への自動送信はしない(下書きまで)
- 設計段階・部分実装(E2E未検証): n8n Webhook への統合、Slack 通知、FAQ Embeddings の n8n 内完結
-
環境: Python 3.11 / openai SDK 1.68.0 / macOS。
base_url=https://api.ai.sakura.ad.jp/v1/、基盤モデル無償プラン -
モデル: Chat
gpt-oss-120b/ Embeddingsmultilingual-e5-large - 記事A/B共通FAQ PoC(部分集計): Chat 7 + Embeddings 19 = 合計 26(ping4 + classify1 + answer_draft2 / build_index15 + search4)
- 当月累計(公式パネル 2026-07-22・全PoC合算・実測確定): Chat 27 / Emb 19 / 文字起こし 1 / 読み上げ 3 / 利用料金 ¥0 → 残 Chat 2,973 / Emb 9,981
- 自作台帳(参考・当月累計・全API): 合計 53 / Chat 28 / Emb 19 — 課金・枠の基準は公式パネル
-
デモ結果: FAQ検索のコサイン類似度スコア 0.9554(1クエリ実測)で請求FAQを1位ヒット。分類は成功。返信下書きは主要部を生成したが
max_tokens=600で末尾が途中切れ(finish_reason: length) -
最大のハマりどころ:
gpt-oss-120bは推論モデルでmax_tokensが小さいとcontentが空。短文(疎通・分類)は 512 前後で安定、長文返信は上限不足で切れる - GitHub: https://github.com/YushiYamamoto/itprodx-sakura-ai-poc
- キャンペーン: 3,000リクエスト使い切りチャレンジ 参加記
はじめに
個人事業主として受託開発と自動化支援をしていると、問い合わせの一次対応に地味に時間を取られます。見積・納期・技術トラブル——内容の多くはFAQに近いのに、一件一件手で返すのはもったいない。
一方、問い合わせ本文には顧客名・連絡先・案件の具体が入ります。海外クラウドのAPIにそのまま流すのは、個人事業主には心理的ハードルが高い。
そこで さくらのAI Engine の基盤モデル無償プラン(Chat 月3,000・Embeddings 月10,000)を使い、国内事業者が提供するAPI上で Python CLI による問い合わせ一次対応 PoC を組みました。最終形は n8n に載せ替える設計ですが、本記事で E2E 検証済みなのは Python パイプラインのみ(n8n は設計段階・部分実装・E2E未検証)です。
本記事の切り口は次の4点です。
- n8n×自動化(設計段階・部分実装・E2E未検証) — 目標は Webhook→分類→検索→下書き→Slack。現状は Python PoC + n8n は分類・簡易下書きのみ(FAQ Embeddings 未接続)
- 国内事業者のAPI — 問い合わせデータをさくらのAI Engine上で処理(データ所在地の保証までは公式表現を超えない)
- リクエスト予算経営 — Chat 3,000 / Emb 10,000 をそれぞれ台帳で計測
- 使い倒すための予算設計 — 記事A/B共通FAQ PoCで核は見えた。当月累計(公式)Chat 27(消費率 0.90%)で、残 Chat 2,973 / Emb 9,981 を計画的に使う設計
さくらのAI Engine 無償プランの前提整理
無償枠一覧(2026年7月時点・公式)
| カテゴリ | モデル | 無償枠/月 | 超過時(無償プラン) |
|---|---|---|---|
| Chat completions |
gpt-oss-120b, llm-jp-3.1-8x13b-instruct4
|
3,000リクエスト | レートリミット(自動課金なし) |
| Embeddings | multilingual-e5-large |
10,000リクエスト | 同上 |
| Audio transcription | whisper-large-v3-turbo |
50リクエスト | 同上 |
| Text-to-Speech | VOICEVOX各種 | 50リクエスト | 同上 |
| RAGドキュメント | — | 無償枠なし(100チャンク/3円) | 従量課金 |
出典: さくらのAI Engine 公式(提供モデルと料金) / 利用手順
利用開始の前提: 無償プランでもクレジットカード登録が必要です。超過分の自動課金はありません(レートリミットで止まる)。
OpenAI SDK 互換で始められる
さくらのAI Engine は OpenAI 互換APIです。既存の openai Python SDK で base_url を差し替えるだけで動きます。
import os
from openai import OpenAI
SAKURA_BASE_URL = "https://api.ai.sakura.ad.jp/v1/"
client = OpenAI(
base_url=SAKURA_BASE_URL,
api_key=os.environ["SAKURA_AI_TOKEN"], # UUID:secret 形式
)
認証ヘッダは Authorization: Bearer <ID>:<シークレット> です(マニュアル記載)。
RAG公式機能を使わない理由
公式RAG(ドキュメント保管)は無償枠がありません。PoCでは Embeddings API + SQLite でFAQ検索を自前実装しました。
作ったもの(全体像)
検証済み vs 設計(将来形)
| レイヤ | 状態 | 内容 |
|---|---|---|
| Python CLI PoC | ✅ 検証済み | 分類・FAQ索引・検索・返信下書き(自動送信なし) |
n8n inquiry-triage.workflow.json |
🔧 設計段階・部分実装(E2E未検証) | Webhook→分類→簡易下書き。FAQ Embeddings は Python PoC に委譲(Sticky Note 記載) |
| Slack 通知 | 📋 未検証 | 設計のみ |
処理フロー(Python PoC — 検証済み)
[問い合わせ] → [classify.py] → [search.py + faq.db] → [answer_draft.py] → [人間が確認して送信]
構成図
図1: Python PoC(本記事で動作確認済み)
図2: n8n 現状(設計・E2E未検証)
PoCで確認したこと(2026-07-21)
| ステップ | コマンド | 結果 |
|---|---|---|
| 疎通 | python test_ping.py |
OK / 応答例「了解」 |
| FAQ索引 | python build_index.py |
架空FAQ 15件 → faq.db
|
| 検索 | python search.py "請求書の支払期限を教えてください" |
1位 類似度スコア 0.9554 |
| 分類 | python classify.py "本日中にAPIキーが漏洩した可能性があります" |
{"category":"技術","urgent":true} |
| 下書き | python answer_draft.py "Netlifyのビルドが失敗…" |
主要部生成。max_tokens=600 で末尾途中切れ
|
FAQ・問い合わせ文はすべて架空(faq_data.py)。実顧客データは不使用です。
疎通テスト(実行例) — test_ping.py(2026-07-21 実測。※本文表の応答例「了解」とは別実行)
~/sakura-poc-venv/bin/python test_ping.py
OK: Chat API 疎通成功
reply: はい
FAQ検索(実行例) — search.py "請求書の支払期限を教えてください"(2026-07-21 実測)
~/sakura-poc-venv/bin/python search.py "請求書の支払期限を教えてください"
[
{
"id": 9,
"question": "請求書の発行日と支払期限を教えてください。",
"answer": "月末締め翌月末払いが標準です。個別契約で異なる場合は契約書をご確認ください。",
"category": "請求",
"score": 0.9554
},
{
"id": 12,
"question": "対応時間帯を教えてください。",
"answer": "平日10:00〜18:00が標準です。緊急障害は別途契約の監視プランで24時間対応可能です。",
"category": "その他",
"score": 0.8743
},
{
"id": 1,
"question": "見積もりの回答までどのくらいかかりますか?",
"answer": "要件ヒアリング後、通常3営業日以内に概算見積もりをお送りします。",
"category": "見積",
"score": 0.8573
}
]
リクエスト予算の設計方法
Chat 3,000 と Embeddings 10,000 は公式上別枠です。予算設計も API 種別ごとに考えます。
設計予算 vs 記事A/B共通FAQ PoC(部分集計)
| purpose | API種別 | 設計上限 | 部分集計 | 備考 |
|---|---|---|---|---|
| ping | Chat | 400 | 4 | test_ping 再実行含む |
| classify | Chat | 800 | 1 | |
| answer_draft | Chat | 600 | 2 | 内部で search_faq() を呼ぶが Emb は search として別カウント(記事掲載用に2026-07-22再実行1回含む) |
| build_index | Embeddings | 500 | 15 | FAQ1件=1 Emb リクエスト |
| search | Embeddings | — | 4 | 単体テスト2 + answer_draft 内2 |
| Chat小計(部分集計) | 3,000 | 7 | ping4 + classify1 + answer_draft2 | |
| Emb小計(部分集計) | 10,000 | 19 | build_index15 + search4 | |
| 部分集計合計 | Chat+Emb | — | 26 | 記事A/B共通・問い合わせFAQ PoC |
answer_draft.py実行1回 = Chat 1回 +(内部)Embeddings 1回。台帳ではanswer_draft:2とsearch:4に分けて記録されます。
リクエスト台帳スナップショット(記事A/B共通PoC分・目的別抜粋)
usage_ledger.json 本体(当月累計 total=53)は .gitignore 対象(リポジトリには含めません)。以下は記事A/B共通・問い合わせFAQ PoC分を目的別に抜粋した集計例です(現行ファイルの total ではありません)。
{
"scope": "記事A/B共通・問い合わせFAQ PoC",
"month": "2026-07",
"total": 26,
"by_purpose": {
"ping": 4,
"build_index": 15,
"search": 4,
"answer_draft": 2,
"classify": 1
}
}
自作台帳全体(usage_ledger.json 現行・2026-07-22 実読)
| purpose | 回数 | API種別 |
|---|---|---|
| ping | 4 | Chat |
| classify | 1 | Chat |
| answer_draft | 2 | Chat |
| bench_chat | 20 | Chat |
| audio_summary | 1 | Chat |
| build_index | 15 | Embeddings |
| search | 4 | Embeddings |
| transcribe | 1 | Transcription |
| tts | 5 | TTS |
| 合計 | 53 | usage_ledger.py 経由の全API呼び出し |
Chat 小計(自作): 28 / Emb 小計: 19
公式コントロールパネル(当月 2026年7月 — 記事の基準数字)
スクショで確認した**当月累計(全PoC合算)**の公式利用量です。課金・レートリミットの判断はこちらを信頼します。
| 項目 | 当月累計(公式) | 月間無償枠 | 残り |
|---|---|---|---|
チャット生成(gpt-oss-120b / llm-jp-3.1-8x13b-instruct4) |
27 | 3,000 | 2,973 |
ベクトル埋め込み multilingual-e5-large
|
19 | 10,000 | 9,981 |
音声の文字起こし whisper-large-v3-turbo
|
1 | 50 | 49 |
音声の読み上げ shikokumetan
|
3 | 50 | 47 |
| 当月利用料金 | ¥0 | — | — |
Chat の部分集計 7 回は記事A/B共通FAQ PoC分。当月累計(公式)27 にはベンチマーク(bench_chat 20)・音声要約(audio_summary 1)等が加算。Emb の当月累計 19 は記事A/B シリーズ全体の FAQ 索引(build_index 15)+ 検索(search 4)の合算です。
台帳 vs 公式:差分の整理
| 指標 | 自作台帳(当月累計) | 公式パネル(当月累計) | 差 |
|---|---|---|---|
| Chat | 28 | 27 | −1(既知パターン・公式を正) |
| Embeddings | 19 | 19 | 一致 |
| 文字起こし | 1 | 1 | 一致 |
| 読み上げ(TTS) | 5 | 3 | −2(規約反映ラグの400失敗。台帳は失敗も加算・公式は成功のみ) |
学び: 自作合算カウンタ(2,900安全弁)は PoC 中の暴走防止に有効ですが、無償枠の残数・消費率の公式値はコントロールパネルを正とします。週1回はパネルと台帳を突合し、差分が出たら公式を優先してください。
消費率の算式(公式パネル基準)
パーセントは API種別ごと に計算します(Chat と Emb を足して1つの%にしない)。
| 指標 | 算式 | 公式パネル(当月累計) |
|---|---|---|
| Chat 消費率 | 27 ÷ 3,000 | 0.90% |
| Embeddings 消費率 | 19 ÷ 10,000 | 0.19% |
| 記事A/B共通FAQ PoC(部分集計・参考) | Chat7 + Emb19 = 26 | 合算%は使わない |
当月累計 Chat 0.90% — まだ初期実測段階です。n8n E2E・FAQ500件化・連載記事デモを計画的に進め、キャンペーン期間中に残枠を使い切る設計を続けます。
安全弁:2,900リクエストで止める(自作・全API合算)
PoCの usage_ledger.py は、check_and_record() を通った全API呼び出し(Chat / Embeddings / Transcription / TTS 等)を月次 53/2,900 のように合算カウントします。これは自作の安全弁であり、さくら公式パネルの Chat 3,000 / Emb 10,000 等のAPI別枠とは別物です。
| カウンタ | 上限 | 用途 |
|---|---|---|
| 公式(コントロールパネル) | Chat 3,000 / Emb 10,000 / 文字起こし 50 / 読み上げ 50 別々 | サービス側の無償枠 |
| 自作(usage_ledger / n8n Static Data) | 合算 2,900 | PoC中の暴走防止マージン |
# usage_ledger.py(抜粋)
MONTHLY_LIMIT = 2900 # check_and_record 経由の全API呼び出し合算
def check_and_record(purpose: str) -> None:
ledger = load_ledger()
total = int(ledger.get("total", 0))
if total >= MONTHLY_LIMIT:
raise UsageLimitExceeded(
f"月内リクエスト累計 {total} 回が上限 {MONTHLY_LIMIT} 回に達しました。"
)
# purpose 別に +1 して save
現行台帳: total=53(全PoC合算)→ 安全弁未到達・正常。週1回は公式パネル(API別)と突合してください。
共通設定(config.py)
以下は抜粋です(import・エラーハンドリング等は省略。コピペ時はリポジトリの config.py を参照)。
# 抜粋 — import: os, OpenAI, usage_ledger 等
SAKURA_BASE_URL = "https://api.ai.sakura.ad.jp/v1/"
def create_client() -> OpenAI:
token = os.environ["SAKURA_AI_TOKEN"].strip()
return OpenAI(base_url=SAKURA_BASE_URL, api_key=token)
def chat_completion(messages, *, purpose="chat", temperature=0.2, max_tokens=512, ...):
usage_ledger.check_and_record(purpose)
response = client.chat.completions.create(
model=os.environ.get("CHAT_MODEL", "gpt-oss-120b"),
messages=messages,
max_tokens=max_tokens,
stream=False,
)
content = response.choices[0].message.content
if content is None:
raise RuntimeError("Chat completion returned empty content")
return content
.env.example:
SAKURA_AI_TOKEN=
CHAT_MODEL=gpt-oss-120b
EMBEDDING_MODEL=multilingual-e5-large
実装①:問い合わせ分類(Chat Completions)
PoC結果
入力: 本日中にAPIキーが漏洩した可能性があります
出力: {"category": "技術", "urgent": true}
コード(classify.py — Python PoC)
def classify_inquiry(text: str) -> dict:
raw = chat_completion(
[...],
purpose="classify",
temperature=0.0,
max_tokens=120, # Python PoC では JSON 短文のため 120 で動作確認済み
response_format={"type": "json_object"},
)
return json.loads(raw)
n8n HTTP Request ノード(設計・E2E未検証)
同梱 n8n/inquiry-triage.workflow.json では max_tokens: 512 を指定しています。
| 項目 | 値 |
|---|---|
| Method | POST |
| URL | https://api.ai.sakura.ad.jp/v1/chat/completions |
| Header | Authorization: Bearer {{ $credentials.sakuraToken }} |
{
"model": "gpt-oss-120b",
"messages": [ "…" ],
"temperature": 0,
"max_tokens": 512,
"response_format": { "type": "json_object" }
}
なぜ n8n は 512 か: 本 PoC 後半で判明した gpt-oss-120b の推論トークン問題(後述)を踏まえ、JSON短文出力の n8n 側は 512 に設定。Python classify.py は 120 でも動きますが、推論モデルでは余裕を持たせる方が安全です。
実装②:FAQ Embeddings + SQLite検索
検索結果(実測)
クエリ: 請求書の支払期限を教えてください
| 順位 | 類似度スコア | ヒットFAQ | category |
|---|---|---|---|
| 1 | 0.9554 | 請求書の発行日と支払期限を教えてください。 | 請求 |
| 2 | 0.8743 | 対応時間帯を教えてください。 | その他 |
| 3 | 0.8573 | 見積もりの回答までどのくらいかかりますか? | 見積 |
※ 0.9554 は上記1クエリに対するコサイン類似度スコア(再現性は FAQ 更新・モデル変更で変動し得ます)。
実装③:返信下書き生成(answer_draft.py)
FAQ top3をコンテキストに注入。自動送信はしません(下書きまで・人間確認必須)。
answer_draft.py 実行時: Chat 1回 + 内部 search_faq() で Embeddings 1回(台帳では answer_draft / search に分離記録)。
PoC結果(正直な評価): 主要部(Node/環境変数/ビルドコマンドの3点確認+追加情報依頼)は生成できたが、max_tokens=600 でも末尾が途中切れ(finish_reason: length)。短文の疎通・分類は 512 前後で安定する一方、長文返信は上限不足。実務では finish_reason を確認し、出力を短く制約するか max_tokens を増やす必要があります。
返信下書き(実行例) — answer_draft.py "Netlifyのビルドが失敗しています"(2026-07-22 実測・記事掲載用)
~/sakura-poc-venv/bin/python answer_draft.py "Netlifyのビルドが失敗しています"
〇〇様
お問い合わせいただき、ありがとうございます。
Netlify のビルドが失敗する場合、まず以下の3点をご確認いただくことをおすすめしております(FAQ [1])
1. **Node バージョン**
- プロジェクトで使用している Node のバージョンが、Netlify のビルド環境と合致しているかをご確認ください。`.nvmrc` や `package.json` の `engines` 設定がある場合は、そこに記載されたバージョンが正しく指定されているかをご確認ください。
2. **環境変数**
- 必要な環境変数(例: API キー、DB 接続情報など)が Netlify の「Site settings」→「Build & deploy」→「Environment」へ正しく設定されているか、またスペルミスや値の抜けがないかをご確認ください。
3. **ビルドコマンド**
- `npm run build` や `next build` など、実際に実行しているビルドコマンドが正しく設定されているかをご確認ください。ローカルで問題なくビルドできるかどうかも併せてご確認いただくと、原因特定に役立ちます。
上記をご確認いただいたうえで、まだビルドエラーが解消しない場合は、以下の情報をご提供いただけますと、より具体的にサポートできるかと思います。
- ビルドログのエラーメッセージ(該当箇所のスクリーンショットやテキスト)
- 使用中の Node バージョン(`.nvmrc` や `package.json` の記載内容)
- 設定しているビルドコマンド
- 環境変数の有無(機密情報は伏せていただいて構いません)
お手数をおかけいたしますが、上記をご確認・ご提供いただけますようお願い申し上げます。
何かご不明点がございましたら、遠慮なく
※ 上記は主要部生成+末尾切れのPoC結果です。finish_reason: length を確認し、実務では上限調整または出力制約が必要です。
実装④:音声ルート ※本記事スコープ外
本記事Aの Python CLI PoC スコープ外です。シリーズ横断では Transcribe 1 / Speech 3 の成功実測済み(公式パネル値)。Whisper→要約→TTS パイプラインの詳細は別記事で詳述予定(記事C)。
3,000リクエストの消費実績
公式コントロールパネル(2026-07-22 — 基準数字・当月累計)
| 項目 | 当月累計(公式・全PoC合算) | API種別 | 残り |
|---|---|---|---|
| チャット生成 | 27 | Chat | 2,973 |
ベクトル埋め込み multilingual-e5-large
|
19 | Embeddings | 9,981 |
| 音声の文字起こし | 1 | Transcription | 49 |
| 音声の読み上げ | 3 | TTS | 47 |
| 当月利用料金 | ¥0 | — | — |
部分集計 vs 自作全体(参考)
| 集計スコープ | Chat | Emb | 合計 | 備考 |
|---|---|---|---|---|
| 記事A/B共通FAQ PoC(部分集計) | 7 | 19 | 26 | ping/classify/answer_draft + build_index/search |
| 自作台帳全体(usage_ledger.json) | 28 | 19 | 53 | bench/audio 等を含む全API |
公式枠残数(基準・当月累計): Chat 2,973 / Embeddings 9,981 / 文字起こし 49 / 読み上げ 47
使い倒しロードマップ
| フェーズ | 状態 | 内容 |
|---|---|---|
| 2モデルベンチ | ✅ 完了 | bench_chat 20 回(自作台帳) |
| 音声パイプライン | ✅ 完了 | Transcribe 1 / Speech 3(公式パネル) |
| 記事A/B共通FAQ PoC | ✅ 完了 | 部分集計 Chat7 + Emb19 = 26 |
| FAQ 500件索引 | 📋 今後 | build_index 拡張(+485 Emb 想定) |
| n8n E2Eテスト | 📋 今後 | Webhook 載せ替え (未検証) |
| 負荷試行 | 📋 今後 | 使い倒し本番検証 |
ハマった所
★ gpt-oss-120b は推論モデル:max_tokens が小さいと content が空
疎通テストで max_tokens=50 にしていた時期、HTTP 200・認証成功なのに:
response.choices[0].message.content # → None(空)
レスポンスJSONには reasoning_content があり、推論トークンで max_tokens を使い切っているのに、ユーザー向け content が生成されていない状態でした。
対策(用途別):
| 用途 | max_tokens | 結果 |
|---|---|---|
| 疎通・分類(短文JSON) | 512 前後 | 安定(test_ping.py / classify.py で確認) |
| 返信下書き(長文) | 600 |
主要部生成+末尾切れ(finish_reason: length) |
512 は全用途の万能値ではありません。長文返信は finish_reason を確認し、出力を短く制約するか上限を増やしてください。
max_tokens=50 時代のレスポンス(実行例・トークンマスク) — HTTP 200・認証成功なのに content が空になる現象。実物JSONは保存していないため、構造のみ概念化(値は "..." でマスク)。
{
"id": "...",
"object": "chat.completion",
"model": "gpt-oss-120b",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": null,
"reasoning_content": "...(推論トークンで max_tokens=50 を使い切った内部推論テキスト)..."
},
"finish_reason": "length"
}
],
"usage": {
"prompt_tokens": "...",
"completion_tokens": 50,
"total_tokens": "..."
}
}
非エンジニア事業主向けに簡略化するなら
まず Chat 分類のみ(1 req/件) から。記事A/B共通FAQ PoCで「核」は見えました。Emb 19 は当月累計(記事A/B 合算の FAQ 索引+検索)であり、本記事単体の増分ではありません。
まとめ
-
Python CLI PoC で検証済み: OpenAI SDK の
base_url差し替えだけで、分類・FAQ検索(類似度スコア 0.9554)・返信下書きまでさくらのAI Engine上で動いた(自動送信なし) -
gpt-oss-120b の推論トークン問題: 短文は 512 前後で安定、長文返信は
max_tokens不足で切れる。finish_reasonとreasoning_contentの確認がデバッグの近道 - 当月累計(公式) Chat 27 / Emb 19 / 文字起こし 1 / 読み上げ 3 / ¥0(残 2,973 / 9,981)。記事A/B共通FAQ PoC 部分集計 Chat 7 + Emb 19 = 26 — 差分は公式優先
- n8n 載せ替え・Slack 通知・FAQ の n8n 内統合は設計段階・部分実装(E2E未検証)
- 当月 Chat 消費率 0.90% — 使い倒すための予算設計と初期実測段階。キャンペーン期間中に残枠を計画的に消費する
参考リンク
- さくらのAI Engine 公式
- 利用手順
- 操作ガイド(モデル一覧)
- Qiitaキャンペーン
- PoCリポジトリ: https://github.com/YushiYamamoto/itprodx-sakura-ai-poc
この記事を書いた人✏️@YushiYamamoto
ITPRODX.com代表 / AIアーキテクト
Next.js / TypeScript / n8nを活用した自律型アーキテクチャ設計を専門としています。
付録A:架空FAQ 15件(抜粋)
全15件はリポジトリの faq_data.py を参照。
| id | category | question |
|---|---|---|
| 6 | 技術 | Netlifyのビルドが失敗する場合の確認ポイントは? |
| 9 | 請求 | 請求書の発行日と支払期限を教えてください。 |


