AIエージェントを「確認待ち地獄」に落とさない技術 — Approval Contract / Decision Queue 実践ガイド
AIコーディングエージェントを使っていると、だんだんこういう場面が増えてきます。
「この変更、進めていいですか?」
「このファイルを削除していいですか?」
「本番に近い設定を触ってもいいですか?」
「APIキーを入力してください」
最初は丁寧でいいんです。
でも、毎回これを人間が読んで、毎回その場で判断して、毎回「OKです」と返していると、AIに任せているはずなのに、なぜか人間の方が忙しくなるんですよね。
これ、めっちゃもったいなくないですか。
この記事で言いたいことはシンプルです。
AIエージェントに仕事を任せるなら、プロンプトだけではなく「承認の設計」も先に書いた方がいい。
僕はこれをこの記事では Approval Contract と呼びます。
Approval Contract は、日本語で言えば「承認契約」です。難しく聞こえるかもしれませんが、要するにこういうことです。
- どんな操作は AI が勝手に進めていいのか
- どんな操作は人間に確認するべきなのか
- 確認するとき、何をセットで出すべきなのか
- 人間が反応しない場合、AI はどう止まるべきなのか
- 承認した証拠をどこに残すのか
これを事前に決めておく。
AI時代の開発では、コードを書く力よりも、こういう 判断の流れを設計する力 がかなり大事になってくる気がしています。
いま起きている変化: AIは「補完」から「作業者」に寄っている
少しだけ最新状況を整理します。
GitHub Copilot の coding agent は、Issue を割り当てると GitHub Actions 上の環境で作業し、draft Pull Request を作り、session logs で進捗を追える方向に進んでいます。GitHub の説明でも、branch protection はそのまま効き、CI/CD ワークフローを動かす前に人間承認が入る保護コントロールが説明されています。
OpenAI Codex も、単なるコード補完ではなく、Pull Request、リファクタ、migration、ドキュメント作成、複数エージェントでの並列作業、background work のような方向に進んでいます。
さらに MCP、Model Context Protocol ですね。これはざっくり言うと、AI が外部ツールやデータソースへアクセスするための共通規格です。GitHub Copilot の agent mode でも MCP を使うことで、リポジトリ、Issue、PR、外部APIなどにアクセスしやすくなります。
つまり何が起きているか。
AIは、エディタの横にいる補助輪から、開発フローの中で実際に手を動かす作業者になりつつある。
ここまではワクワクする話です。
ただし、作業者が増えると、必ず必要になるものがあります。
それが 権限と承認の設計 です。
人間のチームでも同じですよね。
新しく入ったメンバーに、いきなり本番DBの削除権限を渡すことはありません。まず小さなタスクを任せる。レビューする。権限を分ける。危ない作業はペアで見る。ログを残す。
AIエージェントも同じです。
「AIだから全部任せる」でもなく、「AIだから全部疑う」でもない。
任せていい範囲を設計する。迷ったら確認する道を作る。危ないものは止める。
この当たり前を、Markdown と TypeScript と CI に落とし込むのが今回の記事です。
Approval Contract とは何か
Approval Contract は、AIエージェントと人間の間に置く「判断ルール」です。
プロンプトが「何をしてほしいか」を伝えるものだとしたら、Approval Contract は「どこまで勝手にやってよくて、どこから人間に聞くべきか」を伝えるものです。
たとえば、こんな違いがあります。
悪い例: ふわっと確認する
必要なら確認してください。
危ないことはしないでください。
いい感じに進めてください。
これは、人間同士なら空気で伝わることもあります。
でもAIにとっては曖昧です。
「必要なら」とは、どの条件でしょうか。
「危ないこと」とは、何でしょうか。
「いい感じ」とは、誰にとっていい感じでしょうか。
ここが曖昧だと、AIは2つの方向にブレます。
1つは、慎重になりすぎて何度も確認する方向。
もう1つは、勝手に進めすぎて事故る方向。
どちらも困ります。
良い例: 判断単位を分ける
approval_policy:
auto_approve:
- docs_only_change
- add_unit_tests_without_production_code_change
- refactor_private_function_with_tests
require_human_approval:
- delete_file
- change_database_schema
- modify_auth_or_payment_logic
- add_external_network_access
- change_ci_cd_or_deployment_config
always_deny:
- request_api_key_or_password_in_chat
- expose_secret_values
- run_production_destructive_command
これなら、かなり判断しやすくなります。
AIは「この操作は docs_only_change だから進める」「これは database schema 変更だから承認を求める」「これは APIキー要求だから拒否」と判断できます。
人間もレビューしやすいです。
ポイントは、AIに人格的な賢さを期待しすぎないことです。
AIが迷わないように、判断の棚を先に作っておく。
これが Approval Contract の基本です。
Decision Queue: 確認はチャットに流さず、キューに積む
もう1つ大事なのが Decision Queue です。
AIが人間に確認したいことを、チャットの流れにそのまま投げると、すぐ埋もれます。
「これ確認してください」
「こっちも確認してください」
「追加で質問です」
こうなると、人間は何を判断すればいいのか分からなくなります。
だから、確認事項はキューとして扱います。
Decision Queue は、AIが止まっている理由を1件ずつ構造化するための箱です。
id: DQ-2026-001
status: waiting_human
risk: medium
requested_by: coding-agent
summary: ユーザー設定テーブルに timezone カラムを追加してよいか
decision_needed:
question: DBスキーマ変更を実施してよいですか?
options:
- approve
- reject
- request_smaller_plan
context:
related_issue: ISSUE-123
files:
- prisma/schema.prisma
- src/users/userSettings.ts
impact:
user_visible_change: タイムゾーン別の通知時刻を保存できる
rollback_plan: migration revert を追加し、リリース前なら安全に戻せる
expires_at: 2026-05-10T18:00:00+09:00
fallback_if_expired: request_smaller_plan
こうしておくと、人間は「何を判断するのか」だけに集中できます。
コード全体を読み込む前に、まず判断の単位が見える。
これが大事なんです。
AI時代のレビューでしんどいのは、AIが書いたコードそのものよりも、そもそも何を判断すればいいのか分からない状態 なんですよね。
Decision Queue は、その迷子感を減らしてくれます。
MCP Elicitation から学べること
MCP の draft specification には Elicitation という考え方があります。
Elicitation は、MCPサーバーがクライアント経由でユーザーに追加情報を求めるための仕組みです。要するに、AIやツールが作業中に「この情報が必要です」と人間に聞くための標準化された流れです。
ここで面白いのは、単に「質問できる」という話ではないことです。
仕様上、クライアントは要求元を明示し、ユーザーが拒否やキャンセルを選べるようにし、送信前に内容を確認・変更できるようにする必要があります。
さらに重要なのが、秘密情報の扱いです。
MCP Elicitation では、form mode でパスワード、APIキー、アクセストークン、決済情報のような秘密情報を要求してはいけないとされています。そういう機密性の高い操作は URL mode のように、適切な外部画面と同意を通すべき、という考え方です。
ここから開発現場が学べることは大きいです。
AIに確認させるなら、確認のUIと境界も設計しないといけない。
ただチャットで「APIキーを教えてください」と言わせてはいけないんです。
これはAIの賢さの問題ではなく、システム設計の問題です。
人間が設計し、AIに任せ、CIで守る範囲
ここで責任分界をはっきりさせます。
人間が設計するもの
- 何を成功とするか
- 何を禁止するか
- どの変更を自動承認するか
- どの変更を人間承認にするか
- どのログを残すか
- どの失敗なら中断するか
AIに任せるもの
- 影響範囲の洗い出し
- 変更案の作成
- テスト追加
- ドキュメント更新
- PR本文の下書き
- Decision Queue の作成
CIやツールで守るもの
- 禁止ファイル変更の検知
- secret scanning
- migration の検知
- CODEOWNERS によるレビュー要求
- dangerous command のブロック
- approval.yml との整合性チェック
人間が全部見る必要はありません。
でも、人間が何も設計しないまま、AIに全部任せるのも危ない。
人間は判断基準を設計する。AIは作業と整理を担当する。CIは約束違反を止める。
この三角形で考えると、AI開発はかなり安定します。
まず作るべき approval.yml
最初から大げさな仕組みを作らなくて大丈夫です。
まずはリポジトリのルートに approval.yml を1枚置くだけでも、かなり変わります。
version: 1
project: sample-web-app
principle: deny_by_default_for_high_risk_changes
risk_levels:
low:
description: ユーザー影響が小さく、戻しやすい変更
examples:
- README更新
- テスト追加
- 内部関数の小さなリファクタ
medium:
description: ユーザー影響はあるが、レビューとテストで制御できる変更
examples:
- UI文言変更
- 新しいAPIレスポンス項目の追加
- 非破壊的なDBカラム追加
high:
description: 認証、課金、個人情報、本番運用、破壊的変更に関わる変更
examples:
- 認証ロジック変更
- 決済処理変更
- DBカラム削除
- CI/CD設定変更
- 外部ネットワークアクセス追加
auto_approve:
- risk: low
required_checks:
- unit_tests_pass
- no_secret_detected
- no_production_config_change
require_human_approval:
- risk: medium
required_packet:
- summary
- affected_files
- test_plan
- rollback_plan
- risk: high
required_packet:
- summary
- affected_files
- risk_reason
- alternatives
- test_plan
- rollback_plan
- owner_review
always_deny:
- expose_secret_values
- ask_user_for_api_key_in_chat
- run_destructive_command_against_production
- bypass_branch_protection
これ、ただのYAMLです。
でも、あるだけでAIへの指示が変わります。
「このファイルを読んで、変更前に risk を分類して。medium 以上なら Decision Queue を作って」と言えるからです。
TypeScriptで Approval Contract を検証する
YAMLを書くだけでも効果はありますが、できれば機械的に検証したいです。
たとえば TypeScript と Zod で、Decision Queue の形式をチェックできます。
import { z } from "zod";
const RiskSchema = z.enum(["low", "medium", "high"]);
const DecisionStatusSchema = z.enum([
"draft",
"waiting_human",
"approved",
"rejected",
"expired",
]);
const DecisionQueueItemSchema = z.object({
id: z.string().regex(/^DQ-\d{4}-\d{3}$/),
status: DecisionStatusSchema,
risk: RiskSchema,
summary: z.string().min(10),
decisionNeeded: z.object({
question: z.string().min(10),
options: z.array(z.enum(["approve", "reject", "request_smaller_plan"])).min(2),
}),
context: z.object({
relatedIssue: z.string().optional(),
files: z.array(z.string()).min(1),
}),
impact: z.object({
userVisibleChange: z.string(),
rollbackPlan: z.string().min(10),
}),
expiresAt: z.string().datetime(),
fallbackIfExpired: z.enum(["reject", "request_smaller_plan"]),
});
export type DecisionQueueItem = z.infer<typeof DecisionQueueItemSchema>;
export function validateDecisionQueueItem(input: unknown): DecisionQueueItem {
return DecisionQueueItemSchema.parse(input);
}
これで、AIが作った確認依頼が薄すぎる場合に落とせます。
たとえば rollbackPlan が空ならエラーにする。
affected files がなければエラーにする。
選択肢が approve しかなければエラーにする。
人間に聞く前に、AIの質問品質をチェックするわけです。
これ、地味ですがかなり効きます。
悪い確認依頼って、人間の時間を奪うんですよね。
「どうしますか?」だけ聞かれても困る。
「A案、B案、C案があります。影響はこうです。戻し方はこうです。おすすめはAです。承認しますか?」まで出してほしい。
そこまでを型で強制する。
GitHub Actionsで危険変更を止める
次に CI です。
たとえば、AIエージェントが作ったPRで prisma/schema.prisma や .github/workflows が変わっていたら、人間承認ラベルを必須にする、というチェックができます。
name: approval-contract-check
on:
pull_request:
types: [opened, synchronize, labeled, unlabeled]
jobs:
check-approval-contract:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Detect changed files
id: changed
run: |
git diff --name-only origin/${{ github.base_ref }}...HEAD > changed.txt
cat changed.txt
- name: Require human approval for high risk files
run: |
HIGH_RISK=false
if grep -E '^(prisma/schema\.prisma|\.github/workflows/|src/auth/|src/payment/)' changed.txt; then
HIGH_RISK=true
fi
if [ "$HIGH_RISK" = "true" ]; then
echo "High risk files changed. Checking labels..."
echo '${{ toJson(github.event.pull_request.labels) }}' > labels.json
if ! grep -q 'human-approved' labels.json; then
echo "This PR changes high risk files and requires the human-approved label."
exit 1
fi
fi
この例はシンプルですが、考え方としては十分です。
AIが高リスクファイルを触ったら、CIが止める。
人間が確認して human-approved ラベルを付けたら進む。
この流れを作ると、AIに安心して低リスク作業を任せやすくなります。
「全部怖いから任せない」ではなく、怖いところだけ止める。
これが現実的です。
プロンプト例1: 作業開始時に Approval Contract を読ませる
あなたはこのリポジトリのAIコーディングエージェントです。
作業前に必ず approval.yml を読み、今回のタスクを low / medium / high に分類してください。
出力してください:
1. タスク概要
2. 想定変更ファイル
3. risk分類
4. 自動実行してよい作業
5. 人間承認が必要な作業
6. もし承認が必要なら Decision Queue item の下書き
禁止事項:
- APIキー、パスワード、アクセストークンをチャットで要求しない
- 本番環境への破壊的操作を実行しない
- approval.yml の always_deny に該当する操作を提案しない
最初にこれを渡すだけで、AIの動き方が変わります。
「とりあえず実装します」ではなく、「まず分類します」になる。
AI開発では、この最初の姿勢がけっこう大事です。
プロンプト例2: 確認依頼を Decision Queue 形式にする
人間の判断が必要な場合、自由文で質問しないでください。
必ず次の形式で Decision Queue item を作ってください。
- id
- risk
- summary
- decision_needed.question
- decision_needed.options
- context.related_issue
- context.files
- impact.user_visible_change
- impact.rollback_plan
- expires_at
- fallback_if_expired
質問は1つの Decision Queue item につき1判断に限定してください。
複数の判断を1つに混ぜないでください。
これも効きます。
AIは、放っておくと複数の質問を1つの文章に混ぜがちです。
「DB変更していいですか?ついでにUIも変えていいですか?あとAPI仕様も変えますか?」みたいな感じです。
人間からすると、答えづらい。
だから 1キュー1判断 にします。
これだけで承認が速くなります。
プロンプト例3: 承認後にやることを固定する
Decision Queue item が approved になった場合のみ、対象作業を進めてください。
承認後に必ず実施してください:
1. 承認IDをコミットメッセージまたはPR本文に記録する
2. 変更ファイル一覧をPR本文に記録する
3. 実行したテストコマンドと結果を記録する
4. rollback_plan が実行可能か再確認する
5. 承認範囲を超える変更が必要になった場合は、作業を止めて新しい Decision Queue item を作る
承認範囲を勝手に拡張しないでください。
承認でよくある事故は、「承認した範囲を超えて、ついでに色々やる」ことです。
人間でもありますよね。
「この文言だけ直して」と言ったのに、ついでにレイアウトまで変わっている。
AIはもっとやりがちです。
だから、承認にはスコープがあります。
承認は白紙委任ではない。指定された範囲の通行証。
この感覚をプロンプトに入れておくと安全です。
プロンプト例4: レビュー時に承認違反を探す
このPRを Approval Contract の観点でレビューしてください。
確認してください:
1. approval.yml に照らして risk 分類は妥当か
2. high risk 変更に human-approved ラベルまたは承認記録があるか
3. Decision Queue item の承認範囲を超えた変更がないか
4. secret、token、password、private key らしき文字列が含まれていないか
5. rollback_plan がPR本文に書かれているか
6. テスト結果が証跡として残っているか
出力形式:
- OK / NG
- NGの場合の理由
- 修正すべきファイル
- 人間が追加判断すべき項目
AIにレビューさせる時も、ただ「レビューして」では弱いです。
何を見てほしいのかを決める。
ここでも人間の仕事は、レビュー観点の設計です。
小さな導入手順
ここまで読むと、ちょっと重く感じるかもしれません。
でも最初の一歩はかなり小さくできます。
Step 1: high risk だけ書く
まずは approval.yml に high risk だけ書きます。
high_risk_files:
- prisma/schema.prisma
- .github/workflows/**
- src/auth/**
- src/payment/**
- infra/**
always_deny:
- ask_for_secret_in_chat
- run_production_delete
- bypass_branch_protection
これだけでも十分です。
Step 2: AIに最初に読ませる
次に、AIへの作業依頼の冒頭にこう書きます。
作業前に approval.yml を読んでください。
high risk に該当する場合は、実装前に Decision Queue item を作って止まってください。
Step 3: PRテンプレに承認欄を足す
## Approval
- Risk level: low / medium / high
- Decision Queue ID:
- Human approval required: yes / no
- Human-approved label required: yes / no
## Evidence
- Test command:
- Test result:
- Rollback plan:
この3つだけで、AI開発の安全性はかなり上がります。
完璧な仕組みを作る必要はありません。
まずは、未来の自分がレビューするときに「これ残してくれて助かった」と思える情報を残す。
それでいいと思います。
よくある失敗
失敗1: 何でも人間承認にする
安全に見えます。
でも、全部人間承認にすると、AIを使う意味が薄れます。
READMEの修正、テスト追加、内部関数の小さな整理まで全部止めていたら、人間がボトルネックになります。
大事なのは、全部止めることではありません。
止めるべきところだけ止めること。
失敗2: 承認した証跡を残さない
チャットで「OK」と言っただけだと、後から追えません。
なぜその判断をしたのか。
どの範囲まで許可したのか。
誰が見たのか。
ここが残っていないと、レビュー不能になります。
最低でも PR本文、Issueコメント、ラベル、Decision Queue ファイルのどこかに残したいです。
失敗3: 秘密情報を確認フローに乗せる
これは本当に避けたいです。
APIキー、パスワード、アクセストークン、秘密鍵、決済情報。
こういうものをAIチャットやフォームで直接受け渡しする設計は危ないです。
必要なら、適切な管理画面、シークレットマネージャー、認可されたURL遷移、権限分離を使うべきです。
AIに「キーを貼ってください」と言わせない。
これはチームルールにしていいと思います。
AI時代のエンジニアの役割は、判断の設計に寄っていく
ここまで書いてきたことは、単なる安全対策ではありません。
エンジニアの役割変化の話でもあります。
昔は、実装できる人が強かった。
もちろん今も実装力は大事です。
でもAIがかなりの実装を手伝ってくれるようになると、人間の価値は少しずつ別の場所に移っていきます。
- 何を作るべきか
- 何を作らないべきか
- どこまで任せるか
- どこで止めるか
- 何を証拠として残すか
- どのリスクを許容するか
つまり、判断の設計 です。
AIにコードを書かせる技術だけだと、どこかで詰まります。
AIに安心して任せるための、コンテキスト、制約、承認、証跡、評価。
ここまで含めて設計できる人が、これからの開発現場ではかなり強いと思います。
まとめ
AIエージェントが強くなるほど、「確認してください」は増えます。
でも、その確認を毎回チャットで受けていると、人間が疲れます。
だから、先に設計しておく。
- Approval Contract で、承認ルールを明文化する
- Decision Queue で、確認事項を1判断ずつ構造化する
- MCP Elicitation の考え方から、ユーザー同意・拒否・秘密情報の扱いを学ぶ
- TypeScript や CI で、確認依頼と危険変更を機械的に検証する
- 人間は判断基準を設計し、AIは作業と整理を担当し、CIが約束違反を止める
AIを使う開発は、雑にやると怖いです。
でも、ちゃんと設計すると、めちゃくちゃ頼もしい相棒になります。
最初から完璧な承認基盤を作る必要はありません。
まずは approval.yml を1枚置く。
PRテンプレに承認欄を足す。
AIに「作業前にリスク分類して」と頼む。
それだけで、明日のレビューはかなり楽になるはずです。
未来の自分が、たぶんこう言います。
「この承認ルール、先に作っておいてくれて助かったな」と。
そういう小さな設計を、今日のうちに1つだけ置いておく。
AI時代の開発って、案外そこから始まるのかなと思っています。