1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

llm-task-router 書くAIと見るAIを分ける:Claude Code × codex × 人間で OSS に「自動推敲ループ」を実装した開発記

1
Posted at

本記事は、OSS CLI llm-task-routerarticle:refine コマンドを追加した過程を、コード解説ではなく進め方の実例としてまとめたものです。

  • Author(Claude Code) … 書く AI(提案・実装・改稿)
  • Critic(codex) … 見る AI(レビュー・指摘)
  • Orchestrator(人間) … 決める役(停止・受け入れ・最終判断)

に分け、設計レビューを複数ラウンド回してから実装に入る進め方が、品質の安定に効きました。そしてこの「書く → 見る → 決める」という三分割は、奇しくも今回作った機能の中身(評価する → 直す → 止める)とそっくり同じ形をしています。詳しくは後半で触れます。

対象リポジトリや実装の参照先は、読者が検証できる形で手元の公開情報にリンクするのが本来望ましいのですが、ここでは開発プロセスそのものに焦点を当てます。したがって本記事で述べる数値や所感は、開発時の記録に基づく事実と、その解釈としての主観を分けて書きます。

特に伝えたいのは次の 2 点です。

  1. AI に一括委任するより、役割を分けたほうが品質が安定した
  2. その進め方自体が、今回実装した article:refine の Critique Loop と同じ構造だった

そして仕上げに、この記事自体へ、開発した「自動推敲ループ」をかけてみました。結果は——AI(書く役)がもっともらしい嘘の前提(EOL 済みの古い Node.js バージョン)を足してスコアが悪化し、人間がスナップショットから前の版へ戻す、という、まさに本記事の結論を地で行く結末でした。その一部始終も、終盤で隠さず公開します。

この記事で扱う対象

今回追加した article:refine は、生成済みの記事に対して

  1. 評価する
  2. 必要なら修正する
  3. 再評価する
  4. 続けるか停止するか判定する

というループを回す CLI コマンドです。

なお、停止条件の全種類やスコアリングの式といった 機能内部の設計判断そのものは、別記事の「技術設計編」 にまとめています。本記事はその対になる 「開発プロセス編(実践)」 にあたります。設計編が「職人が作った道具の解説」だとすれば、本記事は「その道具を、AI と人間のチームでどう作り、どう裏切られ、どう御したか」のドキュメンタリーです。

ただし、この記事の主題は機能内部の詳細ではなく、

  • 誰が
  • どの役割で
  • どう収束させたか

です。要するに「AI が機能を作った話」ではなく、AI 同士をレビューさせ、人間が交通整理して完成まで持っていった話です。

登場人物と役割

最初に固定したのは、誰が何を担当するかでした。

  • rex0220: リポジトリオーナー兼ディレクター
    課題設定、優先度判断、スコープ管理、レビュー結果の交通整理、最終受け入れ、実コマンド実行、課金判断を担当
  • Claude Code(Claude Opus 系モデル): 設計者・実装者
    実コード読解、設計書作成、改訂、実装計画、実装、テスト、生成物修正を担当
  • codex: レビュアー / クリティック
    設計書、実装計画、実装後コード、生成物をレビューし、穴や曖昧さを指摘

モデル名の細かい版番号は執筆時点での公開情報と一致する表現に留めます。この記事の主眼はモデル比較ではなく、役割分担の効き方です。

構造としては次のような分業でした。

工程                      | Claude Code          | codex                 | rex0220
-------------------------|----------------------|-----------------------|-------------------------
課題整理                  | 方針提案             | 観点補助              | 起案・優先度判断
設計                      | 設計書を書く         | 設計レビュー          | 分岐の最終判断
実装計画                  | フェーズ化           | 計画レビュー          | スコープ管理
実装                      | コードを書く         | 実装レビュー          | 受け入れ判断
テスト / E2E              | 修正・追従           | 結果の観点提示        | 実行・確認
生成物の最終品質確認      | 修正対応             | 批評                  | 事実基準で最終承認

短く言えば、こうです。

  • author = Claude Code
  • critic = codex
  • orchestrator = 人間

きっかけ:外部 AI の提案を、そのまま採用しなかった

出発点は、別の AI にリポジトリ評価を依頼し、「次に作る候補」を出してもらったことでした。

ここで重要だったのは、提案をそのまま採用しなかったことです。Claude Code に実際のリポジトリを読ませ、既存構造や資産の有無を見たうえで優先度を引き直しました。

候補には RAG や SEO のような大きめのテーマもありましたが、それらは既存方針と衝突しやすく、導入コストも重い。一方、評価→修正ループは既存資産を流用しやすく、比較的薄く閉じられると判断できました。

