AIへのルール、プロンプトに書くのをやめた話
AIに同じことを何度も言い直してしまう人へ
「必ずJSON形式で返して」
プロンプトにそう書いた。最初はちゃんと守られた。でも少し複雑な指示を重ねたら、いつの間にか普通の文章で返ってきた。
また書き直す。また守られる。また崩れる。
こういう経験、ありませんか?
AIに渡すルールが、なぜか定着しない。毎回同じことを書き足している。「前も言ったのに」と思いながら、また同じ一文を追加する。
これ、プロンプトの書き方が悪いんじゃないんです。
ルールを置く場所が間違っている、というのが本当の原因でした。
この記事は以下のXを深掘りした内容になります。
AIへの制約をプロンプトに書き続けてたら、トークンが増えるだけでモデルがちゃんと守らないことも多かった。linterやCIに移した途端に壊れにくくなった。制約を環境に埋め込むほど逆に自律性が上がるって、MartinFowlerが書いてた話がようやく実感できてきた感じ。
— あすすけ (@asusuke1104) March 31, 2026
なぜ「プロンプトに書いたルール」は守られなくなるのか
プロンプトに書いた指示は、AIにとって「その会話の中でのお願い」です。
会話が短ければ問題ない。でも指示が増えて、文章が長くなるにつれて、AIは全体をまんべんなく読めなくなっていきます。
研究(18種類のAIモデルを対象にした調査)によると、AIは長い文章の最初と最後には強く注意を向けるけれど、中間部分は見落としやすいという特性があります。U字型、と表現されることもある。
つまり、ルールをプロンプトの中ほどに書けば書くほど、読み飛ばされるリスクが上がる。
さらに別の問題もあります。
プロンプトに書いたルールは、会話が終わると消えます。次に新しい会話を始めたとき、また同じルールを書かないといけない。書き忘れれば、守られない。
これが「毎回書き直している」の正体です。AIが賢くなっていないのではなく、ルールを「会話の中に置く」という設計そのものに限界があった。
ルールを「プロンプトの外」に置くとどうなるか
少し視点を変えてみます。
職場のルールを例に考えると、「毎朝、口頭で今日の注意事項を伝える」方法と、「マニュアルや規則集として紙に残す」方法があります。
口頭だけだと、言い忘れた日はルールが存在しないも同然。でもマニュアルがあれば、誰も言わなくても参照できる。
AIへのルールも同じです。
プロンプトに毎回書くのは、口頭で伝えるのと同じです。言い忘れたら、ルールは消える。
一方、ルールを別の場所に置いておくと、AIが自動的に読んでくれる仕組みがあります。たとえば、AGENTS.md や CLAUDE.md というファイルをフォルダのトップに置いておくだけで、Claude や Cursor(※AIを使ったコーディングツール)は会話を始める前に自動でそのファイルを読みます。
プロンプトに書かなくても、ルールが毎回有効になる。
「そんな方法があったのか」と思うかもしれないけれど、これがハーネス設計(※AIが動く「環境」をデザインすること)の入口です。
もっと「壁」にする方法もある
AGENTS.md に書く方法は、AIが「読んで守ろうとする」ものです。ただ、それでもたまに無視されることがあります。
より確実にしたいなら、守られなかったら物理的に止まる仕組みを作ります。
たとえば、「Pythonのコードは必ずこの書き方で」というルールを linter(※コードの書き方を自動チェックするツール)に設定しておくと、AIが書いたコードがルールを破っていたとき、自動でエラーを出して止めてくれます。人間が確認するより前に、機械が検出する。
「守ってもらう」から「守られなければ通過できない」に変わる。
ここが、プロンプトにルールを書き続けることとの、一番大きな違いです。
まず何から始めるか
「linterとかCIとか、なんか難しそう」と感じた人は、まずここだけ試してみてください。
今日できる最小ステップ
プロンプトに3回以上書き直したルールを1つ思い出す
フォルダのトップに AGENTS.md という名前のファイルを作る
そこにそのルールだけ書いて保存する
それだけでいい。ツールのインストールも設定も不要。テキストファイルを1つ作るだけで、「会話が終わると消えるルール」から「ファイルに残り続けるルール」に変わります。
有料パートでは、このステップの続きを扱っています。
どのルールをどの場所に置くかの判断フローと、AGENTS.md / linter / pre-commit hook それぞれのコピーして使える設定例を出しています。
「設定の仕方がわからない」「どこから始めればいいかわからない」という状態から、手を動かして試せるところまで持って行く内容にしました。
プロンプトに同じことを書き続けることに疲れてきたなら、続きを読んでみてください。
判断フロー:このルールはどこに置くべきか
ルールを1つ選んで、上から順に答えていく。
Q1. このセッション限りの指示か?(「今回はこの条件で」「この文体で」など)
YES → プロンプトに残す。環境に移す必要はない
NO → Q2へ
Q2. コードや出力の「形式・書き方」に関するルールか?
(命名規則、フォーマット、import順序、出力形式など)
YES → linter / formatter へ(→ 設定例A)
NO → Q3へ
Q3. コミットやマージのタイミングで自動検証したいルールか?
(「.envをコミットしない」「テストが通らなければマージしない」など)
YES → pre-commit hook / CI へ(→ 設定例B)
NO → Q4へ
Q4. セッションをまたいで常に有効にしたいルールか?
(「このフォルダは触らない」「関数を追加するときは必ずコメントを書く」など)
YES → AGENTS.md / CLAUDE.md へ(→ 設定例C)
NO → プロンプトに残す。または上の分岐を見直す
迷ったときのシンプルな基準:同じルールをプロンプトに3回以上書いたことがあれば、環境に移す候補
設定例A:linter / formatter(形式・書き方のルール)
Python の場合(Ruff)
# インストール
pip install ruff
# 一度走らせてみる(エラーが出た箇所がルール化の候補)
ruff check .
設定ファイル(pyproject.toml に追記):
[tool.ruff]
line-length = 88
select = [
"E", # スタイルエラー
"F", # 未使用変数・インポート
"I", # import順序
]
[tool.ruff.isort]
force-sort-within-sections = true
AIが生成したコードがこの設定に違反していると、ruff check . を走らせた瞬間にエラーになる。CIに組み込めば、pull requestを出した時点で自動検出される。
JavaScript / TypeScript の場合(ESLint)
npm install --save-dev eslint @eslint/js
npx eslint --init
.eslintrc.json の設定例:
{
"rules": {
"camelcase": "error",
"no-unused-vars": "error",
"no-console": "warn"
}
}
linterを入れて最初にやること:
既存のコードに対して一度走らせ、エラーが大量に出るようなら "error" を "warn" に下げて段階的に厳しくする。最初から厳しくしすぎるとAIが修正ループを繰り返してかえって遅くなる。
設定例B:pre-commit hook(コミット前の自動検証)
pre-commit hookは「git commit を実行した瞬間に自動で走るチェック」。AIが生成したファイルをコミットしようとしたとき、基準を満たさなければコミット自体が止まる。
pip install pre-commit
pre-commit install # リポジトリで一度だけ実行。以降は自動で走る
設定ファイル(.pre-commit-config.yaml):
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: detect-private-key # .envや秘密鍵の混入を防ぐ
- id: check-added-large-files # 大きなファイルの混入を防ぐ
- id: no-commit-to-branch # mainへの直接コミットを防ぐ
args: ['--branch', 'main']
- id: end-of-file-fixer # ファイル末尾の改行を統一する
- id: trailing-whitespace # 行末の余分なスペースを除去
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.3.0
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
これを設定すると、git commit のたびに上記のチェックが自動で走る。AIが .env を誤ってステージングしても、コミット前に止まる。
設定例C:AGENTS.md / CLAUDE.md(セッションをまたぐルール)
Claude Code や Cursor はプロジェクトのルートに AGENTS.md(または CLAUDE.md)があれば、会話開始時に自動で読み込む。プロンプトに書かなくてもルールが毎回有効になる。
書き方のポイント:
「なるべく〜」「できれば〜」は無視されやすい。「〜は禁止」「〜は必ず〜する」と断定形で書く
条件がある場合は「〜の場合のみ〜してよい」と明示する
# AGENTS.md
## 変更禁止エリア
- `/legacy/` 以下のファイルは読み取りのみ。変更・削除・移動は禁止
- `config/production.yaml` は変更しない
## コーディングルール
- 新しい関数を追加する場合、必ず最初の行にその関数の目的をコメントで書く
例: # 注文IDからユーザー情報を取得して返す
- テストファイルは必ず tests/ 以下に置く。ルートに置かない
- 変数名は snake_case に統一する(JavaScript の場合は camelCase)
## コミット前の確認
- ruff check . を通してからコミットする
- 型エラーが出る場合は修正してからコミットする
linterとの使い分け:
linterで検証できるルール(形式・書き方)はlinterに任せて、AGENTS.mdには「AIの判断・行動範囲」に関するルールを書くと整理しやすい。両方に同じルールを書く必要はない。
よくある失敗パターンと対処
パターン1:linterを入れたのにCIで引っかからなかった
原因:ローカルとCIのlinterバージョンが違い、ルールの適用が異なっていた。
対処:pyproject.toml でバージョンを固定する。
[tool.ruff]
required-version = ">=0.3.0"
パターン2:AGENTS.mdのルールをAIが無視した
原因:「なるべく〜」「できれば〜」という曖昧な表現を使っていた。
対処:断定形に書き直す。「〜は禁止」「〜の場合は必ず〜する」。条件があるなら「〜の場合のみ〜してよい、それ以外は〜しない」まで書く。
パターン3:pre-commit hookが重くてcommitが遅くなった
原因:全ファイルに対してlinterを走らせていた。
対処:重いチェックはCIに移して、pre-commitは軽いものだけにする。
向かないケース・tradeoff
このアプローチが向かない場面:
タスクごとに内容が変わる指示(「今回はこの条件で」)→ プロンプトに残す
AIの創造的な判断に委ねたい部分(「最適な設計を選んで」)→ 制約にすると却って質が落ちる
1人・小規模プロジェクトで整備コストが見合わない初期段階 → まず AGENTS.md だけで始める
tradeoff:
設定ファイルが増えるため、初期整備に時間がかかる(最初の1時間)
ルールが厳しすぎると、AIが修正ループに入りかえって遅くなる。段階的に厳しくする
チームで使う場合、全員が pre-commit install しないと機能しない。CI側にも同じチェックを入れておくと確実
今日やる最小手順
今使っているプロンプトを開いて、「3回以上書き直したルール」を1つ探す
そのルールが形式・書き方に関するなら → pip install ruff && ruff check . を一度走らせる。出たエラーが即移行候補
そのルールがセッションをまたぐものなら → プロジェクトのルートに AGENTS.md を作り、そのルールだけ断定形で書く
プロンプトからそのルールを削除して1日使う。崩れなければ成功
1ルールずつ移していくだけで、プロンプトは短くなり、守られない回数は減っていく。
