前の記事で、単体テストを自律生成した話を書いた。今回はターン数が少なく俯瞰しやすいatomsのStorybook生成でのターン数の内訳と何が影響してターン数が変わったのか2ファイルをピックアップして紹介していく。
セッションをローカルのJSONファイルとして残すと、エージェントが何を呼んだか、何に失敗したかが全て読める。この記録から数字が取れる。数字が取れれば改善できる。
参考としてローカルで管理している各セッションファイルもgistに上げておいた。
ターン数をKPIとして扱う
前の記事でこう書いた。
コンテキストに積まれる構造はこうだ。
UserInstruction + (FunctionCalling + ToolResponse) × N + ModelResponse
Nが何かをセッションファイルから数えることができる。これがターン数だ。
Storybookストーリー生成(atoms)のハッピーパスはこうなる。
(タイトルにある通り、この記事を書いてる途中までハッピーパスは10ターンだと思っていた)
UserInstruction
+ (ts_test_strategist)
+ (write_file)
+ (validate_code.sh) ← tsc + format + lint を内包
+ ModelResponse
= N=3、8ターン
これを目標値として、実際の軌跡を追う。
Button: 34ターン(gist)
2026-01-08 20:38 — atoms、Storybook自動生成の初回実装。
Turn 0 user_task
Turn 1 ts_test_strategist
Turn 2 ← 結果
Turn 3 mkdir -p Button/__stories__ ← (A)
Turn 4 ← 結果
Turn 5 write_file
Turn 6 ← 結果
Turn 7 tsc --noEmit
Turn 8 ← exit_code: 0
Turn 9 validate_code.sh
Turn 10 ← exit_code: 0
Turn 11 read_file(Button.stories.tsx) ← (B)
Turn 12 ← 内容
Turn 13 read_file(package.json) ← (C)
Turn 14 ← 内容
Turn 15 npm run build-storybook ← (D)
Turn 16 ← 成功(storybook-static生成)
Turn 17 model_response: "実装完了" ← 地雷を踏んでいる
Turn 18 user_task(Conductorのリトライ)
...
Turn 33 model_response: "前回の失敗原因は
storybook-staticがformatterの対象に
含まれていたことによるものでした"
当時のプロシージャにはStep 5cとして「Verify Storybook renders」が存在した。エージェントはそれをnpm run build-storybookで実行した。ビルド成果物のstorybook-static/がprettierの対象に混入し、validate_code.shがexit_code: 1を返した。Conductorがリトライを送り、エージェントは原因を自分で説明しながら後始末をした。
余剰ターンの分類:
| 分類 | 増加 |
|---|---|
| (A) mkdir(ディレクトリ作成) | +2 |
| (B)(C) 不要なread_file × 2 | +4 |
| (D) build-storybook + 汚染 + 後始末 | +12 |
| リトライ起因の重複呼び出し | +6 |
合計 +24ターン。
プロシージャへのフィードバック(f67a0976、同日)
Buttonのターン列を読んで、当日中に修正した。
- Step5C[Step 5c: Verify Storybook renders]
- CheckStorybook{Storybook renders?}
+ Success[Step 6: Report Success]
+ **IMPORTANT - Directory Pre-Creation**:
+ - The `__stories__` directory is **already created** by the orchestration script
+ - **[PROHIBITED]** Creating or checking for the `__stories__` directory
二点だ。Step 5c(npm run storybook)の削除。ディレクトリ作成をスクリプト側に移し、エージェントへの禁止事項として明記。これで(A)(D)の余剰は構造上発生しなくなった。
Label: 10ターン(gist)
2026-01-09 05:55 — atoms、プロシージャ修正の翌朝。
Turn 0 user_task
Turn 1 ts_test_strategist
Turn 2 ← 推奨ストーリー数: 2件
Turn 3 write_file
Turn 4 ← 書き込み完了
Turn 5 tsc --noEmit ← (*)
Turn 6 ← exit_code: 0
Turn 7 validate_code.sh
Turn 8 ← exit_code: 0 ✅
Turn 9 model_response: "完了"
プロシージャの修正が効いた。当時はこれをハッピーパスと認識していた。
しかしこの記事を書きながらセッションを精査して気づいた。Turn 5のtsc --noEmitはvalidate_code.shの内部で[2/4]として既に実行されている。エージェントがTurn 5を呼んでいたのは、プロシージャのワークフロー図がStep 5a(tsc)→ Step 5b(validate_code.sh)の2段階で描かれていたからだ。本文とワークフロー図が矛盾していた。
3ヶ月ほど経ってしまったが、気付いた段階でプロシージャを修正した。
atomsの安定ライン
Buttonで露わになった設計上の欠陥を修正して以降、atomsは10ターン前後で安定した。実際のセッションを見ると、write_fileを一度書き直したりして+2〜4ターン増えることはある。それでも設計上の欠陥が引き起こしたような+24ターンは再発しなかった。
プロシージャのワークフロー図の修正により、次回以降のハッピーパスはN=3の8ターンになる。
設計上の欠陥: プロシージャを更新して次回から排除する
実行時の失敗: +2〜4ターン、都度回復する
余剰ターンの発生源が変わった。「プロシージャが間違っていたから」ではなく「エージェントがその場でミスをしたから」という種類に。改善のアプローチも変わる。設計の修正ではなく、コンテキストの精度を上げる方向だ。
なお、moleculesやorganismsになるとレイヤーが上がり、RHFやストアとの統合が増える。ターン数の基準値も変わる。それは別の話だ。
なぜベンダーのUIでは取れない数字か
ターン数を数えるにはセッションの中身が見える必要がある。Function Callingの呼び出し、Tool Responseの全文、失敗したコマンドの出力——これらはベンダーのUIでは隠蔽される。「なんとなく動いた」か「なんとなく失敗した」しか残らない。
pipeがセッションをローカルのJSONファイルとして管理する理由はここにある。34ターンが10ターンになったことをファイルを開いて確認できる。Button(Turn 15)にnpm run build-storybookがあったことが、プロシージャからStep 5cを削除した根拠になる。Label(Turn 5)にtsc --noEmitがあったことが、この記事を書く過程でプロシージャの矛盾を発見する根拠になった。ツール履歴は改善の根拠だ。