上原正吉(EarthLink Network Co., Ltd.)。Claude Codeを開発の主体に据え、20を超えるプロダクトを1人で同時に開発・運用しています。これは、その現場の実測記です。

AIO Helper は、私たちが作っているSEO運用のSaaSです。検索データを取り込み、AIがページごとの改善案を出し、承認した案をページへ反映し、反映した後に効いたかどうかを確かめます。この4つの段階を、1つの画面と1つのデータベースで回します。
反映した変更の履歴と、検証の結果を見て付けた学びの記録は、次の改善案を作るときにAIが検索する材料に戻します。この記事では、4つの段階を実際の画面とコードで説明します。
AIO Helper の元は、私たちのCMS「Plovant」の中にあったSEOの機能です。Plovant は最初、SEOのプラットフォームとして作り始めました。その後、1つのリポジトリにCMSとSEOという2つの製品が同居し始めたので、SEOの機能を別の製品として切り出しました。それが AIO Helper です。
切り出したときに考えたのは、SEOの何がいちばん手間なのか、ということでした。
改善案を出すこと自体は、今はそれほど難しくありません。Search Console の数字を見れば「表示回数は多いのにクリックが少ないページ」は分かりますし、AIに聞けばタイトルの書き換え案も出てきます。
難しいのは、その後です。
これをスプレッドシートで管理し始めると、すぐに追い切れなくなります。案を出す場所、ページを書き換える場所、数字を見る場所がばらばらだからです。
そこで AIO Helper では、改善案を「提案」という1つのレコードとして持ち、そのレコードが承認・反映・検証と状態を変えていく形にしました。どのページに何をして、指標がどう変わったかを、提案ごとに後から追えるようにするためです。
管理画面は、左のメニューで次の4つのグループに分かれています。
CONTROL コントロールルーム
STRATEGY 戦略パイプライン / 拡張ロードマップ / 成長構造
INSIGHTS キーワード / ページ管理 / ページ目標 / コンテンツ / サイト構造 /
E-E-A-T / リンク / パフォーマンス / 効果検証
SYSTEM レポート / 設定
画面上部では、見る人の立場を「オペレーター」「マネージャー」「エグゼクティブ」で切り替えられます。日々の施策を回す人、優先順位を決める人、全体の数字だけを見たい人で、同じデータの見せ方を変えています。
ここから、4つの段階を順に見ていきます。
最初の段階は、検索データの取り込みです。中心は Google Search Console のデータで、ほかに Google Analytics 4・PageSpeed Insights・広告のデータを取り込む処理もあります。
毎日の処理は、決まった順番で動きます。時刻は日本時間です。
03:30 Search Console の取り込み
04:00 サイトの文書をベクトル化し直す(後で説明する検索の準備)
04:10 反映した施策の前後の数字を集計する
04:20 自分のサイトを巡回して、ページの状態を集める
04:45 改善案をまとめて作る
05:00 反映した施策の効果を確かめる
05:30 リスクの低い案を、ページの下書きへ反映する
取り込んだ数字が、その日の提案と検証の材料になる順番にしてあります。

取り込みで気をつけたのは、Search Console のデータが確定するまでに時間がかかることです。昨日の分を昨日のうちに取りに行くと、まだ0件のことがあります。0件のまま「取り込み完了」と扱うと、その日のデータが欠けたまま残ります。
そのため、日次の取り込みは毎回、7日分をまとめて取り直します。既定では、3日前を終わりにした7日分です。
// seo-ingest-gsc/src/dates.ts
export const DEFAULT_REFETCH_DAYS = 7;
すでに取り込んだ日でも、確定した値で上書きします。同じ日を何度取っても結果が変わらないようにしてあるので、取り直しを毎日繰り返しても重複しません。この「0件のまま完了扱いになって欠ける」問題は、実際に起きてから直しました。詳しい経緯は、このシリーズの別の記事で書きます。
取り込んだ数字は、ページ単位・キーワード単位・日単位でデータベースに入ります。キーワード画面では、追跡しているキーワードの順位のほかに、「検索ボリュームはあるのに、まだ自分のサイトが順位を取れていないキーワード」をギャップとして出します。
2つ目の段階が、AIによる改善案です。「戦略パイプライン」の画面に、ページごとの提案が並びます。

