0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AIが作り、AIが配信し、AIが落ちる — 誰も見ていないシステムの品質保証

0
Posted at

📝 この記事は forge.workstyle.tech に掲載した記事の転載です。

AI アバターが YouTube と Twitch に無人で配信し続けるシステムを作りました。番組を登録しておけば、時間になると配信枠が作られ、GPU が起動し、視聴者のコメントに音声で応答し、尺が来たら締めて終わります。当日、人間は何もしません。

作っている間、ずっと引っかかっていたのが「これ、何をテストすれば品質を保証したことになるんだ」でした。出力は毎回違う。正しさは主観。そして一番厄介なことに、失敗しても止まらない。誰も見ていないので、誰も気づかない。

この記事は、その状況で「保証する対象を組み替えた」記録と、その一部として避けて通れなかった「長時間でしか出ないバグ」への向き合い方をまとめたものです。前半は QA の設計思想、後半はソークテストの実務です。開発者向けに、つまずき → 原因 → 解決の順で書きます。

従来のテストが前提にしていた3つのこと

書いていて気づいたのは、これまでのテストが暗黙に置いていた前提が、3 つとも成り立たないということでした。

前提 無人 AI 配信では
入力が決まっている 視聴者が何を書くか分からない
出力が判定できる LLM の出力は毎回違う。正解の文字列がない
失敗すると止まる **失敗しても止まらない。**無言のまま配信が続く

3 つ目が一番厄介でした。普通のシステムは、壊れると例外が飛び、リクエストが失敗し、誰かが気づきます。**配信は壊れても流れ続けます。**映像は出ている。プロセスも生きている。ヘルスチェックも緑。ただアバターが一言もしゃべらない。

これは実際に起きた障害です。対話サーバだけが再起動したとき、途中の層が切断を下流に伝えていなかったため、**視聴者から見ると「AI が黙り込んだ配信」**になっていました。どの監視項目にも出ません。この「生きているのに機能していない」状態は、この記事を通して何度も出てきます。無人運用における最悪の状態だからです。

保証する対象を組み替える

そこで、保証の対象を出力から振る舞いへ移しました。「アバターが良いことを言うか」は保証できません。保証できるのは、システムが期待どおりに振る舞うかです。

具体的には 4 つに絞りました。

1. 止まるべきときに止まる

自動化されたシステムで怖いのは、失敗ではなく成功の継続です。誰も見ていないので、動き続けている限り誰も気づきません。そして GPU の課金は続きます。

止まる条件を明示的に実装しました。

  • 番組の尺が来たら、締めの挨拶をして終了する
  • 一定時間ライブにならなければ、失敗として片付けて Pod を破棄する
  • レンダラーが短時間に復帰を繰り返したら、自分でプロセスを終了する

3 つ目は「自分から死ぬ」実装です。直感に反しますが、異常を上位に伝える最も確実な手段でした。

2. 壊れたら気づける

「生きているのに機能していない」状態を、最悪の状態として定義しました。

そのうえで、各層に「上流が死んだらどう振る舞うか」を明示しました。既定の振る舞いは、たいてい「何もしない」です。何もしないと、切断が途中の層で吸収されて消えます。冒頭の「黙り込んだ配信」は、まさにこれが原因でした。

  • 上流の切断を検知したら、自分の下流の接続も切る
  • 切られた側は再読み込みし、再接続する
  • 再接続が失敗しても、上流が戻るまでリトライする

**死を伝播させる。**可用性のために生き延びようとする層が、全体の可用性を下げていました。

3. 壊れたら戻る(そして戻ることを実測する)

復帰処理は、書いただけでは動きません。実際に壊して測りました。

壊し方 復帰
配信中に GPU の Pod を手で削除 32 秒で検知、別ホストで再開
対話サーバをローリング再起動 41 秒で完全復帰(状態の復元込み)

障害注入は、それぞれ数分の作業です。**やらずに書いた復帰コードは、たいてい一箇所で止まります。**我々も、実際に壊してみて初めて「再接続のリトライがないと、再起動中に当たったとき止まる」ことに気づきました。前節の「リトライを入れる」は、机上ではなくこの実測から出た対策です。

