Zendeskのチケット情報をAIで要約・分類するためのデータ統合手法

Zendeskデータはそのまま使うな:実務で使えるAI分析のためのPython前処理パイプライン構築術

この記事は急速に進化する技術について解説しています。最新情報は公式ドキュメントをご確認ください。

約12分で読めます
文字サイズ:
Zendeskデータはそのまま使うな:実務で使えるAI分析のためのPython前処理パイプライン構築術
目次

この記事の要点

  • ZendeskチケットデータのAI分析精度向上
  • HTMLタグ、個人情報(PII)、定型署名の除去
  • Pythonによるデータ前処理パイプライン構築

実務の現場では、CSチームから「AIで問い合わせ内容を自動分類したい」という要望が頻繁に挙がります。「SaaSツールを繋げばすぐに終わるだろう」と考えがちですが、AI駆動開発において本当に重要であり、かつ最も泥臭い工程は「データの前処理」です。

この記事では、長年の開発現場で培った知見をベースに、SaaS連携ツールだけでは到達できない、エンジニアのための「データ統合パイプライン」構築方法を説明します。Pythonで生データを加工し、実務に耐えうるAI分析システムを作る道筋を、経営と現場の両方の視点から解説します。まずはプロトタイプとして「動くもの」を作り、仮説を即座に検証していきましょう。

本学習パスの概要とゴール設定

目指すゴールは、単なるデータの右から左への移動ではなく、「AIが正しく理解できる状態」へのデータ加工です。

なぜ「生データ」をそのままAIに渡してはいけないのか

多くの開発者が陥りがちな罠が、生データをそのままAIに渡してしまうことです。Zendeskのチケットデータ(ticket.descriptioncomments)には、AIにとって以下の問題が含まれています。

  • HTMLタグ: <p>, <div>, <br> などのタグはトークンを浪費するだけでなく、意味解析の邪魔をします。コストの無駄遣いは経営的にも避けるべきです。
  • 個人情報(PII): メールアドレスや電話番号をそのまま外部のLLMに送信することは、コンプライアンス上、非常に危険です。
  • コンテキストの欠落: チケット単体では「誰が」「いつ」発言したかが分からず、会話の流れ(スレッド)が失われます。

未処理のままLLMに渡すと、コスト増大や「分類不能」、ハルシネーション(もっともらしい嘘)の原因になります。

学習のロードマップ:データ取得から自動化まで

この学習パスでは、以下の4つのステップでパイプラインを構築します。高速プロトタイピングの精神で、まずは一通り動かしてみましょう。

  1. 取得 (Extraction): API制限を回避しながら、会話履歴を正しく取得する。
  2. 加工 (Transformation): ノイズを除去し、匿名化を行い、AI用フォーマットに整形する。
  3. 推論 (Inference): 適切なプロンプトとスキーマで、要約と分類を実行させる。
  4. 統合 (Loading/Feedback): 結果をシステムに戻し、継続的に改善する。

このパスで作成する成果物

最終的に、Pythonスクリプト一つで「Zendeskから未処理チケットを取得し、クリーンなテキストに変換して要約・タグ付けを行い、CSVまたはデータベースに保存する」流れを自動化します。

所要時間はハンズオンを含め約6時間です。週末プロジェクトや業務内のプロトタイプ作成に最適な規模感です。準備はいいですか?では、コードエディタを開いてください。

Step 1:Zendesk APIによるデータ取得の最適解

データ分析の第一歩は、データを正しく取得することです。Zendesk APIは強力ですが、大量データを扱う際にはシステム設計上の注意点があります。

Cursor PaginationとIncremental Exportsの使い分け

通常のリスト取得API (/api/v2/tickets.json) をページネーションで回すと、データ増加に伴いパフォーマンスが低下し、処理中の新規チケットによりデータ整合性が崩れる恐れがあります。

そこで推奨するのが Incremental Exports API です。これは「前回取得以降に変更があったデータ」のみを効率的に抽出する仕組みです。ビジネスの現場では、こうした差分更新の設計がシステム負荷とコストを劇的に下げます。

import requests
import time

# 基本設定
SUBDOMAIN = 'your_subdomain'
USER = 'your_email@example.com'
TOKEN = 'your_api_token'
BASE_URL = f'https://{SUBDOMAIN}.zendesk.com/api/v2'

# 認証
auth = (f'{USER}/token', TOKEN)

