__usage_ai-trainingの書き方:robots.txtでAI学習を断る2行と、Geminiの引用まで消える落とし穴

AIの学習に自社サイトを使わせない指定が、robots.txtの2行で済むようになります。Google、Microsoft、OpenAI、Anthropicの4社が10月30日、共通の書き方__usage_ai-trainingを尊重すると表明しました。ただし、この2行だけを足すと、GoogleではGeminiアプリの回答で自社の記事が引用される道まで閉じます。

結論から書きます。2行は今日書いても構いませんが、効き始めるのはMicrosoftが2027年2月1日、Googleが2027年初めの予定で、OpenAIとAnthropicは時期が決まっていません。GPTBotやClaudeBotなど、すでに書いてある会社ごとの行は消さないでください。Googleについては「学習は断り、引用は残す」を選べないので、どちらを取るかを先に決める必要があります。

書いた後の確かめ方にも落とし穴があります。Python標準のrobotparserで判定すると、2か所でドラフトと違う答えが返りました。正しく判定する確認用スクリプトと、11月2日に国内外18サイトのrobots.txtを調べた結果も載せます。

目次

書くのは2行。「User-agent: *」の拒否は意思表示にならない

共通の指定は、ふだんのrobots.txtと同じ文法で書きます。User-agentの欄にクローラー名の代わりに__usage_ai-trainingという決まった名前を置き、学習に使ってほしくない範囲をDisallowで示します。

User-agent: __usage_ai-training
Disallow: /

仕様の元になっているのは、Anthropicの技術者が10月30日にIETFへ出したインターネットドラフトです。IETFの作業部会が採択した文書ではなく個人の提案で、RFCのような正式な標準でもありません。読むと、運用で迷いそうな点がはっきり決められています。

  • User-agent: *のグループは、この名前に当てはめてはいけない(MUST NOT)。サイト全体を*で拒否していても、学習についての意思表示にはならない。
  • 名前の大文字・小文字は区別しない。Allow/Disallowの書き方とパスの照合は、既存のrobots.txtと同じ。
  • アクセスを止める仕組みではない。クローラーが取りに来ること自体は、別の行で制御する。
  • さかのぼって効かない。robots.txtが反映された後に取得した内容から対象になる。

つまり、今のrobots.txtに__usage_ai-trainingの行がなければ、どのサイトも「意思表示なし」です。各社の学習用クローラーを名前で止めている行は、それぞれの会社のルールとして今までどおり働きます。

効き始める時期は会社ごとに違う。Microsoftは2027年2月1日から

4社がそろって「今日から尊重する」と言ったわけではありません。11月2日時点で確認できた各社の扱いと、今すぐ効く会社ごとの指定を並べます。

会社(今すぐ効く個別の指定)共通指定への対応
Google(Google-Extended)2027年初めに対応予定。Google-Extendedを明示していないサイトでは、その拒否として扱う
Microsoft(NoArchive/NoCache)2027年2月1日から、クロール時に学習の指定として尊重。3月1日以降、NoArchive/NoCacheは学習・引用の指定として読まない
OpenAI(GPTBot)対応を表明。報道では今後数か月以内。検索用のOAI-SearchBotは学習用と独立
Anthropic(ClaudeBot)実装を開始。全サービスへの対応時期は未発表
Meta(meta-externalagent)10月30日に表明した4社に入っていない

Googleの時期は10月30日のSearch Central Blogと、同日に更新されたクローラーの説明ページで確認しました。Anthropicについては、ドラフトを書いたKevin Kelley氏がIETFのメーリングリストで「実装を始めた」「全サービスへの対応には時間がかかる」と書いています。OpenAIの発表ページは確認時に開けなかったため、時期はImpress Watchの報道に基づきます。

Microsoftは、もう一歩踏み込んだ設計です。Bingの10月30日の告知によると、学習の指定は共通の2行かページ単位のmetaタグで受け付け、生成AIの回答に使う「グラウンディング」はページ単位でだけ別に指定します。何も書かなければ、どちらも許可の扱いです。

