teacher asking a question to the class
Photo by Max Fischer on Pexels.com

【授業レポート】システム開発入門 第27週目 〜生成AI組込ハンズオン:設計をコードにする〜

第27週目は、先週作成した設計図をもとに生成AIを安全に組み込む実装ハンズオンを行いました。前処理(サニタイズ)→モデル呼び出し→出力検証→フォールバック→表示、という一連の流れを実際にコードとして組み上げ、動作確認と改善を繰り返しました。


■ 先生の導入:「設計を動くものにする楽しさと責任」

田中先生:「図にした設計は“考えの約束”。今日のゴールは、その約束をコードで確かめることです。安全対策は面倒に見えるかもしれないけれど、使う人のための大事な仕事です。」

授業は学校が用意した学習用環境(外部公開鍵なし・ログ管理・API呼び出し上限あり)で実施しました。


■ 今日の流れ(短縮)

  1. 前処理(sanitize)実装
  2. モデル呼び出しラッパー(安全タイムアウト・例外処理)作成
  3. 出力検証(形式チェック・禁止語チェック・簡易ファクトフラグ)実装
  4. キャッシュ&フォールバック実装
  5. UIへの出力表示と注意書きの自動付与
  6. 動作確認と班内レビュー

■ 実装例(授業で扱った擬似コード・概要)

※授業で扱ったコードは学習用の簡易実装です。実運用ではさらに堅牢な検証・監査が必要です。APIキーは環境変数や秘密管理で扱い、ソースに直書きしません。

import os
import time
from typing import Tuple

# 簡易キャッシュ(授業用)
CACHE = {}

def cache_get(key):
    entry = CACHE.get(key)
    if entry and time.time() - entry["ts"] < entry["ttl"]:
        return entry["value"]
    return None

def cache_set(key, value, ttl=300):
    CACHE[key] = {"value": value, "ts": time.time(), "ttl": ttl}

def sanitize_input(text: str) -> str:
    # 1) 個人情報パターンをマスク(例示的)
    text = mask_email_phone(text)
    # 2) 禁止語チェック(見つかったら例外)
    if contains_prohibited(text):
        raise ValueError("入力に不適切な語句が含まれています。")
    # 3) 長さ制限
    return text[:1000]

def call_model_api(prompt: str, timeout=5) -> str:
    # 学習環境のモデル呼び出しラッパー(疑似)
    api_key = os.getenv("SCHOOL_API_KEY")
    if not api_key:
        raise RuntimeError("APIキーが設定されていません。")
    # 実際の呼び出しはここ(requests or provider SDK)
    # 例外・タイムアウトをハンドルする
    resp_text = safe_model_call(prompt, timeout=timeout)
    return resp_text

def validate_output(output: str) -> Tuple[str, str]:
    # 形式チェック(例:箇条書きか)
    if not matches_expected_format(output):
        return ("retry", "出力形式が違います。再生成します。")
    if contains_prohibited(output):
        return ("fallback", "不適切な表現が含まれていました。代替応答を表示します。")
    if needs_fact_flag(output):
        return ("confirm", "一部情報は要確認です。")
    return ("ok", output)

def get_ai_response(user_input: str) -> str:
    try:
        clean = sanitize_input(user_input)
    except ValueError as e:
        return f"入力エラー:{e}"

    key = f"ai:{clean}"
    cached = cache_get(key)
    if cached:
        return cached

    try:
        resp = call_model_api(make_prompt(clean), timeout=5)
    except Exception:
        return local_fallback_response()

    status, body = validate_output(resp)
    if status == "ok":
        cache_set(key, body, ttl=300)
        return body
    elif status == "retry":
        # 短いリトライを行ってみる
        try:
            resp2 = call_model_api(make_prompt(clean) + "\n出力は箇条書きでお願いします。", timeout=5)
            s2, b2 = validate_output(resp2)
            if s2 == "ok":
                cache_set(key, b2, ttl=300)
                return b2
        except Exception:
            pass
        return local_fallback_response()
    elif status == "fallback":
        return local_fallback_response()
    elif status == "confirm":
        return f"{body}\n※生成結果に重要な事実が含まれています。必ず確認してください。"

■ ハンズオンでの生徒の気づき

  • 「最初はAIが全部答えてくれる気がしてたけど、検証パイプラインを書かないと安心して使えないと分かった」
  • 「キャッシュすると早いし、APIの呼び出し回数が減るからコスト管理にもなる」
  • 「フォールバックのテンプレートを作る発想が便利。ユーザーに変な体験をさせない工夫だね」
  • 「エラーメッセージを具体的にするとテスト担当にも親切だと感じた」

■ 教師の観察とアドバイス

田中先生は、生徒のコードを見て回りながら次の点を強調しました。

  • 「入力検査は厳しめで良い。想定外のデータが入ると取り返しがつかない」
  • 「自動判定(要確認フラグ)を出すと、ユーザーにとっても運用側にとっても安全」
  • 「ログは残すが、個人データはマスク・短期保存に。運用ルールを設計に落とし込もう」
  • 「テスト(ユニットテスト/結合テスト)を書いて、想定外挙動を自動で検出できるようにすると安心」

■ まとめのひとこと

「設計どおりに動く喜びに加えて、『こういう失敗が起きるかもしれない』を考え、それを防ぐ仕組みを作ることが重要です。今日学んだパターンは、生成AIに限らず安全なシステム設計の基礎になります。」


■ 宿題(実践+振り返り)

  1. 今日作成した実装の**フロー図(入力→sanitize→API→validate→表示)**を提出。
  2. サニタイズで扱ったルールを3つ列挙し、それぞれの根拠(30〜80字)を書く。
  3. 自分の実装で想定される失敗ケースを2つ挙げ、その対処法を示す(各1〜2行)。

■ 来週の予告:運用テスト&ログ解析入門

次週は、作ったシステムを想定負荷で動かす運用テストと、残したログを使った**簡単な解析(誤応答の頻度・多かった入力パターン)**を行います。設計の改善サイクルを回すフェーズに移ります。


設計をコードに落とし、動く安全対策を実践した27週目。生徒たちは「便利さ」と「責任」を両立させる実装の感覚を、確かに体得し始めています。

投稿者 greeden

コメントを残す

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

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