Pascal CESCATO が私のSEO監査エージェントの記事を読んで、コメント欄にこれを残しました:
"これにはLLMは不要です。Claudeに送っている内容はすべてPythonで直接できます——費用はゼロ、完全に決定的で、幻覚のリスクもありません。"
彼は正しかった。にもかかわらず、間違ってもいました。そして、その後の会話が、私が全部作り直す理由になりました。
Pascalが実際に言ったこと
私が公開した監査エージェントは、タイトル長、メタディスクリプション長、H1の数、そしてcanonicalタグをチェックします。Pascalの主張はこうです。これらは文字数のカウントと存在チェックである、と。レギュラーエクスプレッションでそれはできます。レギュラーエクスプレッションに対して、URLごとに$0.006を払う必要はありません。
私は反論しました。flags配列には判断が必要です——「タイトルがページ説明というよりナビゲーションラベルっぽい」といったのは、文字数ではありません。Pascalはそれを認め、そのうえでこう言い換えました:
"二段階がより理にかなっています。バイナリ判定は決定的なPythonで。モデル呼び出しは機械的な監査に通ったページだけにして、2回目の確認が必要な場合に限定します。本当に曖昧なケースにだけ課金され、URLごとに払うわけではありません。"
これは、私が出したものよりも良いアーキテクチャでした。だから公にそう言いました。
そして Julian Oczkowski がそれを拡張しました:
"まず決定的なルール。トリアージには軽量なモデル。明確に曖昧なエッジケースだけに大きなモデルを使う。レイテンシを低く保ち、コストを予測可能にし、不要なLLM依存を減らします。"
コメントスレッドの3人は、私がまだ名前をつけていなかった何かを設計してしまったわけです。Pascalはそれを二段階(two-pass)と呼びました。Julianは階層化(tiered)と呼びました。私はそれをコストカーブ——必要なタスクに応じて、無料から高額までスライドするスケール——と呼びました。
コストカーブ
Tier 1 — 決定的なPython。コスト:$0。
タイトル>60文字?FAIL。ディスクリプションが欠けている?FAIL。H1の数==0?FAIL。これらは判断ではありません。シェイクスピアを理解できるモデルなら、60まで数えるのに呼び出す必要はないでしょう。
Tier 2 — ハイク(Haiku)。コスト:URLあたり約$0.0001。
タイトルはあるが4文字しかない。ディスクリプションはあるが30文字。ステータスコードがリダイレクト。これらは機械的な監査は通りますが、どこかおかしい。ハイクは安いので、曖昧なケースに呼び出しても、決定的なチェックが見落としたものをデバッグするのに費やす時間より安く済みます。
Tier 3 — ソネット(Sonnet)。コスト:URLあたり約$0.006。
ハイクが、意味的な判断が必要だとフラグを立てたページ。"このタイトルは長さはクリアしているが、ナビゲーションラベルのように読める。" "このディスクリプションはタイトルをそのまま複製している。" ソネットはここでそのコストを回収します。常にではありません。
ポイントはルーティングです。典型的な代理店サイトのほとんどのページには機械的な問題があります——ディスクリプションがない、タイトルが長い、canonicalがない。そういうものはモデルを必要としません。面白いケース——すべてのバイナリチェックは通るのに、それでも違和感があるページ——が、モデルを置くべき場所です。
直近の50URLの実行では、8件がソネットに到達しました。残りはTier 1で解決しました。総コストは約$0.30から約$0.05に下がりました。ソネットに当たった8件は、支払う価値があるものでした。
私が実際に作ったもの
私はこのアーキテクチャに合わせてリポジトリ全体を再構成しました。
core/ はフラットでMITライセンスです。元の7つのモジュールはそのままです。v1をクローンした誰でも、python core/index.py を実行すれば同一の挙動が得られます。
premium/ には4つのモジュールが追加されています。cost_curve.py がティアのルーティングを担当します——audit_url(snapshot, tiered=True) はまずTier 1を実行し、何かが失敗したらハイクへエスカレート。ハイクが意味的な曖昧さを示した場合だけソネットへエスカレートします。multi_client.py はプロジェクトフォルダを管理します——--project acme は projects/acme/ から読み書きし、状態・入力・レポートを分離して持ちます。enhanced_reporter.py は、URLごとのスクリーンショット付きでWeasyPrintのPDFを生成し、重大度ごとに課題を並べ、推奨修正案を出します。rewrite_agent.py はPascalが想定していなかった部分です——監査の後、--rewrite は同じコストカーブを使って改善提案を生成します:Tier 1はタイトルを決定的に短縮し、ハイクはメタディスクリプションの提案を書き込み、ソネットは冒頭段落を書き換えます。--voice-sample ./my-writing.txt を渡すと、プロンプトにあなたの文章のサンプルが含まれます。提案はClaudeっぽくではなく、あなたっぽく聞こえます。
main.py は統一されたエントリポイントです。無料ユーザーは python main.py を実行するとv1の挙動になります。有料ユーザーは --pro を追加し、SEO_AGENT_LICENSE を設定すると、プレミアム層が解放されます。
フルのプロ実行はこんな感じです:
python main.py --project client-x --pro --tiered --rewrite
この1コマンドで:projects/client-x/input.csv から読み込み、すべてのURLをコストカーブでルーティングし、失敗したページに対する書き換え提案を生成し、スクリーンショットと重大度つきのPDFレポートを書き込み、監査履歴に実行記録を追記します。
重要なアーキテクチャ上の意思決定
core/ は premium/ から何もインポートしません。二度と。
これは単なるきれいなコードではありません。リポジトリをフォークする誰に対しても信頼契約になるものです。MITライセンスのcoreはパブリックな善——監査可能で、フォーク可能で、PRを受け付けます。プレミアム層はプロプライエタリでクローズドですが、誰でも検査できる土台の上に築かれています。
コメント欄のMads Hansenが挙げた適切な問いはこれです。繰り返し発生するパターンに対して、監査者が盲点を作り込まないようにするにはどうするのか? 当時私が持っていなかった答え:state.json に実行履歴を記録することです。完了した各実行はレコードを追記します——タイムスタンプ、通過数、失敗数、レポートのパス。時間が経つと、"このページは連続6回、ディスクリプション長に失敗している" は "このページは今日失敗した" とは別のシグナルになります。監査はスナップショットではなく、モニターになるのです。
コメントスレッドがもたらしたコスト
なし。そして、私が一人でやっていたら作れなかったより良いアーキテクチャになりました。
Pascalの反論は、決定的(deterministic)な部分と意味的(semantic)な部分を切り分けてくれました。Julianの本番向けの言い方によって、3層構造が見えてきました。Apex Stack の89Kページのサイトは、孤児(オーファン)検出問題がどこに潜んでいるかを教えてくれました。Mads Hansen が、私がまだ聞いていなかった盲点の問いを名づけてくれました。
これらはすべて、元の記事にはありませんでした。すべて今はリポジトリにあります。
公開されたコメントスレッドは、私が予定していなかったアーキテクチャレビューです。正直なバージョン——あなたの手元のコンテンツで失敗するデモ——を公開すると、そういうことが起きます。段階(ステージド)されたものではなく。
ステージドのデモなら通ったでしょう。正直なデモは、積み重なっていました。
フルリポジトリ:dannwaneri/seo-agent。coreはMIT。premiumはライセンスキーが必要です。freeCodeCampのチュートリアルでは、v1のビルドを詳細に解説しています:How to Build a Local SEO Audit Agent with Browser Use and Claude API。