def fetch_tickets_incrementally(start_time):
    url = f'{BASE_URL}/incremental/tickets.json?start_time={start_time}&include=users,metric_sets'
    all_tickets = []
    
    while url:
        response = requests.get(url, auth=auth)
        if response.status_code == 429:
            # レート制限対策
            sleep_time = int(response.headers.get('Retry-After', 60))
            print(f"Rate limit hit. Sleeping for {sleep_time} seconds.")
            time.sleep(sleep_time)
            continue
            
        if response.status_code != 200:
            print(f"Error: {response.status_code}")
            break
            
        data = response.json()
        all_tickets.extend(data['tickets'])
        
        # 次のページのURLがあるか確認
        url = data.get('next_page')
        
        # API負荷を考慮して少し待機
        time.sleep(1)
        
    return all_tickets

# Unixタイムスタンプ(例: 1日前)
start_time = int(time.time()) - 86400
tickets = fetch_tickets_incrementally(start_time)
print(f"取得件数: {len(tickets)}")

会話コンテキストを維持するためのサイドローディング

コード内の include=users,metric_setsサイドローディング(Side-loading) です。

チケット単体には requester_idassignee_id などの数字しかなく、AIに顧客や担当者を伝えるのが困難です。サイドローディングにより、ユーザー情報やメトリクス(初回応答時間など)を一度のリクエストで一括取得できます。

結果として、後続処理で「ID:12345」を「顧客(山田さん)」に変換し、会話の文脈を補完できます。APIの呼び出し回数を減らすことは、高速化の基本ですね。

Step 2:AIのためのデータクレンジングと匿名化

Step 1:Zendesk APIによるデータ取得の最適解 - Section Image

ここが重要です。取得した生データはそのままでは使えません。普段見るメールやチャット画面は、ブラウザがレンダリングした結果に過ぎないからです。AIモデルの比較・研究を行う上でも、入力データの品質は結果を大きく左右します。

BeautifulSoupを使ったHTMLタグと非表示文字の除去

Zendeskのチケット本文はリッチテキスト形式(HTML)で保存されているため、プレーンテキストに戻す必要があります。

from bs4 import BeautifulSoup
import re

def clean_html(html_content):
    if not html_content:
        return ""
    
    # BeautifulSoupでパース
    soup = BeautifulSoup(html_content, "html.parser")
    
    # <br>や<p>を改行に変換して可読性を維持
    for br in soup.find_all("br"):
        br.replace_with("\n")
    for p in soup.find_all("p"):
        p.replace_with("\n" + p.get_text() + "\n")
        
    text = soup.get_text()
    
    # 連続する空白や改行を整理
    text = re.sub(r'\n\s*\n', '\n\n', text)
    return text.strip()

正規表現によるPII(個人情報)のパターンマッチングとマスキング

次に、セキュリティとプライバシーの観点から個人特定情報(PII)をマスクします。データガバナンスは経営層が最も気にするポイントの一つです。

公式ドキュメントによると、Microsoft Foundry(旧Azure OpenAIが統合されたプラットフォーム)等には個人情報を自動検出してブロックする機能が備わっています。しかし、APIの仕様変更やプラットフォームの移行に備え、送信前にPythonで基本的なマスキングを行うことは極めて大切です。

例えば、OpenAIの公式発表によれば、2026年2月13日をもってGPT-4oやGPT-5.1などの旧モデルがChatGPTのUIから完全に引退し、デフォルトモデルはInstant、Thinking、Auto、Proの4モードを備えたGPT-5.2へ一本化されました。API経由での開発においても、今後はGPT-5.2やコーディングに特化したGPT-5.3-Codexへの移行が強く推奨されています。このように、利用するAIモデルやプラットフォームは絶えず進化しており、それに伴ってフィルター機能の挙動も変化する可能性があります。

そのため、サーバー側の機能だけに依存するのではなく、送信するデータそのものを手元でクレンジングし、データ流出のリスクを根本から絶つ「多層防御」のアプローチが不可欠です。事前にマスキングされたクリーンなデータセットを用意しておけば、GPT-5.2のような最新モデルへ移行する際のプロンプトの再テストや評価も、安全かつスムーズに実施できます。

def mask_pii(text):
    # メールアドレスのマスキング
    email_pattern = r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}'
    text = re.sub(email_pattern, '[EMAIL_REMOVED]', text)
    
    # 電話番号のマスキング(日本の一般的な形式に対応)
    phone_pattern = r'\d{2,4}-\d{2,4}-\d{3,4}'
    text = re.sub(phone_pattern, '[PHONE_REMOVED]', text)
    
    return text

