Claude Codeの性能を引き出すワークフロー設計

はじめに

こんにちは。カイポケコネクトの開発推進チームでエンジニアをしている @_kimuson です。

開発推進チームではエンジニアの生産性向上をミッションに掲げているため、最近では積極的にAI活用を推進しています。

上記エントリでは、タスクごとの協業レベルを定義しより低い協業レベル(=できるだけLLMに移譲しきる)を実現するための方針を紹介しました。

このエントリではより具体的に、Claude Codeをフル活用してこういったワークフローの設計を組織に適用する際の知見をまとめてみようと思います。

設計するワークフローの協業レベルを意識する

前回のエントリでは、ワークフローを設計するに当たって協業レベル、つまりどの程度LLMに権限移譲するか、をデリゲーションポーカーの分類を借りて整理しました。

よくあるLLMの利活用シーンと対応する移譲レベルは下記のように対応します:

流れ 協業レベル 主体
ChatGPTに設計について相談し、自分で実装 Consult 人間
Copilot や Cursor の補完を使いながら、自分で実装 Consult 人間
細かい指示を出しながらエージェントが実装。Driver がエージェントなモブプロ Agree 人間 & LLM
LLM にチケットを渡してPRまで作ってもらう Inquire LLM
LLM にチケットを渡してPRを作ってもらい、LLMによるレビューでマージまで完結 Delegate LLM

広く「AI活用」といっても、目的ややりたいこと次第で設計すべきワークフローの協業レベルは変わってきます

  • AgreeやConsultレベルで、エンジニアとAIが協業しながら出せるアウトプットの質を高めようという話なのか
  • Inquireレベルに持っていくことで、責務を移譲しデザイナーが簡単なFE実装をできるようにしたり、QAの人が自動テストを書きやすくしたりといった職能の拡張をしたいのか
  • Inquireレベルに持っていくことで、エンジニアの作業時間をブロックせずに小さいタスクをゴリゴリ進めてもらおうとしているのか

これらはすべてAI活用ですが、ワークフロー設計の考え方が大きく変わってきます。

なので最初に明確に意識しておくのが良いと思っています。

エージェントはコンテキストがすべて

エージェント設計でもっとも重要なことはコンテキスト管理だと思っています。

イメージしてみてください。われわれ人間が開発をするうえでも

  • 設計をしているときは、要件・非機能要件を満たせるか、対応できないEdgeケースがないかをワーキングメモリに置きながら考えるし
  • フロントエンド開発をするときはTypeScriptの型システムやReactの思想を意識しながらコーディングするし
  • 同じフロントエンド開発でもユニットテストを書くときは仕様の漏れや境界値など検証すべき内容を意識しているし

と言った形でワーキングメモリに置いていることが全く違います。

人間はこういうコンテキストの切り替えを自然にできるんですが、LLMはこれができません。

したがって工夫をしないと、エージェントはこれらすべての視点をすべてワーキングメモリに載せた注意散漫な状態で様々な作業を行う羽目になります。これではいくらモデル性能があがっても質の高いアウトプットは出しづらいです。

→ つまり、コンテキストを最適化すること、それを実現するためのプロンプトがすべてなので、そこに投資をするのが大事になってきます。

コンテキストをどうやって分離するのか

このコンテキスト分離の答えの1つがマルチエージェントです。

単一のエージェント・会話セッションでやりたいことを全部載せると指示も会話ログが非常に長くなってしまうので、それぞれの個別の責務を持つエージェントが協業することでコンテキストを分離しようという考え方です。

この領域ではGoogleが発表されている A2A プロトコル等もありますが、普及しているコーディングエージェントのCLIツールで利用できるわけではないのでとっつきづらさがあります。

Claude Codeではやや制約もある*1 もののタスクツール・サブエージェントが組み込まれているので、手軽にこれを実現できます。

