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

ServiceNow AIインシデント分析をPythonのStreamlitでWebアプリ化した手順まとめ

    前回、PythonでServiceNowのインシデントレコードを取得し、ChatGPTに分析させその結果をExcelにレポートでまとめるアプリを作成しました。

    プロトタイプ的な機能としては完成したのですが、、振り返ると、
    ・専用UIもなく、PythonプログラムをPowerShellから実行
    ・インシデントレコードを抽出する定義はハードコード
    ・分析させるプロンプトもハードコードで固定
    という、いわば ”エンジンむき出しのモデルカー”みたいな状況でした。

    そこで、今回は前回の続きとして、
    ・専用UIをWeb画面で提供
    ・ServiceNowインシデントレコード抽出条件を画面で入力可能
    ・その他LLMの切り替えやプロンプト編集が可能な画面入力項目
    などを作成して、実際に現場でも利用ができるようなアプリケーションの完成形まで漕ぎつけようと思います。

    1.UI設計とアーキテクチャ構成

    まずUI設計についてです。今回はGUIをWebにするか、クラサバのような画面のどちらかで悩みましたが、今風なのでWebアプリにしようと思います。
    ・WebのGUIが比較的に綺麗に作りやい”Streamlit
    “で作成してゆきます。

    そして、UI設計としては以下図のイメージです。

    画像
    UI設計

    全体のアーキテクチャを図解すると以下のようなイメージです。
    全体は「UI → データ取得 → AI分析 → 出力」の4層の構成とします。

    画像
    全体アーキテクチャ

    今回のアプリケーション機能の物理構成は以下となる予定です。

    画像
    アプリケーション構成

    ・sn_ay_analyzer:物理フォルダ
    ・app.py:UI層ロジック
    ・servicenow.py:ServiceNowへの接続およびインシデント取得
    ・llm.py:LLMの切り替えと接続
    ・.env:APIキー、認証情報の格納

    2.環境構築と準備


    まずプロジェクト用フォルダを作成し、依存ライブラリをインストールします。当方の環境ではPowerShellを使っています。

    mkdir sn_ai_analyzer; cd sn_ai_analyzer
    python -m venv venv
    .\venv\Scripts\Activate.ps1
    pip install streamlit requests openai cohere google-generativeai openpyxl pandas python-dotenv
    pip freeze > requirements.txt

    1)stremlit:WebアプリのUI全体を構築するフレームワーク
    2)requests:ServiceNow へのREST APIリクエスト送信用ライブラリ
    3)openai, cohere, google*:個社Model用SDK。
    4)openpyxl:Excelアクセス用ライブラリ。
    5)pandas:APIレスポンス(JSON)をDataFrameに変換ライブラリ。
    6)python-dotenv:envファイルからAPIキーや認証情報を読み込み。
    7)freeze > requirements.txt:インストール済ライブラリ情報の書き出し

    インストールが完了すると以下のようなファイルやフォルダが生成されます。

    画像
    ファイル&フォルダの生成

    3.APIキー&認証情報の保管環境ファイルの定義


    続いて、ServiceNowインスタンスや各LLMへ接続するための認証情報やAPIキーを格納するファイルを定義します。

    New-Item .env
    ```
    
    中身をテキストエディタで開いて記述します。
    ```
    SN_INSTANCE=https://your-instance.service-now.com
    SN_USER=admin
    SN_PASS=パスワード
    OPENAI_API_KEY=sk-...
    COHERE_API_KEY=...
    GEMINI_API_KEY=...

    4.ServiceNowインスタンスへの接続&インシデントレコードの取得機能の実装


    続いてServiceNowインスタンスへの接続からインシデントレコードを取得する機能を作成します。
    プロジェクトフォルダ内に、servicenow.pyファイルを作成します。この作業はコマンドで実施せず、メモ帳などでの作成でも問題ありません。

    New-Item servicenow.py

    続いて、pyファイルを開き、以下コードを定義します。

    import requests
    import pandas as pd
    from requests.auth import HTTPBasicAuth
    
    def fetch_incidents(instance, user, password, state=None, priority=None, limit=50):
        query_parts = []
        if state:    query_parts.append(f"state={state}")
        if priority: query_parts.append(f"priority={priority}")
        sysparm_query = "^".join(query_parts) if query_parts else ""
    
        url = f"{instance}/api/now/table/incident"
        params = {
            "sysparm_limit": limit,
            "sysparm_fields": "number,short_description,description,state,priority,assigned_to",
            "sysparm_query": sysparm_query,
        }
        try:
            resp = requests.get(url, auth=HTTPBasicAuth(user, password), params=params)
            resp.raise_for_status()
            return pd.DataFrame(resp.json()["result"])
        except requests.exceptions.HTTPError as e:
            raise Exception(f"APIエラー: {e}")
        except Exception as e:
            raise Exception(f"接続エラー: {e}")

    1)以下コードステップに関して
    ここでは、ServiceNowのテーブルAPIに渡すフィルタ条件を動的に組み立てています。

    query_parts = []  
      if state:    query_parts.append(f"state={state}")
        if priority: query_parts.append(f"priority={priority}")
        sysparm_query = "^".join(query_parts) if query_parts else ""

    ServiceNowでは複数条件を ^(キャレット)でつなぐルールがあるため、条件がある時だけリストに追加して最後に "^".join() で結合しています。
    条件が未選択(None)の場合はリストに追加しないことで、不要な条件がクエリに混入するのを防いでいます。

    ちなみにこのsysparam_queryで複数条件結合で"^"を使うルール定義は以下Table APIの公式ドキュメント(Docs)に記載されています。

    2)Table API接続やエラーハンドリング
    その他コードステップに関しては前回、プロトタイプを作成した際のブログ記事(こちら)と同じ内容です。

    5.各社LLMへ切り替え接続を行う機能の実装


    続いて、各社のLLMへ接続する機能を作成します。
    プロジェクトフォルダ内に、llm.pyファイルを作成します。上記と同様にこの作業はコマンドで実施せず、メモ帳などでの作成でも問題ありません。

    New-Item llm.py

    続いて、このllm.pyファイルを開いて、コードを定義します。

    import openai
    import cohere
    import google.generativeai as genai
    
    def analyze(provider, prompt, incident_text, api_key):
        full_prompt = f"{prompt}\n\n---\n{incident_text}"
    
        if provider == "OpenAI":
            client = openai.OpenAI(api_key=api_key)
            r = client.chat.completions.create(
                model="gpt-5.4-mini",
                messages=[{"role": "user", "content": full_prompt}],
                max_completion_tokens=300,
            )
            return r.choices[0].message.content
    
        elif provider == "Cohere":
            client = cohere.ClientV2(api_key=api_key)
            r = client.chat(
                model="command-a-03-2025",
                messages=[{"role": "user", "content": full_prompt}],
            )
            return r.message.content[0].text
    
        elif provider == "Gemini":
            genai.configure(api_key=api_key)
            model = genai.GenerativeModel("gemini-3-flash-preview")
            return model.generate_content(full_prompt).text

    1)各LLMへの切り替え接続を分岐ステートメントで画面から入力されてきたパラメータ値でスイッチさせています。
    2)接続ヘッダなどの異なる箇所は個々のPythonの接続用ライブラリで吸収されていますので、非常にシンプルなステップで簡単に組めるのが良いですね。
    3)max_completion_tokens:こちらのブログ参考ください。

    6.Web画面(GUI)をStreamlitで作成


    さて、ここからはGUIとしてのWeb画面をStremlitで作成してゆきます。

    少しここで、Streamlitフレームワークについてコメントしておきます。このフレームワークはGUIを簡単に作れるメリットが非常に高いです。

    通常のWebフレームワークはHTML/CSS/JavaScriptの知識が必要ですが、StreamlitはPythonコードだけでWebアプリのUIを構築できるように設計されています。データサイエンティストやエンジニアがフロントエンドの知識なしに素早くアプリを作ることを目的としたフレームワークとなっています。

    今回のアプリでいえば、st.button()・st.text_area()・st.dataframe() などを呼び出すだけでUI部品が自動生成されます。HTMLを一行も書かずに済むのがStreamlitの特徴です。

    以下ファイルをプロジェクトフォルダ下に定義します。

    New-Item app.py

    続いてこのapp.pyファイルを開いて、コードを定義します。

    import streamlit as st
    import pandas as pd
    from io import BytesIO
    from dotenv import load_dotenv
    import os
    import openpyxl
    from servicenow import fetch_incidents
    from llm import analyze
    
    load_dotenv()
    
    st.set_page_config(page_title="SN AI Incident Analyzer", layout="wide")
    
    with st.sidebar:
        st.header("接続設定")
        instance = st.text_input("Instance URL", os.getenv("SN_INSTANCE", ""))
        user     = st.text_input("Username",     os.getenv("SN_USER", ""))
        password = st.text_input("Password",     os.getenv("SN_PASS", ""), type="password")
    
        st.header("LLM選択")
        provider = st.selectbox("LLM", ["OpenAI", "Cohere", "Gemini"])
        api_key  = st.text_input("API Key", type="password")
    
        st.header("抽出条件")
        state    = st.selectbox("State", ["", "1", "2", "3"],
                     format_func=lambda x: {"": "All", "1": "Open", "2": "In Progress", "3": "On Hold"}.get(x, x))
        priority = st.selectbox("Priority", ["", "1", "2", "3", "4"])
        limit    = st.slider("取得件数", 5, 100, 20)
    
        run = st.button("分析実行", use_container_width=True)
    
    prompt = st.text_area("プロンプト", "以下のインシデントを分析し、要約・推奨アクション・優先度を出力してください。", height=100)
    
    if run:
        with st.spinner("ServiceNowからデータ取得中..."):
            df = fetch_incidents(instance, user, password, state or None, priority or None, limit)
    
        results = []
        for _, row in df.iterrows():
            text = f"番号: {row['number']}\n件名: {row['short_description']}\n詳細: {row.get('description', '')}"
            with st.spinner(f"{row['number']} を分析中..."):
                res = analyze(provider, prompt, text, api_key)
            results.append({"番号": row["number"], "件名": row["short_description"], "AI分析結果": res})
    
        result_df = pd.DataFrame(results)
        st.dataframe(result_df, use_container_width=True)
    
        buf = BytesIO()
        wb = openpyxl.Workbook()
        ws = wb.active
        ws.append(list(result_df.columns))
        for r in result_df.itertuples(index=False):
            ws.append(list(r))
        wb.save(buf)
        st.download_button("Excelダウンロード", buf.getvalue(), "incidents_ai.xlsx",
                           "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")

    1)st.set_page_config:ページタイトルなどの設定
    2)st.sidebar:サイドバー設定
    3)st.text_input:テキストの入力フォーム
    4)st.selectbox:選択リストボックス
    5)st.dataframe:データフレームを対話型テーブルで表示
    6)st_dowload_button:

    今回利用したStreamlitのAPIレファレンスは↓です。

    こんな便利なリファレンス一覧も用意されていますね。

    全ウィジェット・レイアウト・データ表示・ユーティリティ関数がコード例付きで一覧になっており、素早く検索したい時には ↑ が便利ですね。

    これだけのシンプルで可読性の高いWeb画面のコードステップ作成は初めてです。通常はHTMLやCSSなどが入り込むと可読性の低い(読みにくい)ステップが作られるのがほとんどでしたので。

    最後に、このアプリケーションがバッチファイルをダブルクリックすると起動して利用できるようにしておきたいと思います。

    以下のバッチファイルを"start_Analyze.bat"という名前のファイル名で作成しておきます。

    @echo off
    cd /d C:\work\sn_ai_analyzer
    call venv\Scripts\activate
    streamlit run app.py


    ここまでの作業で、アプリケーションは完成しました。

    7.動作確認

    さて、ここから作成したアプリケーションの動作確認を行います。
    以下のコマンドをPowerShell上で実行するか、

    streamlit run app.py

    上記の作業で作成した、バッチコマンドファイル"start_Analyze.bat"をダブルクリックします。

    以下のようなステータスが応答され、ブラウザが8501ポート接続で自動起動します。

    画像
    ステータス画面の応答

    ブラウザ上で画面がロードされるのを待つと、以下のような画面が起動します。

    画像
    アプリケーション起動後のWeb画面

    まずは左画面で
    ・ServiceNowインスタンスの接続先の指定
    ・LLMの接続先の設定
    ・インシデントレコードの抽出条件の設定
    を行います。

    設定後に、[分析実行]を押下します。

    画像
    分析実行ボタンの押下

    インシデントレコードの取得から分析までが実行されます。
    良い感じですね。

    画像
    インシデントレコード分析

    処理が完了すると、一覧画面が表示され、一緒に[Excelダウンロード]ボタンも表示されます。

    画像
    分析結果一覧とダウンロードボタン

    Excelをダウンロードして、開いてみます。
    分析結果レポートとしてレコードデータが書き込まれています。

    画像
    Excel分析結果レポート

    プロンプトの入力もデフォルト値を変更してみます。
    [入力したプロンプト内容]

    以下のServiceNowインシデント情報を分析し、3点を簡潔に日本語で回答してください。
    1. 要約(2文以内)
    2. 推奨アクション(具体的に1〜2つ)
    3. 対応優先度評価(High / Medium / Low)と理由
    画像
    プロンプトの入力

    実行後、得られたレポートを確認してみます。
    3つの要約要件を含めてレポートが正しく作成されているようです。

    画像
    Excel結果レポート

    停止させる場合には、以下のいずれかで停止できます。
    ・PowerShellからコマンド実行した場合には、"Ctrl+C"を押下
    ・バッチファイル起動であれば、以下の図のように”Y”を指定してEnterキーを押下

    画像
    アプリケーションの停止処理

    8.このアプリを配布して他者環境でも利用させる場合の考慮点


    まず、以下が考慮すべきポイントです。
    ・ServiceNowインスタンス接続やLLMなどのAPIキーを共通利用は避ける
    ・環境依存系するライブラリファイルは新規インストールさせる

    ①他端末環境でのインストール作業
    `venv/` フォルダは環境依存のため配布に含めず、`requirements.txt` から再構築してもらう形が正しいです。

    したがって以下コマンドをPowerShellで叩くかバッチファイルを作ってバッチファイルを一回実行してもらう形が良いかと。

    python -m venv venv
    .\venv\Scripts\Activate.ps1
    pip install -r requirements.txt

    ②配布すべきファイル群
    以下を"sn_ai_analyzer"に含む形で配布すると良いかと。

    app.py
    servicenow.py
    llm.py
    requirements.txt
    start.bat
    .env.example   ← .envのサンプルとして用意しておくと親切
    

    ③.envファイル
    以下は配布先のメンバーが実際に自分で利用する接続設定を入れて貰う形で配布するのが良いかと。APIキーの共通利用は不可とする。

    SN_INSTANCE=https://your-instance.service-now.com
    SN_USER=
    SN_PASS=
    OPENAI_API_KEY=
    COHERE_API_KEY=
    GEMINI_API_KEY=


    如何でしたでしょうか?
    今回はServiceNowのインシデント分析アプリに
    ・WebのGUIをStreamlitで簡単で高速に作成
    ・分析エンジンとしての各LLMに接続先を変えられる機能も追加
    ・分析プロンプトの入力も行える
    ・インシデントレコードの抽出条件もフィルター定義できる
    機能を実装して、運用でも利用しやすいアプリに仕立て上げてみました。

    Pythonによるアプリ開発がかなり効率化されている点で、また驚きました。

    そして、各LLM個社のAPIも頻繁にアップデートされ機能拡張されていることから、作ったものがすぐに陳腐化するのも、アプリ保守するうえではなかなか鋭敏な対応を求められそうだなという印象です。

    バイブコーディングが盛んになっているとはいえ、、アップデートに対してどのように機能更新をすすめてゆくべきなのかは、生成AIをAPI経由で使いこなしてゆくためにはデザイン含めた考慮すべきポイントではないかと。
    ※このAPIのアップデートとそれに関わる保守観点については、個人的には別途調査しておいた方が良いなと思いました。

    また次回のブログでも面白いアプリの作成を試みようと思います!では。

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

    あなたへのおすすめ