4. 出してはいけないものを出さない

出力そのものは判定できませんが、出力の境界は判定できます。

  • チャットの入口で NG ワードと連投を落とす(汚れたものは入口で止める)
  • AI 生成コンテンツであることをプラットフォームに申告する
  • 検証中は限定公開に固定し、公開に切り替えるのは人間の判断にする

「何を言うか」ではなく「何を入れるか、どう出すか」を制御する、という発想の切り替えです。

それでも人間が見る場所を残す

すべてを機械で判定できるわけではありません。実際、性能指標が全部正常なまま映像だけが壊れる不具合がありました。キャラクターの顔が明滅しているのに、fps もエラーも GPU 使用率も正常。

見つけたのは人間の目です。以降、描画や音声に関わる変更は、実物を見聞きしてから確定する運用にしました。

自動化できる検査を増やすほど、人間が見る場所を意識的に残す必要がある、というのが逆説的な学びでした。全部緑のダッシュボードは、全部正常であることを意味しません。

長時間でしか出ないものがある

ここまでの検査を短時間で全部通しても、まだ足りませんでした。2 時間回すと壊れるものがあるからです。10 分のテストは全部通っていたのに、本番相当の長さで回すと別の顔が出てきます。

実際に長時間稼働で見つかった不具合を並べたら、きれいに 4 つに分かれました。**どれも短時間のテストでは原理的に検出できません。**回す時間そのものが検査項目だった、ということです。

類型 例 なぜ短時間で出ないか
累積 タイマーの遅れが積もってリップシンクが漂流 1 回の誤差が小さい。時間に比例して増える
増加 履歴・メモリ・状態が際限なく増える 上限に当たるまで時間がかかる
周回 同じ話題を繰り返し始める バッファが一周するまで起きない
再起動 更新されたトークンが再起動で失われる プロセスが生きている間は症状が出ない

以下、4 類型を一つずつ、原因と検出のコツで見ていきます。

類型1: 累積 — 1回の誤差 × 回数

配信映像のフレームを一定間隔で書き出す処理を setInterval で書いていました。タイマーの発火は遅れることがあり、遅れた分は取り戻されません。

1 回あたり 1ms の遅れでも、30fps なら 1 分で 1.8 秒ぶんのフレームが欠けます。音声は実時間で流れているので、映像だけが遅れていく。開始時は合っているのに、時間が経つほどズレるという症状でした。

検出のコツ: 「時間が経つほど悪化するか」を明示的に確認する。開始直後と 1 時間後で同じ測定をして比較します。「調整しても再発する」と感じたら、たいてい累積です。

類型2: 増加 — 上限があるかを実測する

配信の状態(会話履歴、視聴者ごとの記憶など)を保持していました。設計上は直近のぶんだけ持つつもりでしたが、設計の意図と実装が一致しているかは別問題です。

2 時間の稼働で確認した数字がこれです。

項目 結果
状態スナップショットのサイズ 6.2KB で頭打ち
プロセスのメモリ 増加なし

「頭打ち」を確認できたことが重要でした。増え続けるなら、いずれ必ず落ちます。上限があるという設計を、実測で裏付ける。

検出のコツ: サイズやメモリを定期的に記録して、グラフが寝るか、伸び続けるかを見る。10 分では両者の区別がつきません。

類型3: 周回 — バッファが一周する

コメントが来ないときに自分で話題を出す機能を入れていました。直近の会話履歴を見て、同じ話題を避けるようになっています。

問題は、履歴の保持量がそのまま「話題が一周する周期」になることです。履歴から消えた話題は、また新鮮な話題として選ばれます。視聴者から見ると「さっきも同じ話をしていた」になります。

これは 10 分のテストでは絶対に出ません。履歴が一周していないので。

検出のコツ: 「N 件保持」「N 分保持」という設計値があったら、その N を超える長さで回す。リングバッファ、キャッシュ、重複排除、レート制限。同じ構図はいろいろな場所にあります。

類型4: 再起動 — プロセスが生きている間は正常

