fugu ultra が示した「実測で仮説を更新するAI開発」
- ccf代表
- 6月29日
- 読了時間: 10分
匿名化ケーススタディ:基幹システムにおけるリソース割り当て機能の性能改善
※ 本記事は、実プロジェクトの技術的な学びをもとにした匿名化ケーススタディです。 業種・企業名・実際の機能名・APIパス・テーブル名・カラム名などは、公開用に抽象化しています。
ある大規模基幹システムの再構築プロジェクトにおいて、特定業務画面(リソース割り当て画面)で利用する候補一覧 API /api/v1/resource-assign/candidates に性能課題がありました。
症状は、ページ送りをすると応答が遅くなることです。浅いページでは許容範囲でも、深いページでは約900msまで伸び、実務担当者の操作テンポを落としかねない状態でした。
今回、sakana.ai の fugu ultra は、この課題に対して独立に方針検討を行い、実機検証の進め方と改善案を提示しました。 最終的には、当初想定していた大きなDB変更や複雑な二段化クエリではなく、1ファイル・実質10行の最小修正で、深いページの応答を 約900ms → 約380ms台へ改善しました。
このケースで特に重要なのは、fugu ultra が「一発で正解を当てた」ことではありません。 むしろ、自らの初期仮説を、実測によって更新し、より小さく正しい修正へ収束させた点にあります。
背景:過去の対策では改善しなかった
対象は、処理待ちのトランザクションデータから割り当て候補を一覧表示する API です。
この API には、以下のような問題がありました。
ページが深くなるほど遅くなる
1ページ目より、後続ページの応答が悪化する
SQL Server 実機で、深ページが約900msまで伸びる
すでに試した索引追加・ORDER BY 正規化では、実測上の改善がなかった
過去の対策は、いずれも理屈としては妥当でした。
対策 | 狙い | 実機での結果 |
順序保持索引の追加 | ORDER BY を索引で吸収し、Sortを消す | 索引が使われず改善なし |
ORDER BY 正規化 | 並び順をDBが扱いやすい形に整理する | Sortが残り、改善なし |
二段化案 | 先にページ対象キーだけ確定し、後から表示列を取る | 以前の試行では逆に遅くなる懸念あり |
つまり、単純に「索引を足す」「ORDER BY を直す」では解けない問題でした。
fugu ultra が最初に提案した方針
fugu ultra は、コード・実測履歴・SQL構造を読み解き、主な原因候補を次のように整理しました。
1. 非被覆索引の問題
候補一覧は多数の列を取得し、WHERE 句でも複数列を参照しています。 そのため、既存索引だけでは足りず、余分なルックアップが発生している可能性がありました。
2. 表示用 OUTER APPLY がページ送りに比例して重くなる問題
名称や属性マスタなどの表示用情報を取得する処理が、ページ確定前に走ることで、深いページほど評価回数が増える可能性がありました。
3. 最新状態判定サブクエリ
MAX(SEQ_NO) を使って最新のシステム設定状態を判定するサブクエリが存在しました。 ただし、これは外側の行を参照しない非相関サブクエリです。
そのため fugu ultra は当初、この原因を「理論上は1回評価されるはずであり、根本原因としては低優先」と判断しました。
この分析に基づき、fugu ultra が提案した当初方針は以下でした。
方針 | 当初評価 |
被覆索引、特に不足列の INCLUDE 追加 | 有力候補 |
二段化 / keyset 的なページング | 主軸候補 |
表示用 OUTER APPLY をページ確定後に後置 | 主軸候補 |
サブクエリの事前計算 | 低優先 |
ただし、fugu ultra の提案で最も重要だったのは、具体的な実装案そのものではありません。 それ以上に重要だったのは、次の検証方針です。
形だけの改善ではなく、実機の実行計画と実測で判断する。
fugu ultra は、以下を受け入れ条件として明確にしました。
実機 SQL Server で実行計画を確認する
どの条件が遅さに効いているかを段階的に分離測定する
page1 だけでなく、深いページも測る
画面経由・API経由の両方で再測する
行集合や並び順が変わっていないことを確認する
読み取り専用E2Eで、実画面の導線に問題がないことを確認する
この「実測で判断する」規律が、後に決定的な意味を持ちます。
実測で判明した真因:低優先と見ていたサブクエリが律速だった
実装・診断の段階で、WHERE 句の条件を切り分け、実機で段階的に測定しました。
その結果、当初は低優先と見ていた設定管理テーブル(SYSTEM_CONTROL)の最新版サブクエリが、実はボトルネックであることが分かりました。
問題の構造は、概念的には次のようなものです。
WHERE
h.STATUS_FLAG = '2'
OR h.LATEST_VERSION_NO = (
SELECT MAX(SEQ_NO)
FROM SYSTEM_CONTROL
WHERE ACTIVE_FLAG = '0'
AND RTRIM(FILE_NAME) = 'TARGET_FILE'
)このサブクエリは、外側の行を参照していないため、論理的には1回だけ評価されれば十分です。 しかし実機では、SQL Server が OR 条件の中でこの非相関サブクエリを、オフセット行数ぶん繰り返し評価していました。
段階診断では、深ページ相当の条件で以下のような差が出ました。
診断内容 | 実測 |
現状:OR 内にサブクエリあり | 587ms |
サブクエリ条件を除去 | 1ms |
最新値を事前計算して変数で渡す | 9ms |
これにより、真因は明確になりました。
問題は、索引不足でも、二段化不足でも、表示用 OUTER APPLY でもなく、OR 内の非相関サブクエリが実機で繰り返し評価されることでした。
これは、静的なコード理解だけでは見落としやすい問題です。
fugu ultra は当初、このサブクエリを「非相関なので低優先」と見ていました。 しかし、fugu ultra 自身が提案した「実機で段階測定する」という方針によって、その初期仮説が修正されました。
間違えないことではなく、間違った仮説を実測で素早く更新できること。今回の成果は、まさにその例でした。
改善内容:1回だけ取得して、パラメータとして渡す
真因が分かると、修正は非常に小さくなりました。
やったことはシンプルです。
設定テーブルから最新シーケンス番号を、事前に1回だけ取得する
その値を WHERE 句にパラメータとして渡す
型は decimal(10,0) として明示し、SQL Server の暗黙変換リスクを避ける
概念的には、以下の変更です。
Before:
WHERE (... OR h.LATEST_VERSION_NO = (SELECT MAX(SEQ_NO) FROM SYSTEM_CONTROL ...))
After:
latestSeqNo = SELECT MAX(SEQ_NO) FROM SYSTEM_CONTROL ...
WHERE (... OR h.LATEST_VERSION_NO = CAST(@latestSeqNo AS decimal(10,0)))この変更により、サブクエリの繰り返し評価がなくなりました。
項目 | 内容 |
対象 | 候補一覧クエリ処理 |
変更ファイル | src/lib/repo/prisma/resourceAssignRepo.ts (相当) |
変更量 | 追加10行 / 削除2行 |
DBスキーマ変更 | なし |
索引追加 | なし |
二段化 | 不要 |
業務挙動 | 変更なし |
並び順 | 変更なし |
特定テナントにおけるスキップ分岐 | 維持 |
重要なのは、挙動を変えずに速くしたことです。
サブクエリは非相関で、クエリ全体に対して一定値です。 そのため、事前に1回取得してパラメータ化しても、取得される行集合は変わりません。
つまり、業務仕様を変えず、DBスキーマも変えず、最小のコード変更でボトルネックだけを取り除きました。
改善前後の実測
改修後の実機診断では、DB層で以下の改善が確認されています。
ページ位置 | 改善前 | 改善後 |
page1 相当 | 約563ms | 約381ms |
深ページ相当 | 約900ms | 約379ms |
さらに深い offset | — | 約386ms |
深いページで 約900ms → 約380ms。 さらに、ページが深くなるほど悪化する傾向も解消し、ほぼフラットになりました。
デプロイ後のE2E・API再測定
改修後、検証用環境にデプロイし、実際の画面・API経路で再測定しました。 ヘルスチェックも正常でした。
ブラウザE2E
実DBに接続しているため、E2E は読み取り専用で実施しました。 確定・登録・帳票出力・データ更新など、状態変更を伴う操作は行っていません。
項目 | 結果 |
特定業務画面の表示 | PASS |
初期一覧表示 | PASS |
検索ボタンで一覧データ GET 成功 | PASS |
次へで2ページ目へ遷移 | PASS |
前へで1ページ目へ復帰 | PASS |
読み取り専用ガード違反 | なし |
console error | なし |
画面操作の体感性能
検証スクリプトにて画面操作を自動化し、複数回実行しました。
測定 | 体感 median | API median | 体感 p95 | API p95 | 1秒バジェット |
1回目 | 527.5ms | 478.3ms | 560.1ms | 518.1ms | PASS |
2回目 | 497.8ms | 451.3ms | 525.9ms | 475.8ms | PASS |
いずれも、1秒以内の性能バジェットを満たしました。
APIページ別性能
パフォーマンステスト用スクリプトで page1 / page2 / page5 / page7 を測定しました。
ページ | median |
page1 | 456.4ms |
page2 | 446.7ms |
page5 | 463.9ms |
page7 | 431.8ms |
全ページ ok=10 / bad=0。 深いページでの悪化は見られず、ほぼフラットでした。
また、以下も確認済みです。
同一条件で取得した行の並びが完全に一致:並び順は決定的
特定テナントにおけるスキップ分岐:正常動作を確認済み
必須日付未指定時の挙動:400エラー応答を正しく維持
関連ローカルテスト:すべて PASS
データが増えたら、また遅くなる可能性はあるか
結論から言うと、ありえます。
ただし、今回の真因であるサブクエリの繰り返し評価は解消済みです。 そのため、同じ原因で page が深くなるほど急激に悪化する問題は、今回の修正で取り除かれています。
一方で、当該APIには、データ量に影響を受ける処理が残っています。
COUNT_BIG(*) による件数カウント
OFFSET/FETCH によるページング
JOIN
ORDER BY
表示用の OUTER APPLY
検索期間・テナント・拠点スコープ内の候補件数
現在のデータ量では、APIは約430〜465msで安定しています。 しかし将来、同一条件でヒットする候補件数が大きく増えれば、別の要因で速度低下が起こる可能性はあります。
その場合の次の打ち手も整理済みです。
状況 | 次の対応候補 |
件数カウントが律速になる | 深ページでの count 省略、軽量count、キャッシュ検討 |
OFFSET/FETCH が再び効いてくる | keyset方式、または二段化を再検討 |
JOIN / APPLY が重くなる | ページ確定後に表示列を後置 |
索引不足が明確になる | 最小限の INCLUDE 索引を検討 |
継続的に監視したい | 専用の perf profile を追加し、定点観測 |
つまり、今回の修正は「今だけ速くする」ものではありません。 真因を最小修正で取り除きつつ、将来データが増えた場合に備えた次の改善余地も残しています。
この事例が示す fugu ultra の強み
このケーススタディは、sakana.ai / fugu ultra の優秀さを示す好例です。
ポイントは3つあります。
1. 大改修ではなく、最小修正へ収束した
当初は、被覆索引、二段化、keyset ページング、APPLY後置など、複数の重い対策が候補に上がっていました。
しかし実測の結果、本当に必要だったのは、最新のシステム状態変数を1回だけ事前取得する最小修正でした。
結果として、以下をすべて回避できました。
DBスキーマ変更
索引追加
テーブル定義書など設計書の更新
利用部門の合意が必要なDB変更
複雑な二段化クエリ
画面仕様変更
ページング仕様変更
「賢い大改修」ではなく、正しい小修正を選べたことが大きな成果です。
2. 自分の初期仮説を、実測で更新できた
fugu ultra は当初、当該のサブクエリを「非相関なので低優先」と判断していました。
しかし同時に、実機で段階測定する方針も提示していました。 その検証によって、実際にはこのサブクエリが律速であると判明しました。
優秀なAIとは、最初から常に正しいAIではありません。事実に基づいて、自分の仮説を更新できるAIです。
今回、fugu ultra はその能力を実務の中で示しました。
3. 複数AI運用の価値が出た
この課題検証では、別のAIによる検討も並行して行われていました。
複数のAIが独立に分析し、方針を残し、実装後にレビュー・E2E・性能測定まで行うことで、単一の視点では見落としやすい問題を検出できました。
特にエンタープライズシステムの移行・再構築プロジェクトでは、以下のような落とし穴が多くあります。
SQL Server等のRDBMS特有の実行計画
旧データベースとの挙動差
ORDER BY の安定性
OFFSET/FETCH の性能
型変換・decimal・NULL
業務上の例外分岐
現行踏襲すべき仕様と、直すべき不具合の切り分け
今回のような複数AIによる独立検討は、こうした罠に対して有効です。
まとめ
本件では、候補一覧 API の性能課題に対し、fugu ultra が独立に方針検討を行い、実機検証を通じて最小修正へ到達しました。
項目 | 結果 |
対象 | /api/v1/resource-assign/candidates |
改善内容 | 最新のシステム設定状態を事前取得し、WHEREにパラメータ渡し |
変更量 | 1ファイル・実質10行 |
DBスキーマ変更 | なし |
深ページ応答 | 約900ms → 約380ms台 |
API実測 | 約430〜465msでフラット |
画面E2E | PASS |
1秒性能バジェット | PASS |
業務挙動 | 変更なし |
特定テナントのスキップ分岐 | 維持・正常確認済み |
データ増加時のリスク | ありえるが、今回の真因は解消済み。次の打ち手も整理済み |
この事例は、fugu ultra が単にコードを書くAIではなく、 仮説を立て、実測し、誤りを修正し、最小の変更で成果を出すAIエージェント であることを示しています。
AI開発の価値は、派手な大改修だけではありません。 現場にとって本当に価値があるのは、業務挙動を壊さず、現場部門の合意が必要な変更を避け、実測で効果を確認しながら、最小の修正で確実に改善することです。
本件のパフォーマンスチューニングは、その好例となりました。




コメント