takizawadetox
サービスレベルからプロダクトを逆算する設計について
2026年03月31日
takizawadetox
2026年03月31日
見出しはありません
要約を生成中...
こんにちわ。 ハッカソンからShift Bのイベントに参加したたっきーです。

ハッカソンをきっかけに作ってみたかったサービスである「様子見KB」を無事リリースすることができました。

サービスの内容はWindows Updateで適用されるパッチ(KB)の不具合情報を事前に調べられるサービスです。

公式情報では拾えない、未認定の不具合情報なども収集しており、Microsoft公式サイトの解析結果と合わせて情報を表示しています。

今回は、私がこのサービスの設計にあたり、「サービスレベルからプロダクトを逆算して実装する」というアプローチをとった過程について振り返り、その裏側のアーキテクチャを解説したいと思います。
具体的なアーキテクチャの話に入る前に、この記事の結論を先に書きます。
個人開発・業務ツールによくある「LLMや検索APIを使って何かを要約・判定するシステム」を作る時、誰もが直面するのが 「API代による個人破産」「対象サイトのDOM変更によるサイレント死」「狼少年化によるユーザー離れ」 という3つです。
これらを防ぐために、あらかじめ 「コスト要件(月〇〇円に抑える)」「信頼性要件(裏で壊れたらすぐ気づく)」「UX要件(ノイズは送らない)」 という3つのサービスレベルを設定しました。 そして、そのレベルを守るために、システムの大半を「AIへデータを渡す前の、泥臭い除外ロジックとパース処理」に費やしたこと。
それが、今回私が行った「逆算設計」の正体です。
「様子見KB」は、毎月配信される Windows Update(KB)に対して、「適用する前に不具合があってやばそうか」 を情シス担当者に代わって情報を集約するサービスです。
情シス担当者が抱える以下の課題を解決するために作られました。
Microsoftの公式ドキュメント()は情報が散在しており読み解くのが辛い。

公式が不具合(Known Issues)を認める前に、X(Twitter)やRedditで「印刷できない」「ブルースクリーンになる」といった困った事態になっていることが多い。

これらを人力でパトロールするのは毎月やるのは困難である。
この仕様を満たすため、「Microsoft公式情報のスクレイピング」「Brave SearchによるSNS情報の収集」「Claude 4.5 Haikuによる判定」という3段構えのアーキテクチャを採用しています。
ここから先は、私がこの開発過程で直面した「壁(課題)」と、サービスレベルの制約から逆算した「方法」を、章立てで解説していきます。
起点となるのはMicrosoft公式情報のスクレイピングです。
しかし、MSのサイト構成は突然変わります。昨日まで動いていたスクレイパーが、ある日突然空の配列を返し始める。ユーザーが「今月不具合ゼロだな」と誤認してKBを適用した瞬間に大事故が起きます。
更新がないか確認するために全KBのページに毎回アクセスしていれば、相手のサーバーに負荷をかけ、早晩IP BANされるでしょう。
データベースに保存された最終チェック日時や判定ステータスをもとに、「いま取得しにいくべきか」を事前に評価する hasChanged ロジックを実装しました。
async function hasChanged(kbNumber: string): Promise<boolean> {
const { data } = await supabase.from('kb_cache').select('title, checked_at').single()
// 1. 未保存の新規KBなら必ず取得
if (!data) return true
// 2. タイトルが「KBxxxx - Windows」のような自動生成フォールバック文字なら、
// MS側のページが完全に公開されきっていなかった証拠なので強制再取得
const isAutoGenerated = /^KB\d+\s+-\s+Windows/.test(data.title ?? '')
if (isAutoGenerated) return true
// 3. 最終チェックから7日以上経過=サイレント修正の確認のため再取得
const daysSinceCheck = (Date.now() - new Date(data.checked_at).getTime()) / (1000 * 60 * 60 * 24)
if (daysSinceCheck >= 7) return true
return false
}この3段構えにより、リクエスト数を激減させつつ情報の鮮度を保ちます。
さらに、「開発者(私)専用の死活監視アラート」として、スクレイパーの裏側にLINE Messaging APIを仕込みました。 実際にまだ異常を検知できていないので通知テストの際のスクショを共有します。

Cronジョブが走った際、取得件数が0件だったり、エラー率が50%を超えた場合はすぐに私のLINEのスマホが鳴ります。「相手(DOM)は絶対に変わる」という諦めを前提に、ユーザーが気づく前に裏で検知して直す体制を構築しました。
公式では認めていない「 コミュニティで芽吹いた不具合情報」を拾うためにBrave Search APIを採用しました。しかし、Brave APIはコール数課金です。$5までなら毎月無料で使えます。
「全パッチ」×「毎日」で検索をかければ、個人開発の財布は一瞬で吹き飛び(API破産)、サービスを停止せざるを得なくなります。コスト要件を満たしつつ、危険なKBを見逃さないためにはどうすればいいのか?