ここでの教訓はシンプルです。

  • 外部評価は議論の起点
  • 最終優先度は実コードの実態で決める

AI の提案は便利ですが、リポジトリ文脈までは自動で保証してくれません。

設計ドリブンで進めた:コードを書く前にレビューを重ねた

今回いちばん効いたのは、いきなり実装に入らなかったことです。

まず Claude Code が設計書を書き、codex がレビューし、Claude Code が改訂する、という流れを回しました。設計書は 1 回で完成せず、複数版にわたって更新されました。開発記録上、初版を含めて 計 12 版まで進み、codex レビュー反映は v2〜v12 に及びました。

ここで言いたいのは「版数が多いこと自体が偉い」ではありません。重要なのは、

実装ゼロ行の段階で、曖昧さを潰すためにレビューを前倒しした

ということです。

同じ論点が、次のレビューで別の角度から再浮上することも何度もありました。たとえば停止条件を決めたつもりでも、ログや成果物の整合性から見るとまだ穴がある、といった具合です。

レビュー指摘は累計で数十件規模になりました。ここで急いでコードを書き始めるより、先に不確定要素を減らしたほうが結果的に安定しました。

複数 AI を使う場合、実装前の曖昧さはそのまま増幅されやすいです。設計レビューを長めに取るのは遠回りに見えますが、後工程のズレ修正を減らしやすくなります。

レビューで詰めた代表論点と、最終的な決着

レビューで特に効いたのは、「一見決まっているように見えるが、運用すると崩れる点」を拾えたことでした。抽象論だけでは再現価値が低いので、ここでは最終的にどう決めたかまで書きます。

1. max-rounds の定義

論点は、「round を何と数えるか」でした。

候補は次の 3 つです。

  • evaluate の回数
  • revise の回数
  • evaluate + revise の 1 セット

最終的には、1 回の評価と、それに続く 0 または 1 回の修正を含む試行単位を 1 round とみなす整理に寄せました。少なくとも利用者向けには、「何回ループを試すか」が伝わる定義にしないと誤読されやすいためです。

2. 停止できない組み合わせの回避

approved 条件と最低重要度の組み合わせによっては、

  • 合格に届かない
  • でも修正対象にもならない

という停止不能に近い状態が起こり得ました。

最終的には、到達不能な改善状態に入った場合は stalled として止める方針を明示しました。完了でも失敗でもなく、「これ以上回しても改善が見込めない停止」を独立させた形です。

3. 失敗・中断時の整合性

停止理由、メタ情報、成果物の書き込み順がずれていると、

  • 完了扱いだが成果物がない
  • ログ上は成功に見えるが、実体が欠けている

という不整合が起きます。

ここは、状態記録と成果物出力の順序を揃え、停止理由を明示的に残す方針に詰めました。

4. 悪化検出時の扱い

修正後の記事が、評価上は悪化している場合にどうするか。

ここは完全自動で巻き戻すより、悪化を検知して停止または保持できるようにし、人間が最終判断しやすい形に寄せるほうを採りました。自動巻き戻しは便利ですが、「何を正とするか」が評価軸依存になるためです。

5. 再開・追跡可能性

途中で止まったときに、何が起きたか追えないと実運用で困ります。

そのため、途中状態と停止理由を追跡できるメタ情報を残すことを優先しました。完全な再開機構まで広げるとスコープが大きくなるので、まずは「追跡可能であること」を先に取りました。

6. ユーザー表示の誤読防止

価格未設定モデルのコスト表示が 0 に見えると、「無料」と誤読される恐れがあります。

ここは、未知コストを 0 と同一視させない表示にする観点を重視しました。仕様だけでなく、表示の意味づけもレビュー対象に入れたのは効果的でした。

人間が閉じた分岐:AI だけでは決まらないことがある

ここが、3 つの役割のパワーバランスがいちばん表れた部分です。書く役(author)と見る役(critic)は案を出し、指摘を返すが、最終的な可否は決めない。 決定権は進行役(orchestrator=人間)に集約しました。

レビューを重ねるほど明確になったのは、AI だけでは閉じない論点が必ずあるということです。

たとえば、rex0220 が最終決定したのは次のような分岐です。

  • 成果物のファイル命名をどうするか
  • 既定の厳しさをどこに置くか
  • 悪化時に自動巻き戻しを採るか
  • 今回のスコープに何を含め、何を切るか

これらは「技術的に唯一の正解」があるというより、リポジトリの運用方針と保守コストで決まります。だから人間が握る必要がありました。

