こんにちは。SEQ(Software Engineer in Quality)のtsubonneです。
私たちのチームは、現在自動テストの基盤開発、さらには開発フィードバックサイクルの高速化を目指した開発を進めています。
freee QA Advent Calendar 2025 15日目です。
本日は私が2025/10に入社してからの取り組みの中で仕組み化を行なった 人間とAIが「ストーリー」で協調するワークフローとナレッジ獲得の仕組み 【ストーリー駆動ハーネス】 と題しまして一本書かせていただきます。
[💡課題編:AIエージェント活用で直面した壁]
freeeのE2Eテストはアドベントカレンダー4日目の記事にあるように、 Playwright*1 をベースにした実装基盤で開発しています。
freeeに入社して、私が最初に与えられたミッションは 要素指定に起因する壊れやすいE2EテストをAI活用して自動化する ことでした。
ミッションで解決すべき課題
このミッションを行うにあたって、freeeのE2Eテストの現場では以下のような課題が存在しました。
- 要素指定の方針と現実のギャップ:
- E2Eテスト基盤では、Testing Library *2 が推奨する「ユーザー目線の要素の探し方」(ボタンの文字やラベルなど、画面に表示される情報での指定)を基本方針としている。
- しかし、プロダクトの実装都合上、変更に弱い要素指定(CSSクラス名や階層構造への依存など)にならざるを得ない箇所がある。
- こうした箇所には data-testid を付与して対応していたが、手動対応は非効率であり、テストメンテナンスのボトルネックになっていた。
- コンテキストスイッチのコスト:
- 作業対象が「E2Eテスト基盤」と「プロダクトコード」の2つのリポジトリにまたがるため、行き来するコストが高い。
課題解決にあたり、最初は自律型コーディングエージェント Devin*3 での自動化を検討しましたが、現状ではコンテキストとナレッジ不足によりコストがかさんでしまい、Claude Code*4 との 伴走によるナレッジ蓄積を優先する形にしました。
※詳細な経緯に興味がある方はこちらをチェックしてみてください。
Devin での試行錯誤
datatest-id付与のAI対応事例はDevinでのfreee内対応事例が存在していたため、まずはDevinによる自動化ワークフローを検討することにしました。
実装事例は大変シンプルな要件のみをワンショットで指示しDevinSessionに作業させるものでした。 この1つのDevinSessionにてfreee会計フロントエンド、PlaywrightベースのE2Eテスト基盤の2つのリポジトリに対して修正を行います。
実際のDevinプロンプト例:
E2Eのpage objectのセレクタについて改善したい。 (GitHub URL省略)/lib/pages/fizz/buzz/hoge.ts#L220 のセレクタが壊れやすいので改善したい。 freee-kaikeiの実装側に適切にdata-testidを付与する e2e-testsのセレクタを1に合わせて修正する
こちらのプロンプトを参考に実際に修正対応を行わせてみたところ、修正作業自体は行えたものの早速問題が出てきました。
実践の中で遭遇した問題
- ACU消費の数字がfreee内推奨値を大きく超える消費になってしまった。
- ACUとはDevinが作業を行う際の計算リソースや作業量を表す単位のことで、この数字によって費用が決定する。
- 成功事例のDevinSessionも改めて確認してみたところ、こちらも推奨値を超える結果になっていた。
- 修正対象となるfreee会計フロントエンドの要素特定や調査、data-testid 付与の手法に関するナレッジが足りなかった。
上記の内容をもとに、改めてfreee会計フロントエンドでのdata-testid付与の状況について調査することにしました。
freee会計フロントエンドにおけるAI活用の課題
freee会計フロントエンドの実装を調査したところ、以下のようなAI活用における障壁を発見しました。
- 大多数のエンジニアが開発に参加しており、機能や時期によって実装パターンが多岐にわたる。
- そのため
data-testid付与対象となるコードの記述も多様な状況にある。
このような「新旧アーキテクチャの混在と実装の多様性」 が大きな障壁であり、単にワンパターンでのアプローチでは難しいことがわかりました。
解決すべき課題
発生した問題を鑑みて、以下の課題を改めて整理・解決する必要があると再認識しました。
- ナレッジベースを含むコンテキストの未整備
- AIが自律的に判断して最短距離で作業するために必要な情報が不足していた。
- 作業手順(playbook)、アーキテクチャ(foundation)、エラー対処法(trouble shoot)のナレッジを明文化して整備する必要がある。
- 環境的な制約:
- リポジトリ内にE2Eテストのナレッジベースを格納する場所が決まっていなかった。
- Devinはfreeeでも試験導入中で以下制約もあった。
- Confluence等の外部ドキュメントを参照させるMCPが準備中だった。
- チーム単位のACUリソース制約により、試行錯誤の回数を十分に確保できなかった。
Claude Codeによる伴走型でのナレッジ蓄積の検討
まずは 人の判断を介在させる AIワークフローを採用し、実際にdata-testid付与の作業をAIと並走し完遂していくことで、信頼できるナレッジの蓄積を目指すことにしました。
ナレッジ蓄積にあたって、世間・freee内的にもデファクトスタンダードになりつつある Claude Code を選択しました。
Claude Codeを選択した理由としては以下になります。
- Devinが自律型のコーディングエージェントであるのに対し、開発者のすぐ隣で働く、高速で賢いペアプログラマーとして優秀。
- エージェントとして内部でどのように考え、どのファイルを確認し、どう修正するかをリアルタイムに表示してくれる。
- freee内の利用上限がDevinよりも高く試行錯誤しやすい。
ナレッジ蓄積の手法
まずは私とエージェントの作業プロセスを管理しやすい形で可視化するための 作業ログ を外部ファイルとして保存することを考えました。 ローカルにてClaude Codeと一緒にdata-testid付与対応を実際に行っていき、蓄積された手順や工程に応じた調査手法やトラブルシューティングを振り返ってナレッジ化するイメージです。
セッションコンテキストの管理
コーディングエージェントを扱うにあたって、AIの回答精度を保つためには セッションに与えるコンテキスト を設計するのが非常に重要になってきます。 LLMには処理できるトークン数(コンテキストウィンドウ)に上限があり、大量のコードやログを読み込むとこの上限に達してしまいます。
セッションコンテキストが圧迫されると作業を進めてく上で以下のような不都合が起こってしまいます。
- AIの回答精度が落ちる。
- エージェント側でのauto compactによる意図しない要約が行われてしまう。
- 作業フェーズに関係のないコンテキストがノイズになってしまう。
今回のミッションではリポジトリを横断的に作業させる関係上、Claude Codeの1セッションでauto compactを走らせずにコンテキストを保持して作業させることは難しいと感じていました。 セッションを横断して作業状況を引き継ぐための進捗管理も必要だったため、ストーリー(スクラムなどで使われる作業単位)でコンテキストがまとまるように管理していくことにしました。
このストーリーをClaude Codeの エージェントハーネス*5 として機能させストーリーを中心に駆動させる仕組みを ストーリー駆動ハーネス として命名し、設計することにしました。
[💡解決編:ストーリー駆動ハーネスでの実運用]
今回のミッションであるリポジトリを横断したdata-testidの付与作業の作業工程を整理すると、以下のイメージになります。
こちらの作業工程を含むタスクに必要なコンテキスト情報を含んだ「ストーリー」を中心に、エージェントを駆動させるためのエージェントハーネスを構築しました。
data-testidの付与の作業工程
準備フェーズ
- 🤖(AI): e2e-tests, freee-kaikeiリポジトリの作業ブランチ、ワークツリーの発行を行う。
調査フェーズ
- 🤖: e2e-testsリポジトリにて修正対象の要素指定が定義されているPOMファイルと修正箇所を特定する。
- 🤖: freee-kaikeiリポジトリにて1で特定した要素指定に紐づくviewファイルと付与箇所を特定する。
実装フェーズ
- 👨(人間): 調査結果を確認し問題なければ修正作業をエージェントに依頼する。
- 🤖: freee-kaikeiリポジトリにて2で特定したviewファイルに修正作業を行う。
- 🤖: e2e-testsリポジトリにて1で特定したPOMファイルに修正作業を行う。
検証フェーズ
- 🤖: freee-kaikeiリポジトリの作業ブランチを検証環境にデプロイする。
- 👨: e2e-testsリポジトリにて検証環境に向けて修正したE2Eテストを実行する。
- 🤖: freee-kaikei, e2e-testsリポジトリの作業ブランチをPR発行する。
ストーリー駆動ハーネスの構成
ストーリー駆動ハーネスは以下の要素で構成されます:
- 作業単位で発行した作業計画・進捗・ログを管理するストーリーディレクトリ
- ストーリーの作業をエージェントに進行させるための窓口になる /command
このエージェントハーネスを利用することで、以下を実現します。
- コンテキスト圧迫によるLLM精度低下前に速やかなセッションの引き継ぎを行う。
- 模範となる対応事例の作業ログからナレッジ抽出を行う。
ストーリーのファイル構成
1つのストーリーの内容はディレクトリ単位で管理し、構成は以下のように定義します。
-
story.yml: 作業全体の進捗管理とセッション開始時に参照させるファイル群を記録。 -
subtasks/: 各工程の作業計画とプロセスログを記録。 -
sessions/: 人間が指示したプロンプトの記録(セッションごとに管理)。 -
worktree/: エージェント用のgit作業環境を格納(git worktreeを使用してブランチごとに独立した作業ディレクトリを作成)。
ディレクトリ構造の例
以下は、実際にdata-testid付与対応を行ったストーリーのツリーになります。
├── sessions
│ ├── 20251210-015001.yml
│ └── 20251210-015621.yml
├── story.yml
├── subtasks
│ ├── 01_preparation/. # git worktree, branchの準備フェーズ
│ ├── 02_investigation/ # 調査フェーズ
│ ├── 03_implementation_frontend/. # 実装フェーズ frontend
│ ├── 03_implementation_e2e/ # 実装フェーズ E2E
│ └── 04_verification # 検証フェーズ frontend
└── worktree
├── freee-kaikei-feature-add-data-testid
└── e2e-feature-add-data-testid
ストーリー駆動ハーネスでの作業進行
ストーリーは Claude Code上での /command をトリガーとし、エージェントに作業を実行させます。
- ストーリーの作成
-
/story-create: 事前に定義したストーリー構造に基づき作業要件・リポジトリ・フェーズなどの必須項目の補完と作業のプランニングを人とエージェントで行い、ストーリーの作成を行う。
-
# =================================================================== # 作成されるstory.yml (抜粋) # =================================================================== # ストーリー管理用に付与するmetadata群 metadata: id: "20251210-freee-kaikei-data-testid: ****.tsの要素指定をdata-testid化" title: "data-testid: wizards.tsの要素指定をdata-testid化" description: "e2e-tests:lib/pages/accounting/annual_reports/final_return/wizards.tsの..要素指定をdata-testid付与を行いgetByTestId指定に修正" created_at: "2025-12-10T00:42:58" status: "implementation_e2e" template: "data-testid-migration" current_phase: "implementation_e2e" # ストーリーで予定している作業フェーズとそのステータス phases: - id: "preparation" name: "準備フェーズ" status: "completed" started_at: "2025-12-10T00:42:58" completed_at: "2025-12-11T15:52:00" - id: "investigation" name: "調査フェーズ" status: "completed" started_at: 2025-12-09T15:52:44 completed_at: 2025-12-09T16:00:34 - id: "implementation_frontend" name: "実装フェーズ(フロントエンド)" status: "completed" started_at: 2025-12-09T16:26:24 completed_at: 2025-12-09T16:50:01
- ストーリーの作業開始/再開
-
/story-resume: 指定されたストーリーディレクトリのstory.ymlをもとにエージェントが作業開始・再開に必要なコンテキストを読ませる。
-
- 各フェーズの作業終了時
-
/story-update: 各フェーズごとにセッションを終了する前に作業ログを記録させる。- セッションのライフサイクルもフェーズ単位で行う運用にすることでコンテキストの枯渇を防ぐ。
- storyディレクトリファイル群の更新は、予め約束事として明示していてもエージェントが書き込みを忘れるため
/story-updateの実行時に更新を行わせる。 - 作業精度が悪い工程ではこのタイミングで作業ログの確認を行う。
-
# =================================================================== # subtasks/investigation.yml 調査フェーズで蓄積された作業ログ (抜粋) # =================================================================== phase: investigation status: completed started_at: "2025-12-10T00:52:47" completed_at: "2025-12-10T00:52:47"" summary: | 調査フェーズを完了しました。****.ts含む申告関連の3つのPageObjectファイルで locator("..")を5箇所特定し、対応するReact/TSXコンポーネントを特定しました。 すべてセクション特定パターンであり、data-testid命名戦略を決定しました。 completed_tasks: - task: "locator(\"..\")使用箇所の特定" details: | - 合計5箇所のlocator("..")使用を特定 - すべて「親コンテナ系(セクション特定)」パターン - ファイル別内訳: * ****.ts: 2箇所 * ****.ts: 1箇所 * ****.ts: 2箇所 ...
- ストーリーの終了
-
/story-complete: 指定されたストーリーディレクトリ内の各ログファイルとstory.ymlを更新しストーリーディレクトリをアーカイブする。
-
- ストーリーのレビュー
-
/story-review: 指定されたストーリーディレクトリ内の各ログファイルとstory.ymlを参照し人とエージェントの作業プロセスをレビューする。
-
[💡成果編:実践で得られた効果と今後の展望]
ストーリー駆動ハーネスを用いたClaude Codeとの伴走により、実際にdata-testid付与作業を10件ほど対応しました。 件数としてはまだ少ないですが、以下の当初の目的が達成できたことを確認できました。
- 信頼できるナレッジベースの構築: 作業完了したストーリーをレビューすることでベストなアプローチを抽出できる基盤が整った。
- 作業プロセスの可視化:
story.ymlとsubtasksによる進捗管理が可能になり、AIの作業プロセスのブラックボックス化を防げた。 - セッション跨ぎの引き継ぎ: ファイルベースでコンテキストを保持することで作業の中断・再開がスムーズになり、AIの回答精度を保てるようになった。
現在は蓄積されたdata-testid対応のストーリーから作業手順や対応方法に関してのナレッジを抽出し、対象の作業に特化した ストーリーテンプレート の作成と導入を検討しています。
[💡まとめ:AI活用の本質とこれから]
今回の取り組みで得た学び
- AIの完全自律実行には事前準備が必須: 作業手順、アーキテクチャ、エラー対処法のナレッジ整備が大前提。
- 伴走型からナレッジを蓄積する: 完全自動化を急ぐのではなく、まずは人が伴走してナレッジを蓄積することが、結果として自動化実現への第一歩。
今後の課題
私に与えられたミッションは本来AIが自律的に作業を完遂するワークフローの構築し.
data-testid付与を自動化することなので、 現状は下準備が一つ進んだといった状況です。
さらなる自動化に向けて以下の課題に取り組んでいます。
- ナレッジのさらなる拡充: エージェントが自律判断できる範囲を拡大する。
- エラー時の自動対応: フロント・E2Eテスト共に、修正後のエラーハンドリングを自動化する。
最後に
完全自動化は道半ばですが、AIに任せる前に何を準備すべきかの再認識とナレッジの元になるストーリーを蓄積する仕組みを整備することができました。
現状のコーディングエージェントは「魔法の杖」ではなく、きちんとしたストーリー(作業単位)とコンテキスト/ナレッジがあって初めて力を発揮するツールだと改めて感じています。
本記事が、コーディングエージェント導入を検討している方や、AI活用で試行錯誤している方の参考になれば幸いです。
明日は、freeeサインのQAエンジニアのmatsujun-san が「転職1年目のQA活動記 - 経験を活かし、新たに学んだこと」について記事を書いてくれます。お楽しみに〜!
それでは、よい品質を〜
*1: https://playwright.dev : Microsoftが開発したモダンなWebアプリケーション向けのUI自動化・テストフレームワーク
*2: https://testing-library.com : ユーザーの視点に立ってUI(ユーザーインターフェース)をテストするために設計された、JavaScriptのライブラリ群の総称
*3: https://docs.devin.ai : 「世界初のAIソフトウェアエンジニア」を目指して設計された、自律的に開発タスク全体を実行できる大規模言語モデルベースのエージェント
*4: https://code.claude.com/docs/en/overview : Anthropic社が開発した、コード生成・デバッグ・修正をCLI環境で自律的に実行できるAIコーディングエージェント
*5: https://www.anthropic.com/engineering/effective-harnesses-for-long-running-agents : AIエージェントが複雑で長時間にわたるタスクを安定して遂行するための「土台」や「フレームワーク」
