【ITニュース解説】I Built an AI Pipeline That Reads Support Emails and Drafts Replies (Here's What Actually Broke, and the Math on Whether It's Worth It)
2026年09月19日に「Dev.to」が公開したITニュース「I Built an AI Pipeline That Reads Support Emails and Drafts Replies (Here's What Actually Broke, and the Math on Whether It's Worth It)」について初心者にもわかりやすく解説しています。
ITニュース概要
AI(Claude API)でサポートメール自動分類と返信文案作成システムを開発。AIがメールを処理し、人間が確認・承認。OAuthやメール解析の課題を解決。手作業より大幅なコスト削減を実証し、効率的な顧客対応に貢献。複雑な問い合わせは人間が担当する。
ITニュース解説
中小企業が抱えるカスタマーサポートメール対応の課題として、日々の業務の中で同じような定型的な質問への対応に多くの時間が割かれている現状がある。この課題を解決するため、AIを活用した自動化パイプラインが構築された。このシステムは、Gmailから顧客からの未読メールを取得し、AIがメールの内容を分類・分析して返信のドラフトを自動生成する。その後、生成された分類結果と返信ドラフトは社内コミュニケーションツールのSlackに通知され、最終的に担当者がその内容を確認し、必要に応じて編集・承認してから顧客へ送信するという流れで運用される。これにより、サポート担当者はメールの内容をゼロから理解し、返信を作成する手間から解放され、より迅速かつ効率的に顧客対応を行えるようになる。
このパイプラインの具体的な仕組みは次のようになる。まず、Pythonで書かれたスクリプトがGoogleのGmail APIを通じて、指定されたGmailアカウントの受信トレイから未読メールを取得する。この際、is:unread in:inboxというクエリを使用することで、未読メールのみを効率的に絞り込む。次に、取得したメールから本文のテキストを抽出する。この処理は、メールがmultipart/alternativeという複雑な形式で送られてくることが多いため、単純ではない。この形式では、同じ内容がプレーンテキストとHTMLの両方で含まれており、さらに複数のパートが入れ子になっていることもあるため、不要なHTMLタグや余分な情報を排除し、純粋な本文を正確に抽出するための工夫が必要となる。
抽出されたメール本文は、AIモデルであるClaude APIに送られる。ここで特徴的なのは、AIに単なる自由形式のテキスト生成をさせるのではなく、「構造化出力」という機能を利用している点だ。これは、開発者が事前にJSONスキーマというデータ形式の「設計図」をAIに渡すことで、AIはその設計図に厳密に従った形式で結果を返すことを保証する仕組みである。このアプローチにより、AIの出力が途中で途切れたり、余分な文章が含まれたり、特定の情報のデータ型が予測不能になったりするといった、従来のAIテキスト生成で起こりうる多くの問題を回避できる。具体的には、メールのカテゴリ(例:営業問い合わせ、技術サポート、請求関連など)、優先度(高・中・低)、より具体的な質問の意図(例:SAML認証のリダイレクトループ障害など)、そしてAIが自身の分類にどれだけ自信を持っているかを示す確信度(0〜100)といった情報が構造化されたデータとして出力される。さらに、顧客への返信ドラフトも同時にAIによって生成される。
AIが生成した分類結果と返信ドラフトは、SlackのBlock Kitと呼ばれるリッチな表示形式のカードとして、特定のチャネルに投稿される。このSlackカードには、メールの分類情報、AIが作成した返信ドラフト、そして担当者に関する情報が含まれる。重要な設計思想として、AIが直接「このメールはAさんが担当するべき」と判断するのではなく、AIはあくまでカテゴリと確信度を出力し、それに基づいてシステム側のプログラムが担当者を決定するという点がある。これは、AIが架空の人物を指名したり、担当者の名前の表記揺れによってシステムが誤作動したりするリスクを防ぐためである。担当者の割り当てのように現実世界に影響を与える重要な判断は、人間の管理下にある決定的なプログラムコードで行うことで、システムの安全性と信頼性を高めている。Slackへの通知後、システムは処理済みのメールをGmail上で「既読」とマークし、さらに処理結果をSQLiteデータベースに記録することで、同じメールが重複して処理されることを防ぐ。最終的に、担当者はSlackカード上の承認ボタンをクリックするか、直接Gmailでドラフトを編集・送信することで、顧客への返信が完了する。このフローでは、AIがドラフトを作成しても自動で送信されることはなく、必ず人間が内容を確認し承認するプロセスを挟むことで、誤送信のリスクを最小限に抑えている。
開発を進める中で、いくつかの予期せぬ技術的な課題に直面し、その解決策を実装した。一つ目は「OAuthスコープの罠」と呼ばれるGmail API利用時の認証に関する問題である。当初、メールの読み取りと送信に必要なgmail.readonlyとgmail.sendという権限(スコープ)を要求していた。しかし、メールを「既読にする」機能を追加するためには、より広範な書き込み権限を含むgmail.modifyスコープが必要となった。ここで、gmail.readonlyとgmail.modifyの両方を同時に要求すると、Googleは内部的に重複するgmail.readonlyスコープをサイレントに無視し、gmail.modifyのみを許可する。この状態ではすぐに問題は発生しないが、認証トークンが自動更新される際に「Scope has changed(スコープが変更された)」というエラーが発生した。この問題の解決策は、必要なすべての操作をカバーする、最も狭い「単一の」スコープ(この場合はgmail.modify)のみを要求することであった。
二つ目は「text/plainがtext/htmlに敗れる」というメール本文抽出に関するバグである。前述の通り、メールはプレーンテキストとHTMLの両方の形式を含むmultipart/alternative構造を持つことがある。当初の本文抽出ロジックは、メールの構造を再帰的にたどり、各階層でプレーンテキストが見つからなければHTMLにフォールバックするというものだった。しかし、メールの送信元によっては、HTML部分がプレーンテキスト部分よりも構造上で先に現れることがあり、この場合、たとえ階層の奥深くにプレーンテキストが存在していても、手前のHTMLが優先されて抽出されてしまうという問題が発生した。このバグを修正するために、メールのツリー構造全体を二段階で検索するアプローチが採用された。まず、ツリー全体をくまなく探してプレーンテキストを探し、それが全く見つからない場合にのみ、二度目の検索でHTMLを探すようにしたことで、常に優先されるべきプレーンテキストを正確に抽出できるようになった。
三つ目は「ルーティングロジックの誤解」である。当初の想定では、メールのルーティングはAIが分類したカテゴリに基づいて、担当者を割り当てるシンプルなものであった。しかし、テストケースを試すうちに、特定の請求に関する問い合わせが、本来の請求担当者ではなく、苦情対応の担当者にルーティングされるという予期せぬ挙動が見られた。調査の結果、AIの「確信度」が低いメールについては、カテゴリに関わらず、より幅広い知識を持つ特定の担当者へエスカレートするという暗黙のルールが存在することが判明した。つまり、AIが自信を持って分類できないメールは、安易に自動ルーティングするのではなく、慎重な人間の判断を必要とするケースとして扱うべきだったのだ。この問題は、確信度を単なる表示情報としてではなく、ルーティングの重要な判断基準としてプログラムコードに組み込むことで解決された。また、confidence_tier(確信度レベル)のようなビジネス上の判断基準は、AIモデルに直接生成させるのではなく、数値としての確信度からプログラムコードで導出することで、ビジネスロジックの変更が容易になるよう設計された。
システムが予期せぬ停止や再起動をした場合でも、同じメールを二重に処理しないようにするための「べき等性」の確保も重要な設計要素である。このパイプラインでは、二重処理を防ぐために二重の対策が講じられている。一つは、メールを処理し終えたらすぐにGmail上で「既読」とマークすることである。これにより、次回スクリプトが実行された際に「未読メールを取得する」というクエリからそのメールが除外され、再処理されるのを防ぐ。もう一つは、処理結果を保存するSQLiteデータベースにおいて、メールのユニークIDに対して「重複したIDが挿入されようとした場合は、既存の行を更新する」という制約(ON CONFLICT(gmail_message_id) DO UPDATE)を設定することである。これにより、万が一、既読マークが間に合わず二度目の処理が走ったとしても、データベース上では同じメールのデータが重複して作成されることなく、既存のデータが更新される形となる。これら二つの独立した防御策によって、システムが不安定な状態に陥っても、データの整合性が保たれるようになっている。
このAIパイプラインの構築が、実際に費用対効果の面でどれだけの価値があるのかも具体的な数値で検証された。AIサービスの利用コストについて、Claude Sonnetの現在の料金体系(入力トークンと出力トークンに対する課金)に基づいて計算すると、システムプロンプトとメール本文、そしてAIの生成する返信ドラフトを含めて、1通のメール処理にかかるAIのAPIコストは約0.01ドルと見積もられた。一方、サポート担当者が手作業で1通の定型的なメールを処理するのにかかる時間は、業界調査によると3〜8分程度とされており、ここでは最も控えめな4分を採用した。サポートスタッフの時給を25ドル(諸経費込み)と仮定すると、手作業での1通のメール対応にかかる人件費は約1.67ドルとなる。この比較から明らかなように、AIを利用した場合のコスト(約0.01ドル)は、人間の手作業(約1.67ドル)と比較して約167分の1という、大幅なコスト削減効果があることがわかる。パイプラインは人間を完全に排除するわけではないが、「メールを読み、内容を理解し、ゼロから返信を作成する」というプロセスを、「AIが分類した要約とドラフトを確認し、承認または必要に応じて編集する」という数秒の作業に短縮する効果がある。もちろん、この費用対効果の計算は、あくまで定型的な高確信度ケースにのみ当てはまる。複雑なメールやAIが自信を持って分類できないケースは、人間による丁寧な対応が必要であり、そのようなケースをAIに無理に自動化させようとすると、誤った対応によるコストの方がはるかに大きくなるため、このシステムは「自動化率を最大化する」のではなく、「間違いなく処理できる定型的なケースを保守的に自動化する」ことを目指している。
将来的な本番環境での運用を見据えると、いくつかの改善点が考えられる。一つは「プロンプトキャッシュ」の導入である。AIモデルに毎回送信されるシステムプロンプトは固定されており、これをキャッシュの対象とすることで、AIの入力コストをさらに削減できる可能性がある。二つ目は「ポーリングではなくWebhookの利用」である。現在のシステムは定期的にGmailをチェック(ポーリング)しているが、リアルタイムにメールの到着を検知し、すぐに処理を開始するためには、Gmail APIのusers().watch()機能とGoogle Cloud Pub/Subを組み合わせたWebhook方式に移行することが望ましい。三つ目は「Slackレートリミットへの対応」の強化である。SlackのAPIには、特定の時間内に送信できるメッセージ数に制限(レートリミット)があるため、大量のメールを処理する本番環境では、メッセージ送信間に適切な遅延を設けたり、レートリミットエラー(HTTP 429)が発生した場合にリトライ処理を組み込んだりする必要がある。