【誤検知が71%】Claude Codeのフックを自作したら失敗しました ― 122本で検証して7%に直すまで

ブログ運営

AIに「こういう仕組みを作って」と頼むと、たいてい、すぐ作ってくれます。動かしてみると、ちゃんと動きます。「できました」と言われます。

わたしはそれで、だいたい満足していました。動いているんやから、いいやん、と。

先日、記事のチェック機能を作ってもらったときも同じでした。動作テストも見せてもらって、問題なさそうでした。ところが念のため、手持ちの記事122本にまとめてかけてみたら、87本が警告になりました。7割です。

中身を読んでみたら、もっと悪かった。弾かれていたのは、わたしがいちばん気をつけて書いた、正しい文章のほうでした。

動いていたけれど、使える状態ではなかった。この違いに気づけたのは「念のため全部にかけてみた」からで、それをしなければ、たぶん今も気づいていません。同じように悩んでいる方の参考になればと思い、まとめておきます。

(AIに詳しいわけではありません。ブログを書くのにClaude Codeという道具を使っている、ただの素人の記録です。そもそもClaude Codeって何?という方は、パソコンが苦手なわたしが「Claude Code」でブログを書いていますを先にどうぞ)

この記事でわかること

  • AIが作ったものが「動くのに使えない」とき、何が起きているのか
  • 誤検知が出たとき、消す前にやるべきこと
  • AIに何かを作らせるときに、わたしが決めた3つのルール

まず結論から ― 「できました」と言われたら、手持ちのデータ全部にかけて数えるのが最短でした

先に結論です。動作テストが通ったかどうかより、手持ちのデータ全部にかけて「何件ひっかかるか」を数えるほうが、はるかに役に立ちました。

動作テストだけ122本にかけて数えた
わかること壊れていないこと実際に使えるかどうか
このときの結果問題なし87本が警告(71%)
かかった時間数十秒1分ほど

数えるのに1分もかかりませんでした。それで「このままでは使えない」が分かったので、費用対効果としては、この1分がいちばん大きかったです。


何を作ってもらったのか ― 記事の禁止表現を自動で警告する仕組み

わたしのブログには、自分で決めた書き方のルールがあります。そのひとつが、読者に成果を保証するような書き方をしないというものです。

「必ず儲かります」「誰でも審査に通ります」「絶対に損しません」。こういう書き方は、読者に誤解を与えますし、検索エンジンや広告の規約の面でも危ないところです。

とはいえ、書いているうちにうっかり混ざります。人間の注意力に頼るのはしんどいので、記事を書いたら自動で警告してくれる仕組みをClaude Codeに作ってもらうことにしました。こういう自動の仕掛けの話は、【最低限は4つ】Claude Codeの設定は何を書けばいい?にもまとめています。

考え方は単純です。禁止したい言葉のリストを渡して、それが記事にあったら教えてもらう。渡したのは、この5つでした。

  • 必ず
  • 絶対
  • 100%
  • 誰でも
  • 損しません

作ってもらったら、すぐできました。試しに「誰でも審査に通ります」という文で動かすと、ちゃんと警告が出ます。ここで終わっていたら、この記事はありません。


122本にかけたら87本が警告 ― 動いてはいたが、使える状態ではありませんでした

念のため、手持ちの記事全部にかけてみました。結果がこれです。

項目
調べた記事122本
警告が出た記事87本
割合71%

言葉ごとの内訳も出しました。

言葉見つかった数
必ず39件
絶対12件
確実に7件
100%6件
誰でも1件

7割の記事で警告が出る仕組みは、使えません。 数日で見なくなります。そして見なくなった頃に、本物の問題が混ざっていても気づけません。

警告というのは、出しすぎると無いのと同じになります。 むしろ「チェックしているつもり」になるぶん、無いより悪いかもしれません。


中身を読んだら、正しい文章ばかり弾いていた ― 誤検知の実例3つ

ここでいったん立ち止まって、実際にどの文が弾かれているのかを読んでみました。 これが分かれ道でした。

① 断定を「否定している」文

他社比較はしておらず、絶対的に一番とは言い切れない点は正直に書いておきます

体調によって最適な選び方は変わるはずなので、絶対的な正解ではないと理解しています

「絶対」という言葉は入っています。でも、言っている内容は正反対です。「絶対とは言えない」と書いている、いちばん誠実な部分でした。

② デメリットを正直に書いた文

ここは乗り降りのたびに靴が当たるので、確実に傷が入ります

食べかすがケージの外に飛びます。掃除機の回数は確実に増えます

これも弾かれました。でもこれは、わたしがいちばん大事にしている「いい面だけ書かない」を実行している部分です。ここを消したら、記事の価値がなくなります。

③ 読者への注意喚起

始める前に必ず公式のホームページで最新の情報をご確認ください

