メインコンテンツへスキップ
見出し画像

ExcelからServiceNowインシデントをAI分析する業務自動化ツールをPythonで作成

    Excel起点の分析業務は多く求められると思います。そこで、今回はExcelを起点にServiceNowのIncidentレコード収集からOpenAIへ分析要約までを実装したアプリケーションを作成します。

    1.準備

    今回は、以下の環境やソフトウェアを準備します。
    ・ServiceNow PDI ※Australiaバージョン
    ・OpenAI API  ※APIキー取得
    ・Excel
    ・Python  ※最新バージョンを推奨

    ServiceNow PDIの払い出しについてはこちらより実施ください。
    OpenAI APIのAPIキーの払い出しはこちらをご参考ください。
    Pythonのインストーラはこちらより取得ください。

    作業用フォルダを作成しておきます。
    例)C:\work\sn_ai_tool

    2.全体構成と処理シーケンス


    今回のアプリケーションの全体の構成図は以下の図の通りです。

    画像
    全体構成図

    今回は、このロジック部分をPythonスクリプトで実装してみようと思います。ServiceNow内にクローズした開発となると、ロジック部分の作成手段はFlowステップだったり、JavaScriptに限られます。

    しかし、実際にLLMのAPIを扱う上でのプログラムのフレキシビリティはPythonの方が適しているのではと思ったことも今回の要件を実装してみようと考えた背景です。

    続いて、処理シーケンスは以下のような形を構成する予定です。

    画像
    処理シーケンス

    3.環境周りの準備

    ServiecNowのインシデントレコードをREST APIで取得できるかの確認を行います。PDIにログイン後に、[REST APIエクスプローラー]を起動します。

    画像
    REST APIエクスプローラー

    REST APIエクスプローラーを使い、Table APIへ疎通確認を行ってみます。
    ・名前空間:now
    ・API名:Table API
    ・APIバージョン:v1
    ・tableName:インシデント[incident]
    ・sysparam_limit:1

    画像
    REST APIエクスプローラーの設定

    送信を押下します。

    画像
    送信ボタン

    ステータス200番で応答が返りました。Table APIを利用できることが分かりました。

    画像
    REST APIの応答確認

    続いて、Basic 認証でPDIに接続し、Incidentレコードを取得できるかを確認します。curlコマンドが簡単なので、これを使います。

    Windows 10(バージョン1803以降)からcurlがデフォルトで組み込まれています。コマンドプロンプト(cmd)またはPowerShellどちらからでも実行できます。PowerShellコマンドでは以下のような定義を行います。

    curl.exe -u "admin:password" "https://instance.service-now.com/api/now/table/incident?sysparm_limit=1" -H "Accept: application/json"

    1)password:adminユーザのパスワード文字列を指定
    2)instance:PDIのインスタンス名を指定

    実行すると以下のような結果が戻ってくれば、成功です。
    incidentレコードが応答されています。

    画像
    curlコマンドの実行結果

    続いて、Python実行環境の確認とインストールを行います。

    python --version

    当方環境では"3.13.12"がインストールされていました。インストールされてない場合にはインストールの実行を行ってください。

    続いて、以下の2つのPython用ライブラリをインストールします。
    ・PythonでExcelの読み取り/書き込みを行うopenpyxl
    ・PythonでOpenAI APIへ接続するライブラリ

    pip install requests openpyxl openai

    ライブラリのインストールが成功したら、環境準備は完了です。

    4.Pythonによるコード作成

    では、ここから実際のプログラムを作成してゆきます。

    まず読み取り用構成定義ファイルの作成です。接続定義を分離して管理できるようにします。
    ・ファイル名:config.py
    ・定義:ファイルへ以下定義を追加

    # ServiceNow接続情報
    SN_INSTANCE = "INSTANCE"
    SN_USERNAME = "admin"
    SN_PASSWORD = "XXXXXX"
    
    # OpenAI接続情報
    OPENAI_API_KEY = "OpenAI APIキーをここに指定"
    
    # 取得するインシデント数上限
    INCIDENT_LIMIT = 5

    1)SN_INSTANCE:PDIのインスタンス名
    2)SN_PASSWORD:System Administratorのパスワード文字列
    3)OPENAI_API_KEY:OpenAIのAPIキーをここに指定

    今回のメイン処理であるPythonスクリプトを作成します。
    ・ファイル名:sn_ai_report.py
    ・全体コード:以下のサンプルをコピー&ペースト

    import requests
    import openpyxl
    import json
    from requests.auth import HTTPBasicAuth
    from openai import OpenAI
    from config import SN_INSTANCE, SN_USERNAME, SN_PASSWORD, OPENAI_API_KEY, INCIDENT_LIMIT
    
    # ===== 1. ServiceNow接続設定 =====
    SN_BASE_URL = f"https://{SN_INSTANCE}.service-now.com"
    auth = HTTPBasicAuth(SN_USERNAME, SN_PASSWORD)
    headers = {"Accept": "application/json"}
    
    # ===== 2. OpenAI接続設定 =====
    client = OpenAI(api_key=OPENAI_API_KEY)
    
    # ===== 3. インシデント一覧取得 =====
    def get_incidents():
        url = f"{SN_BASE_URL}/api/now/table/incident"
        params = {
            "sysparm_limit": INCIDENT_LIMIT,
            "sysparm_fields": "number,short_description,description,priority,state,opened_at",
            "sysparm_query": "active=true^ORDERBYDESCsys_created_on"
        }
        response = requests.get(url, auth=auth, headers=headers, params=params)
        response.raise_for_status()
        return response.json().get("result", [])
    
    # ===== 4. ChatGPTで分析 =====
    def analyze_incident(incident):
        prompt = f"""
    以下のServiceNowインシデント情報を分析してください。
    
    【インシデント番号】{incident.get('number')}
    【タイトル】{incident.get('short_description')}
    【詳細】{incident.get('description')}
    【優先度】{incident.get('priority')} (1=Critical, 2=High, 3=Moderate, 4=Low)
    【状態】{incident.get('state')}
    
    以下の3点を簡潔に日本語で回答してください:
    1. 要約(2文以内)
    2. 推奨アクション(具体的に1〜2つ)
    3. 対応優先度評価(High / Medium / Low)と理由
    """
        response = client.chat.completions.create(
            model="gpt-5.4-mini",
            messages=[{"role": "user", "content": prompt}],
            #max_tokens=300
            max_completion_tokens=300
        )
        return response.choices[0].message.content
    
    # ===== 5. Excelに出力 =====
    def export_to_excel(results):
        wb = openpyxl.Workbook()
        ws = wb.active
        ws.title = "インシデント分析レポート"
    
        # ヘッダー行
        headers_row = [
            "インシデント番号",
            "タイトル",
            "優先度",
            "登録日時",
            "AI要約・推奨アクション・優先度評価"
        ]
        ws.append(headers_row)
    
        # ヘッダーのスタイル設定
        from openpyxl.styles import Font, PatternFill
        for col in range(1, 6):
            cell = ws.cell(row=1, column=col)
            cell.font = Font(bold=True, color="FFFFFF")
            cell.fill = PatternFill(fill_type="solid", fgColor="4472C4")
    
        # データ行
        for row in results:
            ws.append([
                row["number"],
                row["short_description"],
                row["priority"],
                row["opened_at"],
                row["ai_analysis"]
            ])
    
        # 列幅調整
        ws.column_dimensions["A"].width = 18
        ws.column_dimensions["B"].width = 35
        ws.column_dimensions["C"].width = 10
        ws.column_dimensions["D"].width = 22
        ws.column_dimensions["E"].width = 80
    
        # 折り返し設定
        from openpyxl.styles import Alignment
        for row in ws.iter_rows(min_row=2):
            for cell in row:
                cell.alignment = Alignment(wrap_text=True, vertical="top")
    
        output_path = "incident_ai_report.xlsx"
        wb.save(output_path)
        print(f"\n✅ 完了!出力ファイル: {output_path}")
    
    # ===== 6. メイン処理 =====
    def main():
        print("🔄 ServiceNowからインシデントを取得中...")
        incidents = get_incidents()
        print(f"✅ {len(incidents)}件取得完了")
    
        results = []
        for i, incident in enumerate(incidents, 1):
            print(f"🤖 AI分析中... ({i}/{len(incidents)}) {incident.get('number')}")
            analysis = analyze_incident(incident)
            results.append({
                "number": incident.get("number"),
                "short_description": incident.get("short_description"),
                "priority": incident.get("priority"),
                "opened_at": incident.get("opened_at"),
                "ai_analysis": analysis
            })
    
        print("\n📊 Excelに出力中...")
        export_to_excel(results)
    
    if __name__ == "__main__":
        main()

    この作成したファイル2つを先の作業フォルダ内に保存しておきます。

    コードステップ数が多いのでステップを分割しながら要点部分をまとめてコメントの記載をします。
    [ライブラリ取り込み]

    import requests
    import openpyxl
    import json
    from requests.auth import HTTPBasicAuth
    from openai import OpenAI

    1)opepyxl:先にインストールしたExcelアクセス用ライブラリ
    2)from openai import OpenAI:OpenAI APIへの接続用ライブラリ

    このライブラリにより後続のOpenAI APIへの接続ステップがとても簡略化されます。内部的には、エンドポイントURL管理(api.openai.com/v1/chat/completions)、Authorizationヘッダーへのキー設定、リクエストボディのJSON変換、HTTPエラーハンドリング(429レートリミット・500エラー等)、レスポンスのオブジェクトマッピングをすべて自動でやってくれています。こうしたライブラリが使えるので、やはりPythonを使うメリットは高いですね。

    [ServiceNowとOpenAI 接続定義]

    # ===== 1. ServiceNow接続設定 =====
    SN_BASE_URL = f"https://{SN_INSTANCE}.service-now.com"
    auth = HTTPBasicAuth(SN_USERNAME, SN_PASSWORD)
    headers = {"Accept": "application/json"}
    
    # ===== 2. OpenAI接続設定 =====
    client = OpenAI(api_key=OPENAI_API_KEY)

    config.py設定ファイルからの接続用パラメータ値の読み取りされます。
    1)ServiceNow接続:Basic認証でログイン
    2)OpenAI接続:ライブラリにAPIキーを渡して接続
    繰り返しのコメントになりますが、OpenAIのライブラリが利用できるので、この単一ステップ1行で接続できるようになってます。

    [Incidentレコードの取得関数の定義]

    # ===== 3. インシデント一覧取得 =====
    def get_incidents():
        url = f"{SN_BASE_URL}/api/now/table/incident"
        params = {
            "sysparm_limit": INCIDENT_LIMIT,
            "sysparm_fields": "number,short_description,description,priority,state,opened_at",
            "sysparm_query": "active=true^ORDERBYDESCsys_created_on"
        }
        response = requests.get(url, auth=auth, headers=headers, params=params)
        response.raise_for_status()
        return response.json().get("result", [])

    1)Table APIのIncidentよりレコード取得
    2)sysparam_query:ServiceNow REST APIの検索条件パラメータ。以下はREST APIパラメータの利用に関するDocsです。参考にしてください。

    3)response.raise_for_status:レスポンスの応答検証をこの単一ステップで実施。200番→正常で処理継続、400番や500番→エラー停止処理といったように自動対応させるステップとなっており、この単一ステップでエラーハンドリグステップを組めるあたりもPyrhonは効率化されている気がします。

    [ChatGPTへの分析要求の関数定義]


    # ===== 4. ChatGPTで分析 =====
    def analyze_incident(incident):
        prompt = f"""
    以下のServiceNowインシデント情報を分析してください。
    
    【インシデント番号】{incident.get('number')}
    【タイトル】{incident.get('short_description')}
    【詳細】{incident.get('description')}
    【優先度】{incident.get('priority')} (1=Critical, 2=High, 3=Moderate, 4=Low)
    【状態】{incident.get('state')}
    
    以下の3点を簡潔に日本語で回答してください:
    1. 要約(2文以内)
    2. 推奨アクション(具体的に1〜2つ)
    3. 対応優先度評価(High / Medium / Low)と理由
    """
        response = client.chat.completions.create(
            model="gpt-5.4-mini",
            messages=[{"role": "user", "content": prompt}],
            #max_tokens=300
            max_completion_tokens=300
     
        )
        return response.choices[0].message.content
    

    1)ChatGPTへのプロンプト:incidentレコードの各フィールド値をセット
    2)client.chat.completions.create():OpenAI APIライブラリのresponse API。これがOpenAIモデルとやり取りするための主要なAPIでテキスト生成が行えるようになっています。


    3)model:ChatGPTのバージョン指定
    ※2026/03/25時点でのChatGPTモデル一覧を纏めました。今回のようなAPI接続利用の可否も併せて一覧に記載しています。ご参考までに。

    画像
    ChatGPTモデル・バージョン一覧

    3)max_completion_tokens:API接続時に受領メッセージサイズ規定です。
    旧モデルでの定義は"max_tokens"で、これを最初は設定していたところ、当該最新モデルではエラー停止してました。ですので、モデルを最新で実行する場合には、この"max_completion_tokens"を定義しておきます。

    ちなみに日本語は1トークン≒1〜2文字になります。日本語は英語より文字あたりのトークン消費が多い点が重要です。
    面白いので、どれくらいのコストがかかるかも試算してみました。
    gpt-5.4-miniの価格は入力100万トークンあたり$0.75、出力100万トークンあたり$4.50あたりとなっているので、これを係数として算出してみます。

    入力トークン(プロンプト):約200トークン
     ・インシデントデータ送信分
     ・指示文(要約・推奨・優先度)
    
    出力トークン(AI回答):約300トークン
     ・max_tokens=300の上限
    
    1回の呼び出しコスト:
     入力 200 × $0.75 / 1,000,000 = $0.00015
     出力 300 × $4.50 / 1,000,000 = $0.00135
     合計 約 $0.0015 / 1回
    $5 ÷ $0.0015 ≒ 約3,300回

    5$をチャージしておくと、3,300回くらいのインシデントレコードの分析は行える算段です。
    参考までに旧モデルと比べると、gpt-4o-miniは入力$0.15/100万トークン・出力$0.60/100万トークンなのでgpt-5.4-miniよりコストは安いですが、精度が劣ります。

    [Excelへの書き込み定義]

    # ===== 5. Excelに出力 =====
    def export_to_excel(results):
        wb = openpyxl.Workbook()
        ws = wb.active
        ws.title = "インシデント分析レポート"

    openpyxl.workbook():openpxlライブラリを使ったExcelアクセス定義
    書き込み処理はforステートメントを使ったレコード転記

    [メイン処理]

    # ===== 6. メイン処理 =====
    def main():
        print("🔄 ServiceNowからインシデントを取得中...")
        incidents = get_incidents()
    
       --(中略)--
    
        for i, incident in enumerate(incidents, 1):
            print(f"🤖 AI分析中... ({i}/{len(incidents)}) {incident.get('number')}")
            analysis = analyze_incident(incident)
    
        -- (中略) --
           
        export_to_excel(results)

    1)get_incients():インシデントレコード取得関数の呼び出し
    2)analyze_incident():ChatGPT分析関数の呼び出し
    3)export_to_excel():Excel書き出し関数の呼び出し
    これがメイン処理の実行シーケンスです。

    これで全機能が完成しました。

    5.動作確認


    それでは、実際に動作を確認してゆきたいと思います。
    PowerShellを使ってPythonプログラムを実際に動かしていきます。

    cd C:\work\sn_ai_tool
    python sn_ai_report.py

    以下のようにステップが実行されてゆきます。今回は5件までのレコード取得を試みています。

    画像
    プログラムの実行と完了

    作業用フォルダにExcelファイルが作成されています。

    画像
    Excelファイル

    早速開いてみます。
    おおっ! バッチリ、分析要約を含むレポートが作成されていますね。
    体裁も良い感じで整っています。

    画像
    ChatGPTにインシデントを分析要約させたExcelレポート

    実際のインシデントレコードがこの要約に記載されている内容なのかどうかをPDI上で確認してみましょう。

    INC0000055は、重大インシデント(1:重大)ですので、緊急度が高いインシデントでした。
    short_descriptionとdescriptionフィールドの内容から、SAPセールス関連アプリが利用不可という点をChatGPTが分析しています。
    そして、レポート内に推奨アクションなどをテキスト生成してくれたという動きでした。

    画像
    実際のIncidentレコード

    如何でしたでしょうか? 今回はExcelを起点にServiceNowのデータをAPI経由でレコード取得し、それをOpenAI APIへ分析要約を行い、結果をExcelへ転記させるという機能を開発してみました。

    個人的には、今回の気づきとして、ベタですが”Pythonは生成AIを扱うプログラム言語としては非常に効率的である”と感じました。

    常日頃、ServiceNowプラットフォームの内側で開発作業を進めてきた手前、プラットフォーム内の技術制約の中で機能開発をしており、なかなかこうした感覚は味わえなかったというところです。

    外に機能構成を切り出してみると、”こんなに簡単に便利な機能がすぐ使える!”という点、良い知見にもなり、面白かったです。
    また次の技術ブログにご期待ください。では。


     
     
    東京都在住/自動化(テスト・RPAなど)大好き/LLMへAPI接続して遊ぶことが趣味/外資系SaaSの導入向け技術職/中学受験向け我が家塾の経験/Gemini Enterprise / Google ADK / マルチエージェント(A2A・MCP)× エンタープライズ業務実装

    あなたへのおすすめ