
こんにちは。kickflow CREチームの西山です。
先日、テクニカルサポートチームがDifyを活用し、AIヘルプデスクを立ち上げた経緯をご紹介しました。
しかし、AIを導入して終わりではありません。ユーザーに安心して使ってもらうためには、情報の正確さや継続的な運用を支えるための技術的な仕組みが必要になります。
今回はCREの視点から、裏側の技術的な仕組みづくりを解説します。
AIヘルプデスクの仕組みと見えてきた課題
AIヘルプデスクはLLMアプリケーション開発プラットフォームであるDifyを使って構築し、kickflowのヘルプセンターに埋め込んでいます。
現在、Difyが参照するデータには既存のヘルプ記事に加えて、Zendeskで対応済みの過去のお問い合わせを整形してナレッジ化したものを利用しています。
実装中や実際に運用を始めるといくつかの課題が明らかになってきました。たとえば、意図したフォーマットで回答が生成されないケースや、ナレッジの更新作業の手間、WebhookやAPIといった複雑な質問に汎用プロンプトでは十分に対応できないといった問題です。
こうした課題を解消し、AIヘルプデスクを「安心して使ってもらえる仕組み」に育てていくために、CREがエンジニアリング面でサポートを行いました。
AIヘルプデスクの立ち上げでCREが支援したこと
ヘルプセンターの記事をナレッジ化する
以下の記事でも紹介しましたが、kickflowのヘルプセンターの記事は Markdown で執筆をする仕組みを構築していて GitHub で管理しています。
Dify のナレッジのデータソースには「テキストファイル」「Notion」「ウェブサイト」から選べます。ヘルプセンターはインターネットに公開されているので「ウェブサイト」を選択してクローリングによるナレッジ化ができますが、 Markdown で管理していたこともあり「テキストファイル」を選択しました。
ヘルプ記事は日々更新されるため、Difyのナレッジもアップデート追従する必要があります。
記事を管理しているリポジトリでは main ブランチに Markdown ファイルがマージされると、 GitHub Actions から Zendesk Guide に反映するワークフローを組んでいます。その仕組みにあわせて、マージされたタイミングで Dify のナレッジにも反映するよう対応しました。
これにより常に新しいヘルプの情報でAIが回答できるようになっています。
サポートの問い合わせ対応を効率よくナレッジ化する
AIヘルプデスクの初期構築時には過去のサポートチケットを Zendesk API から抽出し、やりとりに含まれている個人情報など、生成AIに学習させるべきではない内容を除外したうえで、手動で Dify に登録していました。 継続して運用するにはこの作業を自動化し、新しいサポートチケットも同じように反映する必要があります。
一方で、チケットの会話をそのままナレッジとして利用するには、いくつかの課題があると感じていました。
- 問い合わせにはユーザー固有の環境や状況に依存する内容が多く、汎用的ではない
- チケット全体を含めると余計な情報が多い
- ビジネス的な定型文や引用のような、メールに含まれる重複した情報
そのため、チケットの中でナレッジとして活かせる情報を 「ユーザーが解決したい課題」 と 「サポートから案内した解決方法」 の2点に絞り、これを抽出するプロンプトを設計しました。
現在もチューニングを続けていますが、以下のようなルールでサポートチケットの会話ログから Q&A 形式の学習データを生成しています。
テクニカルサポートの会話の履歴から、AIサポートエージェント向けの学習データを生成します。
以下に定義したルールをもとに、チケットのやりとりからユーザーの課題(Q)とその解決方法(A)を抽出・生成してください。
## 学習データの生成に関するルール
- 『チケットのやりとり』を読み込んで、ユーザーの課題(Q)とその解決方法(A)を抽出します。
- 会話はマーカーで区切られ、最初のマーカーより上に記載されている内容が問い合わせ本文です。
- 基本的には最初の問い合わせにユーザーの解決したい課題が記載されています。
- ただし、詳細にヒアリングして課題を適切に突き止めることもあるため、すべてのやり取りを注視してください。
- ユーザーの課題を抽出する際は以下の点について考慮・注意をしてください。
- 氏名、会社名、メールアドレス、電話番号などのような個人情報は絶対に含まないよう除外してください。
- 簡潔にまとめすぎると、学習データとして有効に機能しない可能性があるので気をつけてください。
- 問い合わせをしたユーザー固有の名称や用語(例えば「申請理由のフィールド」など)と思われる場合は、汎用的な言い回し(例えば「任意のフィールド」など)に置き換えてください。
- 解決方法には、課題を解決するためにユーザーが行うべき手順を簡潔に記載してください。
- 抽出したユーザーの課題(Q)とその解決方法(A)を、『出力フォーマット』で規定した通りに出力します。
- 最後に「学習データ化の可否」について、あなたの判断をコメントを記載してください。
- テクニカルサポートチームは、この可否コメントも参考に学習データとするかを判断します。
:
このプロンプトによる処理は、Zendesk Support のチケットのステータスが「終了」となった後に GitHub Actions によって自動で行われます。Zendesk からは直接 GitHub の Webhook を実行できないため、Zapier経由でGitHubにチケットIDを渡しています。
GitHub Actions 側ではチケットIDをもとに Zendesk API からサポートとのやりとりをすべて取得し、AI に要約・抽出させて以下のようなフォーマットで出力しています。
## ユーザーの課題
{抽出したユーザーの課題(Q)のテキスト}
## 解決方法
{抽出した解決方法(A)のテキスト}
## 学習データ化の可否
**{可 / 否}** - {判断についてひとこと}
理由:
- {理由を簡潔に箇条書き}
- {理由を簡潔に箇条書き}
:
出力内容には「ユーザーの課題」「解決方法」に加えて「学習データ化の可否」を含めています。これは、要約されたチケットの内容をDifyのナレッジとして取り扱うべきかを判断する際の参考情報のひとつにするため、以下のような指示をプロンプトに加えて出力させています。
- 「学習データ化の可否」について、あなたの判断をコメントを記載してください。
- テクニカルサポートチームは、この可否コメントも参考に学習データとするかを判断します。
- 次のような内容だった場合は「否」としてコメントしてください。
- テクニカルサポートチームの範疇外だった場合
- 他社のサービスに関する無関係な問い合わせだった
- 契約に関する問い合わせで別チームにエスカレーションした
- テクニカルサポートチームが返信をしても、ユーザーからの応答がない場合があります。
- その場合は返信がない旨を記載してください。
- 解決に繋がったかはわからないが学習データとして有益と判断できる場合は、この会話を推薦してください。
:
チケットごとに出力した要約データはMarkdownファイルとしてコミットされ、プルリクエスト(PR)が自動で作成されます。テクニカルサポートチームは、PRのレビューと必要に応じて要約の修正を行い、マージをするフローを組んでいます。
sequenceDiagram
actor テクサポチーム
rect rgba(255, 0, 255, 0.2)
opt チケットのステータスが完了に移行したら実行
Zendesk-->>Zendesk: 自動化によりWebhookを実行
Zendesk->>GitHub: Webhook(Zapierを中継)
GitHub-->>+GitHub: Run workflow
Note right of GitHub: チケットごとに要約を作成
GitHub->>Zendesk: Zendesk APIからチケットを取得
Zendesk->>GitHub: Response
GitHub->>Claude: チケットから「ユーザー課題」と「解決方法」を抽出
Claude->>GitHub: Response
GitHub-->>GitHub: Markdownファイルとして保存
GitHub-->>-GitHub:プルリクエストを自動作成
end
end
rect rgba(191, 223, 255, 1)
テクサポチーム->>GitHub: プルリクエストをレビュー
Note right of GitHub: ナレッジ化する場合は @claude にメンション
end
rect rgba(255, 0, 255, 0.2)
opt プルリクエストのコメントでメンションされたら実行
GitHub-->>Claude: Mention to `@claude`
Claude-->>+Claude: Run workflow
Note right of Claude: 公開用ナレッジファイルを更新
Claude-->>Claude: 要約ファイルとプロンプトをもとに、公開用ナレッジのMarkdownに反映
Claude-->>-GitHub: Feedback
end
end
rect rgb(191, 223, 255)
テクサポチーム->>GitHub: プルリクエストをレビュー
Note right of GitHub: 問題なければマージ
end
rect rgba(255, 0, 255, 0.2)
opt プルリクエストがマージされたら実行
GitHub-->>GitHub: Run workflow
Note right of GitHub: Difyのナレッジを更新
GitHub->>Dify: 更新された公開用ナレッジをアップロード
Dify->>GitHub: Response
end
end
一連のフローの中でテクニカルサポートチームが青い網掛けの部分で2回登場しますが、基本的にはナレッジとしてマージする判断のみで運用が回っています。 要約やマージ処理はAIに委ね、担当者の負荷を抑えるように考慮しています。
ハルシネーションに対応する
ナレッジを整備しても、AIが常に期待通りの回答を返すとは限りません。
実際の運用ではハルシネーションが発生し、AIに回答させないことを指示するなどの細かいプロンプトのチューニングを行っていました。
中でもテクニカルサポートチームが最も苦労していたひとつが、回答の参考にしたヘルプセンターの記事の出典元URLです。
GitHubのリポジトリで管理している記事のMarkdownファイルは、ヘルプセンターの記事のIDとロケール(公開言語)をもとに {記事ID}-{ロケール}.md の規則でファイル名が決まっています。
https://support.kickflow.com/hc/{ロケール}/articles/{記事ID}
当初、出典元URLは記事のファイル名から生成するようなプロンプトが定義されていましたが、あるタイミングからAIが 『存在しないURL』 を回答するようになったとフィードバックされることが多くなりました。
突如発生するようになった原因はわからなかったのですが、Difyがナレッジから知識検索を行った結果、複数のMarkdownファイルに該当する場合に、存在しない記事のIDを生成しているようでした。
記事データのMarkdownファイルには、ツールを介してZendeskに反映する際に必要となるメタ情報をFrontmatterとしてMarkdown内に保持しています。
--- title: フォームの自動計算フィールドで使用可能な変数や関数を教えて下さい : html_url: https://support.kickflow.com/hc/ja/articles/360053280013 --- :
この中の html_url というプロパティに公開URLを含んでいるので、直接 html_url を参照するプロンプトに修正してもらったところ、出典元URL問題は収束しました。
1. 知識検索の結果として提供された各チャンクから、回答に関連する内容と正確な記事URLを特定します。 * 出典としたいのは、`dataset_name`が「xxxx」であるデータセットの知識のみです。他のデータセットは無視してください。 * 上記のデータセットのチャンクの本文中から、`html_url`(例: `https://support.kickflow.com/hc/ja/articles/43657146304409`)を正確な記事URLとして抽出します。 * URLは絶対に生成、変更、または推測しないでください。必ず本文内にある`html_url`の値をそのまま使用してください。 * もし、回答に利用する具体的な知識内容を持つチャンクから有効な`html_url`が一つも抽出できない場合でも、次のステップに進んでください。
今回の出典元URLのようにAIのレスポンスに含めたい情報がナレッジに含まれる場合は、「生成させずに参照する」ことがプロンプト設計において有効であり、この点がAI活用の学びにつながりました。
おわりに
今回はテクニカルサポートチームとAIヘルプデスクの導入に伴走した事例をご紹介しました!
AIを活用した取り組みにおいてナレッジ更新の自動化は、どのAI活用プロジェクトにも共通する課題であり、仕組み化することが継続運用の鍵になります。今回紹介した事例がみなさんの参考になれば幸いです。
kickflowでは、最新技術を活用してプロダクトの価値向上に挑戦する仲間を募集しています!ご興味のある方は、ぜひ採用サイトをご覧ください。