1件の提案は、次のような形のデータです。デモデータの1件を、APIの応答からそのまま抜き出しました。
{
"type": "Meta",
"target": "/pricing",
"locale": "ja",
"impact": 82,
"confidence": 91,
"risk": "Low",
"risk_reason": "タイトル・説明文の変更のみ。構造変更なし。",
"effort": "Low",
"status": "New",
"evidence_tags": ["GSC", "SERP", "GA4"],
"summary": "/pricing のタイトル・説明文が未設定。CTR改善で月+120クリック見込み"
}
提案の主な種別は、タイトルや説明文の変更(Meta)、本文の書き直し(Content)、関連するページ群の追加(Cluster)、内部リンク(InternalLink)、サイト構造(Architecture)、表示速度(Performance)、ブランド表記(Brand)などです。
それぞれに「impact(見込みの効果)」「confidence(確信度)」「risk(リスク)」「effort(手間)」が付きます。画面では見込みの効果・確信度・リスクで並べ替え、種別・リスク・手間で絞り込めるので、「効果が大きくてリスクが低いもの」から順に片付けられます。risk には理由も付けています。タイトルの書き換えと、新しいページを3本足すことでは、失敗したときの影響がまったく違うからです。
提案を作るとき、AIは何もない状態から考えるわけではありません。サイトに関する文書をベクトル検索(文章の意味の近さで探す仕組み)で引いてから、案を作ります。その検索の並び順が、次のコードです。
SELECT d.id AS doc_id, d.doc_type, d.title, d.content,
(e.embedding <=> $1::vector) AS distance
FROM seo_embeddings e
JOIN seo_docs d ON d.id = e.doc_id
WHERE e.embedding IS NOT NULL
ORDER BY
CASE WHEN d.site_id = $2 THEN 0 ELSE 1 END,
CASE d.doc_type
WHEN 'brand_rules' THEN 0
WHEN 'learning' THEN 1
WHEN 'outcome_report' THEN 2
WHEN 'change_log' THEN 3
ELSE 4
END,
e.embedding <=> $1::vector
LIMIT 8
並び順の意味は次のとおりです。
つまり、前に効いた施策と効かなかった施策が、次の改善案を作るときの根拠として最初のほうに引かれます。提案を出して終わりにせず、効果の検証まで追う理由の1つがここにあります。検証の記録が残っていなければ、AIは毎回同じような一般論の案を出すことになります。
3つ目の段階が、ページへの反映です。
反映の経路は2つあります。
1つ目は、AIO Helper が持つページごとのSEO設定に書き込む経路です。タイトルや説明文を変えると、この設定(PUT /v1/seo)が更新されます。サイト側は、このSEO設定をAPI(/v1/seo)から読んでページの表示に使います。そのための部品も用意しています。
// packages/seo-kit/src/fetch-seo.ts(抜粋)
const url = `${SEO_API_URL}/v1/seo?siteId=${encodeURIComponent(siteId)}&path=${encodeURIComponent(path)}&locale=${encodeURIComponent(locale)}`;
const res = await fetch(url, { cache: "no-store" });
書き込みと同時に、次の3つを行います。
seo.updated を送る2つ目は、自社のCMS「Plovant」のページへ直接反映する経路です。サイトごとの設定で有効にすると、リスクの低い提案を毎日、Plovant のページの下書きへ自動で反映します。下書きを公開するところまで自動にするかどうかは、別の設定で決めます。
自動で反映する範囲を「下書きまで」と「公開まで」の2段階に分けたのは、AIの提案をそのまま公開して検索順位を落とすと、元に戻すのが大変だからです。まず下書きで中身を確かめられる状態を作り、公開まで任せるかどうかはサイトごとに決められるようにしました。
4つ目の段階が、効果の検証です。効果を検証する対象は、ページと狙うキーワードの組として登録した施策です。反映したすべての提案が自動で対象になるわけではありません。「効果検証」の画面に、対象にした施策ごとの結果が並びます。

検証では、公開した日から14日ごとに期間を区切り、反映前の期間と比べて、対象のキーワードの順位やページのクリックがどう変わったかを見ます。観察は、通常は公開から56日までです。基準の値は次のとおりです。
// rank-watch-judge.ts
export const DEFAULT_JUDGE_CONFIG: JudgeConfig = {
minImpressions: 50, // 期間内の表示回数がこれ未満なら結論を出さない
improvedDelta: 0.5, // 順位がこれ以上良くなれば「改善」
worsenedRankDelta: 2.0, // 順位がこれ以上悪くなれば「悪化」
clickGuardRatio: 0.85, // ページ全体のクリックが反映前の85%未満なら「悪化」
clickGuardMinBaseline: 10, // 反映前のクリックが10未満ならクリックでは見ない
imprGuardRatio: 0.7, // ページ全体の表示回数が反映前の70%未満なら「悪化」
imprGuardMinBaseline: 100, // 反映前の表示回数が100未満なら表示回数では見ない
};
ここで大事にしたのは、「データ不足」を検証の結果として持つことです。
検索の数字は、もともと揺れます。表示回数が数回しかないキーワードで順位が1つ上がっても、それが施策の効果なのか、たまたまなのかは分かりません。そこで、対象のキーワードの表示回数が期間内で50回に届かなければ、「改善」とも「悪化」とも言わず、データが足りないとだけ記録します。
また、目当てのキーワードの順位が上がっても、ページ全体のクリックが大きく減っていれば「悪化」とします。1つのキーワードだけを見て、ページ全体で損をしていることに気づかない、という状態を避けるためです。
検証の結果を見た人は、その提案に学びの記録(learning)を付けられます。この記録が、前の章で書いたとおり次の提案の根拠として検索されます。悪化となった施策は、画面の「次のアクション」に、元に戻すかどうかの検討として出ます。
AIO Helper の作りには、はっきりした限界もあります。
AIO Helper の記事は、この紹介を入口に、機能ごとの説明と、作る途中で判断したことを順に書いていきます。予定している話題は次のとおりです。
この AIO Helper についての記事は、どんな機能があるか・どう実装したかを、順次シリーズとして公開していきます。 興味のある方は、ぜひ「いいね」と記事の購読をお願いいたします。
そのほかの自社プロダクトは https://www.eln.ne.jp/products にまとめています。
上原正吉。EarthLink Network Co., Ltd. でAI開発をしています。2025年からClaude Codeを開発の主体に据え、今は20を超えるプロダクトを1人で同時に開発・運用しています。この連載では、その現場で実際に起きたこと(うまくいったことも、失敗も)を、数字と一緒に書いていきます。
また、AIで業務や開発を組み替えたい会社・チーム向けに、AI活用のコンサルティングも受け付けています。ご相談は www.eln.ne.jp からどうぞ。
EarthLink Network は、会社の全業務を AI で回すために、必要になったものを自社で作っています。いま作っているプロダクトの一覧と概要は、こちらにまとめています。
→ EarthLink Network が自社でつくっている18のプロダクト
会社と各プロダクトの詳細は、公式サイト www.eln.ne.jp をご覧ください。