AIの可視性サンプルサイズ:実際に必要な実行回数はどれくらいか
Senior Web Scraping Engineer
TL;DR:
- AIの回答の単一キャプチャは、同じプロンプトの5回のキャプチャが浮かび上がらせた引用ドメインの**49.6%**しか示さず、5つの回答エンジン、4つのプロンプト、5回の繰り返しを通じて、各入力が一定に保たれる100キャプチャマトリックスで測定されました。
- 引用ドメインセットは平均対称ジャッカードで0.384の範囲で繰り返し重複しましたが、個々のエンジンとプロンプトのセルは0.114から0.956の範囲でした — 「AIの可視性」をカバーする単一の安定性数値は存在しません。
- 回答テキスト内のブランド言及は最も信頼性のない層でした:137のブランド別セルの観察のうち、**44.5%**が5回すべてに現れ、**24.8%**がちょうど1回だけ現れました。
- セットメンバーシップの質問は迅速に収束します — 3回の繰り返しで5回の繰り返しが見つけた引用ドメインの**83.1%**を取り戻しました — したがって、引用監査には少数の実行が必要であり、日々のキャプチャの四分の一を必要としません。
- レートの質問はゆっくりと収束します:観察された40%の出現率では、5回の実行で95%の区間が65ポイントの幅を持ち、±10ポイントに達するにはエンジンあたりおおよそ100回の実行が必要です。
- 全体のマトリックスは4分9秒で実行されたため、これらの数値はモデルレベルの非決定論を単独で測定します。日をまたいでサンプリングするプログラムは、この程度の動きを期待する必要があり、それ以下になることはありません。
- Scrapelessの無料プランで同じマトリックスをあなた自身のプロンプトセットに対して実行し、この数値を借りるのではなく、自分の数値を得てください。
すべてのAI可視性ダッシュボードは数値を報告します。引用のシェア、ボイスのシェア、推薦ランク、感情 — 各々はトレンド矢印とともにクリーンな数値として到着します。それらはエラーバーを伴って到着することはありません。
その省略は、測定される表面がリクエストごとにその回答を再生成するため、従来の検索よりもここで重要です。5つのモデルを名目上決定論的な設定に固定した研究者たちも、実行間で最大15%の精度の変動を記録し、最良の実行と最悪の実行の間のギャップは70%に達しました。指示されても再現しないモデルからは、1回のキャプチャから再現可能な可視性の数値は得られません。
この投稿の残りは、同じエンジンに対して同じプロンプトを繰り返し送り、すべての入力を一定に保ち、回答が実際にどれだけ動くかを測定します。その測定された動きは、モニタリングプログラムが実際に行動できる唯一の出力、すなわち実行回数に変換されます。
パイプラインの概要
パイプラインは4つの段階を持ち、最後の段階が納品物です:
繰り返しマトリックスをキャプチャ → 各回答を比較可能な層に減らす → セルごとに安定性をスコアリング → 安定性を必要なサンプルサイズに変換
ステージ1は、エンジンごとに同一のリクエストを何度も送信し、すべての生の応答を保存します。ステージ2は、各応答を繰り返し間で比較可能な3つの要素に削減します:引用したドメインのセット、回答のテキスト、およびテキストが言及するウォッチリスト名とその順序。ステージ3は、繰り返しごとにこれらの3つの層がどれほど動くかを測定します。ステージ4は、測定された動きを取り、報告チームが実際に抱える質問、すなわち主張の背後にどれだけのキャプチャが必要かに答えます。
これは、そのパイプラインが依存するキャプチャパイプラインの1層上に位置します。キャプチャ側をまだ構築していない場合は、6つのAI回答エンジンにわたる引用シェアパイプラインが基盤であり、この投稿はそのパイプラインが生成するシリーズが、あなたが実行しているサンプルサイズで読み取れるかどうかを測定します。
この測定が回答すること
4つの決定は回答に応じて変わり、すべての4つが今日それなしで行われています。
週ごとの動きが本当かどうか。 引用のシェアが40%から25%に落ちると、回帰のように見えます。セルごとに5回の実行で、両方の数値は同じ信頼区間内にあるため、落ち込みはエンジンが回答を再生成することと区別がつきません。
プログラムが必要とするキャプチャ予算の量。 サンプルサイズ設定にはコストがかかります。レート推定が必要なチームと、引用在庫が必要なチームは、桁数以上に異なる要求を持っており、両方のために大きなものを購入すると、予算の大部分を無駄にします。
目標を築くためのメトリック。 AI回答のいくつかの層は、目標を設定するのに十分安定しています。他はそうではなく、不安定なものにOKRを設定すると、進捗として解釈されるノイズの四分の一を生み出します。
どのエンジンを重視するか。 エンジンごとの安定性は、このマトリックスで大体2倍の要因で変わります。引用がほとんど動かないエンジンは、数回のキャプチャから有用な読み取りを提供しますが、再配置するエンジンは同じ信頼性を得るために何倍もサンプリングが必要です。
サンプルデザイン、前面で述べる
マトリックスは5つのエンジン、4つのプロンプト、5回の繰り返し: 100キャプチャ、20のエンジンとプロンプトのセルが5回の繰り返しを保持しています。
| 次元 | 値 |
|---|---|
| エンジン | Perplexity, Grok, Gemini, Google AI Mode, ChatGPT |
| プロンプト | 1つの製品カテゴリ内の4つのカテゴリーレベルの質問 |
| セルごとの繰り返し | 5 |
| 合計キャプチャ数 | 100 |
| 国 | 毎回の呼び出しでUSに固定 |
| Grok推論モード | 毎回の呼び出しでMODEL_MODE_FASTに固定 |
| キャプチャウィンドウ | 4分9秒、単一セッション |
| 異なる入力フィンガープリンツ | 20 — セルごとに1つ、5回の繰り返しで共有 |
2つのデザイン選択が結果に影響します。最初は、すべての入力が固定されていることです:同じプロンプト文字列、同じ国、同じ推論モード、同じウェブ検索フラグ。これらのいずれかを変えると、入力の違いがばらつきに混入し、測定が無意味になります。ステージ1のフィンガープリンツ数は、ピン留めが保持されていることを証明するために存在します。
2つ目は圧縮されたウィンドウです。全100キャプチャが4分以内に収まりました。これは意図的なものであり、これは全体の演習における最も厳しい制約でもあります — ここでの数字を引用する前に制限セクションを参照してください。
前提条件
- Python 3.10以上、標準ライブラリのみ。
SCRAPELESS_API_KEYとしてエクスポートされたScrapeless APIキー。Universal Scraping APIの下にあるLLM Chat Scraperアクターは、1つのエンドポイントと1つのレスポンスエンベロープの背後に5つのエンジンを配置し、これによりクロスエンジンマトリックスが実用的になります。- 各ブランド名を1行ごとに含む
watchlist.txtファイル — 自分のブランドと、そのカテゴリで競合すると予想される名前。リストが機密である場合は、バージョン管理から除外してください。 - 初回のために約100回のアクター呼び出しの予算。使用ベースの価格設定はScrapeless価格ページに記載されています。
bash
export SCRAPELESS_API_KEY="your_scrapeless_api_key"
mkdir -p ai-visibility-variance && cd ai-visibility-variance
printf 'YourBrand\nRivalOne\nRivalTwo\n' > watchlist.txt
ステージ1 — 繰り返しマトリックスのキャプチャ
ステージ1は各セルの5回の繰り返しを実行し、各生のレスポンスをディスクに書き込みます。解析された要約の代わりに完全なエンベロープを保存することは重要です。比較したいレイヤーが、広がりを見て初めて明らかになるからです。
各エンジンは同じpromptとcountryを使用します。必要な追加項目のみが異なり、各追加項目は固定されているため、繰り返しの間に変化することはありません。セルは同時に実行されます。100キャプチャをシリアルで処理するとウィンドウが引き伸ばされ、実世界の変化が測定に漏れ込むことがあるため、これを除外する必要があります。
python
# capture.py — capture a fixed engine x prompt x repeat matrix
import hashlib
import json
import os
import time
import urllib.request
from concurrent.futures import ThreadPoolExecutor
API = "https://api.scrapeless.com/api/v2/scraper/execute"
KEY = os.environ["SCRAPELESS_API_KEY"]
OUT = "runs"
REPEATS = 5
PROMPTS = {
"P1": "What are the best web scraping APIs for developers in 2026?",
"P2": "Which services provide cloud browsers for automated data collection?",
"P3": "What tools do developers use to collect Google search results programmatically?",
"P4": "Recommend a proxy provider for large-scale public web data collection.",
}
# Same prompt and country everywhere. Only actor-required extras differ, and
# every one of them is pinned: an input that moves between repeats would be
# measured as engine variance.
ENGINES = {
"perplexity": ("scraper.perplexity", {}),
"grok": ("scraper.grok", {"mode": "MODEL_MODE_FAST"}),
"gemini": ("scraper.gemini", {}),
"aimode": ("scraper.aimode", {}),
"chatgpt": ("scraper.chatgpt", {"web_search": True, "shopping": False}),
}
def execute(actor, payload, timeout=300):
request = urllib.request.Request(
API,
data=json.dumps({"actor": actor, "input": payload}).encode(),
headers={"Content-Type": "application/json", "x-api-token": KEY},
method="POST",
)
started = time.time()
with urllib.request.urlopen(request, timeout=timeout) as response:
return json.loads(response.read().decode()), round(time.time() - started, 1)
def capture(job):
engine, prompt_id, repeat = job
actor, extra = ENGINES[engine]
payload = {"prompt": PROMPTS[prompt_id], "country": "US", **extra}
# Hash the actor with the payload. Three of these engines take an identical
# payload, so a payload-only hash would collapse them into one fingerprint
# and stop proving that each cell repeated the same request.
fingerprint = hashlib.sha256(
json.dumps({"actor": actor, **payload}, sort_keys=True).encode()).hexdigest()[:16]
try:
body, elapsed = execute(actor, payload)
except Exception as exc:
# A cell that returns nothing is logged as missing, never dropped.
# A silent hole would bias every statistic computed downstream.
return {"ok": False, "note": f"{engine} {prompt_id} r{repeat}: {type(exc).__name__}"}
with open(f"{OUT}/{engine}__{prompt_id}__r{repeat}.json", "w") as handle:
json.dump({
"engine": engine, "prompt_id": prompt_id, "repeat": repeat,
"elapsed_s": elapsed, "input_fingerprint": fingerprint,
"captured_utc": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
"response": body,
}, handle)
return {"ok": True, "fingerprint": fingerprint}
os.makedirs(OUT, exist_ok=True)
jobs = [(e, p, r) for e in ENGINES for p in PROMPTS for r in range(1, REPEATS + 1)]
print(f"{len(jobs)} captures = {len(ENGINES)} engines"
f" x {len(PROMPTS)} prompts x {REPEATS} repeats")
with ThreadPoolExecutor(max_workers=12) as pool:
results = list(pool.map(capture, jobs))
stored = [r for r in results if r["ok"]]
print(f"stored {len(stored)} captures in {OUT}/")
print(f"missing cells: {[r['note'] for r in results if not r['ok']] or 'none'}")
print(f"{len({r['fingerprint'] for r in stored})} distinct input fingerprints "
f"across {len(stored)} captures")
フィンガープリンツの行は誠実性のチェックです。100キャプチャにわたる20の異なるフィンガープリンツは、各セルの5回の繰り返しがバイト同一のリクエストであったことを意味しますので、レスポンスの中で異なるものはエンジンから来たものです。
ステージ2 — 各キャプチャを3つの比較可能なレイヤーに減らす
ステージ2は、5つの異なるレスポンスエンベロープを3つの測定レイヤーを持つ1つの行の形状に平坦化します。なぜなら「AIの可視性」は1つの量ではなく、1つのものとして扱うのがエラーの始まりだからです。
3つのレイヤーは異なる質問に答え、結果が示すように異なるサンプルサイズが必要です:
| レイヤー | それが持つもの | 指標ファミリー |
|---|---|---|
| L1引用 | エンジンが引用した登録可能なドメインのセット | 引用のシェア、引用ドメイングラウンド |
| L2文章 | 答えのテキスト、文字とトークンセットとして | 感情、要約、テキスト差分 |
| L3ブランド | テキストが最初に言及したウォッチリストの名前 | ボイスシェア、推奨ランク |
各エンジンは自身のキーの下でそのソースを報告します — Perplexityはweb_resultsを使用し、Grokはfootnotesを引用IDで使用し、GeminiとAI Modeはどちらもcitationsを使用し、ChatGPTの検索パネルはsearch_resultとして到着します。登録可能なホストに対して正規化することで、深い記事のURLとホームページリンクの違いが消え、これが実際に引用のシェア指標が数えるものです。
python
# layers.py — reduce every capture to three comparable measurement layers
import json
import os
import re
from urllib.parse import urlparse
WATCHLIST = [line.strip() for line in open("watchlist.txt") if line.strip()]
CITATIONS = {
"perplexity": lambda r: [c.get("url") for c in r.get("web_results") or []],
"grok": lambda r: [c.get("url") for c in (r.get("footnotes") or {}).values()],
"gemini": lambda r: [c.get("url") for c in r.get("citations") or []],
"aimode": lambda r: [c.get("url") for c in r.get("citations") or []],
"chatgpt": lambda r: [c.get("url") for c in r.get("search_result") or []],
}
ANSWER_KEY = {
"perplexity": "result_text", "grok": "full_response", "gemini": "result_text",
"aimode": "result_text", "chatgpt": "result_text",
}
def registrable(url):
netloc = urlparse(url).netloc.lower()
return netloc[4:] if netloc.startswith("www.") else netloc
def mentions(text):
"""Watchlist names present in the answer, keyed by first-mention offset."""
lowered = text.lower()
found = {}
for name in WATCHLIST:
hit = re.search(rf"(?<![a-z0-9]){re.escape(name.lower())}(?![a-z0-9])", lowered)
if hit:
found[name] = hit.start()
return found
rows = []
for filename in sorted(os.listdir("runs")):
record = json.load(open(f"runs/{filename}"))
result = record["response"].get("task_result") or {}
engine = record["engine"]
answer = result.get(ANSWER_KEY[engine]) or ""
found = mentions(answer)
rows.append({
"engine": engine,
"prompt_id": record["prompt_id"],
"repeat": record["repeat"],
"cited_hosts": sorted({
host for url in CITATIONS[engine](result)
if url and (host := registrable(url))
}),
"answer_chars": len(answer),
"answer_tokens": sorted(set(re.findall(r"[a-z0-9]+", answer.lower()))),
# Ordered by first mention, so list position survives into stage 3.
"brands_present": sorted(found, key=found.get),
})
with open("layers.jsonl", "w") as handle:
for row in rows:
handle.write(json.dumps(row) + "\n")
cited = sum(len(r["cited_hosts"]) for r in rows)
print(f"reduced {len(rows)} captures -> layers.jsonl")
print(f"L1 citations: {cited} cited hosts, {cited / len(rows):.1f} per capture")
print(f"L2 prose: {sum(r['answer_chars'] for r in rows) / len(rows):.0f} chars per answer")
print(f"L3 brands: {sum(len(r['brands_present']) for r in rows)} watchlist hits total")
ステージ3 — エンジンとプロンプトごとの安定性をスコアリング
ステージ3は各セル内部の動きを測定し、プールではなくエンジンごとに報告します。なぜなら、プールされた数字は最も知っておくべきことを隠すからです。
3つの統計が作業を行います。引用と文章のレイヤーについては、5回の繰り返し全体での平均ペアワイズJaccardインデックスです — 交差面積のサイズを合併面積のサイズで割り、セル内のすべての10対にわたって平均化します。答えの長さについては、変動係数、これは測定された広がりの標準偏差を平均のパーセンテージとして表現するもので、異なる長さの答えが比較可能になります。ブランドについては、すべての5回の繰り返しに現れた名前のシェアです。
python
# stability.py — how far each layer moves inside a cell
import json
import statistics as stats
from collections import defaultdict
from itertools import combinations
cells = defaultdict(dict)
for line in open("layers.jsonl"):
row = json.loads(line)
cells[(row["engine"], row["prompt_id"])][row["repeat"]] = row
def jaccard(left, right):
union = left | right
return len(left & right) / len(union) if union else None
def mean_pairwise(sets):
scores = [j for a, b in combinations(sets, 2) if (j := jaccard(a, b)) is not None]
return stats.mean(scores) if scores else None
per_engine = defaultdict(lambda: defaultdict(list))
brands = defaultdict(lambda: {"stable": 0, "flipping": 0})
for (engine, _prompt), reps in sorted(cells.items()):
keys = sorted(reps)
hosts = [set(reps[k]["cited_hosts"]) for k in keys]
tokens = [set(reps[k]["answer_tokens"]) for k in keys]
lengths = [reps[k]["answer_chars"] for k in keys]
per_engine[engine]["cite"].append(mean_pairwise(hosts))
per_engine[engine]["prose"].append(mean_pairwise(tokens))
per_engine[engine]["cv"].append(stats.pstdev(lengths) / stats.mean(lengths) * 100)
per_engine[engine]["union"].append(len(set().union(*hosts)))
per_engine[engine]["core"].append(len(set.intersection(*hosts)))
for brand in set().union(*[set(reps[k]["brands_present"]) for k in keys]):
seen = sum(1 for k in keys if brand in reps[k]["brands_present"])
brands[engine]["stable" if seen == len(keys) else "flipping"] += 1
header = f"{'engine':<12}{'citeJaccard':>12}{'proseJaccard':>13}{'lengthCV%':>11}"
print(header + f"{'hostsEveryRun':>15}{'brandsStable%':>15}")
for engine, series in per_engine.items():
core, union = sum(series["core"]), sum(series["union"])
counts = brands[engine]
stable_pct = counts["stable"] / (counts["stable"] + counts["flipping"]) * 100
print(f"{engine:<12}{stats.mean(series['cite']):>12.3f}"
f"{stats.mean(series['prose']):>13.3f}{stats.mean(series['cv']):>11.1f}"
f"{f'{core}/{union}':>15}{stable_pct:>14.1f}%")
ステージ4 — 安定性をサンプルサイズに変換
ステージ4はスプレッドを実行回数に変換し、2回行います。なぜなら、2つの質問タイプはまったく異なる動作をするからです。
セットメンバーシップ質問 — このエンジンが私のカテゴリのために引用しているドメインはどれか — に対して、有用な出力はカバレッジカーブです:5回の実行で浮かび上がったドメインのうち、1回の実行がどのくらいのシェアを生み出すのか、2回、3回ではどうかです。これは、リピートのすべての組み合わせをサブサンプリングすることによってキャプチャから直接計算されます。
レート質問 — 私のブランドに言及する回答のシェアはどれくらいか — に対して、出力は割合に対する信頼区間です。ウィルソンスコア区間は、これらのサンプルサイズにおいて適切なツールです:NISTの参照実装は、割合の信頼限界に対するデフォルトメソッドとしてそれを設定し、1から1,000のサンプルサイズ間の系統的な比較は、ウィルソン区間が教科書のワルド区間よりも優れていることを発見しました。特にnが小さく、割合が0または1の近くにあるときにそうです。これらの条件はブランド言及データを正確に記述します。
python
# samplesize.py — measured stability -> a required number of runs
import json
import math
import statistics as stats
from collections import Counter, defaultdict
from itertools import combinations
cells = defaultdict(dict)
for line in open("layers.jsonl"):
row = json.loads(line)
cells[(row["engine"], row["prompt_id"])][row["repeat"]] = row
def wilson(hits, runs, z=1.96):
proportion = hits / runs
denominator = 1 + z * z / runs
centre = (proportion + z * z / (2 * runs)) / denominator
half = z * math.sqrt(
proportion * (1 - proportion) / runs + z * z / (4 * runs * runs)) / denominator
return max(0.0, centre - half) * 100, min(1.0, centre + half) * 100
coverage = defaultdict(list)
for reps in cells.values():
keys = sorted(reps)
universe = set().union(*[set(reps[k]["cited_hosts"]) for k in keys])
if not universe:
continue
for n in range(1, len(keys) + 1):
coverage[n].append(stats.mean([
len(set().union(*[set(reps[k]["cited_hosts"]) for k in combo])) / len(universe)
for combo in combinations(keys, n)
]))
print("SET QUESTION — share of the 5-run cited-domain union that n runs surface")
for n in sorted(coverage):
print(f" n={n}: {stats.mean(coverage[n]) * 100:5.1f}%")
observed = Counter()
for reps in cells.values():
keys = sorted(reps)
for brand in set().union(*[set(reps[k]["brands_present"]) for k in keys]):
observed[sum(1 for k in keys if brand in reps[k]["brands_present"])] += 1
total = sum(observed.values())
print(f"\nRATE QUESTION — {total} brand-by-cell observations at n=5")
for seen in sorted(observed):
low, high = wilson(seen, 5)
print(f" seen {seen}/5 in {observed[seen]:>3} cases ({observed[seen] / total * 100:4.1f}%)"
f" 95% interval {low:5.1f}%-{high:5.1f}% width {high - low:4.1f}pt")
print("\nRATE QUESTION — interval width at a 40% appearance rate, by run count")
for n in (1, 5, 10, 20, 30, 50, 100, 200):
low, high = wilson(round(0.4 * n), n)
print(f" n={n:<4} {low:5.1f}%-{high:5.1f}% width {high - low:4.1f}pt")
マトリックスが実際に示したこと
すべてが動き、エンジンは同じ量だけでは動きませんでした。
| エンジン | 引用ドメインジャカード | プロストークントジャカード | 回答の長さCV | すべての実行で引用されたホスト | ブランド言及の安定性 |
|---|---|---|---|---|---|
| Perplexity | 0.534 | 0.446 | 28.7% | 34のうち8 | 47.6% |
| Gemini | 0.511 | 0.361 | 16.3% | 29のうち6 | 40.6% |
| Grok | 0.366 | 0.392 | 10.2% | 61のうち6 | 45.5% |
| Google AI Mode | 0.282 | 0.333 | 15.9% | 182のうち11 | 50.0% |
| ChatGPT | 0.228 | 0.342 | 9.4% | 80のうち1 | 40.0% |
20のセル全体で、引用されたドメインの平均ペアワイズジャカードは0.384でした。セルレベルのスプレッドはより有用な数字です:低い端で0.114、高い端で0.956です。単一のエンジンに対する1つのプロンプトからの安定性の主張は、その範囲内のどこにでも落ち着く可能性があります。これが正確に、単一のエンジンの3回の実行調査がリードであり、発見ではない理由です。
カバレッジカーブは最も書き留める価値のある数値です:
| 実行回数 | 5回の実行で引用されたドメインの合併に対するシェア |
|---|---|
| 1 | 49.6% |
| 2 | 70.3% |
| 3 | 83.1% |
| 4 | 92.3% |
| 5 | 100% |
1回のキャプチャは、5回のキャプチャが示すドメインの約半分を示しています。n=1での各エンジンでは、Perplexityで73.6%からChatGPTで35.1%までの範囲で動いているため、最も安定性のないエンジンの単一キャプチャ引用監査では、インベントリするはずのものの約3分の2を見逃しています。このカーブはまた、急速に平坦化します:3回目の実行は12.8ポイントを追加し、4回目は9.2を追加します。引用インベントリに対して、3回から4回のリピートがほとんどすべてを確保します。
Google AI Modeは、最も広範なドメインの合併を生み出しました — 4つのプロンプトにわたる182の異なるホストで、すべてのリピートで11のみが表示されます。Google自身のガイダンスはメカニズムを説明しています:AI ModeとAI Overviewsは、サブトピックを横断して複数の関連検索を発行するクエリファンアウト技術を使用する可能性があり、したがって、従来の検索よりも広範で多様なリンクセットを提示できます。エンジンがサブトピック間に引用を広げるように構築されている場合、1回のキャプチャはそのスプレッドの一部しかサンプリングしません。
レート層は対照的な物語を伝えます。137のブランド-セル観察の中で、**44.5%**のみがすべての5回のリピートに存在しました。**24.8%**は正確に5回のうち1回に出現しました — 単一キャプチャが報告する際は、キャプチャによって自信のある存在または自信のある不在として扱われます。
| 見られた回数 | ケース | シェア | 真のレートに対する95%区間 | 幅 |
|---|---|---|---|---|
| 1 of 5 | 34 | 24.8% | 3.6% – 62.4% | 58.8 pt |
| 2 of 5 | 16 | 11.7% | 11.8% – 76.9% | 65.2 pt |
| 3 of 5 | 14 | 10.2% | 23.1% – 88.2% | 65.2 pt |
| 4 of 5 | 12 | 8.8% | 37.6% – 96.4% | 58.8 pt |
| 5 of 5 | 61 | 44.5% | 56.6% – 100% | 43.4 pt |
5回の実行で2回見られたブランドは、真の出現率が12%から77%の間にあるため、利害関係者が行動する声明を支持するには広すぎます。サンプルを広げることで徐々に狭まります:
| セルあたりの実行回数 | 観察された40%レートでの95%区間 | 幅 |
|---|---|---|
| 1 | 0.0% – 79.3% | 79.3 pt |
| 5 | 11.8% – 76.9% | 65.2 pt |
| 10 | 16.8% – 68.7% | 51.9 pt |
| 20 | 21.9% – 61.3% | 39.5 pt |
| 30 | 24.6% – 57.7% | 33.1 pt |
| 50 | 27.6% – 53.8% | 26.2 pt |
| 100 | 30.9% – 49.8% | 18.9 pt |
| 200 | 33.5% – 46.9% | 13.5 pt |
| 「いくつの実行数が必要か?」の答えは、報告しているレイヤーによって異なり、2つの答えは桁が異なるほど違います。 引用インベントリは、3回から4回の繰り返しで読み取ることができます。週ごとに比較したいレートは、±10ポイントに収束する前に、エンジンごとに100キャプチャ程度を必要とします。2〜3週間の毎日のキャプチャは約14〜21に達しており、設定された質問には十分ですが、レートの質問には約5倍不足しています。 |
記録すべき2つの小さな結果があります。ランクの動きはメンションよりも少なかった:複数の繰り返しに存在する名前の中で、初回メンションの位置はChatGPTで平均0.89位、Grokで1.95位変動しました。このため、ランク追跡メトリックはメンションの不安定性を引き継ぎますが、自身の不安定性はわずかなものです。また、1つのセル — クラウドブラウザのプロンプトでの単一エンジン — は、5回の繰り返しの中でいずれのウォッチリストブランドも名付けず、ベンダーの代わりに能力カテゴリーで回答しました。一貫した不在も安定した読み取りですし、空の結果を失敗したキャプチャとして扱うモニターは、真の信号を捨てることになります。
測定された広がりも発表された研究と一致しており、反対するものではありません。20ブランド、8言語、3モデルにわたる12,933のLLMブランド応答を分解した分散成分研究は、再サンプリングだけで全体の34.8%の分散を説明したことを発見し、単一の応答の信頼性は約0.01でした。この研究は異なるコーパスと異なる統計的方法を使用し、それでも同じ結果に至りました:1つの回答にはほとんどブランドを識別する信号がありません。
すでにエンジンごとのブランド感情パイプラインやAI推薦ランクトラッカーを運用している場合、両方ともプローズレイヤーを読み取り、したがって高速なサンプルサイズではなく遅い収束するサンプルサイズを引き継ぎます。
この表の独自バージョンを取得するには、約100回のコールが必要です。 Scrapelessの無料プランで始めるを選び、自分のプロンプトに対してマトリックスを実行する前に、これらのメトリックの目標を設定してください。
この測定があなたに教えられないこと
圧縮されたキャプチャウィンドウは、上記のすべての数値を規定する制限です。すべての100キャプチャは、ある1日の4分9秒以内に収まりました。これは1つの動きの源を明確に分離します — モデルがその答えを再生成している — そしていくつかの他の要因を完全に除外します:インデックスの更新、ニュースサイクル、あなたが公開するコンテンツ、モデルバージョンの変更、そしてエンジンが取得する際の実際の日々の変動。
その結果は一方向のみで動きます。 これらの数値は底辺であり、全体の分散の推定ではありません。 数週間にわたって同じプロンプトをサンプリングするプログラムは、モデルレベルの非決定性 と 4分間ウィンドウが保持していたすべてにさらされます。したがって、実際の監視プログラムに必要なサンプルサイズは少なくともこの大きさでなければならず、4分間の数値と4週間の数値とのギャップは、ここで誰も測定していないものです。
4つの狭い制限が適用されます:
- 20セルは小さなデザインです。 エンジンごとの数字は4つのプロンプトの平均であるため、エンジンの数値はそのエンジンの精度の高い推定ではありません。セルレベルの範囲は0.114から0.956であり、これは1つのセルがどれだけ異なるかの正直な要約です。
- 1つのカテゴリー、1つの言語、1つの国。 すべてのプロンプトは1つの製品カテゴリーにあり、英語で
USに固定されています。上記で引用した分散成分研究は、クエリ言語が全体の26.5%の分散を占めていることを発見したため、異なる言語がこのように振る舞うと仮定すべきではありません。 - ウィルソン区間は独立した抽出を前提としています。 1セッション内で同時に5つのリクエストが発生すると、上流状態を共有することがあり、その場合、実効サンプルは名目上のものよりも小さくなり、真の区間は表に示されているよりも広くなります。
- ブランドレイヤーはウォッチリストに依存します。 リストにある名前のみがカウントされるため、リストに載せていない競合他社はL3には見えませんが、そのエンジンがそのドメインを引用する場合、L1には完全に見えます。
これらのいずれも、同一条件下で測定されたレイヤー間の比較である見出し結果を破るものではありません。ただし、特定のパーセンテージはこのマトリックスに属し、一般的なAIの回答には属しません。
数値をモニタリングスケジュールに組み込む
2種類の質問を別々にスケジュールしてください。なぜなら、両方に対して1つのリズムを実行すると、設定された質問に対して過剰なコストがかかり、レートの質問に対しては十分でなくなるからです。
引用インベントリの場合、 エンジンごとにプロンプトあたり3~4回のリピートを行い、週ごとに実施します。これにより、キャプチャラウンドごとに83%から92%の引用ドメインを回復し、そのセットの週ごとの差は読みやすくなります。単一のランのリストではなく、ラウンドごとの和集合を保存してください。2つの単一ランを比較すると、主に再サンプリングによる変化が生じます。
レートメトリクスについて — メンションシェア、ボイスシェア、センチメントシェア、ランク — スナップショットではなく蓄積します。週ごとにセルあたり20回のキャプチャで、週の数値は±20ポイント近くのインターバル、月間のプール数値は±10近くになります。毎回、数値の横にインターバルを報告してください。42%を示すダッシュボードが31%–50%を示さない場合、キャプチャ設計が支持する精度を主張していることになります。
どのようなケイデンスを選んでも、すべてをピン留めしてください。 同じプロンプト文字列、同じ国、同じ推論モード。ステージ1からの入力フィンガープリントは保存した行に含めるべきで、将来のアナリストが、誰かが編集したプロンプトと実際の変化を区別できるようにします。
エンジンが変更された際は再測定してください。 安定性は現在のモデルとリトリーバルスタックの特性であり、定数ではありません。エンジンが目に見える変更を導入した場合、前四半期に十分だったラン数は再び仮説となります。ここでの規律は、メトリクスの後ではなく、代表的なデータセットをキュレートし、意味のある方法論を選択することがメトリクスよりも重要なモデル評価と同じです。
結論:数値だけでなくインターバルを報告する
有用な発見は、AIの回答が変動するということではありません。すべてのガイドがすでにそれを示しています。有用な発見は、異なるレイヤーで異なる量だけ移動し、必要なサンプルサイズがそれに応じて分割されるということです:引用インベントリには少数のラン、会議で防御するつもりのレートにはおよそ100回のキャプチャが必要です。
その分割に関する設計は4つのファイルと100回の呼び出しです。入力をピン留めしてキャプチャし、比較可能なレイヤーに減らし、移動をスコアし、ラン数に変換します。自分のプロンプトセットに対して一度実行すれば、ケイデンスを推測するのをやめられます。なぜなら、すべてのAI可視性ダッシュボードが現在欠けている唯一の数値、エラーバーを手に入れることができるからです。
自分のAI可視性の変動を測定する準備はできていますか?
5つの回答エンジンは、Scrapeless Universal Scraping API上の1つのエンドポイントと1つの応答封筒の背後に存在します。これにより、100回のキャプチャマトリックスが5回の異なる統合ではなく、単一の午後になります。ここで使用されるすべてのアクターのパラメータと応答形式は、Scrapeless API ドキュメントに記載されています。
無料のScrapelessアカウントを作成することによって、可視性数値にエラーバーを追加してください。
よくある質問
Q: AI可視性の数値には実際に何回のランが必要ですか?
レイヤーによります。そして、2つの答えは1桁以上の差があります。このマトリックスでは、エンジンごとにプロンプトあたり3~4回のリピートで83%から92%の引用ドメインを回復しました。したがって、このサンプルサイズでの引用インベントリは読みやすくなります。メンションシェアのようなレートメトリクスは、95%のインターバルが約±10ポイントに狭まる前に、エンジンごとにプロンプトあたり約100回のキャプチャが必要であり、5回のキャプチャでは約65ポイントの幅が残ります。
Q: プロンプトと設定が同じでもAIの回答はなぜ変わるのですか?
モデルは確率分布からサンプリングしており、フィードするリトリーバルレイヤーも固定されていないため、同じ入力が必ず同じ出力を保証するわけではありません。5つのモデルを名目上の決定論的設定で8つのタスクに固定した作業でも、ラン間で最大15%の精度の揺れが記録され、その影響は構成ミスによるものではなく、推論バッチの処理方法に起因するとされました。
Q: 引用されたソースは回答テキストよりも安定していますか?
信頼性はなく、どちらのレイヤーでもリピートサンプリングをスキップするには不十分です。このマトリックス内の引用ドメインセットの平均のペアワイズジャカードは0.384であり、単一のキャプチャでは5回のキャプチャが浮上させたドメインの49.6%しか識別しないため、引用レイヤーは絶対的な意味で大きく変動しています。レートメトリクスよりも迅速に収束しますが、安定しているという異なる主張です。
Q: 私が追跡するエンジンは、必要なラン数に影響しますか?
はい、引用レイヤーでおおよそ2倍の係数です。1回のキャプチャで、このマトリックスの最も安定したエンジンで73.6%、最も不安定なエンジンで35.1%の5回のドメインの統合が表面化されました。したがって、1つのエンジンのために選ばれた実行回数は、別のエンジンには適していません。エンジンごとのカバレッジカーブを計算し、全体に1つの数字を適用するのではなく、対応してください。
Q: この測定は日々の変動も測定しますか?
いいえ。すべての100回のキャプチャは4分9秒以内で実行され、モデルレベルの非決定性を取り出し、インデックスの更新、ニュースサイクル、コンテンツの変更、およびモデルのバージョンを除外します。ここにあるすべての数値を最低ラインとして扱ってください。数週間にわたってサンプリングするプログラムは、この変動と短いウィンドウで一定だったすべてを伴います。
Q: この方法でAIの回答をキャプチャすることは合法ですか?
自分が作成したプロンプトに対する公開されているAIの回答をキャプチャすることは、一般的なパブリックウェブデータの収集であり、ここにあるプロンプトと応答には個人データは含まれていません。量を制限し、測定に対して比例に保ち、分析に必要なものだけを保存し、プログラムを大規模に実行する前に、自分の法域と使用例に適用される条件を確認してください。
Q: AIエージェントやSDKなしでこれを実行できますか?
はい。ここにあるすべてのスクリプトは、1つのHTTPSエンドポイントに対するPython標準ライブラリであり、したがって、全体のパイプラインはcronジョブまたはCIスケジュールから実行され、Python以外に何もインストールされていません。唯一の外部依存はAPIキーです。
Scrapelessでは、適用される法律、規制、およびWebサイトのプライバシーポリシーを厳密に遵守しながら、公開されているデータのみにアクセスします。 このブログのコンテンツは、デモンストレーションのみを目的としており、違法または侵害の活動は含まれません。 このブログまたはサードパーティのリンクからの情報の使用に対するすべての責任を保証せず、放棄します。 スクレイピング活動に従事する前に、法律顧問に相談し、ターゲットウェブサイトの利用規約を確認するか、必要な許可を取得してください。