ここで、「検索する価値が高い KB」だけを狙い撃ちにするトリアージ関数 getTargetKbs() をClaudeと一緒に設計しました。やはりこういった計算式を考えるなら文系の私にAIは必須ですね。 鮮度や危険度に応じた「加点方式」のスコアリングと、絶対に弾くための「除外ルール」による網です。
// 加点方式で優先順位を決める
if (daysSinceRelease <= 14) score += 40 // リリース直後は不具合が出やすい
if (kbVerdict === 'pending') score += 20 // まだAIが結論を出せていない
if (knownIssues.length > 0) score += 30 // 公式がすでに問題を認めている(炎上している可能性高)
// 減点・除外(強固なコストカット網)
if (daysSinceSearch < 3) return { score: -999 } // 3日以内に検索済みのものは検索しない!
if (daysSinceRelease > 90) score -= 20 // 3ヶ月前のパッチを今さら調べる必要はない
このスコアが高い上位数十件のみにAPIコールを限定することで、月額の維持費を劇的に圧縮しました。
さらに取得したはいいものの、「修正されたよ!」というポジティブな声までAIに読ませるとトークン代の無駄です。検索結果のスニペットにネガティブワード(BSOD、crash、フリーズ等)が含まれるか泥臭い正規表現バウンサーを通し、RDPなのか認証なのかのジャンル分けまで自作の関数で強制的に正規化(分類)しています。
データが揃ったあと、いよいよ Claude 4.5 Haiku による安全判定を行います。しかし「このKBを要約して、危ないか教えて」と雑に投げても、LLMは自由奔放なマークダウンテキストを返してきます。これではシステム上で色分けしたり、フラグとして扱うことができません。
まず、ここでも「毎回LLMに読ませる」という富豪的なアプローチは捨てました。
新規KB: まだAIが判定していない真新しいパッチ。
報告増加KB: 前回判定時よりも、Brave APIが拾ってきた「不具合報告の数」が増えている(差分がある)パッチ。
この2条件に合致するものだけをClaude 4.5 Haikuに投げます。(さらに Limit=10 と上限を設けてトークン死を防いでいます。)
そして、AIの首輪とも言えるSystem Promptによる出力構造の完全統制です。
{
"summary_ja": "印刷スプーラーのエラーとRDP接続切断が報告されている累積更新...",
"verdict": "wait",
"verdict_reason": "一部環境でRDP接続切断という業務影響の大きい不具合があるため"
}
必ず上記のJSONで返すようにシステムプロンプトで縛り付け、verdict を ok | caution | wait | pending の4つのEnumに限定。これにより、フロントエンドでは安全に条件分岐ができ、不規則な回答によるシステムクラッシュを封じ込めました。
すべての情報が集約され、判定が出ました。あとは通知するだけです。しかし、バッチ処理が回るたびに「最新の状況をチェックしました!判定はOKです!」と通知していては、情シス担当者はアラート疲れを起こし、最終的に通知をミュートしてしまいます(狼少年現象)。 サービスレベル要件である「UXのノイズを最小化する」ためにはどうするべきか。

ユーザーへ通知を飛ばす条件を、極限までシンプルにしました。
// 既存のverdictと今回の結果を比較
if (existing.verdict !== result.verdict) {
await notifyWatchlistUsers(
kbNumber,
`${kbNumber} の判定が変わりました`,
`${existing.verdict} → ${result.verdict}`
)
}
「前回と今回の Verdict(判定)に変化があった場合のみ」。これだけです。
ずっと「OK」と判定されている優等生パッチには口を閉ざします。しかし、「最初はOKだと思っていたが、数日後に海外のフォーラムでブルースクリーン報告が急増し、AIの判定が Caution に変わった」という危機的瞬間にだけ、ユーザーの元へアラートが飛ぶのです。 平和な時は息を潜め、波乱の時だけ立ち上がるアーキテクチャこそが理想のデリバリーだと考えました。
「様子見KB」を通じて学んだのは、AIやAPIなどの強力なパーツを使う時ほど、その手前での「絞り込み(トリアージ)」と「構造化(パース)」の泥臭いコードがものを言うということでした。AI要約の裏側には、地道な正規表現バウンサーとコスト計算のロジックを敷き詰めました。
「サービスが満たすべきレベル(SLO/SLA)」を最初に決めたことで、何を捨て、どこに予算を割くかの設計(逆算)がスムーズにできました。 この知見が、これからAPIやAIを使った個人開発・サービス開発を始める方のヒントになれば幸いです。
最後まで読んでいただき、ありがとうございました!
要約
コメント
まだコメントはありません。