移したあと、写真の向きと写り込みを必ず確認する

これに至っては、読者を守るために書いている文です。これを「危ない表現」として警告するのは、完全に逆でした。

つまり弾かれていたのは、手を抜いた文章ではなく、気をつけて書いた文章のほうだったのです。


原因は「言葉」で探していたこと ― 探し方を変えたら87本が9本になりました

実例を並べて、やっと原因が分かりました。

禁止されているのは言葉そのものではなく、「成果の保証」でした。

書き方判定なぜ
必ず儲かりますだめ成果を保証している
誰でも審査に通りますだめ結果を保証している
必ず確認してください問題なし読者への注意喚起
絶対的な正解ではない問題なしむしろ断定を避けている
同じ「必ず」でも意味は正反対であることを示した図
断定の言葉と「得をする結果」が並んだときだけ拾います

同じ「必ず」でも、そのあとに何が続くかで、意味が正反対になります。 言葉だけを見ていたら、この違いは見分けられません。

そこで、探し方をこう変えてもらいました。

「必ず」「絶対」といった言葉が、「儲かる」「通る」「治る」「痩せる」のような“得をする結果”とセットで出てきたときだけ拾う。

直した結果がこれです。

直す前直したあと
警告が出た記事87本9本
割合71%7%
記事122本にかけた結果の棒グラフ。直す前は87本(71%)が警告、直したあとは9本(7%)
探し方を変えただけで、警告は87本から9本になりました

そして大事なのは、わざと悪い文を書いたときは、ちゃんと今も止まることです。「誰でも審査に通るので、必ず得する方法です」という文を試したら、きちんと警告が出ました。ゆるくしたのではなく、狙いを絞れたということです。

さらに、残った9本の中身も確認しました。

  • 7本は「金額が書いてあるのに、いつ時点の情報かが書かれていない」という指摘 → 本物。直す価値あり
  • 2本は、禁止語のリストそのものを記事に引用している箇所(同じ1記事のmd版とHTML版) → 誤検知だが1記事だけ

9本のうち7本が本物の指摘なら、これは見る気になります。71%のときとは、まるで違います。


この失敗から決めた3つのこと ― AIに何か作らせるときの自分ルール

今回のことで、自分の中で決めたことが3つあります。

① 「できました」で終わらせず、手持ち全部にかけて数を出す

動作テストは「壊れていないか」しか教えてくれません。「使えるか」を教えてくれるのは、実データでの件数だけでした。

そして、これはお願いすれば1分でやってくれます。「手持ちのファイル全部にかけて、何件ひっかかるか数えて」と言うだけです。

② 誤検知が出たら、消す前に中身を読む

わたしは最初、「じゃあ『必ず』はリストから外そか」と言いかけました。それをせずに中身を読んだから、本当の原因にたどり着けました。

もし外していたら、「必ず儲かります」という本物の危ない表現まで、素通りする仕組みになっていたはずです。症状を消すのと、原因を直すのは別でした。

③ 正しいものを弾く仕組みは、いずれ無視されて死ぬ

これがいちばんの学びかもしれません。

チェックの仕組みは、厳しくすればするほど良い、というものではありません。 正しいものまで弾く仕組みは、人間に無視されるようになり、最後には誰も見なくなります。そうなったら、無いのと同じです。

「ちゃんと見てもらえる量に収める」ところまでが、仕組み作りの一部なんやなと思いました。


おまけ:この記事を書いたら、自分のチェックに19件ひっかかりました

最後にひとつ、笑い話を。

この記事を保存した瞬間、自分で直したはずのチェック機能が、19件の警告を出しました。

理由は単純で、この記事には「必ず儲かります」「誰でも審査に通ります」という例文を、説明のために何度も書いているからです。仕組みから見れば、危ない表現がぎっしり詰まった記事に見えます。

これは直しませんでした。「禁止された書き方を解説する記事」が警告されるのは、動作としては正しいからです。ここを例外にすると、今度は本物の違反を見逃す穴になります。

警告は出るけれど、止まりはしない。わたしが一目見て「これは例文やな」と流せば済む話です。ひとつ前の章の③に書いた「見てもらえる量に収める」というのは、こういうことでした。ゼロにすることではありませんでした。


さいごに:「動きました」と「使えます」は別物でした

まとめます。

  • AIが作ったものは、動作テストが通っても「使える」とは限りませんでした
  • 手持ちのデータ全部にかけて件数を数えると、1分で実力が分かりました
  • 誤検知が出たら、消す前に中身を読むと本当の原因が見つかりました
  • 今回は、弾かれていたのがいちばん気をつけて書いた文章のほうでした
  • チェックの仕組みは、見てもらえる量に収めるところまでが作業だと考えるようになりました