例として、実装を丸投げしようと思ってオーケストレーションなしでワークフローを組んでみます。チケット確認→設計→API Spec決定→BE実装→FE実装→型エラー・テスト対応→報告、という流れです。

オーケストレーションなしのワークフロー

この場合、すべてのステップの注意事項やガイドラインを含むと、プロンプトが数百行にもなります。こういうのを作ってみるとわかりますが、

  • それぞれのステップで重要な観点を担保できない
  • どこかのステップで詰まると解決できない・長引くと型エラー対応などのステップがスキップされる

といったことが多くなります。

一方、これをオーケストレーション前提で組んでみると、メインセッションの責務はオーケストレーションだけになります。各ステップの状況を追っていって、サブエージェント(サブタスク)を呼び出して内容は移譲する。これだけです。

オーケストレーションありのワークフロー

こうするとサブエージェントには単一責任なプロンプトを持たせた上で起動できるので、「設計において重要なこと」「BE実装のガイドライン」などそれぞれの責務で必要なプロンプト のみ を渡した上で各ステップを実行できるようになります。

オーケストレーションなしだと他のステップの会話ログも全部N回投げつけることになるのに対して、コンテキストは適時破棄されるのでトークン効率も良いです。

オーケストレーションのトレードオフ

という感じでオーケストレーションは良いことづくめに見えますが、すべてのケースで適用すべきというものでもありません。

実際に使ってみると弱い点もあります

  • 理論上解ける問題の範囲を広げやすいが、現実にはオーケストレーターと各サブエージェントの関わり方の設計の難易度が高い。
  • 対話を重ねることによって成果物をブラッシュアップすることにも向きません。

ブラッシュアップに向かないのがなぜかというと、オーケストレーションではサブエージェントの実装のコンテキストを知ることができません(むしろそれを目的に分離している)。

したがって、ユーザーとメインセッションの会話はいわば伝言ゲーム状態であり、メインセッションも実装の詳細を知らないため、詳しくない者同士が対話している状態になります。

オーケストレーションのトレードオフ

ということで協業レベルを意識することが大事になるわけです。

Agreeレベルで一緒に作っていきたい内容はオーケストレーションの採用を限定的にした方が良いですし、逆にInquire以上のレベルでまるっと移譲したいタスクは積極的にオーケストレーションしていくと良いです。

また、0/100ではなく部分的に使う案はバランスが良くAgreeレベルでも採用しやすく私は好んで使っています。

  • メインセッションの責務は「実装 & オーケストレーション」にする
  • メインセッションは実装に集中しつつ、設計やコードレビュー・動作確認などは自分で行わずサブエージェントへ移譲する

これだと「もうちょいここ直さないとでしょ〜」みたいなフィードバックを柔軟に受けつつ、各ステップの品質を安定させやすくなります。

サブエージェント vs Skill vs コマンド

エージェントに適切なコンテキストを付与する手段としてClaude Codeは複数の手段を提供していて、特に効果的に活用しようと思うと使い分けの判断が難しいのでは?と思ってます。

ここでは私なりにそれぞれが得意とする観点や用途を整理しようと思います。

Progressive Disclosure

Progressive Disclosureは、最近よく主張されているタスクごとに必要なコンテキストだけを段階的に読み込ませるプラクティスです。Claude Skillsの公式のプラクティスで推奨されている概念で、LLMのコンテキストウィンドウを効率的に使うための重要な考え方です。

例えばCLAUDE.mdやMCPはすべてのセッションで例外無く読まれます。10%のセッションでしか使わない知識を入れると無駄になってしまいます。

一方、Skillやコマンド、サブエージェントは必要なセッションでだけプロンプトを読み込む仕組みです。「限られた場面でしか使わない専門的な知識」を置く場所として優れています。

Progressive Disclosure

