元表現: 司令塔 / StudyGuide ブロック詳細
1ページを作る工程を、順番どおりに呼んで結果を次へ渡すだけのブロック。どれを同時に走らせ、どこで待ち合わせるかの判断はここにしかありません。
抽象度 Lv.4(技術用語+システム上のやりとりまで/製品名・ライブラリ名は書かない)1ページを作る一連の工程を、順番どおりに呼び、結果を次へ渡すだけのブロック。 自分では何も判断せず、指示書も持たず、出力の形も持たない。
薄いブロックだが、どれを同時に走らせ、どこで待ち合わせるかという判断はここにしかない。 このアプリの速さと、失敗したときに何が残るかは、実質このブロックが決めている。
人による確認・修正 → 2. 処理オーケストレーション → 3・4・5(同時)→ 6. → 7. → 保存領域
POST /api/generate(受け取り・返りとも application/json)。2番本体そのものはHTTPを受けず、入口処理から関数として呼ばれる。詳しくは5章の各工程に書いた。判断を持つ部品から、順番の管理を切り離すため。 3・6はそれぞれ指示書を持ち、4・5・7はそれぞれ保存領域の形を知っている。 そこに「次は何をするか」まで混ぜると、1か所直すたびに全部を読み直すことになる。 2番が薄いのは手抜きではなく、2番を薄く保つことが目的になっている。
待ち合わせの位置を1か所に集めるため。 3番が終わるのを誰が待つのか、4・5の結果を誰が持っているのかが分散すると、 「なぜ遅いのか」を追えなくなる。同時に走らせる判断も、待つ判断も、このブロックの中だけにある。
| 工程 | 主体 | やりとりの内容 | 方向 |
|---|---|---|---|
| 2-1 | 入口処理 → 2番本体 | タイトル・ページ番号・本文・画像の鍵・利用者 | → |
| 2-2 | 2番本体(内部) | 利用者を表す識別子を作る | ↻ |
| 2-3 | 2番本体 → 3番 | 本文だけを渡して読解ガイド生成を開始 | → |
| 2-4 | 2番本体 → 4・5 | 保存済みページ一覧と習得済み語の読み出しを開始 | → |
| 2-5 | 3. → 2番本体 | 読解ガイド | ← |
| 2-6 | 4・5 → 2番本体 | 過去のページ一式と、習得済み語の一覧 | ← |
| 2-7 | 2番本体(内部) | 3つ揃うまで待ち合わせる | ↻ |
| 2-8 | 2番本体 → 4番 | 復習候補の決定を依頼(過去のページ+今回の本文) | → |
| 2-9 | 2番本体 → 5番 | 今回の本文にある習得済み語の抽出を依頼 | → |
| 2-10 | 2番本体 → 6番 | 本文・読解ガイド・復習候補・出題禁止語 | → |
| 2-11 | 6. → 2番本体 | 8問 | ← |
| 2-12 | 2番本体 → 7番 | 入力一式と2つの結果をまとめて保存依頼 | → |
| 2-13 | 7. → 2番本体 → 入口処理 | 出来たページの識別子 | ← |
2-1 入口処理が、書式を点検したうえで2番本体を呼ぶ。点検は2番本体に入る前に済んでいて、長すぎるタイトルや空の本文はここまで届かない。「誰の操作か」も一緒に渡される。以降の全工程がこの持ち主の情報を持ち回る。
技術的には ブラウザは POST /api/generate へ application/json で送る。1番と違ってファイルが無いのでJSONで足りる。入口処理はまずCookieを検証し(失敗なら 401)、続いて型に当てはめて4項目を検査する。ここで注意。検査に落ちても 400 ではなく 500 が返る。例外をまとめて受けて一律に扱っているためで、1番や12番が出し分けているのと不揃いになっている。
2-2 2番本体は最初に、利用者を表す短い識別子を作る。これは外部モデルを呼ぶときに添える印で、本人のメールアドレスをそのまま外へ出さないための一手間。自分の中だけで作るので ↻。
技術的には メールアドレスを SHA-256(どんな長さの入力も16進64桁に潰す一方向の計算)でハッシュし、先頭32桁だけを使う。一方向なので元のアドレスには戻せない。同じ人からは必ず同じ値になるので、外部側は「同じ利用者だ」とは分かるが、誰かは分からない。
2-3・2-4 ここが2番のいちばん重要なところ。3番(読解ガイド生成)と、4・5が使う過去データの読み出しを、待たずに同時に始める。3番は外部モデルを呼ぶので時間がかかる。その待ち時間に、保存領域からの読み出しを済ませてしまう。矢印を2本に分けて描いているが、実際にはほぼ同時に3本が走り出す。
技術的には 3つの非同期処理をまとめて開始し、全部の完了を待ち合わせる(いわゆる Promise.all の形)。同時に開始するだけで、CPUが3つ分同時に動くわけではない。3番はネットワークの応答待ち、4・5はディスクの読み出し待ちで、どちらも「待っている間に他を進められる」種類の処理なので、まとめると全体が短くなる。計算そのものが重い処理を並べても、こうはならない。
2-5・2-6 3つの結果が、それぞれ終わった順に戻ってくる。どれが先に戻るかは決まっていない。普通は読み出し(2-6)のほうが先に終わる。
技術的には 完了の順番は決まっていない。ネットワーク越しの3番が数十秒かかるのに対し、手元のディスクの読み出しは数十〜数百ミリ秒なので、実際にはほぼ3番を待っている時間になる。
2-7 2番本体は、3つが全部揃うまで先へ進まない。ここが待ち合わせの地点で、2-3から2-7までにかかる時間は「3つのうちいちばん遅い1つ」で決まる。1つずつ順番にやっていたら3つの合計時間がかかっていた。
技術的には 3つのうち1つでも例外を出すと、待ち合わせ全体がその時点で失敗になる。残り2つは止まらず最後まで走るが、戻り値は誰も受け取らない。外部サービスを呼んだぶんの費用は発生済みで、取り消せない。
2-8・2-9 揃ってから、4番と5番に判定をさせる。ここで気を付けたいのは、2-4の「読み出し」と、2-8/2-9の「判定」は別物だということ。同時に走っていたのは保存領域からデータを持ってくるところまでで、「どれを復習候補にするか」「どれを出題禁止にするか」を決める計算は、3つ揃ったあとに順番に行われる。復習候補を選ぶには今回の本文が要るので、こうするしかない。
技術的には ここは外部を呼ばない純粋な計算。大文字小文字、アポストロフィの種類(' と ’)、前後の空白をそろえる正規化をしてから、完全一致で突き合わせる。表記ゆれを先に潰しておかないと、同じ語を別物と数えてしまうため。
2-10 6番へ4点セットを渡す。本文、読解ガイド、復習候補、出題禁止語。6番が「本文にある語」「重複していない語」「禁止されていない語」を選べるのは、2番がこの4つを揃えて渡しているから。
技術的には 関数呼び出しで、4点セットを1つのまとまりとして渡す。
2-11 6番から8問が返る。6番の中で点検と作り直しが済んでいるので、2番は中身を見ずにそのまま次へ渡す。
技術的には 6番の中で点検と作り直しが済んでいるので、2番は中身を見ずにそのまま次へ回す。
2-12 7番へ、入力一式(タイトル・ページ番号・本文・画像の鍵)と、2つの結果(読解ガイドと8問)をまとめて渡す。ここで初めて保存領域に書き込みが起きる。2-1から2-11までの間、保存領域は一度も書き換わっていない。
技術的には ここで初めてディスクに書く。一時ファイルに書いてから名前を付け替える方式。さらに書き込みは順番待ちの列に並べて1件ずつ実行する。同時に2つのリクエストが同じファイルを書き換えて、片方の内容が消えるのを防ぐため。
2-13 7番が返した識別子を、2番本体がそのまま入口処理へ、入口処理が画面へ返す。画面はこの識別子を使って、出来たページへ移動する。
技術的には 200 OK と application/json で、ページの識別子だけを返す。ブラウザはこの識別子を使って /guide/{id} へ画面を移す。
← 横にスクロールできます
| どこで失敗するか | 何が起きるか |
|---|---|
| 2-1 の書式点検 | 2番本体は呼ばれない。ただし 400 ではなく 500 が返る(1番や12番と作りが違う) |
| 2-3〜2-6 のどれか1つ | 500。残り2つは走り切るが、結果は捨てられる |
| 2-10/2-11(6番) | 500。保存されない。3番で作った読解ガイド(AI呼び出し1回分)も捨てられる |
| 2-12(7番) | 500。「作成できた」とは返さない |
| いずれの場合も | 保存領域は書き換わらない。作りかけのページは残らない |
外部モデルを呼んだあとに失敗した場合、その呼び出しぶんの費用がどう扱われるかは未確認。
※ この解説は、2026年8月14日時点の studyguide のプログラムを読んで作成しました。
※ 抽象度は Lv.4 で統一しています。通信の方式・データの形式・認証のしくみ・ステータスコードまでは書き、製品名やライブラリ名、関数名までは踏み込みません(そこは Lv.5)。
※ 工程の番号は全体図の番号と対応しています。全体図はこちら。