2026-07-22に公開した前編(Python CLI PoC)では、さくらのAI Engine上で問い合わせの分類→FAQ検索→返信下書きまでを検証した。一方、n8nワークフローへの載せ替えは「設計段階・部分実装・E2E未検証」のまま残っていた。
本記事はその続きとして、n8nでWebhook受信から下書きペイロード返却までを一気通貫(E2E)で動かすための手順を、初心者向けに整理する。顧客への自動送信は行わず、オペレーターが確認する下書きまでがゴールである。
この記事で扱うE2Eの範囲
| レイヤ | 前編の状態 | 本記事のゴール |
|---|---|---|
| Python CLI | 検証済み | 変更なし(FAQ索引・検索の参照実装) |
| n8n Webhook | 部分実装 | POST受信→分類→下書き→JSON応答まで通す |
| FAQ Embeddings | Python委譲 | 当面は委譲のまま(後述の拡張案を記載) |
| Slack/Telegram通知 | 未接続 | 設計のみ(下書きペイロードの出力まで) |
「E2E」とは、外部システムがWebhookにPOSTした問い合わせが、n8n内で処理され、下書き付きのJSONが返るところまでを指す。メール自動返信や顧客向け送信は含めない。
全体アーキテクチャ
[問い合わせフォーム / テストcurl]
│ POST { "inquiry": "..." }
▼
[n8n Webhook]
▼
[Extract Inquiry] → [Usage Guard] → [Classify (Chat API)]
▼
[Parse Classification] → (エラーなら即返却)
▼
[Wait Rate Limit] → [Usage Guard] → [Draft Answer (Chat API)]
▼
[Format Draft Notification] → [Output Draft Payload]
▼
JSON応答(status / classification / draft / autoSend:false)
前編と同様、APIエンドポイントは https://api.ai.sakura.ad.jp/v1/、Chatモデルは gpt-oss-120b を使う。OpenAI SDK互換のため、n8nのHTTP Requestノードでそのまま呼び出せる。
事前準備
1. さくらのAI Engineのトークン
さくらのAI Engine 利用手順に従い、APIトークン(ID:secret 形式)を発行する。無償プランでもクレジットカード登録が必要だが、超過時の自動課金はない(レートリミットで停止)。
2. n8nの認証情報(Bearer Auth)
n8nのCredentialsで HTTP Bearer Auth を作成する。
| 項目 | 値 |
|---|---|
| Token | さくらのAI Engine APIトークン(ID:secret) |
トークンはワークフローJSONや記事に直書きしない。n8nのCredentialにのみ保存する。
3. ワークフローJSONのインポート
前編PoCリポジトリに同梱の n8n/inquiry-triage.workflow.json をn8nにインポートする。
- リポジトリ: https://github.com/YushiYamamoto/itprodx-sakura-ai-poc
- パス:
n8n/inquiry-triage.workflow.json
インポート後、Classify Inquiry と Draft Answer ノードのCredentialを、上で作成したBearer Authに差し替える。
ノード構成の解説
Webhook Inquiry
| 設定 | 値 |
|---|---|
| HTTP Method | POST |
| Path |
itprodx-sakura-inquiry(任意・一意にする) |
| Response Mode | Last Node |
受け取るJSONの例:
{
"inquiry": "請求書の支払期限を教えてください"
}
message や text キーにも対応するよう、次の Extract Inquiry ノードで正規化する。
Usage Guard(月次リクエスト上限)
前編の usage_ledger.py と同様、PoC中の暴走防止として 月2,900リクエスト で停止する。n8nの $getWorkflowStaticData('global') に月次カウンタを保持する。
const LIMIT = 2900;
const staticData = $getWorkflowStaticData('global');
const monthKey = new Date().toISOString().slice(0, 7);
if (staticData.month !== monthKey) {
staticData.month = monthKey;
staticData.total = 0;
staticData.byPurpose = {};
}
const total = Number(staticData.total || 0);
if (total >= LIMIT) {
throw new Error(`UsageLimitExceeded: monthly ${total} >= ${LIMIT}`);
}
return items;
公式パネルの Chat 3,000 / Embeddings 10,000 とは別枠の自作安全弁である。無償枠の残数はさくらのAI Engineコントロールパネルを正とする。
Classify Inquiry(HTTP Request)
| 項目 | 値 |
|---|---|
| Method | POST |
| URL | https://api.ai.sakura.ad.jp/v1/chat/completions |
| Authentication | HTTP Bearer Auth |
リクエストボディの要点:
{
"model": "gpt-oss-120b",
"messages": [
{
"role": "system",
"content": "…JSONのみ返す分類器プロンプト…"
},
{
"role": "user",
"content": "問い合わせ本文"
}
],
"temperature": 0,
"max_tokens": 512,
"response_format": { "type": "json_object" },
"stream": false
}
max_tokens: 512 を推奨する理由: gpt-oss-120b は推論モデルで、上限が小さいと content が空(reasoning_content のみ)になることがある。前編で max_tokens=50 時代に疎通成功なのに本文が空だった事例がある。JSON短文の分類でも512前後の余裕を持たせる。
Wait Rate Limit
分類と下書き生成の間に 1.5秒のWait を挟む。連続API呼び出しによるレートリミット回避のため。本番では公式のレート制限値に合わせて調整する。
Draft Answer(HTTP Request)
分類結果をコンテキストに、返信下書きを生成する。現行ワークフローではFAQ Embeddings検索は未接続で、分類結果のみを渡す簡易版である。
| 項目 | 値 |
|---|---|
| max_tokens | 512(短文下書き向け。長文は増やすか出力制約を追加) |
| temperature | 0.3 |
Output Draft Payload
最終的にWebhook呼び出し元へ返すJSONの形:
{
"status": "draft_ready",
"inquiry": "請求書の支払期限を教えてください",
"classification": {
"category": "請求",
"urgent": false
},
"draft": "(生成された返信下書き本文)",
"note": "下書きのみ。人間が確認・編集してから送信すること。",
"autoSend": false
}
autoSend: false は固定。自動送信を有効にしない設計である。
E2E動作確認手順
1. ワークフローを有効化する
n8nでワークフローを Active にする。本番運用前にテスト環境で十分検証すること。
2. Webhook URLを取得する
n8nのWebhookノードに表示されるURL(例: https://<your-n8n>/webhook/itprodx-sakura-inquiry)を控える。
3. curlでテスト送信
curl -sS -X POST 'https://<your-n8n>/webhook/itprodx-sakura-inquiry' \
-H 'Content-Type: application/json' \
-d '{"inquiry":"Netlifyのビルドが失敗しています。ログにNode version mismatchと出ます。"}'
4. 期待される結果
| 確認項目 | 期待値 |
|---|---|
| HTTPステータス | 200 |
status |
draft_ready |
classification.category |
見積 / 技術 / 請求 / その他 のいずれか |
draft |
空でない日本語テキスト |
autoSend |
false |
5. n8nの実行ログを確認する
各ノードの入出力を開き、以下を確認する。
- Classify Inquiry の
choices[0].message.contentにJSONが入っているか -
finish_reasonがlengthでないか(切れている場合はmax_tokensを調整) - Usage Guard の月次カウンタが想定どおり増えているか
FAQ EmbeddingsをE2Eに組み込む拡張案
現行ワークフローはFAQ検索をPython PoCに委譲している。E2Eを完成させるには、次のいずれかを選ぶ。
| 方式 | 概要 | 向いているケース |
|---|---|---|
| A. PythonをHTTPラップ |
search.py をFastAPI等で包み、n8nからHTTP Requestで呼ぶ |
前編の実装をそのまま再利用したい |
| B. n8n Sub-workflow | FAQ検索を別ワークフローに切り出し、Execute Workflowで呼ぶ | 責務分離・テストしやすさ重視 |
| C. n8n内完結 | Embeddings API + Codeノード + 外部DB | Python依存を減らしたい |
前編では架空FAQ15件でコサイン類似度 0.9554 のヒットを確認済みである。本番相当の500件索引に拡張する場合、Embeddings APIは1FAQあたり1リクエスト消費する(月10,000枠の設計が必要)。
リクエスト予算の目安(1件あたり)
| ステップ | API種別 | リクエスト数 |
|---|---|---|
| 分類 | Chat | 1 |
| 下書き(FAQ未接続版) | Chat | 1 |
| FAQ検索(将来接続時) | Embeddings | 1 |
| 合計(現行) | Chat 2回/件 |
月3,000 Chat枠であれば、理論上は約1,500件/月まで処理可能だが、テスト・再実行・ベンチマーク分を差し引いた運用上限を別途決めること。前編では自作台帳の安全弁を2,900(全API合算)に設定している。
実装チェックリスト
- さくらのAI Engine APIトークンをn8n Credentialに登録した(コード・記事に直書きしない)
-
inquiry-triage.workflow.jsonをインポートし、Credentialを差し替えた -
Webhookに
{"inquiry":"..."}をPOSTし、draft_readyが返ることを確認した -
autoSendが常にfalseであることを確認した -
分類エラー時に
status: errorが返ることを確認した - Usage Guard が月次2,900で停止することを確認した
- 公式コントロールパネルと自作カウンタを週1回突合する手順を決めた
- FAQ Embeddings接続方式(A/B/C)を選び、次フェーズのタスクにした
失敗パターン
パターン1:推論モデルで content が空になる
gpt-oss-120b は推論トークンを消費する。max_tokens が小さいとHTTP 200でも content: null になる。
→ 対策:分類・短文でも 512前後 を確保する。finish_reason と reasoning_content をログで確認する。
パターン2:Webhookは200だが下書きが途中切れ
finish_reason: length で末尾が欠ける。
→ 対策:出力を箇条書き3点以内に制約するか、max_tokens を増やす。前編の answer_draft.py でも600では切れた事例がある。
パターン3:トークンをワークフローJSONに直書きしてGitにコミット
→ 対策:n8n Credentialのみに保存。リポジトリにはプレースホルダ(REPLACE_WITH_CREDENTIAL_ID)のままにする。
パターン4:下書きをそのまま顧客に自動送信してしまう
→ 対策:autoSend: false を固定し、通知先はオペレーターのみ。Human-in-the-Loopを設計段階から入れる。
パターン5:自作カウンタと公式パネルの数字がずれる
→ 対策:課金・枠の判断は公式パネルを正とする。自作台帳は暴走防止用。
まとめ
- 前編のPython PoCを土台に、n8nで Webhook→分類→下書き→JSON応答 のE2Eパイプラインを組める
- 同梱ワークフロー
inquiry-triage.workflow.jsonは、Bearer Auth設定とActive化で即テスト可能 - FAQ Embeddingsは当面Python委譲。HTTPラップまたはSub-workflowで段階的に統合する
- 自動送信は行わず、
autoSend: falseの下書きペイロードまでが安全なゴール -
gpt-oss-120bの推論トークン問題に注意し、max_tokensとfinish_reasonを必ず確認する
次のステップとして、FAQ検索ノードを接続し、前編で得た類似度スコア0.95台の検索品質をn8n上でも再現する。
参考リンク
- 前編:さくらのAI Engineで問い合わせ一次対応PoC
- さくらのAI Engine 公式
- 利用手順(マニュアル)
- Qiitaキャンペーン:3,000リクエスト使い切りチャレンジ
- PoCリポジトリ: https://github.com/YushiYamamoto/itprodx-sakura-ai-poc
- n8nワークフローJSON: https://github.com/YushiYamamoto/itprodx-sakura-ai-poc/blob/main/n8n/inquiry-triage.workflow.json
この記事を書いた人✏️@YushiYamamoto
ITPRODX.com代表 / AIアーキテクト
Next.js / TypeScript / n8nを活用した自律型アーキテクチャ設計を専門としています。
日々の自動化の検証結果や、ビジネス側の視点(ROI等)に関するより深い考察は、以下の公式サイトおよびnoteで発信しています。