見落とせないのは、これまでBing向けにNoArchiveやNoCacheで学習・引用を断ってきたサイトです。2027年3月1日以降、Microsoftはこの2つを学習や引用の指定として読まなくなります。引用も断り続けたい場合は、ページ単位の新しい指定に加えて、Bing Webmaster Toolsで一時的な指定を記録するよう求めています。

Metaのクローラーを止めたい場合は、従来どおり名前で書く必要があります。Metaの5種類のクローラーの役割と、どれを止めるとリンクのプレビューが崩れるかは、Metaの新規約とrobots.txtの見直し方で詳しく比べました。

Googleでは「学習は断るが、Geminiには引用してほしい」を選べない

いちばん気をつけたいのはGoogleです。Googleのクローラー一覧によると、Google-Extendedは次の2つをまとめて管理する指定です。

  • Geminiアプリと、Vertex AIのGemini APIで使う将来のGeminiモデルの学習
  • Geminiアプリと、Vertex AIの「Grounding with Google Search」で、回答の根拠としてサイトの内容を渡すこと

Googleは、共通の指定で学習を断ったサイトがGoogle-Extendedを明示していない場合、Google-Extendedを拒否したものとして扱うと書いています。一方で、Google-Extendedを明示的に許可したサイトは、Geminiアプリの回答に表示され、クリックを受ける対象になるとも書いています。学習と引用が1つのスイッチなので、Googleに対しては「両方断る」か「両方許す」かのどちらかです。

なお、Google-Extendedは通常のGoogle検索への掲載や順位には影響しないと、同じページに明記されています。ここで失うのは検索結果ではなく、Geminiアプリでの引用です。

11月2日に取得した18サイトのうち、NHK、朝日新聞、読売新聞、毎日新聞、日本経済新聞、産経新聞、Yahoo! JAPAN、クックパッド、New York Timesの9サイトは、すでにGoogle-Extendedを名前で拒否していました。こうしたサイトは、共通の2行を足してもGoogleの扱いは変わりません。影響を受けるのは、これまで何も書いてこなかったサイトが2行だけを足す場合です。

目的別の書き方。既存の行は残し、食い違う指定を書かない

共通指定と会社ごとの指定が食い違ったときの扱いは、まだ各社がそろって説明していません。ドラフトは「具体的な指定を一般的な指定より優先するべき」としていますが、Search Engine Journalは、OpenAIのブログもクローラー文書も、共通指定とGPTBotの指定が食い違う場合を説明していないと指摘しています。私は、同じ会社に向けて逆の意味の行を書かない形をすすめます。

学習をまとめて断り、Geminiアプリでの引用もなくてよい場合は、次のように書きます。共通の2行がまだ効かない会社にも、名前の行で同じ意思を伝えておく形です。

User-agent: __usage_ai-training
Disallow: /

User-agent: GPTBot
User-agent: ClaudeBot
User-agent: Google-Extended
User-agent: meta-externalagent
Disallow: /

学習は断りたいが、Geminiアプリで引用される道は残したい場合は、Googleに限って学習も許すことになります。Google-Extendedを明示的に許可します。

User-agent: __usage_ai-training
Disallow: /

User-agent: GPTBot
User-agent: ClaudeBot
User-agent: meta-externalagent
Disallow: /

User-agent: Google-Extended
Allow: /

プレスリリースや製品資料のように、学習に使われても構わない場所だけを開けることもできます。ドラフト自身が、この形を例に挙げています。

User-agent: __usage_ai-training
Disallow: /
Allow: /press-kit/

この3行だけでは、Google-Extendedの行がないため、Googleでは/press-kit/以外のGeminiアプリでの引用も止まります。上の2つの例のどちらかを使い、Disallow: /を書いた各グループにAllow: /press-kit/を足してください。

どの例も、既存のUser-agent: *のグループやSitemapの行はそのまま残し、追記してください。学習を断らない場合は何も書く必要はありません。Microsoftの説明でも、指定がないときの既定は許可です。

WordPressは、robots_txtフィルターか実ファイルで追記する

WordPressのサイトで/robots.txtを開いて表示される内容は、多くの場合、ファイルではなくWordPressが毎回組み立てたものです。WordPress本体のdo_robots()を読むと、User-agent: *と管理画面の除外を作り、robots_txtフィルターを通して出力しています。サイトマップの行も、同じフィルターで足されています。

