AI で社内業務を自動化したら、直すべきは AI ではなく「仕様の書き方」だった
社内の業務 Web アプリで、Jira の PBI(プロダクトバックログアイテム)を投げると PR ができるまで自動で回るループを作り、実際に PBI を数件片付けました。Claude Code を Amazon Bedrock 経由で動かしています。
やる前は「AI の精度をどう上げるか」が課題になると思っていました。プロンプトを工夫し、モデルを変え、試行錯誤するイメージです。
実際にやった作業は、ほとんどそれではありませんでした。
時間を使ったのは、こちら側が書いていたもの——受入条件、チケットの書き方、試験書の期待値、エージェントへの情報の渡し方——を直す作業です。AI が賢くなれば解決する類の問題は、ほとんど出てきませんでした。
本記事は、その「直すべきだったもの」を 3 つの証拠とともに書きます。うまくいった話だけでなく、無駄にした時間も含めます。
作ったもの
Jira の PBI
↓ 受入条件を抜き出す
┌──────────────────────────────────┐
│ 実装エージェント │ worktree の中でコードを書く
│ ↓ 差分だけを渡す │
│ レビューエージェント │ 別プロセス・別コンテキスト
│ ↓ 全条件 MET ? │
│ No → 指摘を次ラウンドへ引き継ぐ │
└──────────────────────────────────┘
↓ Yes
PR 作成(マージは人間)
シェルスクリプト 1 本と、プロンプトのテンプレート 2 枚だけです。フレームワークは使っていません。
設計でこだわったのは 3 点。
1. 受入条件を「終了条件」にする。 「いい感じに実装して」ではループが止まりません。Jira に書かれた受入条件をそのまま終了条件にして、全条件が MET になるまで回します。
2. 実装とレビューを別プロセス・別コンテキストで動かす。 同じコンテキストで「書いて、自分でレビューして」とやると、自分の書いたコードに引きずられます。レビュー側には差分しか渡しません。実装側の「こう書いたのはこういう理由で……」という言い訳が届かないので、判定が辛くなります。
# 実装
printf '%s' "$prompt" | ( cd "$WORKTREE" && claude -p --permission-mode acceptEdits )
# レビュー(差分と PR 本文だけを置いた別ディレクトリで動かす)
git diff "origin/$BASE...HEAD" > "$REVIEW_DIR/diff.patch"
printf '%s' "$review_prompt" | ( cd "$REVIEW_DIR" && claude -p --add-dir "$REVIEW_DIR" )
3. PR は作るが、マージは必ず人間。 レビューコメントは event: COMMENT で投稿します。APPROVE にすると GitHub が自己承認を弾く(PR 作成者と同一アカウントのため)という実務的な理由もありますが、それ以前にマージの判断は人に残すべきという方針です。
ここまでが前提です。以降が本題になります。
証拠① 同じ仕組みなのに、11 分と 2 時間に分かれた
4 回ループを回して、ラウンド数はこうなりました。
| PBI | ラウンド | 実働 |
|---|---|---|
| A | 3 | 138 分 |
| B | 4 | 127 分 |
| C | 1 | 11 分 |
| D | 1 | 15 分 |
同じスクリプト、同じモデル、同じリポジトリ、同じ人間です。それでも 10 倍以上の開きが出ました。
規模でいえば A・B のほうが大きいのは確かです。しかしラウンドが増えた原因はそこではありませんでした。A と B の受入条件には、こういう形の項目が混ざっていたのです。
- リリース時に ◯◯ の設定を有効にすること
- デプロイ後に管理画面から △△ を確認すること
マージ前のループでは、原理的に検証できません。
エージェントは毎ラウンド真面目に「この条件は現時点では検証不能です」と報告し、レビュー側は NOT_MET を返し、ラウンドだけが溶けていきます。AI が悪いわけではありません。終了条件として成立していないものを終了条件に置いた、こちらの設計ミスです。
C と D は、起動前にこれらを受入条件から外し、「リリース手順のチェックリスト」へ分離しました。受入条件には「コードで検証できる条件」だけを残した結果、どちらも 1 ラウンドで全条件 MET。
受入条件に「リリース時」「デプロイ後」「設定作業」が出てきたら、それはループの終了条件になりません。
別の場所へ移してから起動してください。
そして、これは AI に限った話ではありません。そもそも受入条件の書き方として正しくなかったという話です。人間が実装していたときは、曖昧な条件があっても「まあ後で確認すればいいか」と暗黙に処理されていました。AI に投げると、それがラウンド数という形で可視化されただけです。
自動化の最初の成果物が「受入条件の品質向上」だったというのは、始める前には想像していませんでした。
証拠② 仕様書のほうが間違っていた
PBI が終わったあと、機能試験を Playwright で自動化しました。その過程で、手作業では見落とされていた試験書の誤記が 2 件見つかりました。
いずれも「期待値として書かれている識別子が、実データと違う」というものです。アプリの不具合ではありません。文字列としては似ていて、桁数は正しい。人が目視で確認していたときは、桁数を数えて「合っている」と判断されていたのだと思います。
ここが重要なところです。
人手の試験は「期待値どおりか」を見ているつもりで、実は期待値そのものを疑っていません。
機械は完全一致でしか通さないので、そこが露出します。
自動化して最初に壊れたのは実装ではなく、仕様書のほうでした。「テストが落ちた → 実装を直す」ではなく「テストが落ちた → 期待値が間違っていた」というケースが、最初の実行でいきなり 2 件出たわけです。
自動化の副次的な効果として、「仕様と現実がズレている」ことの検出があるのは発見でした。
証拠③ 止まった原因のほとんどは、AI ではなく渡し方だった
ループが動かなかった・変な結果を返した場面を並べます。**AI の判断ミスは 1 つも含まれていません。**全部こちら側の設定と渡し方の問題です。
--add-dir にプロンプトを食われる
レビューが毎回 1 秒で終わって空の結果を返す、という現象でした。
# ダメな例:プロンプトが --add-dir の引数として解釈される
claude -p "$prompt" --add-dir "$REVIEW_DIR"
# 直した例:stdin で渡す
printf '%s' "$prompt" | claude -p --add-dir "$REVIEW_DIR"
引数の順序の問題ですが、**エラーにならず「それらしく失敗する」**ので気づくのに時間がかかりました。「AI の出力がおかしい」ではなく「AI にプロンプトが届いていなかった」。
acceptEdits はファイル編集しか許さない
--permission-mode acceptEdits にしただけでは、npm install も pytest も走りません。-p(非対話)では権限プロンプトに答えられないので、ループが静かに何もしない状態になります。
許可コマンドを明示する必要があります。
WORKER_ALLOWED_TOOLS="Bash(npm *) Bash(npx *) Bash(poetry *) Bash(python3 *) Bash(git *)"
特にロックファイルは、整合ハッシュをレジストリからしか取れないので、手編集ではなく実際のインストーラを走らせないと壊れます。「AI がテストを実行しない」のではなく「実行を許可していなかった」。
git add -A で調査用ファイルを 23,000 行コミット
エージェントが作業用に大量のファイルを作り、それが丸ごと PR に入りました。.gitignore に入っていないディレクトリだったためです。
除外パスを持たせ、毎ラウンド作業ツリーから消すようにして解決しました。
SCRATCH_PATHS="tmp/"
Jira の description しか読ませていなかった
「本文に書いてあることは全部渡しているから大丈夫」と思っていたら、コメント欄に書かれていた作業指示を丸ごと取りこぼしました。本文には無く、コメントにだけ「ついでにこれもやりたい」と書かれていたパターンです。
意図や補足は、本文よりコメントに書かれていることのほうが多い。AI が読み落としたのではなく、こちらが渡していなかったわけです。
チケットの「正」がどこに書かれているかはチーム次第です。自分のチームで実際どこに書かれているかを確認してから、渡す範囲を決めるのが良さそうです。
一時認証が途中で切れて 28 分が無駄になる
ラウンドの実行中に AWS の一時認証情報が失効し、そのラウンドの成果が丸ごと消えました。
ラウンド開始前に残り寿命をチェックし、閾値未満なら開始しないガードを入れました。
if [ "$remaining_seconds" -lt "${MIN_CRED_SECONDS:-1800}" ]; then
echo "残り寿命が 30 分未満。ラウンド途中で失効するので開始しない"
return 1
fi
長時間走るジョブでは、「途中で失敗する」より「始めない」ほうが安いという当たり前の話です。
ただし、AI 側にも限界はある
ここまで「悪いのは AI じゃない」と書いてきたので、公平に反対側も書きます。AI 起因の問題は確かにありました。
「外部の事実」を語るときの誤報が 3 回あった
実装エージェントが、外部リポジトリの状態について断定的かつ深刻な報告を上げてきたことが 3 回ありました。文面は具体的で、根拠らしきものも添えられています。
3 回とも、一次情報に当たったら事実無根でした。
コードの中の話であれば、書いたものを読めば検証できます。しかし 「外の世界が今どうなっているか」はエージェントの手元には無い情報で、そこは推測で埋まりやすい。断定的な口調は、確信度の指標になりません。
逆に、エージェントのほうが正しかったこともある
もっと厄介なのがこちらです。
レビューエージェントが、実装側の判断に対して「この変更幅は大きすぎて危険だ」と指摘しました。経験則としてもっともらしく、私も一瞬そちらを採用しかけました。
しかし一次資料を 1 件ずつ読み直したら、レビュー側の推す「安全側の案」では要件を満たさないことが分かりました。実装側の判断が正しかったわけです。
どちらの言い分も、一次情報に当たって初めて判断できます。
とくに「常識的に聞こえるほうを採る」のは危険でした。
実装側の非常識に見える判断のほうが、資料を読むと正しい、ということが実際に起きます。
つまり AI 側の限界は「間違えること」そのものではなく、間違いと正解が同じ口調で出てくることです。ここだけは、仕様の書き方では解決しません。人が一次情報に当たるしかない。
コストも、結局は仕様の質で決まる
Cost Explorer の実測値です。PBI 3 件で $59.81。日次で見ると、ループを回した 4 日間にきれいに収まっていました。
| 日付 | USD | その日やったこと |
|---|---|---|
| Day 1 | 3.19 | 環境構築、初回起動 |
| Day 2 | 21.93 | PBI A(ラウンド 1〜3) |
| Day 3 | 19.24 | PBI B 起動 |
| Day 4 | 15.45 | PBI B 完走、C・D |
| Day 5〜8 | 0.00 | ループ停止(試験・文書作成のみ) |
| 合計 | 59.81 | 1 PBI あたり約 $20 |
トークンの内訳が面白かった
| 種別 | トークン | USD | 単価 (USD/Mtok) |
|---|---|---|---|
| キャッシュ読み出し | 51.73 M | 25.86 | 0.50 |
| 出力 | 0.79 M | 19.80 | 25.00 |
| キャッシュ書き込み | 2.22 M | 13.88 | 6.25 |
| 入力 | 0.05 M | 0.26 | 5.00 |
| 合計 | 54.79 M | 59.81 |
トークン数の 94% はキャッシュ読み出しなのに、費用では 43%。
逆に出力はトークン数で 1.4% しかないのに、費用の 33% を占めます。
コストを下げたいなら、狙うべきは「ラウンド数」と「出力量」。
プロンプトに渡す情報を削るのは、費用面ではほとんど効きません。
むしろ情報が足りずにラウンドが増えると、逆効果になります。
ラウンドが 1 回増えるということは、それまでの差分とレビュー結果を丸ごと積んだ状態でもう一周するということなので、増え方は線形より悪くなります。
そしてラウンド数を決めているのは受入条件の質でした。証拠①の話が、そのまま費用の話でもあるわけです。「AI は高い」と言う前に、同じ AI に何周させているかを見たほうがいいと思います。
(正確を期すと、日次の課金額しか取れていないため PBI 単体のコストは切り出せていません。C・D は 1 ラウンド・合計 26 分で終わっているので、3〜4 ラウンド・2 時間超の A・B より大幅に安いのは確実ですが、「何分の 1」とまでは言えません。**ループ側でトークン数をログに残していなかったのは失敗でした。**最初から記録しておくべきです。)
人間に残した範囲
最終的な分担はこうなりました。
| AI | 人間 |
|---|---|
| 受入条件を満たす実装とテスト追加 | すべてのマージ |
| 差分レビューと指摘の反映 | 受入条件の事前設計 |
| PR 作成と CI の追跡 | 本番デプロイの実行 |
| 機能試験の自動化・実行・証跡取得 | DB の更新(試験データ) |
| 試験書・リリース文書の作成 | リスク評価、承認 |
| エージェントの主張の裏取り |
太字にした 3 つが、この検証で「人間側に残った」と結論した部分です。
とくに受入条件の設計。ここを曖昧にしたまま投げると、前述のとおりラウンドが溶けます。逆に言えば、AI に投げられる形まで要件を落とし込む作業が、これまでどおり価値の中心にあります。
まとめ
自動化を検証して、直すことになったものを並べ直します。
| 直したもの | AI の問題か |
|---|---|
| 受入条件に検証不能な項目が混ざっていた | ❌ 仕様の書き方 |
| 試験書の期待値が実データと違っていた | ❌ 仕様の誤記 |
| プロンプトが届いていなかった | ❌ 渡し方 |
| コマンド実行を許可していなかった | ❌ 設定 |
| チケットのコメントを渡していなかった | ❌ 渡し方 |
| 認証が途中で切れた | ❌ 運用 |
| 断定的な誤報が出た | ⭕️ AI 側の限界 |
7 つのうち 6 つは、こちら側の問題でした。
AI を導入すると「AI の精度」が議論になりがちですが、実際にやってみるとそれ以前のところで大量に詰まります。そして詰まりの正体は、たいてい「人間同士だから暗黙に処理できていたもの」です。曖昧な受入条件、疑われていない期待値、口伝で補われていた手順。
AI は、そこを容赦なく可視化してきます。自動化の最初の価値は、作業が速くなることではなく、仕様の穴が見えることかもしれません。
数字としては PBI 3 件で $60、ループの実働は 5 時間弱でした。ただし工数の大半はループの外側(受入条件の整理、試験、リリース文書)にあります。そこを含めて設計しないと「AI で自動化した」とは言えないと思います。
同じような仕組みを作る方の参考になれば幸いです。