タグ: 生成AI

  • AIのSEO診断の指摘を、渡した資料とページの目的で確かめてWordPressの直す場所を決める

    AIのSEO診断の指摘を、渡した資料とページの目的で確かめてWordPressの直す場所を決める

    AIにWordPressサイトのSEO診断を頼むと、noindex、canonical、見出し、速度などの指摘が重要度つきで並びます。どれも直したほうがよさそうに見えますが、指摘の中には、AIが渡された資料から確かめられたことと、資料にないことを推測で言い切ったことが混ざっています。どちらなのかを見分けないまま直すと、必要のない修正をしたり、本当に直すべき設定を後回しにしたりします。

    例として用意したHTML・robots.txt・sitemapと、各ページの目的(検索に出したいかどうか)を5件分まとめてClaudeに渡し、普段どおりにSEO診断を頼みました。掲載したいページにnoindexとクロールの遮断が両方あるような、目的と食い違う設定の判断は、5件とも公式仕様と合っていました。一方で、本文だけの断片には、headとtitleがないこと、本文が薄いことを重要度「高」で断定し、同じ回答の中で、保存のときに一部しか保存されなかった可能性があるので配信中のHTMLを先に確かめるように、とも勧めていました。採ってよい助言と、確かめずに直してはいけない断定が、1つの回答に並んでいたのです。

    この記事では、AIの指摘を1つずつ「渡した資料」「ページの目的」「公式仕様」に戻し、「問題候補」「確認した範囲では正常」「判定不能」の3つに分ける手順を、この診断結果で示します。読み終えたら、どの指摘を直し、どの指摘のために情報を集め直し、WordPressのどこを見に行くかを決められます。

    完成形:指摘を3つに分けた判定表

    手順の最後には、指摘ごとに次の3つのどれかを付けた表が手元に残ります。

    分け方 当てはまる指摘 次にすること
    問題候補 渡した資料で確かめられ、ページの目的と食い違う(検索に出したいのにnoindexがある、など) 公開中のページでも同じかを確かめ、設定の出力元を探す
    確認した範囲では正常 渡した資料の範囲では、目的どおりになっている 何を確かめたか(ファイルと取得日時)を残す。資料にない項目は別に確かめる
    判定不能 資料に含まれていない、ページの目的が分からない、実測値がない 必要な資料を集めてから判断する

    「問題候補」は、すぐ直せという意味ではありません。検索に出す必要のないページなら、noindexは目的どおりです。「正常」にも、何を確かめた結果かを添えます。HTMLに望まない指定がなくても、HTTPのヘッダーから別の指定が出ている可能性は残るからです。

    AIの指摘を渡した資料とページの目的に戻し、問題候補・確認した範囲では正常・判定不能に分けて公開状態を確かめる流れ
    指摘を、渡した資料と目的に戻してから分けます。

    手順1:AIに渡した資料と、ページの目的を書き出す

    まず、診断の前提を2つ書き出します。

    • 渡した資料:完全なHTMLか本文だけの断片か、取得した日時、HTTPヘッダー・robots.txt・sitemapを渡したか。

    • ページの目的:検索結果に出したいか、出したくないか。代表にしたいURL(canonical)はどれか。

    今回は、5つのページの目的と保存ファイルを、ツールを使わずに資料だけで答えるよう指示して、Claudeに1回送りました(2026年10月12日)。依頼文は「ケースごとに問題点と修正案を、重要度(高・中・低)付きで」という、普段頼むのに近い形だけにしました。

    ケース 渡した資料 ページの目的
    A 本文だけの断片(<h1> の1行) 検索に出したい
    B noindexのある完全なHTML 会員向けで、検索に出したくない
    C H1が2つ、コメントとscriptの中にnoindexの文字があるHTML 検索に出したい
    D noindexのあるHTMLと、そのパスを遮断するrobots.txt 検索に出したい
    E WordPress標準に近いrobots.txtとsitemap index 検索に出したい

    手順2:指摘を、資料と目的と公式仕様に戻す

    次に、AIの指摘を1つずつ、渡した資料に実際に書かれていること、ページの目的、公式仕様の3つに照らします。今回の回答の主な指摘と、照らした結果は次のとおりです。

    ケース AIの主な指摘 照らした結果 分け方と次の確認
    A 断片 headとtitleがない、本文が薄い(どちらも重要度「高」)。同時に、配信中のHTMLを先に確かめるよう勧めた 渡したのは断片なので、headの欠落も本文の薄さも資料からは言えない 判定不能。断定は採らず、配信HTMLの確認を採る
    B 会員向け noindexの削除は勧めず、noindexは閲覧の制限にはならないこと、sitemapから外すことを挙げた noindexは検索結果への掲載を防ぐ指示で、閲覧は制限しない。sitemapには検索に出したいURLを入れる 確認した範囲では正常。会員限定ならログインでの制限を検討し、sitemapの生成元を確かめる
    C 見出し コメントやscriptの中のnoindexは効いていない。H1が複数でも問題ない どちらもrobots metaとして読まれる要素ではない。Googleのガイドに理想の見出し数はない 確認した範囲では正常。2つ目をh2にするかは内容で決める
    D 二重の遮断 noindexとクロールの遮断で二重に止めていて、片方だけ直しても解決しない クロールできなければnoindexは読まれない 問題候補。最優先で両方の出力元を確かめる
    E WordPress標準 robots.txtは標準どおりで問題ない。sitemap indexに対象URLが載っていない可能性 robots.txtは最も長く一致する規則が使われるので、対象パスは遮断されない。投稿か固定ページかは資料にない robotsは確認した範囲では正常。sitemapは判定不能で、子sitemapを開いて確かめる

    ケースAの回答には、次の行がありました(Claudeの回答の原文の一部)。

    <head>がなく、<title>もない。検索結果の見出しはGoogleが自動で作るため、ページの中身が伝わらない

    最初に確認すること: 保存のときに一部しか保存されなかった可能性があります。修正の前に、ブラウザの「ページのソースを表示」かSearch ConsoleのURL検査で、実際に配信されているHTMLを確かめてください。

    前者は資料の外の断定で、後者はその断定を確かめるための正しい助言です。重要度「高」の表だけを読んで直し始めると、後者を読み飛ばします。指摘を表から拾うときは、同じ回答の中の「確認すること」まで読みます。

    5件すべてに共通して、タイトルが汎用的なこと、viewportとmeta descriptionがないことも指摘されましたが、これは例として簡略化したファイルの性質です。自分のページで同じ指摘が出たら、公開中のHTMLで確かめる項目として扱います。

    照らすときに使う公式仕様は、主に次の4つです(いずれも確認日:2026年10月12日)。

    • noindexはHTMLのrobots metaか、HTTPの X-Robots-Tag で指定する。robots.txtでクロールを遮断すると、noindexは読まれない(Googleのnoindexの説明)。body内のrobots metaも認識される(robots metaの仕様)。

    • canonicalは代表にしたいURLを伝える強いシグナルだが、指定どおりに選ばれる保証はない。別のURLを意図して代表にしているページもある(正規URLの説明)。

    • robots.txtは、最も長く一致する規則が使われる(robots.txtの仕様)。WordPressが標準で出すrobots.txtには Allow: の行があり、標準のsitemapはsitemap indexになっている(WordPressのrobots.txt生成)。

    • 見出しの数に理想の値はない(Google SEOスターターガイド)。

    手順3:判定表に、WordPressで見る場所を書き足す

    問題候補と判定不能の指摘には、WordPressで最初に見る場所と、変更したあとの確かめ方を書き足します。記事の本文を書き直しても、head内のcanonicalやHTTPヘッダーは変わらないことがあります。

    症状・確かめる対象 最初に見る場所 変更後の確かめ方
    記事の見出し・説明・本文のリンク 記事エディター。ページタイトルのH1はテーマ側の場合もある 公開ページの表示とHTMLで、見出しの対応を見る
    サイト全体に意図しないnoindex 設定→表示設定の「検索エンジンでの表示」、有効なプラグインの設定 公開HTMLとヘッダーを取り直す。設定画面だけで完了にしない
    特定ページのnoindex・canonical SEOプラグインのページ別設定、テーマや追加コード HTML全体のrobots meta、head内のcanonical、ヘッダーに望む指定が1つだけ出ているか
    X-Robots-Tag やHTTPの Link のcanonical サーバー設定、CDN、プラグインなどのヘッダーの生成元 対象URLへのGETの応答で、ステータス・転送先・ヘッダーを見る
    robots.txt・sitemap WordPressやSEOプラグインの生成設定、サーバー上の実ファイル 公開中のファイルを取り直し、対象URLと規則を照らす

    サイト全体の「検索エンジンでの表示」の設定は、WordPress公式の表示設定の説明にあります(確認日:2026年10月12日)。実際の出力元はテーマとプラグインの組み合わせで変わるので、表は見に行く順番として使います。

    直す順番は、まず「検索に出したいページが、意図せずnoindexやクロールの遮断になっていないか」(今回のケースD)、次に「代表URLの指定が重複していないか」です。見出しの整理は内容を読んで決めます。変える値・対象URL・戻し方を決め、1件ずつ確かめてから反映します。

    手順4:判定不能の指摘のために、公開状態を取り直す

    判定不能に分けた指摘は、資料を足してから判断します。自分が管理するページなら、次の記録をそろえます。

    1. 対象URL、最終的なURL、取得日時、HTTPステータスと転送、GETの応答のヘッダー、完全なHTML。

    2. 同じサイトの公開中のrobots.txtと、対象URLが属するsitemap。

    3. Search ConsoleのURL検査の結果。Googleが持つ登録情報と、今のページへのライブテストを分けて記録し、Googleが選んだ正規URLも見る(URL検査の公式説明)。

    4. 変更したあとに、同じURL・同じ条件で取り直した記録。

    速度の指標(LCP・INP・CLS)を指摘されたら、測った元、期間、端末、ラボの測定か実際の利用者のデータかを確かめます。保存したHTMLから値は出せません(GoogleのWeb Vitalsの説明)。

    AIに、指摘の根拠を出し直してもらう

    診断が長く、自分で照らすのが大変なときは、修正案を増やす前に、AIに指摘を判定表の形で出し直してもらいます。貼る情報に、認証情報や個人情報を含めないでください。

    対象URLとページの目的:[URL/検索に出したいか/希望する代表URL]
    渡す資料:[完全なHTMLか本文断片か、取得日時、ヘッダー・robots・sitemapの有無]
    元の診断:[ここに貼る]
    各指摘を「観測した値」「根拠となる箇所」「公式資料」「問題候補/確認範囲内で正常/判定不能」「次の確認」に分けてください。
    ページの目的と食い違う指摘を優先してください。
    取得していないヘッダー、実インデックス、順位、速度の値は判定不能にしてください。
    修正箇所が本文・テーマ・プラグイン・サーバーのどこか、まだ特定できない場合も書いてください。

    返ってきた表も、該当するHTMLや公式資料と照らします。根拠の箇所が示されない指摘は、直す判断を保留します。

    付録:保存したファイルを3つに分ける小さなPythonコード

    AIの指摘と照らすための比較用に、保存したHTML・robots.txt・sitemapから、確かめられる範囲だけを判定するコードを用意しました。Python 3.10以降の標準ライブラリだけで動き、通信はしません。長いコードは折りたたんでいます。3つのファイルを空のフォルダにUTF-8で保存し、python3 run_checks.py を実行すると、18の例で判定を確かめられます。

    入力例 このコードの判定 読み方
    noindex:掲載したいページにnoindex metaが問題候補 公開HTMLと設定を確かめる
    body-noindex:body内にnoindex metaが問題候補 headだけでなくHTML全体を見る
    intended:掲載を望まないページにnoindex metaは範囲内で正常 noindexの削除は不要
    conflict:異なるcanonicalが2つ canonicalが問題候補 重複して出力している元を探す
    fragment:本文だけ metaとcanonicalは判定不能 「titleがない」とは言えない
    two-h1:H1が2つ、コメントにnoindexの文字 metaは範囲内で正常、H1数は2として記録 H1の数は判定に使わない
    wp-default:WordPress標準のrobots.txtとsitemap index robotsとsitemapは判定不能 規則と子sitemapを手で確かめる

    このコードのrobots.txtの判定は、単一の User-agent: * と単純な Disallow だけを扱い、Allow: や Sitemap: の行があれば判定不能にします。WordPressの実際のrobots.txtはこの形に当たるので、手順2のケースEのように、Googleの仕様に沿って手で確かめます。http(ヘッダー)、index(登録状態)、cwv(速度)は、入力に含まれないので常に判定不能です。コードの出力では、「確認した範囲では正常」を「範囲内で正常」と表示します。

    fixtures.py:18の例を作る

    want_index は検索に出したいなら true、出したくないなら false、分からなければ null です。preferred は運営者が代表にしたいURLで、Googleが選ぶURLとは別です。URLはすべて example.com です。

    コードを開く
    import json
    from pathlib import Path
    
    URL = 'https://example.com/guide/'
    OTHER = 'https://example.com/original/'
    CANON = f'<link rel="canonical" href="{URL}">'
    XML = f'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"><url><loc>{URL}</loc></url></urlset>'
    
    def fixture(name, meta='', canonical=CANON, body='<h1>手順</h1>', want=True,
                preferred=URL, robots='User-agent: *\nDisallow:\n', sitemap=XML, fragment=False, base=''):
        d = Path('fixtures') / name
        d.mkdir(parents=True, exist_ok=True)
        html = body if fragment else f'<!doctype html><html lang="ja"><head><meta charset="utf-8"><title>手順</title>{base}{meta}{canonical}</head><body>{body}</body></html>'
        for file, text in [('page.html', html), ('robots.txt', robots), ('sitemap.xml', sitemap),
                           ('intent.json', json.dumps({'url': URL, 'want_index': want, 'preferred': preferred}, ensure_ascii=False, indent=2))]:
            (d / file).write_text(text, encoding='utf-8')
    
    fixture('normal')
    fixture('noindex', meta='<meta name="robots" content="noindex, follow">')
    fixture('fragment', body='<h1>本文だけ</h1>', fragment=True)
    fixture('conflict', canonical=CANON + f'<link rel="canonical" href="{OTHER}">')
    fixture('intended', meta='<meta name="robots" content="noindex">', want=False)
    fixture('unknown-intent', meta='<meta name="robots" content="noindex">', want=None)
    fixture('googlebot-none', meta='<meta name="googlebot" content="NONE">')
    fixture('cross-canonical', canonical=f'<link rel="canonical" href="{OTHER}">', preferred=OTHER,
            sitemap=XML.replace(URL, OTHER))
    fixture('missing-canonical', canonical='')
    fixture('blocked-noindex', meta='<meta name="robots" content="noindex">', robots='User-agent: *\nDisallow: /guide/\n')
    fixture('complex-robots', robots='User-agent: *\nDisallow: /\nAllow: /guide/\n')
    fixture('broken-sitemap', sitemap='<urlset>')
    fixture('two-h1', body='<h1>手順</h1><h1>別の区分</h1>',
            meta="""<!-- <meta name="robots" content="noindex"> --><script>const s = '<meta name="robots" content="noindex">';</script>""")
    fixture('sitemap-index', sitemap='<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"></sitemapindex>')
    fixture('base-relative', canonical='<link rel="canonical" href="guide/">', base='<base href="https://example.com/">')
    fixture('relative', canonical='<link rel="canonical" href="/guide/">')
    fixture('body-noindex', body='<meta name="robots" content="noindex"><h1>手順</h1>')
    # WordPress標準に近いrobots.txtとコアのsitemap index(この最小コードでは判定不能になる例)
    WP_ROBOTS = 'User-agent: *\nDisallow: /wp-admin/\nAllow: /wp-admin/admin-ajax.php\n\nSitemap: https://example.com/wp-sitemap.xml\n'
    WP_INDEX = '<?xml version="1.0" encoding="UTF-8"?><sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"><sitemap><loc>https://example.com/wp-sitemap-posts-post-1.xml</loc></sitemap></sitemapindex>'
    fixture('wp-default', robots=WP_ROBOTS, sitemap=WP_INDEX)

    audit.py:確かめられる範囲だけ判定する

    コードを開く
    import json
    import re
    import sys
    from html.parser import HTMLParser
    from pathlib import Path
    from urllib.parse import urljoin, urlsplit
    import xml.etree.ElementTree as ET
    
    P, N, U = '問題候補', '範囲内で正常', '判定不能'
    
    class Page(HTMLParser):
        def __init__(self):
            super().__init__()
            self.head = False
            self.events, self.robots, self.canon = [], [], []
            self.base, self.h1 = False, 0
    
        def handle_starttag(self, tag, attrs):
            a = dict(attrs)
            if tag in ('html', 'head', 'body'):
                self.events.append(tag)
            if tag == 'head':
                self.head = True
            if tag == 'h1':
                self.h1 += 1
            if tag == 'meta' and (a.get('name') or '').lower() in ('robots', 'googlebot'):
                self.robots += re.split(r'[\s,]+', (a.get('content') or '').lower())
            if not self.head:
                return
            if tag == 'base':
                self.base = True
            if tag == 'link' and 'canonical' in (a.get('rel') or '').lower().split():
                self.canon.append(a.get('href') or '')
    
        def handle_endtag(self, tag):
            if tag in ('html', 'head', 'body'):
                self.events.append('/' + tag)
            if tag == 'head':
                self.head = False
    
    
    def audit(folder):
        d = Path(folder)
        cfg = json.loads((d / 'intent.json').read_text(encoding='utf-8'))
        url, want, preferred = cfg['url'], cfg['want_index'], cfg['preferred']
        h = Page()
        h.feed((d / 'page.html').read_text(encoding='utf-8'))
        full = h.events == ['html', 'head', '/head', 'body', '/body', '/html']
        out = {k: U for k in ('meta', 'canonical', 'robots', 'sitemap', 'http', 'index', 'cwv')}
        noindex = bool({'noindex', 'none'} & set(h.robots))
        if full:
            out['meta'] = U if want is None else (P if noindex and want else N)
            targets = {urljoin(url, x) for x in h.canon if x}
            if not h.base and h.canon:
                if any(not x for x in h.canon) or len(targets) > 1:
                    out['canonical'] = P
                elif preferred and all(urlsplit(x).scheme in ('http', 'https') for x in targets):
                    out['canonical'] = N if targets == {preferred} else P
        # 教材専用: 単一の User-agent: * と単純な Disallow 接頭辞だけ扱う。
        lines = [(x.split('#', 1)[0]).strip() for x in (d / 'robots.txt').read_text(encoding='utf-8').splitlines()]
        lines = [x for x in lines if x]
        rules, supported = [], bool(lines) and lines[0].lower() == 'user-agent: *'
        for line in lines[1:]:
            key, sep, value = line.partition(':')
            value = value.strip()
            if not sep or key.lower() != 'disallow' or (value and not value.startswith('/')) or any(c in value for c in '*$%?'):
                supported = False
            rules.append(value)
        if supported:
            blocked = any(x and urlsplit(url).path.startswith(x) for x in rules)
            out['robots'] = (U if want is None else P if want else N) if blocked else N
        try:
            root = ET.fromstring((d / 'sitemap.xml').read_text(encoding='utf-8'))
            ns = '{http://www.sitemaps.org/schemas/sitemap/0.9}'
            if root.tag == ns + 'urlset':
                locs = [x.text for x in root.findall(ns + 'url/' + ns + 'loc')]
                out['sitemap'] = N if preferred and preferred in locs else U
        except ET.ParseError:
            out['sitemap'] = P
        return {'checks': out, 'observed': {'complete_markers': full, 'html_noindex': noindex if full else None,
                'canonical_hrefs': h.canon if full else None, 'h1_count': h.h1},
                'scope': '確認したのは保存したHTML・robots.txt・sitemap.xml。正常は各項目の確認範囲に限る。HTTP・登録状態・CWVは別に取得して確かめる'}
    
    if __name__ == '__main__':
        print(json.dumps(audit(sys.argv[1]), ensure_ascii=False, indent=2))

    run_checks.py:例ごとの判定を照らす

    コードを開く
    import json
    import runpy
    from pathlib import Path
    from audit import audit, P, N, U
    
    runpy.run_path('fixtures.py')
    # 期待値:例ごとに、記事の3分類(問題候補/範囲内で正常/判定不能)で決めた値。
    expected = {
        'normal': [N,N,N,N], 'noindex': [P,N,N,N], 'fragment': [U,U,N,N],
        'conflict': [N,P,N,N], 'intended': [N,N,N,N], 'unknown-intent': [U,N,N,N],
        'googlebot-none': [P,N,N,N], 'cross-canonical': [N,N,N,N],
        'missing-canonical': [N,U,N,N], 'blocked-noindex': [P,N,P,N],
        'complex-robots': [N,N,U,N], 'broken-sitemap': [N,N,N,P],
        'two-h1': [N,N,N,N], 'sitemap-index': [N,N,N,U], 'base-relative': [N,U,N,N],
        'relative': [N,N,N,N], 'body-noindex': [P,N,N,N], 'wp-default': [N,N,U,U],
    }
    results = {}
    for name, values in expected.items():
        result = audit(Path('fixtures') / name)
        assert [result['checks'][k] for k in ('meta','canonical','robots','sitemap')] == values, name
        assert all(result['checks'][k] == U for k in ('http','index','cwv')), name
        results[name] = result
    assert results['two-h1']['observed']['h1_count'] == 2
    assert results['fragment']['observed']['html_noindex'] is None
    assert results['two-h1']['observed']['html_noindex'] is False
    Path('results.json').write_text(json.dumps(results, ensure_ascii=False, indent=2) + '\n', encoding='utf-8')
    Path('checks.json').write_text(json.dumps({'fixture_cases':len(expected),'status_assertions':len(expected)*7,
        'observation_assertions':3,'passed':True,'expected':expected}, ensure_ascii=False, indent=2)+'\n', encoding='utf-8')
    print(f'CHECK_OK: {len(expected)} cases, {len(expected)*7} statuses, 3 observations')

    python3 run_checks.py の出力は次のとおりで、全例の結果は results.json に保存されます。

    CHECK_OK: 18 cases, 126 statuses, 3 observations

    1つの例を見るときは python3 audit.py fixtures/noindex を実行します。

    {
      "checks": {
        "meta": "問題候補",
        "canonical": "範囲内で正常",
        "robots": "範囲内で正常",
        "sitemap": "範囲内で正常",
        "http": "判定不能",
        "index": "判定不能",
        "cwv": "判定不能"
      },
      "observed": {
        "complete_markers": true,
        "html_noindex": true,
        "canonical_hrefs": [
          "https://example.com/guide/"
        ],
        "h1_count": 1
      },
      "scope": "確認したのは保存したHTML・robots.txt・sitemap.xml。正常は各項目の確認範囲に限る。HTTP・登録状態・CWVは別に取得して確かめる"
    }

    meta が問題候補でも、http や index は判定不能のままです。この例で採る行動は「掲載したいページのnoindexを、公開HTMLと設定で確かめる」で、AIの指摘を一括で直す前に、同じように観測した値と目的を照らします。

  • AIで既存記事に1節足す前に、残す情報を決めて改稿後に照合する

    AIで既存記事に1節足す前に、残す情報を決めて改稿後に照合する

    公開済みの記事をAIに直させると、説明は増えても、元の注意事項や日付、リンク、コードが消えていることがあります。消えた情報は、改稿後の文章だけを読んでも気づけません。反対に、元の情報がすべて残っていても、足された文の中に、根拠のない一文や読者に不要な一文が混ざることがあります。

    WordPressのバックアップ記事に「復元候補を選ぶ」節を1つ足す依頼を、禁止事項(具体的な画面操作、固定の保存間隔、成功の保証などを加えない)を書いて、AIに2回出しました。2回とも、元の注意文・表・画像参照・リンク・コードはすべて残り、保持の検査に合格しました。ところが追加された文には、2回とも、禁止事項をそのまま読者向けの断り書きにした文がありました(「…推奨値ではありません」「…目安となる時間差を定めません」など)。どの禁止事項が断り書きになったかは回ごとに違い、2回目は、依頼に添えた出典のメモまで記事の本文として出力されました。

    この記事では、改稿の前に読者の問いと「変えずに残す情報」を決める改善ブリーフ、改稿後に照合する保持対象表とPythonの検査、追加された文を読み分ける手順を、この2回の実例で示します。読み終えたら、AIの改稿案を採用するか、直してから採るか、保留するかを決められます。

    使う場面と、手元に残るもの

    この手順は、すでに公開している記事の一部だけをAIに書き足させたいときに使います。手元に残るのは次の3つです。

    成果物 中身 使うとき
    改善ブリーフ 読者の問い、記事の役割、残す情報、足す情報、根拠がないときの扱い、扱わないこと AIに依頼する前
    保持対象表 残す情報ごとの照合方法と、照合だけでは分からないこと 改稿案を受け取った直後
    追加文の読み分け 足された文を「採る/削る/出典に戻す」に分ける基準 保持の照合に合格したあと
    改稿前に保持対象を決め、限定改稿のあとに文字列の照合と追加文の読み分けを行う流れ
    元の情報が残っていても、足された文は別に読みます。

    手順1:読者の問いを1文で決める

    最初に、この記事に来た読者が片づけたいことを1文にします。検索語と今の本文から考えます。今回の例は「WordPressのバックアップ設定と保存先」という記事で、Search Consoleでは「WordPress バックアップ 復元」でも表示され、その語の掲載順位が下がっていました(直す記事を選ぶ手順の記事の練習データ)。保存の仕方の記事に、戻し方を探す人も来ていると読めます。

    そこで問いを「保存済みのバックアップから、どれを復元候補にすればよいか」と決めました。問いが今の記事の役割(保存対象と保存先の確認)の続きなら、既存の記事に足します。プラグインの比較のように目的が違う問いなら、別の記事にします。特定のサービスの画面操作のように根拠をまだ確かめていない問いは、保留にします。

    手順2:改善ブリーフに、残す情報と扱わないことを書く

    問いが決まったら、改善ブリーフを書きます。今回のブリーフは次のとおりです。

    項目 今回のブリーフ
    読者の問い 保存分はあるが、どれを復元候補にすればよいか分からない
    記事の役割 保存対象・保存先を確認し、復元の検討へ進む
    残す情報 元の本文と注意事項、表の日時と対象、画像参照とalt、公式リンク先、コード全文
    足す情報 対象サイト・保存日時・ファイルとデータベースの組合せを確かめ、分からなければ保留する説明
    根拠がないとき 未確認の項目と、次に見る公式資料を書く。ボタン名や成功の保証で埋めない
    扱わないこと 復元方式ごとの手順、プラグインの比較、サービス別の画面操作、検索効果の予測

    足す情報の根拠には、WordPress公式の説明を使いました。通常の復元にはデータベースとファイルの両方が必要で、同じ頃の保存分を一組として扱うこと、データベースを復元すると現在の内容が上書きされることです(バックアップの公式説明、データベースのバックアップ・復元、確認日:2026年10月11日)。

    手順3:依頼文に、ブリーフと元の本文を添えて送る

    ブリーフをもとに、次の依頼文を作りました。この後に、改稿前の本文(before.md)の全文と、確認済みの出典2件の要約を続けて送ります。

    読者は、保存済みのWordPressバックアップから復元候補を選びたい人です。
    既存記事の目的は、保存対象と保存先の確認です。
    
    元の本文を書き換えず、「復元候補を選ぶ」節だけを後ろに追加してください。
    元の注意文、表の日時と対象、画像のaltと参照、リンク先、コード全文を残します。
    
    根拠は添付したWordPress公式資料です。
    対象サイト・保存日時・ファイルとDBの組合せを確認する説明を加えます。
    不明なら保留し、記録や利用サービスの公式資料を確認するよう書いてください。
    
    具体的な画面操作、固定の保存間隔、成功保証、未測定のSEO効果は加えません。
    記録例の日時を推奨値として扱わないでください。
    出力は元の本文全体と追加節です。

    この依頼文を、Claudeの新しいチャットに1回送りました(2026年10月20日)。その前に、この記事を作る作業の中で、同じ禁止事項を箇条書きにした指示書でAIに1回書き足させています(2026年10月11日)。以下の「1回目」「2回目」はこの2回です。

    改稿前の本文は次のとおりです。照合を再現するときは、UTF-8の before.md として保存します。

    # WordPressのバックアップ設定と保存先
    
    WordPressのバックアップでは、データベースとファイルを両方保存します。保存先はサーバー内だけにせず、手元や別の保管先にもコピーを置きます。
    
    ## 保存する対象
    
    記事や設定の多くはデータベースに、アップロード画像やテーマ等はファイルに入ります。片方だけでは同じ状態に戻せない場合があります。
    
    | 記録例の保存日時 | 対象 | 確認するもの |
    | --- | --- | --- |
    | 2026-10-10 09:00 JST | データベース | SQLファイル |
    | 2026-10-10 09:00 JST | ファイル | 画像・テーマ等 |
    
    ![データベースとファイルを一組として確認する図](backup-set.webp)
    
    保存対象は[WordPress公式のバックアップ説明](https://developer.wordpress.org/advanced-administration/security/backup/)も確認してください。
    
    ~~~python
    backup_set = ["database.sql", "files.zip"]
    print(len(backup_set))  # 記録例の対象は2種類
    ~~~
    
    ## 実行前の確認
    
    保存日時、対象サイト、ファイルの有無を記録します。復元すると現在の内容が上書きされる可能性があるため、作業直前の状態も別に保存してください。公開サイトでいきなり試さず、検証環境で確認します。
    
    ## 復元について
    
    復元の手順は利用するホスティングやプラグインで異なります。この記事にはサービス別の操作手順はまだありません。

    手順4:保持対象表とPythonで、元の情報が残ったかを照合する

    改稿案を受け取ったら、まず元の情報が残っているかを照合します。保持対象ごとに、照合のしかたと、照合だけでは分からないことを表にしておきます。

    保持対象 照合のしかた それだけでは分からないこと
    3つの注意文 否定や条件を含め、文全体が一致するか 足された文が注意文と矛盾していないか
    表と記録の日時 見出し・区切り・2行の文字列が一致するか 日時が復元に適した保存分か
    画像 altと参照の行が一致するか 画像ファイルの欠落・差し替え
    出典リンク リンク先のURLが完全に残っているか 今の出典がその主張を支えているか
    コード コードブロック全体が一致するか 新しい環境で動くか

    文字列の照合は、次の check.py で行えます。Python 3の標準ライブラリだけで動き、before.md と改稿後の after.md を同じフォルダに置いて python3 check.py before.md after.md で実行します。

    """Python 3標準ライブラリ。保護した箇所の完全一致を確認する小例。"""
    from pathlib import Path
    import re
    import sys
    
    
    def protected(text):
        warnings = (
            "保存日時、対象サイト、ファイルの有無を記録します。",
            "復元すると現在の内容が上書きされる可能性があるため、作業直前の状態も別に保存してください。",
            "公開サイトでいきなり試さず、検証環境で確認します。",
        )
        if not all(s in text for s in warnings):
            raise ValueError("比較元の注意文が不足しています")
        return {
            "注意文": list(warnings),
            "表": re.findall(r"^\|.*\|$", text, re.M),
            "画像参照": re.findall(r"^!\[.*\]\(.*\)$", text, re.M),
            "リンク先": re.findall(r"(?<!!)\[[^\]]+\]\(([^)]+)\)", text),
            "コード": re.findall(r"~~~[^\n]*\n.*?\n~~~", text, re.S),
        }
    
    
    def check(before, after):
        failed = []
        actual = {
            "注意文": after,
            "表": after.splitlines(),
            "画像参照": after.splitlines(),
            "リンク先": re.findall(r"(?<!!)\[[^\]]+\]\(([^)]+)\)", after),
            "コード": re.findall(r"~~~[^\n]*\n.*?\n~~~", after, re.S),
        }
        for kind, pieces in protected(before).items():
            if not pieces:
                raise ValueError(f"比較元に{kind}がありません")
            if any(piece not in actual[kind] for piece in pieces):
                failed.append(kind)
        return failed
    
    
    if __name__ == "__main__":
        if len(sys.argv) != 3:
            raise SystemExit("使い方: python3 check.py before.md after.md")
        before = Path(sys.argv[1]).read_text(encoding="utf-8")
        after = Path(sys.argv[2]).read_text(encoding="utf-8")
        try:
            failed = check(before, after)
        except ValueError as error:
            raise SystemExit(str(error))
        print("FAIL: " + "、".join(failed) if failed else "PASS: 保持対象の文字列が一致")
        raise SystemExit(1 if failed else 0)

    2回の改稿案は、どちらも次の結果でした(終了コード0)。

    PASS: 保持対象の文字列が一致

    表の日時を1つ変えると FAIL: 表、注意文の「上書きされる可能性」を否定に変えると FAIL: 注意文 になります。一方で、注意文を残したまま「検証環境なら現在の内容は上書きされません。」のような根拠のない一文を足しても、この検査は合格します。この検査が確かめるのは「残すと決めた情報が残っているか」だけで、足された文が正しいかは、次の手順で読みます。

    コードを使わない場合は、WordPressのリビジョン画面で改稿前後を見比べられます。投稿の設定サイドバーで「リビジョン」の横の数字を押すと一覧が開き、WordPress 7.0以降では、追加したブロックが緑、削除したブロックが赤、変更したブロックが黄色で示されます(WordPress公式:リビジョン、確認日:2026年10月12日)。公開済みの記事は「更新」を押した時点で反映されるので、改稿は検証環境で保存してから見比べます。

    手順5:足された文を、採る・削る・出典に戻すに分ける

    保持の照合に合格したら、足された文を1文ずつ読みます。Googleも、生成AIで作った内容に正確さ・品質・関連性を求めています(Googleの生成AIコンテンツの指針、確認日:2026年10月11日)。2回の改稿案で、読み分けが必要だった文は次のとおりです(AIの出力の原文の一部)。

    足された文 何が起きたか 分け方
    1回目「…選ぶべき日時や保存間隔の推奨値ではありません。」(文の後半) 禁止事項(日時を推奨値として扱わない)を、読者向けの断り書きにした 削る:表の見出しが「記録例の保存日時」なので要らない
    1回目「候補がそろっても復元の成功が保証されるわけではありません。」 禁止事項(成功を保証しない)を断り書きにした 削る:次の段落の「検証環境で確かめる」で足りる
    1回目「このページでは、未確認の管理画面の操作や復元の実行順序は指定しません。」 禁止事項(画面操作を加えない)を断り書きにした 削る:読者の判断に使えない
    2回目「そのため、この記事では目安となる時間差を定めません。」 禁止事項(固定の保存間隔を加えない)を断り書きにした 削る
    2回目「前掲の表にある2026-10-10 09:00 JSTは、あくまで記録例として置いた日時です。」「選ぶときの基準や推奨値ではありません。」 禁止事項(日時を推奨値として扱わない)を断り書きにした 削る
    2回目「バックアップのファイル名や保存先の表示が、いま復元しようとしているサイトのものかを確認します。」など 添えた出典にない具体的な確かめ方が足された 出典に戻す:使うサービスの公式資料で確かめてから残す
    2回目「## 確認済みの出典(2026-10-11確認)」の見出しと2行 依頼に添えた出典のメモが、元の本文と追加節のあいだに本文として出力された 削る:依頼の外の混入。保持の検査では見つからない

    禁止事項が断り書きの文になった箇所は、1回目に3文、2回目に3文(2か所)ありました。2回に共通していたのは「記録例の日時を推奨値として扱わない」だけで、ほかは回ごとに違う禁止事項が文になっています。依頼に「〜は加えない」と書くと、AIはそれを守ったうえで、守ったことを読者に向けて書くことがある、と考えておくと読み落としにくくなります。

    分け方の基準は次の3つです。

    • 採る:読者が手順や判断に使え、添えた出典で説明できる文。

    • 削る:依頼の禁止事項を読者に向けて言い直しただけの文、依頼の外から入った文。

    • 出典に戻す:出典に書かれていない具体例や確かめ方。根拠を確かめてから残す。

    1回目の追加節から、削る3文を除くと次のようになります。1回目の出力を読み分けの結果に合わせて直した例です。

    ## 復元候補を選ぶ
    
    まず、不具合が起きる前の保存分を候補にします。その保存分が対象サイトのもので、ファイルとデータベースが同じ頃の一組になっているかを確認してください。
    
    対象サイトや保存日時が分からない場合、または片方が欠けている場合は、復元を始めず、保存記録や利用サービスの公式資料を確認します。
    
    復元すると、保存後に追加・変更した内容が戻らなくなる可能性があります。作業直前の状態を別に保存し、検証環境で必要な記事や画像を確認したうえで、利用するホスティングやプラグインの公式手順に進んでください。

    採用するか、保留するかを決める

    照合と読み分けの結果から、次のように決めます。

    • 採用する:保持の照合に合格し、足された文がすべて「採る」だった。

    • 直してから採用する:保持の照合に合格し、「削る」文を除けば残りがすべて「採る」になる。今回の1回目はこれに当たり、上の直した例が採用案です。

    • 保留する:保持の照合に不合格だった、または「出典に戻す」文の根拠がまだ確かめられない。今回の2回目は、出典にない確かめ方が複数あるので、この基準では保留になります。

    採用したら、変更した日と範囲を記録しておきます。検索での評価は、同じ条件の期間のデータで別に見ます。

    保持対象表は、AIに頼むときだけでなく、人が書き直すときにも使えます。この記事自体を書き直したときも、次の表で前後を照らしました。意図して外したものには理由の欄を付けておくと、後で「なぜ消えたか」を追えます。

    保持対象 書き直し後 外した理由
    check.py と before.md の全文 残した(文字列が一致) —
    WordPress公式3件へのリンクと確認日 残した —
    Googleの生成AIコンテンツの指針へのリンク 残した —
    Googleの有用なコンテンツ・画像の説明へのリンク 外した 書き直し後の手順で使う箇所がなくなった
    検査の10ケースの結果表 外した(実験の記録に残した) 読者の判断には、合格・不合格の例2つで足りる