boy in green shirt
Photo by CDC on Pexels.com

【授業レポート】システム開発入門 第28週目 〜運用テスト&ログ解析入門〜

第28週目は、先週実装した生成AI組込システムの**運用テスト(負荷・安定性確認)**と、ログ解析による挙動の可視化に取り組みました。実運用を想定した検証と、問題発見→改善につなげる技術を学ぶ回です。


■ 先生の導入:「テストは“出発点”ではなく“繰り返すべき仕事”」

田中先生:「実際に人が使い始めると、想定外の操作や入力の組み合わせが出てきます。運用テストで“どんな失敗が起きるか”を前もって見つけ、ログで検証できる仕組みを作ることが重要です。」

授業では「信頼できるサービスは、テストとログで裏付けられている」という視点で進みました。


■ 今日のゴール

  1. 簡易負荷テストを実行して応答時間・成功率を計測する。
  2. ログの形式を統一し(タイムスタンプ・ステータス・プロンプトハッシュなど)、集計可能にする。
  3. ログ解析で「エラー率」「多い入力パターン」「再生成の頻度」などを可視化し、改善点を洗い出す。

■ 実習①:負荷テストの設計と実行(学習環境で安全に)

まずは負荷テストの設計。以下のようなシナリオを班ごとに作成しました。

  • ライト負荷:連続リクエスト 1 req/sec を 5 分間
  • ピーク負荷:30 秒間で 10 req/sec のバースト
  • スパイク後回復:急増→待機→通常に戻す、を複数回繰り返す

その後、学習用環境の制限内で擬似リクエストを送る簡易スクリプトを実行し、以下を計測しました。

  • 平均応答時間(ms)
  • 95パーセンタイル応答時間(ms)
  • 成功率(HTTP 200 / 全リクエスト)
  • タイムアウトや例外発生回数

授業で使った簡易負荷スクリプト(疑似、学習用)

import time, requests
def send_requests(url, n, interval):
    results = []
    for i in range(n):
        start = time.time()
        try:
            r = requests.post(url, json={"prompt":"テスト"}, timeout=5)
            latency = (time.time() - start) * 1000
            results.append(("ok", r.status_code, latency))
        except Exception as e:
            latency = (time.time() - start) * 1000
            results.append(("error", str(e), latency))
        time.sleep(interval)
    return results

生徒の反応:「ピークでタイムアウトが増えた。キャッシュやリトライ戦略が有効そう!」


■ 実習②:ログ形式の決定と一貫した出力

運用で役立つログ形式をクラスで決め、アプリに組み込みました。今回採用した最小限のログ項目例:

  • timestamp(ISO8601)
  • request_id(ユニーク)
  • prompt_hash(同一プロンプトを集計するため)
  • user_id(匿名化orマスク)
  • status(ok / retry / fallback / error)
  • latency_ms
  • error_type(存在する場合)
  • note(要確認フラグ等)

ログは JSONL(1行 = 1イベント)で出力し、後処理しやすくしました。


■ 実習③:ログ解析 — Pythonで基本集計

集めたログを読み、頻出エラーや応答時間分布を確認する簡単な解析を実施。授業で使った解析スニペット(学習用例)を示します。

import json
from collections import Counter
from statistics import mean, median

logs = []
with open("app_logs.jsonl", "r", encoding="utf-8") as f:
    for line in f:
        logs.append(json.loads(line))

# 成功率
total = len(logs)
ok = sum(1 for l in logs if l["status"] == "ok")
error = total - ok
print(f"total={total}, ok={ok}, error={error}, error_rate={error/total:.2%}")

# 応答時間(ms)
latencies = [l["latency_ms"] for l in logs if isinstance(l.get("latency_ms"), (int,float))]
print(f"avg={mean(latencies):.1f}ms, median={median(latencies):.1f}ms, max={max(latencies):.1f}ms")

# エラーの種類カウント
error_types = Counter(l.get("error_type", "none") for l in logs)
print("error types:", error_types.most_common(10))

# 多いプロンプト(ハッシュ)上位
prompt_counts = Counter(l.get("prompt_hash") for l in logs)
print("top prompts:", prompt_counts.most_common(10))

生徒の気づき:「特定のプロンプトハッシュで失敗が集中している。入力パターンの問題か、モデル側の制約かを切り分けよう。」


■ 実習④:可視化の簡単な紹介(授業内はプロトタイプ)

解析結果をもとに、簡単にグラフを作って傾向を掴みました(授業では matplotlib を使ったプロトタイプ表示)。

  • 時系列グラフ:リクエスト数・エラー数の推移
  • ヒストグラム:応答時間の分布
  • 棒グラフ:エラー種別の頻度

(※授業では色の指定をせず、標準スタイルで可視化しました)


■ 改善サイクル — ログからの課題抽出と対策立案

ログ解析で見つかった代表的な課題と、クラスで出した対策案の一例:

  • 課題:特定プロンプトでタイムアウト発生が多い。
    対策案:入力長を制限(前処理で切り詰め)・モデル呼び出しのタイムアウト短縮+フォールバック強化。

  • 課題:ピーク時に成功率が下がる。
    対策案:キャッシュTTL延長・リトライのバックオフ実装・簡易レートリミッタの導入。

  • 課題:ユーザーからの再生成要求が頻繁。
    対策案:プロンプトの厳密化・生成結果の形式チェックを強化し、再生成の条件を自動化。


■ 先生のひとこと

「ログは“過去の声”。数字を見れば、どこがおかしいかが見えてきます。重要なのは、発見した課題を小さな改善に分解して、すぐに試すことです。」


■ 生徒のふり返りコメント

  • 「ログを見て、問題点を客観的に語れるようになった」
  • 「負荷テストで初めてユーザー視点の不便さがわかった」
  • 「解析で見つかった ‘多い入力パターン’ を前処理で弾けたのが嬉しかった」

■ 来週の予告:運用改善の実施&A/Bテスト基礎

次週は、今週の解析で出た改善案を実装して再テストを行い、改善の効果を定量的に確認します。また簡単な A/B テスト の考え方(2つの改善案を比較する方法)を学び、どちらがより効果的かを評価します。


運用テストとログ解析を通して、1年生たちは「作る」だけでなく「運用して改善する」実務に近い経験を積みました。システムは作って終わりではなく、使われてはじめて完成する――その実感が育った28週目でした。

投稿者 greeden

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)