このブログのrobots.txtも、11月2日の時点でこの初期状態のままでした。共通指定は書いていません。

WordPressに出力させたまま行を足すなら、子テーマのfunctions.phpか、自作の小さなプラグインに次のコードを置きます。例は共通の2行だけなので、Googleについては前の節で書いた扱いになります。目的別の書き方で選んだ内容に合わせて、文字列の中身を変えてください。

add_filter( 'robots_txt', function ( $output ) {
	return $output . "\nUser-agent: __usage_ai-training\nDisallow: /\n";
} );

このコードは、robots_txtフィルターの公式リファレンスと本体のソースに沿って書いたもので、当サイトのWordPressでは実行していません。追加したら、ブラウザーで/robots.txtを開き、元の行の後ろに2行が付いていることを確かめてください。

ドキュメントルートに実物のrobots.txtを置く方法もあります。その場合は通常、Webサーバーがファイルをそのまま返すため、WordPressが作っていた行は出なくなります。今表示されている内容、特にSitemapの行を写してから追記してください。robots.txtを編集できるSEOプラグインを使っているなら、出力元を1つにまとめ、プラグインの編集画面で書くほうが混乱しません。

Python標準のrobotparserで確かめると、2か所で誤判定する

書いた後に、手元のスクリプトで判定したくなる人は多いはずです。Pythonには標準でrobots.txtを読むurllib.robotparserがあります。ところがPython 3.12.9で、__usage_ai-trainingを名前として渡して試すと、次の2つの結果になりました。

  • User-agent: *とDisallow: /だけのファイルで「取得不可」と答える。ドラフトでは意思表示なしなので、学習を断っていると読むのは誤り。
  • ドラフトの例(Disallow: /とAllow: /press-kit/)で、/press-kit/logo.pngも「取得不可」と答える。robots.txtの標準であるRFC 9309は、最も長く一致した規則を使うと定めているので、本来は許可。

前者は、名前の合うグループがなければ*に従うという、通常のクローラー向けの動きをそのまま当てはめたためです。後者は、標準ライブラリが上から順に最初に合った規則を使うためです。どちらも通常のクローラーの判定では問題になりにくくても、学習の意思表示を確かめる用途では答えが逆になります。

そこで、標準ライブラリだけで動く確認用スクリプトを作りました。共通指定には*を当てはめないこと、最も長い一致を使うこと、同じ名前のグループをまとめることを実装し、主要なクローラーの扱いも並べて表示します。

"""robots.txtのAI学習指定を、RFC 9309とAIPREFドラフトの読み方で判定する。

使い方: python3 check_ai_training.py https://example.com/robots.txt [/path ...]
"""
import re
import sys
from urllib.request import Request, urlopen

SHARED = "__usage_ai-training"
CRAWLERS = ["Google-Extended", "GPTBot", "ClaudeBot", "Meta-ExternalAgent"]


def parse(text):
    """User-agentごとの規則を返す。同じ名前の複数グループは1つにまとめる。"""
    groups, agents, in_rules = {}, [], False
    for raw in text.splitlines():
        line = raw.split("#", 1)[0].strip()
        key, sep, value = line.partition(":")
        if not sep:
            continue
        key, value = key.strip().lower(), value.strip()
        if key == "user-agent":
            if in_rules:
                agents, in_rules = [], False
            agents.append(value.lower())
            groups.setdefault(value.lower(), [])
        elif key in ("allow", "disallow") and agents:
            in_rules = True
            for agent in agents:
                groups[agent].append((key, value))
    return groups


def allowed(rules, path):
    """最も長く一致した規則を使い、同じ長さならAllowを優先する。"""
    best = (-1, True)
    for kind, pattern in rules:
        if not pattern:
            continue
        regex = re.escape(pattern).replace(r"\*", ".*")
        if regex.endswith(r"\$"):
            regex = regex[:-2] + "$"
        if re.match(regex, path):
            length = len(pattern.encode())
            if length > best[0] or (length == best[0] and kind == "allow"):
                best = (length, kind == "allow")
    return best[1]