外部サービスのトークンを更新するとき、新しいリフレッシュトークンが返ってくることがあります。それをプロセスのメモリに持っているだけだと、再起動した瞬間に古い値に戻ります。

古い値がまだ有効なら動きますが、無効化されていれば認証が通らなくなります。プロセスが動き続けている間は完全に正常なので、ソークテストですら見つかりません。連続稼働と再起動耐性は、別のテストなのです。

検出のコツ: ソークテストの途中でわざと再起動する。

終わり際と、後片付け

長時間テストでしか確認できないものが、4 類型のほかにもう 2 つあります。

終わり際の処理。番組の締めの挨拶、終了処理、リソースの解放。これらは最後の 1 分にしか動きません。途中で止めたテストでは一度も実行されていないコードです。「止まるべきときに止まる」を実装しても、その締め処理を最後まで走らせて確かめなければ、動く保証はありません。

後片付け。我々は毎回、終了後に GPU の Pod が本当に 0 台になっているかを確認しています。止まらないと課金が続くので、ここは目視の確認項目にしました。「終わったはず」を信じないことにしています。

ソークで見つかった最悪のもの

2 時間のソークで見つかった中でいちばん重要だったのは、冒頭で触れた対話サーバだけが死んだとき、配信が無音のまま続くという挙動でした。

  • 映像は出ている
  • プロセスは生きている
  • どのヘルスチェックも緑
  • アバターだけが一切しゃべらない

これは「壊れ方が観測されない」という点で、4 類型よりさらに危険です。長時間回していなければ、本番で初めて遭遇していました。「壊れたら気づける」という設計目標そのものが、この一件を長時間回して観測できたからこそ具体化できた、とも言えます。

ソークテストの観測項目

我々が実際に見ているものを挙げておきます。長時間テストの持ち物リストとして使えます。

  • メモリ / 状態サイズの推移(寝るか、伸びるか)
  • 時刻のズレ(開始時と終了間際で同じ測定をする)
  • 出力の実尺 vs 実時間(脱落していないか)
  • 同じ挙動の繰り返し(話題、応答パターン)
  • 途中での再起動と、そこからの復帰時間
  • 終了処理が実際に走ること
  • 後片付け(残っているリソースがゼロか)

まとめ

QA の設計として:

  • 無人で動く AI システムでは、テストの前提(入力が既知・出力が判定可能・失敗すると止まる)が3 つとも崩れる
  • 保証の対象を出力の正しさから、システムの振る舞いへ移す
  • 具体的には 4 つ: 止まるべきときに止まる / 壊れたら気づける / 壊れたら戻る / 出してはいけないものを出さない
  • 「生きているのに機能していない」を最悪の状態として定義する
  • 復帰は実際に壊して計測する(Pod 削除で 32 秒、対話サーバ再起動で 41 秒復帰)。書いただけの復帰コードは動かない
  • 自動検査を増やすほど、人間が見る場所を意識的に残す

ソークテストの実務として:

  • 長時間でしか出ないバグは 累積・増加・周回・再起動 の 4 類型に整理できる
  • 累積は「時間が経つほど悪化するか」で見つける
  • 増加は「上限があること」を実測で裏付ける(状態は 6.2KB で頭打ちを確認)
  • 周回は設計値(N 件保持)を超える長さで回して初めて出る
  • 再起動はソーク中にわざと再起動しないと出ない
  • 終わり際の処理と後片付けは、最後まで回したテストでしか実行されない
  • 「10 分動いたから大丈夫」は、4 つの類型のどれ 1 つも検査していないという意味

「AI の出力の品質をどう保証するか」に正面から答えるのは難しいですが、**「AI が変なことを言っても、システムとしては壊れない」**を保証するのは可能でした。無人運用では、後者のほうがずっと重要でした。そして回す時間を伸ばすだけで見つかるバグが、これだけあります。


元記事: https://forge.workstyle.tech/blog/qa-and-soak-testing-for-unattended-ai/?utm_source=qiita&utm_medium=crosspost&utm_campaign=qa-and-soak-testing-for-unattended-ai

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?