サポート
サポートチケットを根拠ある返信草稿に振り分ける
チケットをマスキングし、Chat で意図と優先度を判断し、ドキュメントから回答 passage を検索し、公開インシデントの会話を確認し、人が承認できるルーティング付き返信草稿を残します。
プロンプトをコピー
角括弧の項目を置き換えて、Claude Code、Codex、ターミナルにアクセスできる AI に貼り付けてください。Felo スキルをインストールし、クレジットを使う前に確認し、ワークフローを実行します。
このサポートチケットをトリアージし、人によるレビュー用の返信下書きを作成してください: [氏名、メール、アカウント ID、支払いデータ、その他特定できる情報を削除したチケットを貼り付け]。 製品領域: [製品]。プランまたは地域: [回答に必要な場合のみ]。ドキュメントソース: [ヘルプセンター URL または LiveDoc short_id]。 承認経路: [誰がレビューするか]。エスカレーションキュー: [チームまたはチャネル]。 以下を作成してください。 1. 意図: 1カテゴリと、1文の根拠。 2. 優先度: 緊急 / 通常 / 低。適用したルールも添えてください。 3. 根拠: チケットに答えるドキュメントのパッセージ(タイトルと関連度付き)。 4. インシデント確認: 公開報告が広範な障害を示唆しているか。リンクと日付、または「公開シグナルなし」。 5. ルート: 下書き、顧客への追加情報依頼、エスカレーションのどれか。理由も。 6. 返信下書き: 2〜4文。根拠だけに基づき、依拠したソースを明記する。 ルール: - 氏名、メール、注文番号、トークン、支払いデータを API 呼び出しや出力に含めないでください。編集済みチケットだけから作業してください。 - 取得したドキュメントだけから答えてください。根拠がなければエスカレーションし、推測しないでください。 - 返金、割引、期限、アカウント変更を約束しないでください。それらはエスカレーションへ回してください。 - 公開インシデントの噂はシグナルであり証拠ではありません。投稿と日付を引用してください。 - クレジットを消費する前に、実行予定の呼び出しを示して私の承認を待ってください。 - 返信の送信、チケット更新、アカウント操作はしないでください。下書きをレビュー用に返してください。 - 結果を ./support-triage.md に書き、チャットで要約してください。
コピー内容にはインストール手順と安全ルールも含まれます。
完成するもの
実行時の出力例です。リアルタイム生成ではありません。
トリアージ — 例のチケット
意図
請求 / 更新の質問。顧客はなぜプランが再び更新されたのか尋ねている。
優先度
通常。請求の質問で障害ではなく、顧客はブロックされていない。
根拠
- 「請求に関するよくある質問」 — 更新時期とカード失敗時のリトライ。関連度: 高。
- 「プラン変更」 — 更新間のダウングレード。関連度: 中。
障害チェック
過去48時間に公開報告なし。
ルート
下書き。ドキュメントが質問をカバーしている。
返信下書き
こんにちは。プランは毎月1日に更新され、カード決済に失敗した場合は3日間リトライした後、シートが一時停止されます。カードは請求設定で変更できます。手順は請求に関するよくある質問 にあります。更新日を変更する必要があれば返信してください。請求チームへエスカレーションします。
レビューメモ
承認、編集、または請求キューへのエスカレーションを選んでください。
実際の呼び出し
開発者向け:同じワークフローを実行順に並べたコピー可能なコード。
実際の API 呼び出しを表示
マスキングしてから分類する
コードでメール、電話、注文番号などの PII を除き、Chat で意図と優先度を判断します。
triage.ts const BASE = "https://openapi.felo.ai"; const headers = { Authorization: `Bearer ${process.env.FELO_API_KEY}`, "Content-Type": "application/json", }; async function felo<T>(path: string, body?: unknown): Promise<T> { const res = await fetch(`${BASE}${path}`, { method: body ? "POST" : "GET", headers, body: body ? JSON.stringify(body) : undefined, }); if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`); return res.json() as Promise<T>; } const redact = (text: string) => text .replace(/[\w.+-]+@[\w.-]+\.\w+/g, "[email]") .replace(/\b(?:acct|account|order|invoice)[-_ ]?\w+\b/gi, "[id]") .replace(/\b(?:\d[ -]?){13,19}\b/g, "[card]"); type Ticket = { id: string; product: string; subject: string; body: string; plan?: string; sensitive?: boolean; }; const ticket: Ticket = { id: "T-1042", product: "Acme Workspace", subject: "Why did my plan renew?", body: "I was charged again and cannot find the renewal date on my account.", plan: "team", }; const summary = redact(`${ticket.subject}\n\n${ticket.body}`).slice(0, 1500); type ChatData = { data: { answer: string; resources: { link: string; title: string; snippet: string }[]; }; }; const triage = await felo<ChatData>("/v2/chat", { query: `Classify this support question by intent and priority, and list what is missing:\n\n${summary}`, }); console.log(triage.data.answer);出力例
{ "answer": "Intent: billing / renewal question. Priority: normal. Missing: the renewal date the customer expects.", "resources": [ { "link": "https://example.com/billing-faq", "title": "Billing FAQ", "snippet": "…" } ] }ドキュメント証拠を検索する
サポート知識ベース LiveDoc から関連 passage を検索し、見出しとスコアを残します。
triage.ts type Hit = { title: string; content: string; score: number }; const KB = process.env.SUPPORT_LIVEDOC_ID!; // the help-center LiveDoc short_id const retrieved = await felo<{ data: Hit[] }>( `/v2/livedocs/${KB}/resources/retrieve`, { query: summary }, ); const hits = retrieved.data.filter((hit) => hit.score >= 0.7).slice(0, 4); if (hits.length === 0) { console.log("No grounded evidence: escalate instead of drafting"); }出力例
[ { "title": "Billing FAQ", "content": "Your plan renews on the first of the month; a failed card retries for three days…", "score": 0.91 } ]公開インシデントを確認する
X で製品名と「障害/ダウン」などの語を検索し、48時間に限定して公開シグナルとして使います。
triage.ts type XData = { data: { tweets: { content: string; author: { username: string }; created_at: string; url: string; }[]; }; }; const since = Math.floor((Date.now() - 48 * 60 * 60 * 1000) / 1000); const incident = await felo<XData>("/v2/x/tweet/search", { query: `"${ticket.product}" (outage OR "not working" OR error)`, limit: 20, since_time: since, }); const reports = incident.data.tweets.map((post) => ({ text: post.content, handle: post.author.username, at: post.created_at.slice(0, 10), link: post.url, }));出力例
[ { "text": "Workspace login is failing for my whole team.", "handle": "example_user", "at": "2026-10-30", "link": "https://x.com/example_user/status/..." } ]ルーティングを決めて返信を組み立てる
ルールでキューを決め、証拠、次の一手、返信草稿をまとめます。
triage.ts type Route = "draft" | "ask-customer" | "escalate"; function decideRoute(): { route: Route; reason: string } { if (hits.length === 0) { return { route: "escalate", reason: "no grounded evidence in the knowledge base" }; } if (ticket.sensitive) { return { route: "escalate", reason: "an account or billing action is requested" }; } if (reports.length >= 3) { return { route: "escalate", reason: "public reports suggest a wider issue" }; } if (!ticket.plan) { return { route: "ask-customer", reason: "the plan is needed to answer the question" }; } return { route: "draft", reason: "the documentation covers the question" }; } const decision = decideRoute(); const packet = [ `# Triage — ${ticket.id}`, "", `Route: ${decision.route} (${decision.reason})`, "", "## Intent and priority", triage.data.answer, "", "## Evidence", ...hits.map((hit) => `- ${hit.title} (score ${hit.score})`), "", "## Public signal", ...(reports.length ? reports.map((report) => `- @${report.handle} (${report.at}): ${report.text} — ${report.link}`) : ["No public reports in the last 48 hours."]), "", "## Reply draft", "[Written by the host Agent from the evidence above — human review required]", ].join("\n"); console.log(packet);出力例
# Triage — T-1042 Route: draft (the documentation covers the question) ## Intent and priority Intent: billing / renewal question. Priority: normal. ## Evidence - Billing FAQ (score 0.91)
トリアージを記録する
トリアージ結果を LiveDoc に書き、振り返りと監査に使います。
triage.ts type LiveDocData = { data: { short_id: string } }; const log = await felo<LiveDocData>("/v2/livedocs", { name: "Support triage log", description: "One document resource per triaged ticket", }); await felo(`/v2/livedocs/${log.data.short_id}/resources/doc`, { title: `Triage — ${ticket.id}`, content: packet, }); console.log(log.data.short_id); // reuse it for the next ticket出力例
{ "short_id": "PvyKouzJirXjFdst4uKRK3" }
含まれるもの
- マスキング後の意図と優先度分類
- スコア付きのドキュメント証拠
- 公開インシデント確認
- ルーティング付き返信草稿
- トリアージ記録 LiveDoc
やらないこと
- 返信送信、チケットクローズ、アカウント操作はしない
- パスワード、支払い情報、完全な顧客データを API に送らない
- ドキュメントにヒットしない場合は必ずエスカレーション
- X は公開インシデントのシグナルであり、サポートシステムの状態ではない
- 優先度ルールは自分で定義し、モデルは提案のみ
よくある質問
どのチケットデータを送れますか?
マスキング後の製品、問題描述、プラン、地域など必要な文脈だけを送ります。パスワード、支払い情報、完全な住所、私的な会話は送らないでください。
返信はどこから来ますか?
ドキュメント LiveDoc から検索した passage から来ます。ヒットしない場合、ワークフローは推測せずエスカレーションします。
直接返信やクローズをしますか?
いいえ。草稿とルーティングだけを作ります。送信とクローズは人か自社システムが行います。
優先度はどう決まりますか?
自分で定義したルールで決まります。モデルは提案と理由を出し、最終優先度はルーティングルールが確認します。
ドキュメントにない場合は?
人手キューにエスカレーションし、トリアージパッケージに「未カバー」と記録します。
1回の費用は?
Chat、検索、X 検索は従量制クレジットです。現在の料金は料金ページにあり、プロンプトは使う前に確認を求めます。
関連ワークフロー
自分のドキュメントでサポート質問に答える
LiveDoc を作成し、ヘルプセンターと社内 runbook を読み込み、チケットに答える passage を検索し、その証拠だけからモデルに回答させます。
ワークフローを開くコンテンツSNSの会話を週次のリスニングブリーフに変える
直近1週間の自社ブランドと競合に関する X 投稿を検索し、出典付きの Web 文脈を加え、その背景ページを読み、毎号をアーカイブして翌週は既知の内容から始めます。
ワークフローを開くリサーチ1つの問いを、出典付きリサーチブリーフに変える
Web に問いを投げ、回答の根拠ページを Markdown で取得し、X の実務者の声を加え、最後に証拠をあとから検索できる LiveDoc に残します。
ワークフローを開く自分の形に変える
同じレシピを、実際の進め方に合わせて調整します。
分類だけ行う
返信は作らず、意図、優先度、推奨キューだけを出力します。
チケットシステムに接続する
自社 webhook でトリアージパッケージをチケットに書き込み、自動送信はしません。
知識ベース回答に昇格する
ドキュメントにヒットしたら、サポート知識ベースのワークフローでより完全な回答を作ります。
さらに進む
このレシピの背景にあるリファレンスと、次に見るべきページ。