良い面ばかり言われるのでネガティブな面にもフォーカスすると、段階的にSkillを使えれば理想なんですが、自動で適切に適用していくのは現実的に難しいと感じています。

  • Must always be enabled when writing/reviewing React code のような強めのdescriptionを入れておいてもReactを触るときに必ず使ってくれるわけではない
  • サブエージェントもLLMが自律的に使うかを決めますが、プロンプトなしでReactを書くときにReact agentへ移譲するとは限りません

ですので、もちろん重要な仕組みではあるのですが、Skillやサブエージェントを増やし続けても人間のように自然なコンテキストスイッチをして実装してくれるわけではありません。

とはいえ考え方自体は非常に重要なので、すべてのコンテキストに読まれてしまうCLAUDE.mdは以下のような方針で最小限にしています。

  • いかに育てるかではなく、いかに情報を落とすかこそが大事。 むやみに増やさない
  • 80%以上のタスクで必要になるか、プロジェクト概要など解像度を上げるため必要なむしろ抽象的な情報を中心にする
  • 情報そのものよりProgressiveにDisclosureするための起点となる情報(例えばトラブルシュートについてはこのドキュメントにまとまっているよ、など)を記載する

また、MCPについてはSkillsの登場の背景としてMCPがProgressiveにDisclosureできないことから説明されていたのでネガティブな印象を受けがちですが、Claude Codeでは段階的な読み込みがサポートされており有効にすればProgressive Disclosure面での不利はもうないので、用途にあわせてSkillsと適切に選択できると良いと思います。

Command vs Skills vs Agent

CLAUDE.mdはProgressive Disclosureの文脈でむやみに育てるべきではないので、代わりにコマンド・スキル・サブエージェントを育てていくのが良いと思います。

これらの使い分けについては悩みがちだと思うので私なりの整理を書いておこうと思います。

Command vs Skill は本来の用途に寄せる

まず機能面の話をすると、コマンドについては実はシステムコンテキストとしてコマンドとスキルはもうほぼ同一視されているので境界はユーザーのわかりやすさ程度しかありません。

ユーザーも「/<skill名>」でスキルをコマンドのように呼び出せますし、エージェントも「Skill(skill=<コマンド名>)」の形でコマンドを有効化できます。エージェントに「スキルの一覧教えて」等と問いかけると当たり前にスラッシュコマンドを含むリストを区別なく返す状態なので、我々利用側も同一のものと考えてしまって良いと思います。

ですので、あとはわれわれのわかりやすさで使い分ければ良い範囲ですが、個人的にコマンドとスキルは設計思想が異なるので「人が直接実行するのを主な目的とするか」で区別して使い分けています。

  • コマンド
    • 人が直接実行する
    • 知識というより手順を載せ、エージェントの動き方を制御したい
  • スキル
    • 人が直接実行することもあるが、サブエージェントで指定されたりProgressive Disclosureで知識を補完するのがメイン
    • 全体の作業手順はあまり書かない(複数のスキルを有効にしたとき、指示矛盾が起きやすい)

Skill vs Agent で重要なのは複数利用したいか

サブエージェントについては @<agent-name> でサブエージェントを指定できるようになってはいますが、コマンド的にメインセッションにモデルやプロンプトを適用することはできません。

Skill vs Agentで重要なのは1セッションに対して複数利用できるかという観点で、例えばReact skill × TypeScript skillはできますが、React agent × TypeScript agentはできません。

なので再利用可能な知識の単位としてSkillを分離し、サブエージェントに読み込むスキルを指定することが望ましいと思っています。

name: frontend-engineer
description: フロントエンドの実装を行う
skills:
  - typescript
  - react

---

<prompt>

責務分解点

責務、つまりサブエージェントをどう切るか。いろいろなパターンがあるかなとは思うのですが、私がよく切るパターンを紹介します。 これらすべてを使うわけではなく、タスクの難易度だったりで部分的に使ったりする前提です。

コンテキスト収集プロセス

カイポケリニューアルのプロジェクトのフロントエンドでは開発時のガイドラインなどがかなりちゃんとガイドライン化されています。