AIは、頼めば本当にすぐ作ってくれます。そこは素直にすごいです。ただ、作ったものが自分の現場に合っているかどうかは、自分のデータで確かめるしかないというのが、今回いちばん納得したところでした。

難しい知識は要りませんでした。やったのは「全部にかけて、数えて、中身を読んだ」だけです。「念のため、手持ち全部で試して」の一言を足すかどうかで、ここまで変わるとは思っていませんでした。

同じように悩んでいる方の参考になればうれしいです。


付録:実際に使ったClaude Codeのhooks設定とコード ― 71%版と7%版の両方を載せます

ここから先は、実際の設定ファイルとコードです。プログラムに興味がない方は、ここで読み終わっていただいて大丈夫です。 本編はここまでです。

同じことをやってみたい方のために、失敗した版と直した版の両方を置いておきます。ネットにあるフックの記事は「こう書けば動きます」で終わっているものが多いのですが、わたしがいちばん知りたかったのは「動いたあと、実際に使えるのか」でした。

① settings.json への登録

Claude Codeの「hooks(フック)」は、.claude/settings.json に書きます。PostToolUse は「ファイルを書いたあと」に走ります。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "python \"C:/Users/user/Desktop/作業場/.claude/hook_article_rules.py\"",
            "timeout": 15,
            "statusMessage": "記事の決まりを確認中"
          }
        ]
      }
    ]
  }
}

matcherWrite|Edit なので、ファイルを新規作成したときと編集したときの両方で走ります。標準入力にJSONが渡ってくるので、その中の file_path を見て対象を絞ります。

② 失敗した版 ― 単語で探していた(誤検知71%)

最初はこう書いていました。禁止したい単語の一覧を作って、記事に含まれていたら警告する、という素直な作りです。

hits = []
for word in ["絶対", "必ず", "100%", "誰でも", "損しません", "確実に"]:
    for m in re.finditer(re.escape(word), text):
        s = max(0, m.start() - 18)
        hits.append(text[s:m.end() + 14].replace("
", " "))

動作テストは通りました。でも手持ちの記事122本にかけたら、87本(71%)が警告になりました。本編に書いたとおり、弾かれていたのは「絶対的な正解ではない」「確実に傷が入ります」といった、むしろ書くべき文のほうでした。

③ 直した版 ― 「断定語」と「得をする結果」の距離で判定(7%)

禁止されているのは単語そのものではなく、成果の保証でした。そこで、断定語のうしろ15文字以内に「儲かる」「通る」などの利得を表す語が来たときだけ拾う形にしました。

STRONG = r"(?:必ず|絶対|100%|100%|誰でも|確実に)"
OUTCOME = (r"(?:儲か|もうか|稼げ|稼ぐ|得する|損しな|通ります|通る|受かる|合格でき|"
           r"治る|治り|痩せ|やせ|効きます|効果があり|成功し|安くなり|無料になり|審査に通)")

hits = []
for m in re.finditer(STRONG + r"[^。
]{0,15}" + OUTCOME, text):
    s = max(0, m.start() - 16)
    hits.append(text[s:m.end() + 12].replace("
", " "))

[^。 ]{0,15} の部分が肝で、句点をまたがないようにしています。文が変われば意味も変わるので、同じ文の中にある場合だけ拾います。

これで警告は87本から9本(7%)になりました。しかも「誰でも審査に通るので、必ず得する方法です」のような本物の違反文は、いまもきちんと検知します。ゆるくしたのではなく、狙いを絞ったということです。

④ いちばん大事なのは、この測り方

コードそのものより、これをやったかどうかで結果が変わりました。手持ちのファイル全部にかけて、何件ひっかかるか数えるだけです。

import glob, json, subprocess, os

files = [f for f in glob.glob('ブログ_*.md') if 'メモ' not in f]
warned = 0
for f in files:
    payload = json.dumps({"tool_input": {"file_path": os.path.abspath(f)}}, ensure_ascii=False)
    r = subprocess.run(['python', '.claude/hook_article_rules.py'],
                       input=payload, capture_output=True, text=True, encoding='utf-8')
    if r.stdout.strip():
        warned += 1
print(f"{len(files)} 本中 {warned} 本で警告")

1分もかかりません。この1分をやるかどうかで、「動くけど使えないもの」を入れてしまうかどうかが決まりました。

(設定項目名や書き方は2026年9月時点のものです。Claude Codeは更新が速いので、最新の書き方は公式のドキュメントでご確認ください。上のコードはわたしの環境(Windows)で動かしているもので、そのまま動く保証はできません)


※本記事の数値(122本・87本・9本など)は、2026年9月時点でわたしの手元のファイルを対象に実際に測ったものです。記事の内容や本数によって結果は変わりますので、ご自身の環境で試された場合は異なる数字になります。

コメント

タイトルとURLをコピーしました