← ガイダンス一覧へ戻る

2処理オーケストレーション

元表現: 司令塔 / StudyGuide ブロック詳細

1ページを作る工程を、順番どおりに呼んで結果を次へ渡すだけのブロック。どれを同時に走らせ、どこで待ち合わせるかの判断はここにしかありません。

抽象度 Lv.4(技術用語+システム上のやりとりまで/製品名・ライブラリ名は書かない)

1. 概要

1ページを作る一連の工程を、順番どおりに呼び、結果を次へ渡すだけのブロック。 自分では何も判断せず、指示書も持たず、出力の形も持たない。

薄いブロックだが、どれを同時に走らせ、どこで待ち合わせるかという判断はここにしかない。 このアプリの速さと、失敗したときに何が残るかは、実質このブロックが決めている。

2. 位置づけ

人による確認・修正 → 2. 処理オーケストレーション → 3・4・5(同時)→ 6. → 7. → 保存領域

3. 処理内容(Lv.4)

4. なぜこの位置にあるか(設計意図)

判断を持つ部品から、順番の管理を切り離すため。 3・6はそれぞれ指示書を持ち、4・5・7はそれぞれ保存領域の形を知っている。 そこに「次は何をするか」まで混ぜると、1か所直すたびに全部を読み直すことになる。 2番が薄いのは手抜きではなく、2番を薄く保つことが目的になっている。

待ち合わせの位置を1か所に集めるため。 3番が終わるのを誰が待つのか、4・5の結果を誰が持っているのかが分散すると、 「なぜ遅いのか」を追えなくなる。同時に走らせる判断も、待つ判断も、このブロックの中だけにある。

5. 詳細プロセス分解

工程主体やりとりの内容方向
2-1入口処理 → 2番本体タイトル・ページ番号・本文・画像の鍵・利用者
2-22番本体(内部)利用者を表す識別子を作る
2-32番本体 → 3番本文だけを渡して読解ガイド生成を開始
2-42番本体 → 4・5保存済みページ一覧と習得済み語の読み出しを開始
2-53. → 2番本体読解ガイド
2-64・5 → 2番本体過去のページ一式と、習得済み語の一覧
2-72番本体(内部)3つ揃うまで待ち合わせる
2-82番本体 → 4番復習候補の決定を依頼(過去のページ+今回の本文)
2-92番本体 → 5番今回の本文にある習得済み語の抽出を依頼
2-102番本体 → 6番本文・読解ガイド・復習候補・出題禁止語
2-116. → 2番本体8問
2-122番本体 → 7番入力一式と2つの結果をまとめて保存依頼
2-137. → 2番本体 → 入口処理出来たページの識別子

各行を文章にすると

2-1 入口処理が、書式を点検したうえで2番本体を呼ぶ。点検は2番本体に入る前に済んでいて、長すぎるタイトルや空の本文はここまで届かない。「誰の操作か」も一緒に渡される。以降の全工程がこの持ち主の情報を持ち回る。

技術的には ブラウザは POST /api/generateapplication/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 OKapplication/json で、ページの識別子だけを返す。ブラウザはこの識別子を使って /guide/{id} へ画面を移す。

← 横にスクロールできます

入口処理
2. 本体
3. 読解ガイドAI利用
4・5 履歴・習得
6. 語彙問題AI利用
7. 永続化
2-1 タイトル・ページ番号・本文・画像の鍵・利用者
2-2 利用者を表す識別子を作る
2-3 本文だけを渡して開始 ←ここから3本が同時に走る
2-4 保存済みページ一覧と習得済み語の読み出しを開始
2-5 読解ガイド
2-6 過去のページ一式と、習得済み語の一覧
2-7 3つ揃うまで待ち合わせる ←ここまでが並列
2-8 復習候補の決定を依頼(ここからは直列)
2-9 今回の本文にある習得済み語の抽出を依頼
2-10 本文・読解ガイド・復習候補・出題禁止語
2-11 8問
2-12 入力一式と2つの結果をまとめて保存依頼 ←最初の書き込み
2-13 出来たページの識別子

6. この分解から見えること

  • 同時に走っているのは「読み出し」だけで、「判定」は直列。 2-4で3本を同時に始めているが、4・5の本当の仕事(復習候補を選ぶ・禁止語を抜く)は 2-8/2-9、つまり全部揃ったあとに始まる。「4・5は並列」と一言で言うと、この違いが消える。
  • 待ち合わせが1か所しかないので、遅さの原因が特定しやすい。 2-7で止まる時間は、3番(外部モデル)で決まる。読み出しがどれだけ速くなっても全体は速くならない。 逆に言えば、このアプリの体感速度は3番と6番の2回のモデル呼び出しでほぼ決まっている。
  • 書き込みは最後の1回だけ。2-12まで保存領域は変わらない。 だから途中で失敗しても、中途半端なページが残らない。 「失敗したら何も残らない」という性質は、2番が書き込みを最後にまとめたことで生まれている。
  • 2番は中身を一度も見ていない。読解ガイドが妥当か、8問が適切かを2番は判断しない。 点検はそれぞれの部品(6番は自分で検証し、作り直しまでする)に任せている。 2番を薄く保つ、という方針が実際に守られている。
  • 利用者の情報だけは全工程を貫いている。2-1で受け取った持ち主が、 2-4の読み出しにも、2-12の保存にも、外部モデルの呼び出しにも付いて回る。 データが混ざらない仕組みが、この1本の線で担保されている。

7. 失敗時の挙動(未確認あり)

どこで失敗するか何が起きるか
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。「作成できた」とは返さない
いずれの場合も保存領域は書き換わらない。作りかけのページは残らない

外部モデルを呼んだあとに失敗した場合、その呼び出しぶんの費用がどう扱われるかは未確認

8. 未確認事項

  • 途中で失敗したとき、利用者が「もう一度」を押したら最初からやり直しになるのか、途中から再開できるのか(再開の仕組みは見つからなかった)
  • 2-3の3番が極端に遅いとき、どこかで打ち切られるのか(時間切れの設定は未確認)
  • 同じ本文で2回続けて作成した場合、重複したページができるのを止める仕組みがあるか
  • 2-2の識別子が、外部モデル側でどう使われるか(こちら側からは確認できない)

※ この解説は、2026年8月14日時点の studyguide のプログラムを読んで作成しました。

※ 抽象度は Lv.4 で統一しています。通信の方式・データの形式・認証のしくみ・ステータスコードまでは書き、製品名やライブラリ名、関数名までは踏み込みません(そこは Lv.5)。

※ 工程の番号は全体図の番号と対応しています。全体図はこちら

akuramochi1.com — personal tools for learning and thinking.