逆にそれがゆえにすべてのドキュメントを読み込ませてしまうとコンテキストが膨れてしまうので、今回のタスクに関連するガイドラインのみを抽出して渡すようなプロセスが効果的です。

今回必要なガイドラインのみ集める・LLM向けに不要な情報を削り、LLM向けの整理されたコンテキストを用意することが責務ですね。

計画・設計プロセス

これはよくあるパターンなので改めて書くことはそれほどないですが、設計において重要な観点などをプロンプトに含めておき設計に集中させます。

Tipsとして非常に重要なステップなのでSonnetを普段使いしていてもここだけOpusにしたり、また少し大変ですがこういうステップはOpenAI系のモデルのほうが得意なのでそちらに流すのもオススメです。LiteLLM やBashツールでCodex CLIを使うことでこういうステップのみAnthropic以外のモデルに任せることもできます。

参考:

コードレビュー

コードレビューも切り出すと効果的です。

体感「Aをしてはいけません」というルールを守らせながらコードを書かせる精度は出づらいように感じており、実装側には否定形ではなく例など用いて「こんな風に書いてください」を書くことが多いです。

逆にレビューステップだと否定形をかなり上手く扱って指摘できるので、特にレビューステップ用のプロンプトではやらないでほしいことを書くことが多いですね。

また、レビューはミュータブルでないタスクなので並列化しやすく、かつ毎回異なるフィードバックが返ってくるので質を上げたければN並列で依頼するのも効果的です。

QA

アプリケーション次第ですが、例えばローカル環境を用意しやすいWebアプリケーションであれば開発サーバーを起動してE2Eで試したり、API開発であればcurlで叩いてみたりします。

プロンプトを書くメタプロンプト

ここまで書いてきたように

  • ワークフローをまず丁寧に設計すること
  • そのワークフロー通りに動かせるようにプロンプトを書くこと

が重要ですが、こういった知見はあっても守った状態で大量のプロンプトを書くのは非常に大変なのでプロンプトを書く用のコマンドを用意しておくのがオススメです。

参考までに私が使っているコマンドを貼っておきます:

このコマンドは私が言語化したプロンプトのプラクティスに沿ってプロンプトを書き、サブタスクを使ったレビュープロセスを行うことである程度期待に沿ったプロンプトを書いてくれるコマンドです。

私が確認する前段である程度問題を検出して修正できますが、とはいえ意図と異なる結果になることも多いので、丸投げはできず全体をちゃんと確認しています。

私はまだそれほど質で困っていないので使っていませんが、DSPy などプロンプトの評価プロセス自体を持つ仕組みを使うことで質を挙げていくことも有用だと思います。

まとめ

Claude Codeを使ったAI活用のワークフロー設計について、私なりの知見をまとめてみました。

  • 協業レベル・責任境界を意識してワークフローを丁寧に設計する
  • ワークフローは協業レベルが低ければオーケストレーション要素を強化し、自律的に動かせるようにする。逆に高密度で協業するのであればレビューやデバッグなど本来の目的と異なる副次的な作業をサブセッションで分ける程度に留める、といった設計が大事
  • CLAUDE.mdを育てない・Skillを再利用単位として適切な粒度で育てる。エントリーポイントとしてコマンドやサブエージェントを構成
  • プロンプトの品質向上には時間がかかりますが、言語化・体系化する機会にもなるので、プロンプトを書くためのプロンプトを用意しておくと良い

皆さんのAI活用の参考になれば幸いです!

*1:サードパーティのLLM APIのProxy等で複雑な構成を組まない限りAnthropic以外のモデルを選択することができません。またサブエージェントはサブエージェントを呼び出すこともできません。一方、制約はこれくらいで以前はチャットの再開が不可能である問題等もありましたが対応され、極端に大規模なマルチエージェント協業が必要でなければ十分な機能を持っています。