引用返信や署名ブロックの特定と削除ロジック

メールの問い合わせで厄介なのが「引用返信」と「署名」です。これらが残ったままだと、AIが過去の会話を重複して読み込んでしまい、トークンを無駄に消費する原因になります。チリも積もれば山となる、ですね。

def remove_noise(text):
    lines = text.split('\n')
    cleaned_lines = []
    
    for line in lines:
        # よくある引用記号や区切り線を検知
        if line.strip().startswith('>') or line.strip() == '--':
            continue
        if 'Original Message' in line or '元のメッセージ' in line:
            break # これ以降は引用とみなして切り捨て
            
        cleaned_lines.append(line)
        
    return '\n'.join(cleaned_lines)

Before / After のイメージ

  • Before (Raw JSON):
    "description": "<p>お世話になります。<br>ログインできません。<br>エラーコード: 503<br>--<br>サポート窓口 山田太郎<br>090-1234-5678</p>"
  • After (Processed):
    "ログインできません。\nエラーコード: 503"

このように情報を研ぎ澄ますことで、AIは「ログインエラー」という本質的な課題にアクセスしやすくなります。

Step 3:分類タクソノミーの設計とプロンプトエンジニアリング

Step 2:AIのためのデータクレンジングと匿名化 - Section Image

データが綺麗になったらAIに分析させます。しかし「このチケットを分類して」と頼むだけではAIが困惑する可能性があるため、「地図(タクソノミー)」が必要です。

CS現場と決める「分析軸(問い合わせ種別)」の定義

エンジニア単独でカテゴリを決めず、必ずCS現場の担当者と協議して実用的な分類軸を策定します。技術の本質を見抜き、ビジネスへの最短距離を描くためには、現場との協調が欠かせません。

  • 悪い例: 「技術的な問題」「その他」 (大雑把すぎる)
  • 良い例: 「ログイン認証エラー」「決済失敗」「機能リクエスト」「バグ報告(UI表示崩れ)」

この分類体系(タクソノミー)をプロンプトに埋め込みます。

JSONモードを活用した構造化データの出力制御

OpenAIのAPI等を使用する際、出力形式をJSONに強制すると後続プログラムでの処理が容易になります。Pydantic等のライブラリでスキーマを定義するのも有効です。

# プロンプトの例
system_prompt = """
あなたは熟練したカスタマーサポート分析官です。
以下の問い合わせ内容を分析し、指定されたカテゴリに分類し、要約を作成してください。
出力は必ず以下のJSON形式で行ってください。

{
  "summary": "問い合わせの要約(50文字以内)",
  "category": "[Login_Issue, Payment_Error, Feature_Request, Bug_Report] のいずれか",
  "sentiment": "[Positive, Neutral, Negative] のいずれか",
  "urgency": "[High, Medium, Low] のいずれか"
}
"""

user_prompt = f"問い合わせ内容:\n{cleaned_text}"

このアプローチにより、AIの出力揺れ(表記ゆれやフォーマット崩れ)を防ぎ、データベースへ直接書き込める品質のデータを取得できます。

Step 4:パイプラインの自動化と品質管理

スクリプト完成後は、定期実行するパイプラインに乗せます。AIは確率的システムであり常に100%正解するとは限らないため、実運用では「間違える可能性」を考慮した設計が不可欠です。一時的な分析に留めず、継続的な業務改善に繋がる仕組みを構築します。

バッチ処理による定期実行フローの構築

AWS Lambda、Google Cloud Functions、GitHub Actions等を使い、スクリプトを毎日または毎時実行するようスケジュール設定します。処理済みチケットIDをデータベースに保存し、二重処理を防ぐエラーハンドリングや再試行ロジックも組み込んでください。

プラットフォーム選定では、最新仕様とコスト体系に注意が必要です。例えば、GitHub Actionsのホストランナー料金体系やセルフホストランナーへの課金ルールは改定されることがあります。AWS LambdaでもPython等のランタイムサポートバージョンが定期的に更新されます。長期運用を見据え、公式サイトで最新の料金プランと推奨環境を確認し、計画的なアップデート体制を整えることを推奨します。

精度評価のためのHuman-in-the-loop(人間による確認)工程

導入初期は、AIの判定結果をそのまま反映せず、人間が確認するプロセス(Human-in-the-loop)を挟むことを強く推奨します。

例えば、AIが「緊急度:高」と判定したチケットのみSlackに通知し、担当者が確認する仕組みです。判定が誤っていた場合は正しいラベルに修正して蓄積し、Few-Shotプロンプティングの例題として再利用します。

