Cloudflare AI Search: 設計からトラブルシューティングまで
設計判断: Webクロール vs R2同期
Markdownドキュメントサイト(VitePress等)にRAGチャットボット導入時、データソース戦略はR2が正解。
| 観点 | Webクロール | R2 + 同期 |
|---|---|---|
| チャンク品質 | HTMLノイズ混入、低い | Markdownソース直接、高い |
| 横断検索 | ページ単位で弱い | frontmatter活用で強い |
| 導入コスト | URL指定だけ | 同期スクリプト保守が必要 |
判断基準: プロトタイプ以外はR2推奨。20-50ファイルでも品質差が大きく、数百ファイルではWebクロールが不安定化する。
アーキテクチャ
VitePress(チャットUI) → Workers API Proxy → Cloudflare AI Search
↑
R2 Bucket(Markdown)
AI Searchがチャンキング・Embedding・ベクトル検索・LLM回答生成を一気通貫で処理。自前でVectorize + Workers AIを組む必要がない。社内数十人規模なら月$5(Workers有料プラン)で運用可能。
API仕様
検索・回答API
/search- 自然言語検索(関連チャンクの取得)/ai-search- AI回答生成付き検索/ask- NLWeb統合(拡張子からの情報取得も可能)/mcp- MCPサーバー経由のアクセス
内部最適化機能
- 自動インデックス: ファイル変更を検知して再構築(R2ファイル削除時も自動反映)
- クエリ書き換え: ユーザーの曖昧な質問を改善
- リランキング:
@cf/baai/bge-reranker-baseモデルで意味的に再スコア - 類似性キャッシング: 頻出クエリの高速化
- 同期クールダウン: 3分、インデックス速度3-5x高速化
Workers呼び出し
const response = await env.AI.autorag("instance-name").aiSearch({
query: "ユーザーの質問",
model: "external-model-name" // オプション: AI Gateway経由で外部モデル使用可能
});
データソース
- R2: Markdown, docx, odt の自動クローリング
- Website: URLベースの自動クロール
- AI Gateway経由でClaude API等の外部LLMに切り替え可能(設定のみ)
セットアップ
R2同期
# GitHub Actions で定期実行
# docs を R2 にアップロード → AI Search が検索インデックス自動更新
APIトークンの権限
R2操作に「Workers R2 Storage: Edit」だけでは不足。「Edit Cloudflare Workers」テンプレートから作成するのが確実(Account Settings Read等が含まれる)。
wrangler r2の --remote フラグ
- ローカル開発・GitHub Actions ともに
--remoteが必須(デフォルトはlocal R2) Resource location: localと表示されたら--remoteが足りない
Embeddingモデル選定(日本語)
| モデル | 特徴 | コスト |
|---|---|---|
@cf/baai/bge-m3 | 多言語・Dense+Sparse同時対応 | $0.012/MTok |
@cf/preferred-networks/plamo-embedding-1b | 日本語特化 | $0.019/MTok |
小規模ドキュメントならbge-m3のコスト効率が優る。
実装知見・トラブルシューティング
SSEストリーミングの罠
aiSearch({ stream: true }) の戻り値はReadableStreamではなく**Responseオブジェクト**。instanceof Responseでチェックしてbodyを転送する。
if (streamResult instanceof Response) {
const headers = new Headers(streamResult.headers);
return new Response(streamResult.body, { headers });
}
wrangler.tomlの [vars] が secret を上書きする
wrangler secret putで設定した値が、wrangler.tomlの[vars]に同名キーがあると上書きされる。ローカル開発用の値は.dev.varsファイルに分離すること。
CORS設定のセキュアデフォルト
ALLOWED_ORIGINS未設定時は**return false(全拒否)** にする。return true(全許可)だと本番で設定忘れ時に全オリジンからアクセス可能になる。