【授業レポート】システム開発入門 第27週目 〜生成AI組込ハンズオン:設計をコードにする〜
第27週目は、先週作成した設計図をもとに生成AIを安全に組み込む実装ハンズオンを行いました。前処理(サニタイズ)→モデル呼び出し→出力検証→フォールバック→表示、という一連の流れを実際にコードとして組み上げ、動作確認と改善を繰り返しました。
■ 先生の導入:「設計を動くものにする楽しさと責任」
田中先生:「図にした設計は“考えの約束”。今日のゴールは、その約束をコードで確かめることです。安全対策は面倒に見えるかもしれないけれど、使う人のための大事な仕事です。」
授業は学校が用意した学習用環境(外部公開鍵なし・ログ管理・API呼び出し上限あり)で実施しました。
■ 今日の流れ(短縮)
- 前処理(sanitize)実装
- モデル呼び出しラッパー(安全タイムアウト・例外処理)作成
- 出力検証(形式チェック・禁止語チェック・簡易ファクトフラグ)実装
- キャッシュ&フォールバック実装
- UIへの出力表示と注意書きの自動付与
- 動作確認と班内レビュー
■ 実装例(授業で扱った擬似コード・概要)
※授業で扱ったコードは学習用の簡易実装です。実運用ではさらに堅牢な検証・監査が必要です。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に限らず安全なシステム設計の基礎になります。」
■ 宿題(実践+振り返り)
- 今日作成した実装の**フロー図(入力→sanitize→API→validate→表示)**を提出。
- サニタイズで扱ったルールを3つ列挙し、それぞれの根拠(30〜80字)を書く。
- 自分の実装で想定される失敗ケースを2つ挙げ、その対処法を示す(各1〜2行)。
■ 来週の予告:運用テスト&ログ解析入門
次週は、作ったシステムを想定負荷で動かす運用テストと、残したログを使った**簡単な解析(誤応答の頻度・多かった入力パターン)**を行います。設計の改善サイクルを回すフェーズに移ります。
設計をコードに落とし、動く安全対策を実践した27週目。生徒たちは「便利さ」と「責任」を両立させる実装の感覚を、確かに体得し始めています。