CH 09 · 中核サブシステムと 1 メッセージの流れ
この章の目標
CH 08 では dsh が「プラグインツリー」であることを説明しました。この章ではズームインします:ツリー上のキーになるプラグイン(サブシステム)がそれぞれ何を扱い、どう協調するのか、そして1 つのメッセージを最初から最後まで追跡します —— Enter キーを押してから返答が来るまでの間に何が起きているか。この 2 つが見えれば、dsh の内部動作がわかります。
中核サブシステム:レストランの裏方チーム
CH 08 の小さなレストランを続けます:厨房は 1 人ではなく、分業がはっきりしたチームで、それぞれが自分の仕事を担当し、互いにものを渡します。dsh の中核サブシステムがこのチームです:
| サブシステム | 役割分担 | 担当 |
|---|---|---|
agent-loop | ヘッドシェフ | デフォルトのドライバ:「次に何をすべきか」を決める —— モデルへのリクエスト、ツール呼び出し、結果の収集;ループ全体はこれが駆動 |
llm | 出前係 | アダプタ経由で外部モデルと接続、メッセージを送り、返答を受け取る |
tools | 工具庫 + 門番 | ツールレジストリ + 制御された実行パイプライン;Agent ごとに自分のものを使う(scope 分離) |
system-prompt | 仕出し皿 | さまざまなプラグインが登録したプロンプト断片とツールスキーマを「モデルに見せる 1 皿」に縫い合わせる |
session | 帳簿 | 追記専用セッションログ:あなたが送ったもの、モデルの返答、動いたツール —— すべて記載 |
scope | 作業場隔離 | 各 Agent の登録が自分のセルでのみ効くようにし、横漏れを防ぐ |
協調の様子は次の図を見てください:
記憶法:ヘッドシェフ(agent-loop)が指示を出し、仕出し皿(system-prompt)が下ごしらえし、出前係(llm)が外注を呼び、工具庫(tools)が工具を渡し、すべての結果が帳簿(session)に記録される。 帳簿が主役なのは次章。
重要な単位:step と turn
流れを見る前に、まず 2 つの用語を区別してください;全体チェーンを理解する基礎になります:
- step = モデルへの 1 回リクエスト + それが呼んだツール群。モデルが「考えて + 動く」を 1 回行うのが 1 step。
- turn = 0 個以上の step。最初の入力を受け取ってからはじめて、未処理の作業がなくなるまで。
なぜ 1 つの返答に複数の step があるのか?モデルはよく「少し考える → ツールを呼ぶ → 結果を見てまた考える → 別のツールを呼ぶ……」を、十分だと感じるまで繰り返してから最終返答を出すからです。この連鎖全体が 1 turn、中の「考えて + 動く」がそれぞれ 1 step。公式の表現:turn は最初の入力を受け取る前に開き、未処理の作業がなくなったら閉じる。
生活のたとえ:あなたが 1 品を注文する(turn)、ウェイターは複数回行き来するかもしれない(step)—— まず品があるかを聞き、次に届け、追加を頼むか聞く —— 注文が完全に済むまで。
1 メッセージの流れ:Enter キーから返答まで
1 つのメッセージを完全に歩いてみます。次の図を上から下へ:
① turn/start:turn が開く。 Enter キーを押す前、この turn はまだ始まっていない;押したあと、turn はまず開き、「入力を受け取る」に入ります。
② 入力を受け取る(inbox)。 すべての入力はまず inbox というメールボックスに入ります。あなたが送ったメッセージは直ちにヘッドシェフを目覚めさせて作業を開始させます;一方 CH 04 で見たような「注入された文脈」はまずメールボックスに留まり、別の本物のメッセージが目覚めさせるのを待ちます —— 「データを注入する」こと自体は流れを中断しません。
③ 組み立て:プロンプト + ツールスキーマ。 system-prompt がさまざまなプラグインが登録した断片を 1 皿に縫い合わせ、モデルに「あなたは誰か、どんなツールがあるか、ツールはどんな形か」を伝えます。
④ agent/pre-step:モデルに見せる内容を決定。 これが最初の「ライブ拡張点」です —— プラグインはここで送出する内容を書き換えたり、直接拒否したりできます。拒否または空への書き換えの場合でも、turn は通常通り閉じ、ログに「一度試したが走らなかった」と記録されます。
⑤ step/start:帳簿 + 履歴取得。 あなたが送ったメッセージはセッションログに user/message として書き込まれ;同時に、このターンのモデル履歴はログから投影されます(deriveMessages())。モデルが見るものはすべてログから来る —— この一文は何度も戻ってきます。
⑥ モデルにリクエスト → 返答受信。 agent/request がリクエストを送り(介入可)、llm/stream がストリームで返し、モデルの返答はログに assistant/message として書き込まれます。
⑦ ツールを呼ぶ必要があるか? モデルが「行動が必要」と判断すれば、tool/call → 実行 → 結果 tool/result をログに書き戻し。実行の前後には介入点があります(pre/post-execute)。ツール不要なら次の step へスキップ。
⑧ step/end:まだ作業があるか? ツール結果をモデルが再度処理する必要があるなら(あるいは新しい入力が来たら)、別の step を開き、「入力を受け取る」に戻る;未処理の作業がなくなるまで agent/turn-stopping → turn/end でこのターンは終わり、最終返答が表示されます。
覚えておくべき重要な区別:図の右側には 2 種類のイベントがマークされています ——
- 永続イベント(
turn/*、step/*、user/message、assistant/*、tool/*):セッションログに書き込まれ、再起動後も残る; - ライブ拡張点(
agent/pre-step、agent/request、llm/stream、tools/*):実行中のみ有効;プラグインはこれらを使って介入し挙動を変えますが、帳簿には自分では記載されません。
これは CH 08 の「イベントは拡張点」と呼応します:実行中の作業を観察または介入したいなら、ライブイベントを見る;事実を残したいなら永続イベントを使う。
モデルの記憶はどこから来るか
1 行で:セッションログがモデルに見せる文脈です。 このターンでモデルが「覚えている」ことは、モデル自身が覚えているのではなく、システムがログから投影した履歴です —— だから「モデルに見えたものは記録された」というのが dsh の厳しいルールです:モデルリクエストに到達するあらゆるものは、ログから再構成可能でなければならず、ランタイムはこれを不変条件としてチェックします。このルールが設計全体の軸であり、CH 10 で展開します。
これまでの章と簡単に相互参照:CH 04 で見た「軌跡ビュー」—— ASSISTANT / TOOL のタイムライン表示 —— 水面下はこのセッションログの可視化に過ぎません。モデルが言ったこと、呼んだツール、結果がなんだったか、すべてログから描画されています。だから「なぜ前に言ったことを覚えているのか」と聞かれたら、答えは単純:覚えているのはモデルではなく、ログが覚えている —— クエリのたびに、システムはログから履歴を投影してモデルに渡します。
この章で学んだこと
- [ ] 中核サブシステム(agent-loop / llm / tools / system-prompt / session)の分業を言え、協調を説明できる
- [ ] step と turn の違いを説明できる(step = モデルへの 1 回リクエスト + ツール;turn = 0 個以上の step)
- [ ] 図に沿って 1 メッセージの完全な流れをなぞれる:turn/start → 入力受け取り → 組み立て → pre-step → step/start → モデルリクエスト → ツール呼び出し → step/end → turn/end
- [ ] 永続イベントとライブ拡張点を区別でき、それぞれの目的を言える(証拠を残す vs 介入する)
- [ ] 「モデルに見えたものが記録されている」というルールの意味を言える