また、codex のレビューを Claude Code にどう渡すか、その優先順位をどう付けるかも人間の仕事でした。複数エージェントを併用すると、単純に知見が増えるだけでなく、交通整理コストも増えます。

AI レビューは観点を増やせますが、そのまま放置するとスコープも膨らみます。レビュー品質を上げるには、観点追加だけでなく「どこで止めるか」の管理が必要です。

実装の進め方:先に安全網を置き、薄く積んだ

仕様が十分固まってから、Claude Code が実装計画をフェーズに分けました。先に見える化したのは次の 3 点です。

  • 変更ファイル
  • 作業順
  • テスト方針

そのうえで、実装はまず共通部分のリファクタから始めました。新機能をいきなり積むのではなく、既存テストを安全網にして、まず挙動が変わっていないことを確認します。

次に、新機能を段階的に追加し、各フェーズごとにビルドとテストを回しました。

この順序の利点は明快でした。

  • どこで壊れたか追いやすい
  • 変更責務が分離される
  • 設計段階で潰した論点が、実装中に再爆発しにくい

進め方としては、かなり普遍的です。

  1. 先に設計を固める
  2. 次に安全網を置く
  3. 最後に薄く実装を積む

最低限の再現前提

実際に試す場合、少なくとも次の前提が必要です。

  • Node.js / npm が使えること
  • llm-task-router が実行できること
  • 対応する API キーが環境変数などで設定されていること
  • 対象となる既存 run ID があること

インストール例は次のとおりです。

npm install -g @rex0220/llm-task-router

実行イメージは次のとおりです。

llm-task-router article:refine --run <runId> --max-rounds 3 --min-severity major

リポジトリを手元で開発中に既存 script から呼ぶなら、次のような形でも実行できます。

npm run article:refine -- --run <runId> --max-rounds 3 --min-severity major

環境によってはモデル指定や API キー設定方法が異なるため、実運用では README や CLI ヘルプに従う前提になります。ここでは、E2E で停止条件を確認したこと自体を主題にします。

実環境テスト:stalled 停止をどう解釈したか

実装後は、実際に API を使って記事 1 本に対し article:refine を走らせました。その結果、停止理由は stalled でした。

ここで重要なのは、stalled を成功談として美化しないことです。stalled には少なくとも 2 つの解釈があります。

  1. 安全弁が正しく働いた
    改善余地が乏しいため、不要な追加修正を止めた
  2. 評価ロジックが改善可能性を取りこぼした
    本当は直せるのに、止めてしまった

今回の時点では、後者の可能性を完全には否定できません。したがって、「安全弁が期待通りだった」というのは事実ではなく解釈です。

一方で、少なくとも確認できた事実は次のとおりです。

  • 停止条件に従ってループが終了した
  • 無限ループや破綻した書き込み状態には入らなかった
  • 停止理由を人間が読める形で確認できた

つまり、E2E で確認できたのは「どれだけ改善したか」よりまず、どう止まるかが破綻していないことでした。実装初期としては、この確認に価値がありました。

メタな気づき:開発プロセスと機能が同型だった

今回おもしろかったのは、開発中にやっていたことが、そのまま article:refine の構造に重なっていたことです。

開発プロセスでは、

  1. Claude Code が書く
  2. codex が評価する
  3. 人間が差し戻すか受け入れるか決める

という流れでした。

これはそのまま、機能側の

  • evaluate
  • revise
  • stop / accept

に対応します。

ただし、これは偶然の奇跡というより、ある程度は構造的に必然です。LLM オーケストレーションで author / critic / orchestrator を分けると、自然に Critique Loop 型に寄ります。

なので本記事で言いたいのは、「すごい発見をした」というより、

実装したい機能の構造を、開発体制の側でもなぞると運用しやすかった

ということです。

限界もあった:評価役は、知らない事実を検証できない

もちろん、限界もありました。

実環境で生成された記事の一部に、実装と食い違う説明が含まれていたことがありました。たとえば、停止条件や挙動説明が、実コード上の最新仕様と微妙にずれていたケースです。

このとき見えたのは、codex や他の LLM がレビュー役として優秀でも、自分が参照していない事実までは強く検証できないという限界でした。もっともらしい文章であっても、グラウンドトゥルースに当たっていなければ見逃します。

実際の修正は次の流れでした。

  1. 人間が記事と実装の差分に気づく
  2. どこが事実とずれているかを実装準拠で特定する
  3. その差分を明示的な指示として Claude Code に渡す
  4. Claude Code が記事を修正する

ここでの教訓は明快です。

評価役は、自分が知らない領域を検証できない

