---
title: "動かさない技術 第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/ugokasanai-03-no-aggregation
series: "動かさない技術"
language: ja
---


今回は、同じ問いを AI に何度も投げたときに、返ってくるものがどれだけ揃うかの話です。同じ問いというのは、渡すファイルも頼み方も 1 バイト違わない、という意味です。私はある日、機械で作った 40 件の指摘の一覧を 12 体に渡して、そこから「先に直すべきもの」を 3 件だけ選ばせました。返ってきた 12 通りの答えは、1 つとして同じ文ではありませんでした。⚠️ **困るのはそこではありません。** ふだん私たちが見ているのは、その 12 通りのうちの**1 通りだけ**です。

2 作目「AIの意見を聞かない技術」の[第 8 回](https://typing-tube.net/articles/kikanai-08-no-inventory)で、**提案の回数は無限でも、種類は有限で収束します**と書きました。根拠にしたのは、私が AI の提案を断り続けた記録が 17 件で止まっていたことです。⚠️ ただしそれは、私が自分のプロジェクトで 1 回だけ数えた数で、**同じ入力を何度も投げて確かめたものではありません。** 今回は、その言い切りのほうを測りに行きます。

まず一つだけ、試してみてください。いつも AI に投げている質問を 1 つ選んで、**新しいセッションを 2 つ開いて、同じ文面で 2 回投げます。** 返ってきた 2 つを並べたとき、同じところと違うところは、それぞれ何でしょうか。

## 同じ答えを返させる設定を、探しに行って空振りしました

私は長いあいだ、AI を使ったテストは「サンプリングの温度を 0 に固定すれば再現する」と思っていました。`temperature=0`、必要なら `seed` も固定する。学習した頃はそれが定石でしたし、いま AI に聞いても、たいていその作法が返ってきます。

**引き直したら、無くなっていました**（2026-09-08 確認）。

| モデルの世代 | `temperature` / `top_p` / `top_k` |
|---|---|
| 現行の上位モデル（Fable 5 / Opus 5 / Opus 4.8・4.7 / Sonnet 5） | **削除。送ると 400 が返る** |
| 1 つ前の世代（Opus 4.6 / Sonnet 4.6） | 使える |
| Haiku 4.5 とそれ以前 | 使える |

`seed` にあたるパラメータは、そもそもありません。**現行の上位モデルでは、同じ答えを返させる設定を置けません。**

そしてこれは、削除される前から本当ではありませんでした。公式リファレンスは `temperature` の説明にこう書いています。

> `temperature` が 0.0 でも、**結果は完全には決定的にならない**（the results will not be fully deterministic）

⚠️⚠️ **つまり、私が持っていた「固定すれば再現する」は、二重に外れていました。** 設定は消えていて、消える前も保証ではなかった。⭐ ここで確かめるものが 1 つ入れ替わります。**同じ文字列が返るかは、確かめても意味がありません。** 確かめる値打ちがあるのは、**文字列が揺れる中で何が揺れないか**のほうです。

## 揺れたのは項目で、種類は揺れませんでした

題材は機械で作りました。「気になったところ」を 40 件並べた一覧です。1 件は、ファイルのパスと数行のコードと 1 文の説明でできています。40 件には**6 種類の欠陥**が混ぜてあり、どの件がどの種類かは生成器が決めています。⚠️⚠️ **その対応表は、体に渡すディレクトリの外に置きました。** 種類の名前は一覧にも頼み方にも 1 度も出てきません。

頼んだのは 1 つだけです。**「この 40 件から、先に直すべきものを 3 件選び、優先順に ID と理由を 1 行ずつ書いてください」。** 12 体に渡したディレクトリは 1 バイトも同じで、生成のたびに印字されるハッシュで照合しています。

12 体とも 3 件を書いて返してきました。並べると、こうなります。

| 層 | 結果 |
|---|---|
| 理由の文 | **36 件が 36 通り。同じ文は 1 つもありません** |
| 選ばれた項目 | 40 件のうち **9 件**に散った。いちばん多い 1 件で **9/12 体** |
| 項目の種類 | 6 種類のうち **2 種類**。⭐ 残り 4 種類は **0 件** |
| 1 位に挙げたもの | **項目は 4 通り。種類は 12 体とも同じ 1 種類**（⚠️ 別の波で測り直すと 30 体中 29 体。→「注意」） |

⭐⭐ **同じ層を上へ 1 つ上がるたびに、散らばりが消えていきます。** 文字列は 36 通りで、項目は 9 通りで、種類は 2 通りで、1 位の種類は 1 通りです。2 作目に書いた「回数は無限でも種類は収束する」は、12 体で測っても同じ向きでした。

⚠️ ただし、**収束したのは 2 種類まで**です。1 種類ではありません。ここに、この回でいちばん見たかったものが出ています。

| 種類 | 選ばれた件数 | 挙げた体 |
|---|---|---|
| 記録や外部に秘密が出る | 30 | 12/12 |
| 消したものが戻らない | 6 | **6/12** |

**半分の体が、もう 1 つの種類を挙げていました。** そして残りの半分は、1 件も挙げていません。⚠️⚠️ **1 体だけに聞いていたら、この種類に当たるかどうかは半々です。**

ここで、12 通りを 1 つに畳んでみます。多数決を取って、票の多い項目を上から 3 件だけ残す。よくやる集約です。

**残る 3 件は、3 件とも「秘密が出る」でした。** 触れられていた 9 件のうち 6 件が落ち、⚠️⚠️ **6 体が挙げていた「消したものが戻らない」は、1 件も残りませんでした。**

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

> **同じ問いを何度も投げると、返ってくる項目は散り、種類は揃う。だから多数決で 1 つに集めると、消えるのは重複ではなく、少数派の種類のほうだ。**

## 測り方: 種類を隠したまま、同じ 40 件を 12 体に渡す

この実験には、**条件がありません。** 前の 2 回は「A と B で何が変わるか」を測りましたが、今回は同じものを 12 回投げるだけです。⭐ **だから、揃えることのほうが設計の全部になります。**

⚠️ 揃っていることは機械で確かめました。**12 体に渡したディレクトリのハッシュが 1 つに一致すること**、40 件の種類ごとの件数が仕様どおりであること、40 件のパスが 1 つも重なっていないこと。⚠️⚠️ **同じ種類の中で見た目が似すぎると、「同じものが並んでいる」と気づかれて、測っているものが変わります。** だから 6 種類 × 6〜8 通りの雛形を用意して、ファイル名も記号名も数値も全部振り直し、40 件を混ぜて並べました。

⚠️⚠️ **頼み方には、種類の話を 1 語も入れていません。** 「種類」「分類」「重い」「深刻」——このあたりが 1 語でも入ると、体は種類で読み始めます。⭐ **問いを渡した時点で、測れるのは答え方だけになります。** これは以前の実験で 1 度踏んだ穴で、今回は禁止語を検査に入れて落ちるようにしました。

数えるのは、体が書いた `ID` と `理由` の 2 列だけです。**種類への読み替えは、こちら側の対応表でやります。**

```bash
# n 体の答えから、項目の散らばりと種類の収束を分けて数える（手元の実装からの簡約版）
python3 - kind_map.tsv body_*/PICK.tsv <<'PY'
import collections, hashlib, sys
kind_of = dict(line.split("\t")[:2] for line in open(sys.argv[1]).read().splitlines() if line)
picks, reasons, first = [], [], []
for path in sys.argv[2:]:
    seen = []
    for line in open(path):
        cell = line.rstrip("\n").split("\t")
        # 未知の ID と、同じ体が 2 度書いた ID は数に入れない
        if len(cell) < 2 or cell[0] not in kind_of or cell[0] in seen:
            continue
        seen.append(cell[0])
        reasons.append(hashlib.sha256(cell[1].encode()).hexdigest())
    picks += seen
    if seen:
        first.append(seen[0])
ids = collections.Counter(picks)
kinds = collections.Counter(kind_of[p] for p in picks)
print(f"理由の文 {len(set(reasons))} 通り / {len(reasons)} 件")
print(f"項目 {len(ids)} 種類 / 最頻 {ids.most_common(1)[0][1]} 体")
print(f"種類 {len(kinds)} 種類 {dict(kinds.most_common())}")
print(f"1 位: 項目 {len(set(first))} 通り / 種類 {len(set(kind_of[i] for i in first))} 通り")
top3 = [i for i, _ in ids.most_common(3)]
print(f"多数決で 3 件に丸めると残る種類: {sorted({kind_of[i] for i in top3})}")
PY
```

⚠️ **理由の本文は 1 文字も出しません。** 出すのはハッシュが何通りあったかだけです。⭐ **本文を並べると読んでしまい、読むと「だいたい同じことを言っている」という印象が入ります。** その印象こそ、この回が数字に置き換えたかったものでした。

## 集めなくなったもの

**n 体の答えを 1 つの一覧に畳むこと**です。以前は、同じ調査を複数の体に投げたら、返ってきたものを突き合わせて重複を消し、1 本のリストにしていました。重複が消えるぶんだけ読みやすくなるので、そうするのが自然だと思っていました。

いまは畳む前に、**種類ごとに何体が言ったかを出します。** 上の実測でいえば、12/12 と 6/12 の 2 行です。⭐ **この 2 行は、畳んだ一覧のどこにも残りません。**

| 畳んだあとに読めるもの | 畳む前にしか読めないもの |
|---|---|
| どの項目が挙がったか | **その項目を何体が挙げたか** |
| 何件あるか | **全員が一致した種類はどれか** |
| —— | ⭐⭐ **半分だけが挙げた種類があるか** |

⚠️⚠️ **12/12 と 6/12 は、意味が違います。** 前者は「同じ入力なら誰でもそこに行く」で、後者は「行くかどうかがコイン投げになる」です。**畳むと、両方とも同じ「1 件の指摘」になります。**

そして、畳み方によっては後者が丸ごと消えます。上位 3 件を残す多数決で消えたのがそれでした。⚠️ **票の少ない項目を落とす、というつもりだったものが、実際には種類を 1 つ落としていました。**

2 作目で数えた 17 件は、typingtube という運用中のサービスで、私が AI の提案を断り続けた記録です。⭐ あのとき私が「17 で止まった」と書けたのは、**断った理由を種類で書き留めていたから**でした。項目のまま溜めていたら、17 にはならず、止まったかどうかも分からなかったはずです。⭐⭐ **種類で数えることの値打ちは 2 作目に書いてありましたが、その手前で n 個を畳んでいたら種類が減る、という話は書いていませんでした。**

## 注意: これは「AI の判断が正しい」の証明ではありません

⚠️⚠️ **この実験に正解表はありません。** 40 件のどれを先に直すべきかを、私は決めていません。測ったのは**一致の度合いだけ**で、12 体が揃って挙げたものが正しいという話は 1 つもしていません。⭐ 揃うことと正しいことは別で、**同じ事前分布から出てくれば、間違いも揃います。**

⚠️ **6 種類の割り当ては、私が作ったものです。** 何を 1 種類と数えるかで、収束の見え方は変わります。今回の「2 種類に収束」は、⚠️ **この分け方での 2 種類**であって、AI の中に 2 つの何かがあるという意味ではありません。

⚠️ **選ばせた数を 3 に固定したので、散らばりの幅も 3 に縛られています。** 「5 件挙げてください」なら、下位の種類が入る余地はもっと広がったはずです。**数を増やしたときに種類が増えるのかは、測っていません。**

⚠️ 母数は 12 体、モデルは 1 つ、条件も 1 つです。そして並列の上限があるので、**12 体は 3 つの波に分けて起動しました。** 波は条件ではありませんが、その日の混み具合が完全に同じだったとは言えません。

⚠️⚠️ **この記事を書いたあとに、同じ問いをもう 30 体に投げ直しました。** 別の波で、3 回に分けて投げ直したものです。そこでは **1 位の種類が 30 体中 29 体**で、**1 体だけ別の種類**でした。この記事の 12 体と合わせて 4 回ぶんでは **42 体中 41 体**です。⭐⭐ **つまり上の表の「12 体とも」は、この 1 回の測定でしか成り立っていません。** 揃うほうは回を増やすほど強くなり、**「とも」だけが弱いまま**でした。⚠️ これはこの回の主張への反証ではなく、**主張そのものの実例**です —— 1 回の測定で出た数をそのまま持っていくのは危ない、というのがこの回の話で、**私自身がそれを 1 回やりました。**

⚠️ **前の 2 回と違って、今回は成果物だけで全部が測れています。** 選んだ項目も理由も `PICK.tsv` に書かれるので、作業の記録を開く必要がありませんでした。⭐ これはこの回がやさしいという話ではなく、**測りたいものが最初から成果物に残る形だった**、というだけです。

## 実験手順: 同じ入力を n 体に投げて、項目と種類を分けて数える

**前提**

- 会話を引き継がない作業者を n 体（できれば 10 体以上）起動できること
- 選択肢を機械で作れること。⚠️ 手で書くと、書いた人の順位が題材に入ります
- 各作業者に渡すものが 1 バイトも同じであることを、ハッシュで照合できること

**所要時間**: 40 分（題材の生成器と集計器を自分で書く場合。すでにあるなら 10 分）

**手順**

1. **選択肢を機械で作ります。** 種類を数個決めて、1 種類につき複数の雛形を用意し、ファイル名・記号名・数値を全部振り直してください。⚠️⚠️ **種類ごとの件数を揃えます**（偏らせると、偏りが収束に見えます）
2. **種類の対応表を、作業者に渡すディレクトリの外へ置きます。** ⚠️ 一覧の本文にも頼み方にも、種類の名前を 1 語も出さないでください
3. **並びを混ぜます。** ⚠️ 同じ種類が固まって並んでいると、並び自体が手がかりになります
4. **頼み方から、種類に関わる語を全部落とします。** ⚠️⚠️ 「種類」「分類」「重い」「深刻」などを禁止語にして、機械で見てください。⭐ 1 語入るだけで、測れるのは答え方だけになります
5. **同じディレクトリを n 個作り、ハッシュが 1 つに一致することを確かめます。** ⚠️ ここが崩れると、散らばりが題材の差なのか出力の揺れなのか分けられません
6. **n 体を起動します。** ⚠️ プロンプトは、パス以外 1 文字も変えないでください
7. **3 層に分けて数えます。** 理由の文が何通りか、項目が何種類か、種類が何種類か。⚠️⚠️ **種類への読み替えは、必ず対応表でやります**（本文を読んで分類すると、読んだ人の判断が入ります）
8. **1 つに畳んでみて、何が消えるかを見ます。** ⚠️ 多数決で上位 k 件を残したとき、落ちた種類があるかどうかです

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

- **n 体に渡したディレクトリのハッシュが 1 通り**（= 題材が揃っている）
- **理由の文が n × k 通り**（= 同じ文字列は返ってこない、が取れている）
- **種類が 1 種類に潰れていない**（潰れたら、選択肢の作り方が偏っています）
- **種類が全種類に広がってもいない**（広がったら、種類の分け方が細かすぎます）

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

- **項目まで全員一致した** —— 選択肢の中で 1 件だけが目立っています。⚠️ 同じ種類の件数を増やしてください
- **種類までばらけた** —— 種類の分け方が細かすぎます。⚠️ まとめて数を減らしてください
- **1 種類しか出なかった** —— 選択肢の作りに偏りがあります。⚠️ 件数を揃え直して、並びを混ぜてください
- **ハッシュが 2 通り以上になった** —— 生成のたびに変わるもの（時刻・乱数・パス）が題材に入っています。⚠️ 先にそれを消してください

**後始末**

- 作業者に渡したディレクトリと、種類の対応表を削除します。⚠️ **対応表も一緒に削除する** —— 残すと、次に題材を作り直したときに、古い対応表で読み替えることになります

文字列は 36 通り、項目は 9 通り、種類は 2 通り、1 位の種類は 1 通りでした。⚠️ **この 4 つの数のうち、1 つに畳んだあとも残るのは、いちばん右の 1 つだけです。**

---

次回は「役に立たない技術」。**私は、指示ファイルに「必要なら」で始まる行を書くのをやめました。**それでも、必要な作業が抜けたことはありません。
