---
title: "AIの意見を聞かない技術 第3回：禁止ルールを作らない技術"
author: garplab
publisher: TypingTube
license: CC BY 4.0
license_url: https://creativecommons.org/licenses/by/4.0/
license_scope: 「CC BY 4.0」の印から始まる節（仕組み・検証手順・コード）。印の無い本文は著作権を留保
canonical: https://typing-tube.net/articles/kikanai-03-scope
series: "AIの意見を聞かない技術"
language: ja
---


> この記事は、連載「AIの意見を聞かない技術」の第 3 回です。各回はファイルやスクリプトを一つ置けば完結します。連載の全体像と各回の一覧は[序論](https://typing-tube.net/articles/kikanai-intro)にあります。

今回は、禁止のルールそのものの話です。禁止事項とは、開発のときに許さないと決めたことを書き並べたルールで、AI にも自分にも守らせるものです。前回、その禁止に理由を添えました。理由まで書いた禁止は、拡大にも縮小にも振れなくなります。それでも、**正しく書いてあるのに守られない禁止**が、私の手元にはまだ残っていました。

前作「読まない技術」の[第 6 回](https://typing-tube.net/articles/569d598941c00f)で、**自動テストに移せるのは、機械が真偽を決められるルールだけ**だと書きました。今回取り上げる禁止は、機械どころか、人が読んでも真偽を決められないものでした。

まず一つだけ、試してみてください。あなたの禁止事項を 1 つ選んで、**「これは違反なのかどうか、自分でも一瞬迷う」というケースを 1 つ作ってみます**。作れたでしょうか。作れたなら、次の問いです。AI がそのケースを踏んだとき、**どちらを選ぶか**を、あなたは答えられますか。

## 「色リテラルを書かない」と決めて 0 にしたのに、254 件残っていた

私の手元の本番プロジェクト——[typingtube](https://typingtube.net) という、YouTube の音楽動画でタイピング練習ができる Web サービスです——には、「色はテーマの変数で書く。色リテラルを書かない」という禁止があります。明るいものから暗いものまで 10 のテーマを切り替えられるので、`#fff` と直に書いた文字は、どれかのテーマで必ず背景に沈みます。理由もはっきりした、譲れない類のものです。

4 月に私は pre-commit hook を置いて、1 週間で 1,775 件を 0 にしました。「達成済」と設計書に書いて、終わったつもりでした。

8 月に、hook が見ていなかった場所を初めて数えました。**254 件ありました。** HTML テンプレートの `style="color:#fff"` と、JavaScript の `el.style.color = "#fff"` です。CSS ファイルには 1 件も無く、その隣に 254 件が並んでいました。文字色を直書きしていた箇所を 10 テーマで測ると、白い面に白い文字が出ているトースト（コントラスト比 1.00）が、10 テーマ中 5 つで見つかりました。

いちばんこたえたのは、同じ月に AI が書いた計画書に、こうあったことです。

> CSS ファイルに色リテラルを増やさないよう、inline style で流す

**AI は禁止を守ろうとして、迂回していました。** 書いた側からすれば、hook に引っかからない場所へ置いた親切な処理です。文面は正しかった。「色リテラルを書かない」と書いてあった。それでも守られなかった。`style` 属性の中の色が「CSS の色」に入るかどうかを、誰も——私も、AI も、hook も——決めていませんでした。hook は CSS ファイルだけを数えていて、AI はその範囲を禁止の範囲だと読みました。

決めていないものは、その場の都合で決まります。テーマの変数に無い色をいま出したい、という目の前の要求のほうが、範囲の書いていない禁止より強い。**善意の迂回路は、いつも範囲の隙間に通ります。**

## 「どっちの判定軸で数えるのか」が決まっていなかった

もう一つ、もっと大きいものを。このサービスには「歌詞タイピングと単語帳の実績・ミッション・統計は完全に分離する」という禁止があります。難易度もプレイコストも違うので、混ぜると片方の実績価値が壊れる。理由もはっきりしています。

ここに 3 つ目の面——記事——が増えたとき、答えられない問いが出ました。**記事から入って単語帳をプレイしたユーザーは、どっちの判定軸で数えるのか。**

流入元で数えるなら記事軸です。行為で数えるなら単語帳軸です。どちらの読み方も「相互乗り入れ禁止」に反していません。**禁止は正しく書いてあるのに、判定できない。** この状態で AI に実装を任せると、そのときの文脈でどちらかを選び、後から辻褄合わせが始まります。

決めた線引きは 1 行です。

> 判定軸は**行為で決まる**（打った = 単語帳 / 読んだ = 記事）のであって、どこから来たかでは決まらない。流入元は導線クリックとして記事軸が別に数える。

理解したことは一文にできます。

> **禁止は、範囲が割れていないと守られない。割れているかどうかは、境界のケースを一つ作れるかで分かる。**

## 仕組み: 適用範囲を「判定軸」で書き、境界のケースを 1 行ずつ置く

禁止事項に「適用範囲」の見出しを作り、**禁止の文言をそのまま繰り返さないこと**だけ守ります。書くのは、**迷ったときにどちらを選ぶか**です。

```markdown
### NG13 歌詞・単語帳・記事の実績／ミッション／統計の相互乗り入れ禁止（3 軸）

**適用範囲**:

- 単語帳のプレイを歌詞側の集計テーブルに計上しない（単語帳は独立系統）
- ⚠️ 記事から入った単語帳プレイは**単語帳軸**に計上する（記事軸ではない）。
  軸は行為で決まる（打った = 単語帳 / 読んだ = 記事）のであって、どこから来たかでは決まらない
- ⚠️ 記事軸に実績・ミッションは当面作らない。持つのはアクセス分析だけ。
  ★ 作りたくなったときは**この行を書き換えてから**作る（黙って足さない）
```

3 行目の ★ は解除の手順です。**禁止だけだと「必要だから作る」か「書いてあるから作らない」の二択になりますが、解除条件があると第三の道が開きます。** この形が実際に使われることは、[第 1 回](https://typing-tube.net/articles/kikanai-01-no-review)の測定で確認できました。

そして、**判定軸は後から増えます**。この禁止は 2 つの判定軸で始まって、3 つの判定軸になりました。増えるたびに境界のケースが新しく生まれるので、判定軸を増やす作業と境界を 1 行足す作業はセットです。

Anthropic が[スキルの書き方について出しているガイダンス](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/best-practices)に、これを一般化した言い方があります。**指示の具体度は、その操作の壊れやすさに合わせよ**——判断が要る開けた領域には散文の目安を、一つの手順しか安全でない狭い橋には正確なコマンドを。禁止の「結論」は判断が要るので散文でよく、「適用範囲」は狭い橋なので**そこだけ正確に**書く。同じ 1 項目の中で、書き方を変えていいということです。

## 相談されなくなったもの

「これは違反に当たりますか」です。以前はその都度こちらが判定していました——AI が聞いてくることもあれば、聞かずに越えていることもあり、後者に気づくのは実装が終わった後です。境界の行を書いてからは、迷いどころが先に文字になっているので、判定を求められません。

## 注意: 判定軸を増やすと、組み合わせが増える

2 つの判定軸なら境界は 1 本ですが、3 つの判定軸なら 3 本です。**判定軸を足すコストは、判定軸の数ではなく組み合わせの数で効きます。** 私の場合、3 軸目を足した日にいちばん時間を使ったのは、記事軸の定義ではなく「記事 × 単語帳」の境界を決めることでした。

## 検証手順: 境界を、両方向から破る

**前提**

- 禁止に「適用範囲」を書き、境界の行が 1 つ以上あること
- **その境界を見ている自動テストがあり、今は通っている**こと
- 作業中の変更が無い状態（`git status` が空）から始めること

**所要時間**: 20 分

境界には向きが 2 つあります。**片方だけ確かめると、半分しか見ていない自動テストを、両方見ていると思い込みます。**

**手順**

1. **あなたが**、境界の行を 1 つ選びます（本文の例なら「記事から入って単語帳をプレイしたユーザーを、どちらの判定軸で数えるか」）
2. **あなたが**、一方の向きに破ります。境界が「行為で数える」と決めているなら、**流入元で数える実装**をわざと 1 行書きます。自動テストを走らせ、結果を控えます
3. **あなたが**、その 1 行を戻し、逆の向きに破ります。今度は反対側から越える実装を書きます。自動テストを走らせ、結果を控えます

**合格条件**（すべて満たすこと）

- 手順 2 で、自動テストが**落ちる**
- 手順 2 の失敗のメッセージに、**どちらの向きで破ったか**が出ている
- 手順 3 でも、自動テストが**落ちる**

**合格しなかったとき**

- **手順 3 で落ちない** —— ⚠️ **その自動テストは片側しか見ていません。** 見つけたのがこの検証の収穫です。逆向きの表明を 1 つ足します
- **どちらでも落ちない** —— 自動テストが、その境界を走査の対象にしていません

**後始末**

- 手順 3 で書いた 1 行を戻し、自動テストが合格に戻ることと、`git diff` が空になることを確かめます

片方向だけの自動テストは、境界の半分しか見ていません。私の環境でこの禁止を見ているテストが「双方向の回帰」という名前になっているのは、片側だけ塞いで安心した過去があるからです。

---

次回は「サブエージェントの入力だけは、確かめろ」。**サブエージェントの入力だけは、確かめないととんでもないことになります。**

---

**連載「AIの意見を聞かない技術」**

- ← 前回: [第 2 回 禁止を書かない技術](https://typing-tube.net/articles/kikanai-02-reason)
- → 次回: [第 4 回 サブエージェントの入力だけは、確かめろ](https://typing-tube.net/articles/kikanai-04-subagent-input)
- 全回の一覧: [序論 断ったはずの提案が、また来る](https://typing-tube.net/articles/kikanai-intro)