だから人間は不要になりません。むしろ人間は、事実の基準線を持つ役として重要でした。

LLM レビューは強力ですが、グラウンドトゥルースを持たない領域では「もっともらしい見落とし」が起こります。仕様や実装の真実を握る役は残しておくべきです。

この記事自身に article:refine をかけたら、同じ落とし穴にはまった

ここまでの教訓を、この記事そのもので追体験することになりました。仕上げに、この記事自体へ article:refine を実際にかけてみたのです。設定は --max-rounds 3 --min-severity major --until clean でした。

結果は次のとおりです。

round スコア(低いほど良い) major 指摘 judge の approved
1 20 3 true
2 23(悪化) 3 true
3 23 3 true → stalled で停止

起きたことを正直に書きます。

  • 改善しなかった。むしろ少し悪化した。 スコアは 20 → 23 と上がり(このツールではスコアは「問題量」なので、上がる=悪い)、major 指摘は 3 件のまま減りませんでした。
  • 書く役が、新しい事実誤りを足してしまった。 「再現性を上げよ」という曖昧な指摘に応えようとして、修正後の本文に「Node.js 18 以上の LTS 系を推奨」という記述が追加されました。しかし対象リポジトリが要求するのは Node.js 20 以上で、しかも Node.js 18 は既に EOL です。元の版には無かった誤情報が、自動修正で混入したわけです。
  • 見る役は、それを止められなかった。 judge は実際の engines 設定を参照していないので、Node のバージョン誤りそのものは検証できず、「再現性が弱い」という曖昧な指摘を出し続けました。残った 3 件の major は、いずれもコマンドの実在性・前提バージョン・run ID 取得手順といった、judge が裏取りできない領域に集中していました。

つまり、この記事のテーマである「評価役は、知らない事実を検証できない」を、推敲ループ自身が実演したことになります。書く役は知らない事実をもっともらしく捏造し、見る役はそれを検証できず、スコアだけが空回りで悪化していく——という構図です。

救いは、安全弁と巻き戻さない設計が効いたことでした。

  • 改善が続かないと見るや、stalled でループが止まりました。延々と回り続けてコストと品質を溶かす事態にはなりませんでした。
  • このツールは悪化版を自動で巻き戻さない代わりに、各ラウンドの修正前スナップショットを残します。そこで人間が、推敲前の版(誤情報の入っていない方)を最終版として選び直しました。いま読んでいるこの記事は、その「戻した版」です。

最後に、設定上の反省もあります。この記事は全ラウンドで judge の approved が true でした。停止条件を --until approved にしていれば、1 ラウンド目で 1 回も書き直さずに終わり、Node.js 18 の誤りも生まれませんでした。--until clean は「コード記事向けの観点で major 指摘を 0 にする」基準なので、プロセス読み物に対しては厳しすぎたのです。記事の種類と、評価観点・停止条件の相性まで含めて選ぶ必要がある、という生きた教訓になりました。

自動推敲は便利ですが、万能ではありません。どこで止めるか・何を正とするかを人間が握るという結論を、最後に身をもって確認した形です。

再現しやすい 7 ステップに整理するとこうなる

今回の進め方は、小さな OSS でも再現できます。手順にすると次の 7 ステップです。

  1. 外部評価で課題候補を洗い出す
  2. 実コードを読んで優先度を再判断する
  3. 設計書を書く → レビュー → 改訂を収束まで回す
  4. 実装計画に落とす
  5. 既存テストを安全網にして、先にリファクタする
  6. 段階的に新機能を積み、各段階でテストする
  7. E2E と生成物レビューで、停止挙動と事実整合性を確認する

ポイントは、AI の数を増やすことではありません。

  • 役割を分ける
  • 主観と事実を分ける
  • 収束するまで回す
  • 最後は人間が事実で締める

この 4 点です。

まとめ

今回 article:refine 開発で効いたのは、AI に一括委任することではなく、役割を分けたことでした。

  • Claude Code を書く役
  • codex を見る役
  • 人間を進行と最終判断の役

に分け、設計レビューを複数ラウンド回してから実装に入る。これによって、品質がかなり安定しました。

また、効果を語るときは主観を事実として言い切らないことも重要でした。たとえば stalled 停止は「安全弁が働いた可能性が高い」とは言えても、それだけで改善性能の高さまでは証明しません。

AI コーディングを実務や OSS に入れると、「任せると品質がぶれる」という壁に当たりがちです。そのとき効いたのは、どのモデルが一番賢いかよりも、どう役割設計するかでした。

小さな OSS でも、これは十分に再現できます。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?