Few-Shotプロンプティングは現在でも非常に有効です。望ましい出力例を2〜3個提示するだけで、AIは形式やトーン、暗黙のルールを正確に学習します。最新LLMは文脈理解能力が向上しており、「あなたはプロのカスタマーサポートです」といった複雑なロールプロンプトの効果は薄れています。良質な具体例を示すFew-Shotアプローチと、推論過程を促すChain-of-Thought(「段階的に考えてください」という指示)の組み合わせで高精度な結果が得られます。

さらに、この確認プロセスは「モデル移行」のリスクヘッジにもなります。LLMのAPIは頻繁に新モデルへの置き換えや旧モデルの廃止が行われます。Claudeへの移行や、思考の深さを自動調整する「Adaptive Thinking」等の新推論モード導入時にも、人間が検証した正解データ(テストセット)があれば、精度検証やプロンプト再調整をスムーズに行えます。

結果をZendeskに書き戻す(タグ更新・内部メモ追加)

分析結果は最終的にZendeskへ書き戻し、現場オペレーターにフィードバックします。APIでチケットに「自動タグ」を付与したり、内部メモとしてAI要約を追記したりすることで、担当者がチケットを開いた瞬間に顧客状況や課題の要点を把握できます。

def update_zendesk_ticket(ticket_id, tags, summary):
    url = f'{BASE_URL}/tickets/{ticket_id}.json'
    payload = {
        "ticket": {
            "tags": tags,
            "comment": {
                "body": f"[AI要約]\n{summary}",
                "public": False  # 内部メモとして投稿
            }
        }
    }
    requests.put(url, json=payload, auth=auth)

顧客対応の現場において、AIはあくまで人間のサポート役です。自動化パイプラインと人間による継続的なフィードバックループを組み合わせることで、実務で役立つAIシステムへと成長させることができます。

学習リソースと次のステップ

ここまで、Zendeskデータの取得から前処理、AI分析と書き戻しまでの一連の流れを解説しました。これは「AI駆動開発」の第一歩に過ぎません。

推奨ライブラリとドキュメント

  • LangChain / LlamaIndex: 今回はrequestsで直接書きましたが、より複雑な処理にはこれらのフレームワークが役立ちます。
  • Zendesk API Reference: 特にレート制限とサイドローディングの項目は必読です。
  • Tiktoken: OpenAIのトークン数を正確に計算し、コスト試算を行うために役立ちます。

さらに高度な分析:RAG構築への応用

今回作成した「クリーンなデータ」は、RAG(検索拡張生成)の知識ベースとしても活用できます。過去の解決済みチケットをベクトル化して検索可能にすれば、「過去の類似問い合わせと解決策」をAIが回答するボットの作成も可能です。

まとめ

Step 3:分類タクソノミーの設計とプロンプトエンジニアリング - Section Image 3

ZendeskとAIの連携は、単なるツールの導入ではなく、「データエンジニアリング」の課題です。

  1. APIの特性を理解し、効率的にデータを引くこと。
  2. 前処理で、ノイズを徹底的に削ぎ落とすこと。
  3. 現場の声を反映したタクソノミーを設計すること。
  4. 自動化とフィードバックループで精度を育てていくこと。

この4つを丁寧に積み上げて初めて、AIは「魔法」ではなく「頼れる存在」になります。まずは手元のPython環境で、最初の10件のチケットを取得するところから始めてみてください。その小さな一歩が、カスタマーサポートの未来を変えるシステムの礎となるはずです。

皆さんのプロジェクトで、どのようなプロトタイプが生まれるのか楽しみにしています。何か疑問があれば、ぜひコミュニティ等で議論を深めていきましょう。

Zendeskデータはそのまま使うな:実務で使えるAI分析のためのPython前処理パイプライン構築術 - Conclusion Image

参考文献

  1. https://ijazurrahim.com/blog/web-scraping-tools-2026.html
  2. https://www.promptcloud.com/blog/how-to-scrape-youtube-data-using-python/
  3. https://www.youtube.com/watch?v=UKXvNiL11qA
  4. https://www.almabetter.com/bytes/articles/web-scraping-tools
  5. https://gist.github.com/Mr0grog/63667aec279f2035c036501bbd876d1a
  6. https://lobehub.com/skills/booklib-ai-skills-web-scraping-python

コメント

コメントは1週間で消えます
コメントを読み込み中...