Felo API PlatformFelo API Platform

サポート

サポートチケットを根拠ある返信草稿に振り分ける

チケットをマスキングし、Chat で意図と優先度を判断し、ドキュメントから回答 passage を検索し、公開インシデントの会話を確認し、人が承認できるルーティング付き返信草稿を残します。

所要時間20 分レベル中級更新日2026-10-30
対象サポート責任者Helpdesk Agentサポートエンジニア

プロンプトをコピー

角括弧の項目を置き換えて、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 呼び出しを表示
  1. マスキングしてから分類する

    コードでメール、電話、注文番号などの 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": "…" }
      ]
    }
  2. ドキュメント証拠を検索する

    サポート知識ベース 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
      }
    ]
  3. 公開インシデントを確認する

    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/..."
      }
    ]
  4. ルーティングを決めて返信を組み立てる

    ルールでキューを決め、証拠、次の一手、返信草稿をまとめます。

    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)
  5. トリアージを記録する

    トリアージ結果を 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 検索は従量制クレジットです。現在の料金は料金ページにあり、プロンプトは使う前に確認を求めます。

関連ワークフロー

自分の形に変える

同じレシピを、実際の進め方に合わせて調整します。

分類だけ行う

返信は作らず、意図、優先度、推奨キューだけを出力します。

チケットシステムに接続する

自社 webhook でトリアージパッケージをチケットに書き込み、自動送信はしません。

知識ベース回答に昇格する

ドキュメントにヒットしたら、サポート知識ベースのワークフローでより完全な回答を作ります。

このレシピの背景にあるリファレンスと、次に見るべきページ。