はじめに
AIコーディングエージェントの進化、かなり速いですよね。
OpenAI Codex はクラウド上のサンドボックスでリポジトリを読み、コードを書き、テストやリンターを実行し、PR相当の変更まで提案できます。GitHub Copilot coding agent も Issue を起点に開発環境を立ち上げ、Draft PR にコミットを積んでいく流れになっています。Claude Code のようなターミナル型エージェントも、プロジェクト内のルールやテストコマンドを読みながら実装を進められます。
つまり、AIに「コードを書いてもらう」こと自体は、もう珍しくなくなってきました。
でも、ここで一つ問題があります。
AIが書いたコードが、本当に仕事として完了しているかを誰が判断するのか。
ここを曖昧にしたままAI開発を進めると、なんとなく動くコードは増えるけど、レビューが重くなります。仕様の認識ズレも増えます。テストは通っているのに、業務的には通っていない、みたいな状態も起きます。
なんかこれって、AIの性能不足というより、人間側が「合格条件」を設計していない問題な気がするんです。
この記事では、AIに実装を任せる前に、人間が 評価仕様 を書くための実践パターンとして、僕なりに Eval Gate / Acceptance Criteria as Code という形で整理してみます。
結論: AI時代の開発は「プロンプト」より「合格条件」が大事
まず結論からです。
AI開発で安定して成果を出すには、プロンプトを上手に書くより先に、次の3つを決めた方がいいです。
- 何をもって完了とするか
- 何が起きたら失敗とするか
- その判断を人間だけでなく、テスト・スクリプト・チェックリストで再現できるか
AIへの指示文はもちろん大事です。
でも、指示文だけだと「いい感じに作って」というお願いに近くなります。AIはその場でかなり賢く補完してくれるので、一見うまくいきます。ただ、補完された前提が人間の期待とズレていた場合、あとからレビューで全部掘り起こすことになります。
そこで、タスク開始前に Acceptance Criteria をコードやMarkdownとして置いておく。
たとえば、こんな感じです。
## Acceptance Criteria
- 未ログインユーザーが `/settings` にアクセスしたら `/login` にリダイレクトされる
- ログイン済みユーザーは自分のプロフィールだけ編集できる
- 他人のプロフィールIDを指定した場合は 403 を返す
- 保存成功時は toast を表示し、フォームの dirty state を解除する
- `npm run test` と `npm run lint` が通る
これだけでもだいぶ違います。
さらに一歩進めて、この条件をテストや小さな検証スクリプトに落とします。これがこの記事でいう Acceptance Criteria as Code です。
なぜ今「評価仕様」が重要なのか
AIエージェントは、昔の補完ツールとは違います。
補完ツールは、基本的に人間がキーボードを握っていました。AIは横で候補を出すだけです。でも、最近のエージェントは、ファイルを読み、複数ファイルを変更し、コマンドを実行し、失敗したら自分で直します。
OpenAI Codex の発表でも、タスクごとに独立したクラウド環境で実行し、テスト出力やターミナルログを証拠として提示する流れが説明されています。GitHub Copilot coding agent も GitHub Actions ベースの環境で作業し、Draft PR に変更を積みます。SWE-bench Verified のようなベンチマークも、実GitHub Issueに近い問題を解けるかどうかを見ています。
流れとしては明らかに、AIは「会話相手」から「作業者」に寄ってきています。
作業者が増えた時に必要になるのは、気合いではなく、受け入れ基準です。
人間の新人エンジニアに仕事を渡す時も同じですよね。
「ログイン周りいい感じに直しておいて」だと、受け取る側も困ります。
でも、
- どの画面で
- どのユーザー種別が
- どの操作をした時に
- 何が起きればOKで
- 何が起きたらNGか
まで書いてあると、実装もレビューも一気に楽になります。
AIでも同じです。
いや、AIだからこそ、もっと明示した方がいい。
AIは空気を読んでくれるように見えます。でも、その空気は確率的に補完された空気です。現場の事情、過去の意思決定、ユーザーとの約束、運用上の地雷までは、文脈として渡さないと見えません。
だから、僕はAI時代のエンジニアの仕事は、実装者から「評価と文脈の設計者」に寄っていくと思っています。
Eval Gate とは何か
この記事でいう Eval Gate は、AIが作業を完了したと言う前に通す、小さな関門のことです。
難しいものではありません。
構造はこれだけです。
# Eval Gate
## 1. Context
このタスクの背景、対象ユーザー、関連ファイル、守るべき制約を書く
## 2. Acceptance Criteria
完了条件を箇条書きで書く
## 3. Negative Criteria
やってはいけないこと、壊してはいけないことを書く
## 4. Verification Commands
AIと人間が実行すべき確認コマンドを書く
## 5. Human Review Points
最後に人間が見るべき判断ポイントを書く
ポイントは、テストで全部を自動化しようとしないことです。
もちろん自動テストは大事です。でも、UIの違和感、文言のニュアンス、業務上の説明責任、セキュリティ上の違和感などは、人間が見た方がいい場面もあります。
なので、Eval Gate は AIに任せる部分 と 人間が判断する部分 を分けるための境界線です。
たとえば、こんな使い分けです。
| 項目 | AIに任せやすい | 人間が見るべき |
|---|---|---|
| 型エラー | yes | no |
| 単体テスト | yes | no |
| 既存仕様との差分整理 | yes | yes |
| セキュリティ影響 | partly | yes |
| UXの気持ち悪さ | partly | yes |
| 事業上の優先順位 | no | yes |
AIに全部任せるのではなく、人間が全部抱えるのでもない。
判断の置き場所を設計する。
ここが大事かなと。
実装例1: Markdownで Eval Gate を作る
まずは一番簡単な形です。リポジトリに docs/eval-gates/ を作って、タスクごとにMarkdownを置きます。
# Eval Gate: settings-profile-update
## Context
ユーザー設定画面のプロフィール更新機能を改善する。
対象はログイン済みユーザー。未ログインユーザーは設定画面に入れない。
既存の認証ミドルウェアと API route は変更してよいが、公開プロフィールページの表示仕様は変えない。
## Acceptance Criteria
- 未ログイン状態で `/settings/profile` にアクセスすると `/login` にリダイレクトされる
- ログイン済みユーザーは `displayName` と `bio` を更新できる
- `displayName` は 1〜40 文字
- `bio` は 160 文字以内
- 他人の userId を指定した更新リクエストは 403 になる
- 更新成功時、画面に「保存しました」と表示される
- 更新失敗時、フォーム入力内容は消えない
## Negative Criteria
- public profile page の URL 仕様を変更しない
- DB schema migration は追加しない
- 認可チェックをフロントエンドだけに置かない
- 既存の `UserProfileCard` の props を破壊しない
## Verification Commands
```bash
npm run lint
npm run typecheck
npm run test -- profile
```
## Human Review Points
- エラーメッセージがユーザーに冷たすぎないか
- 保存成功後の導線が自然か
- 認可チェックが API 側にも存在するか
これをAIに渡して、こう依頼します。
あなたはこのリポジトリの開発エージェントです。
次の Eval Gate を満たすように実装してください。
実装前に、関連ファイルと既存仕様を調査し、変更計画を短く提示してください。
実装後は Verification Commands を実行し、結果を要約してください。
重要:
- Acceptance Criteria を満たさない変更は完了扱いにしないでください
- Negative Criteria に触れる場合は、実装前に理由と代替案を提示してください
- 最後に Human Review Points に沿って、人間が見るべき箇所を列挙してください
[ここに Eval Gate Markdown を貼る]
プロンプトのコツは、「実装して」だけで終わらせないことです。
調査 → 計画 → 実装 → 検証 → 人間レビュー観点の提示 までを1つの流れにします。
AIは作業者としては優秀です。でも、作業の終わり方を指定しないと、終わった雰囲気だけ作ることがあります。
そこを Eval Gate で止めるわけです。
実装例2: Acceptance Criteria を TypeScript で検証する
次は、完了条件の一部をコードにします。
たとえば API の認可やバリデーションは、自然言語だけでなくテストに落としやすいです。
// tests/profile-update.acceptance.test.ts
import { describe, expect, it } from "vitest";
import { updateProfile } from "../src/features/profile/updateProfile";
import { createTestUser, createGuestContext } from "./helpers";
describe("profile update acceptance criteria", () => {
it("rejects guest users", async () => {
const ctx = createGuestContext();
await expect(
updateProfile(ctx, {
userId: "user_001",
displayName: "Sample User",
bio: "hello",
})
).rejects.toMatchObject({ code: "UNAUTHORIZED" });
});
it("rejects updating another user's profile", async () => {
const alice = createTestUser({ id: "user_alice" });
await expect(
updateProfile(
{ currentUser: alice },
{
userId: "user_bob",
displayName: "Bob",
bio: "This should not be allowed",
}
)
).rejects.toMatchObject({ code: "FORBIDDEN" });
});
it("validates displayName and bio length", async () => {
const user = createTestUser({ id: "user_001" });
await expect(
updateProfile(
{ currentUser: user },
{
userId: "user_001",
displayName: "",
bio: "a".repeat(161),
}
)
).rejects.toMatchObject({ code: "VALIDATION_ERROR" });
});
});
このテストは、完璧なE2Eではありません。
でも、AIにとってはかなり強い足場になります。
「認可を忘れないで」と自然言語で言うより、落ちるテストを先に置く 方が、AIは修正しやすいです。Claude Code のベストプラクティスでも、環境設定、明確なコマンド、反復可能なテストが重要視されています。Codex の説明でも、テストやログを証拠として残す流れが出てきます。
つまり、AIに任せるなら、テストは単なる品質保証ではなく、AIへの道案内にもなります。
これ、地味ですがめっちゃ重要です。
実装例3: AIレビュー用のチェックJSONを作る
もう少し運用寄りにするなら、Eval Gate をJSONにして、AIレビューやCIで読みやすくしておくのもありです。
{
"taskId": "settings-profile-update",
"riskLevel": "medium",
"contextFiles": [
"src/app/settings/profile/page.tsx",
"src/features/profile/updateProfile.ts",
"src/server/auth.ts"
],
"acceptanceCriteria": [
"Guest users are redirected to /login",
"Authenticated users can update displayName and bio",
"Users cannot update another user's profile",
"Validation errors preserve form input",
"lint, typecheck, and profile tests pass"
],
"negativeCriteria": [
"Do not remove server-side authorization",
"Do not change public profile URLs",
"Do not add database migrations"
],
"verificationCommands": [
"npm run lint",
"npm run typecheck",
"npm run test -- profile"
],
"humanReviewPoints": [
"Check wording of validation messages",
"Check UX after successful save",
"Check whether authorization exists on the API side"
]
}
これを使って、AIレビューに次のように投げます。
あなたはPull Requestレビュアーです。
以下の Eval Gate JSON と、変更差分を比較してください。
レビュー観点:
1. acceptanceCriteria が満たされているか
2. negativeCriteria に違反していないか
3. verificationCommands の結果から見て、追加確認が必要か
4. humanReviewPoints として、人間が見るべき箇所はどこか
出力形式:
- PASS / WARN / FAIL
- 根拠
- 修正すべきファイル
- 人間レビューへの引き継ぎ
この形にすると、AIを「実装担当」だけでなく「一次レビュアー」としても使いやすくなります。
ただし、注意点があります。
AIレビューの結果をそのまま正解にしないことです。
AIはレビューもできます。でも、事業判断やユーザー影響の最終責任までは持てません。なので、AIレビューは 人間レビューを軽くするための下ごしらえ と考える方が健全です。
Prompt例: タスク開始前・実装後・レビュー時
ここまでを実務に落とすために、すぐ使えるプロンプトを3つ置いておきます。
プロンプト1: タスク開始前の Eval Gate 作成
あなたはシニアエンジニア兼テックリードです。
次の開発タスクについて、AIコーディングエージェントに渡すための Eval Gate を作成してください。
タスク概要:
[ここにタスクを書く]
出力してほしいもの:
1. Context
2. Acceptance Criteria
3. Negative Criteria
4. Verification Commands
5. Human Review Points
条件:
- 初心者エンジニアでも迷わない粒度にしてください
- 自動テストで確認できるものと、人間が判断すべきものを分けてください
- セキュリティ、既存仕様破壊、データ破壊の観点を必ず含めてください
プロンプト2: 実装エージェントへの依頼
あなたはこのリポジトリの実装担当AIエージェントです。
以下の Eval Gate を満たすように実装してください。
進め方:
1. 関連ファイルを調査する
2. 変更計画を短く書く
3. 実装する
4. Verification Commands を実行する
5. Acceptance Criteria の充足状況を表で報告する
6. Human Review Points を人間に引き継ぐ
禁止:
- Negative Criteria に違反しない
- テストを通すためだけに仕様を弱めない
- 既存テストを理由なく削除しない
[Eval Gate を貼る]
プロンプト3: PRレビューAIへの依頼
あなたはPull Requestの一次レビュアーです。
以下の Eval Gate と差分を比較し、レビューしてください。
確認項目:
- Acceptance Criteria は満たされているか
- Negative Criteria に違反していないか
- テストや型チェックは十分か
- 実装が過剰に複雑になっていないか
- 人間が最終判断すべき点は何か
出力形式:
## 判定
PASS / WARN / FAIL
## 根拠
箇条書き
## 修正提案
ファイル名と内容
## 人間レビューへの引き継ぎ
最終確認すべき観点
この3つだけでも、AI開発の事故はかなり減ると思います。
開発フローに組み込むならこうする
実務で使うなら、僕はこういう流れにします。
Issue作成
↓
Eval Gateを書く
↓
AIに調査と実装計画を出させる
↓
人間が計画だけ確認する
↓
AIが実装する
↓
Verification Commandsを実行する
↓
AIが自己レビューする
↓
人間がHuman Review Pointsだけ重点確認する
↓
マージ判断
大事なのは、人間が全部レビューしないことです。
全部を見ると、AIを使っているのに疲れます。
でも、何も見ないと危ない。
なので、見る場所を先に決める。
これが Eval Gate の役割です。
人間が設計するもの:
- ゴール
- 制約
- 完了条件
- 失敗条件
- 最終判断ポイント
AIに任せるもの:
- 関連ファイル調査
- 実装案の比較
- コード変更
- テスト追加
- コマンド実行
- 一次レビュー
人間が判断するもの:
- ユーザー体験として自然か
- 事業上の優先順位に合っているか
- セキュリティや運用リスクを許容できるか
- 今マージすべきか、後回しにすべきか
こう分けると、AIはかなり頼れる作業者になります。
逆に、この分担がないと、AIは「速いけど危なっかしい人」になります。
よくある失敗パターン
失敗1: 「テスト通ったのでOK」にする
テストは大事です。
でも、テストが仕様を代表していないなら、通っても意味が薄いです。
たとえば「保存できる」テストはあるけど、「他人のデータを保存できない」テストがない。これは危ないですよね。
テストは合格条件の一部であって、全部ではありません。
失敗2: Acceptance Criteria が抽象的すぎる
「使いやすいUIにする」だけだと、AIも人間も困ります。
こう分解した方がいいです。
- エラー時に入力内容が消えない
- 保存中はボタンが disabled になる
- 保存成功後に toast を表示する
- 画面遷移せず、同じページで編集を続けられる
抽象的な価値を、観測できる条件に落とす。
ここが人間の仕事です。
失敗3: Negative Criteria がない
AIは良かれと思って広めに直します。
これは便利な時もありますが、既存仕様を壊す原因にもなります。
なので、やってほしくないことを書きます。
- 認証方式は変更しない
- DB schema は変更しない
- 既存URLは変えない
- 課金状態の判定ロジックには触らない
AIは「何をするか」だけでなく、「何をしないか」を渡すと安定します。
これ、プロンプトエンジニアリングというより、ほぼ業務設計です。
まとめ
AIコーディングエージェントが強くなるほど、エンジニアの仕事は減るというより、少し場所が変わるんだと思います。
コードを書く量は減るかもしれません。
でも、何を作るべきか。何を守るべきか。どこまでできたら完了なのか。どこから先は人間が判断するのか。
ここは、むしろ重要になります。
AIに実装を任せる前に、評価仕様を書く。
自然言語で Acceptance Criteria を置く。
落とせるものはテストやJSONにする。
最後に Human Review Points を残す。
これだけで、AI開発はかなり「仕事として扱いやすい形」になると思います。
AIにコードを書かせる技術より、AIが書いたコードを 安心して受け取れる状態にする技術。
これからの現場では、こっちの価値がどんどん上がる気がしています。
まずは次のタスクで、1枚だけ Eval Gate を書いてみる。
それだけで、AIへの指示も、レビューも、ちょっと楽になるはずです。