def describe(groups, agent, path):
    rules = groups.get(agent.lower())
    if rules is not None:
        return "許可" if allowed(rules, path) else "拒否"
    if agent == SHARED:
        return "指定なし(意思表示なし。* の規則は当てはまらない)"
    if "*" in groups:
        return ("許可" if allowed(groups["*"], path) else "拒否") + "(名前の指定なし。* に従う)"
    return "許可(指定なし)"


def report(text, paths=("/",)):
    groups = parse(text)
    lines = []
    for path in paths:
        lines.append(f"[{path}]")
        for agent in [SHARED, *CRAWLERS]:
            lines.append(f"  {agent}: {describe(groups, agent, path)}")
        shared_refuses = groups.get(SHARED) is not None and not allowed(groups[SHARED], path)
        if shared_refuses and "google-extended" not in groups:
            lines.append("  注意: Googleは対応後、Google-Extendedの拒否として扱う予定(Geminiアプリでの引用も対象)")
    return "\n".join(lines)


def main():
    url, *paths = sys.argv[1:]
    request = Request(url, headers={"User-Agent": "robots-ai-check/1.0"})
    with urlopen(request, timeout=15) as response:
        text = response.read().decode("utf-8", "replace")
    print(report(text, paths or ["/"]))


if __name__ == "__main__":
    main()

check_ai_training.pyとして保存し、確かめたいサイトのrobots.txtのURLを渡します。パスを続けて書くと、そのパスごとに判定します。

python3 check_ai_training.py https://example.com/robots.txt / /press-kit/

このブログのrobots.txtで実行した結果です。共通指定は「指定なし」、名前の指定がないクローラーは*のグループに従って許可、と読めます。

[/]
  __usage_ai-training: 指定なし(意思表示なし。* の規則は当てはまらない)
  Google-Extended: 許可(名前の指定なし。* に従う)
  GPTBot: 許可(名前の指定なし。* に従う)
  ClaudeBot: 許可(名前の指定なし。* に従う)
  Meta-ExternalAgent: 許可(名前の指定なし。* に従う)

上の2つの誤判定を含む11件のテストを用意し、すべて通ることを確認しました。テストで確かめたのは、この判定がドラフトとRFC 9309の書き方どおりに読めることまでです。各社のクローラーが実際にこの指定を尊重するかは、どの会社もまだ尊重を始めていないため確かめられません。

国内外18サイトで共通指定を書いていたのは0件。それでも今書くか

11月2日午前9時24分(日本時間)に、新聞社、IT媒体、投稿サービスなど18サイトのrobots.txtを取得してスクリプトで判定しました。__usage_ai-trainingを書いていたサイトは0件です。発表から3日なので当然の結果で、普及の度合いを示す数字としては使えません。

分かれていたのは、会社ごとの指定のほうです。前述の9サイトは、Google-ExtendedとGPTBotをどちらも名前で拒否していました。Yahoo! JAPANを除く8サイトは、ClaudeBotとmeta-externalagentも拒否しています。ITmedia、GIGAZINE、Impress Watch、note、Qiita、Zenn、そしてこのブログは、AIの学習用クローラーを名前で1つも指定していません。

私の考えでは、すでに学習を断ると決めているサイトは、今のうちに共通の2行を足しておいて損はありません。既存の行と同じ意味なので食い違いが起きず、Microsoftが2027年2月1日に尊重を始めた時点から効きます。NoArchiveやNoCacheでBingに断ってきたサイトは、2027年3月1日という期限があるので、年内に方針を決めておくべきです。

何も書いてこなかったサイトは、急いで2行だけを足す必要はありません。先に決めるべきなのは、Googleの学習とGeminiアプリでの引用をまとめて断るのか、まとめて許すのかです。その答えが出てから、上の書き方のどちらかを選んでください。

この判断が変わるとすれば、Googleが学習と引用を別々に指定できるようにしたとき、OpenAIが共通指定とGPTBotの優先順位を文書にしたとき、そしてドラフトがIETFの作業部会で書き換えられたときです。Googleの対応が始まる2027年初めとMicrosoftの開始日の後に、自社のrobots.txtをもう一度このスクリプトで確かめることをおすすめします。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

コメント

コメントする

目次