Codex を使ってみた:サイトクローラ × RAG のCLIを数時間で作るまで
はじめに
OpenAI の Codex が話題になっている。
Claude Code より賢いとか、Claude Code をやめて Codex に移行したとか。
試しに Codex を使って、サイトマップからページを取得 → ローカルにインデックス → OpenAIでQ&AできるCLIを数時間でゼロから作ってみた。
今回作った CLI は次の通り。
- 指定サイト配下のページをクロール
- HTML → テキスト抽出 → チャンク分割 → OpenAI Embeddings → ベクトル保存
- 類似検索してコンテキストを作り、Chatで回答
codex の環境設定
GitHub でリポジトリを作成
Codex のプロジェクトを作成するのに使うので先に作成しておく。private リポジトリでOK。
Codex の画面でプロジェクトを作成
Codex の Web サイトで 「Cursor で開く」を選択
- すると Cursor が起動し、Codex プラグインが自動的にインストールされる。
- ログインを求められるのでログインする
ローカル環境で git clone
git clone https://github.com/nshmura/orb-note.git
cd orb-note
Cursor で上記のディレクトリを開く
-
Cursor で orb-note を開く
-
Codex のプレーンを左から右に持っていく(こっちの方が落ち着くので)
これで準備は完了
Codex によるコーディングの流れ
CODEXプレーンから、自然言語のプロンプトで指示を出していくことになる。
この辺は Cursor や GitHub Copilot と同じ。
最初のプロンプトはこんな感じ
ローカルで動くCLIツールを作りたい。
指定されたサイトの sitemap.xml を読み込み、そこに書いてあるURLをクローリングしてダウンロードし、notebook lm のように、それらのドキュメントについて問い合わせできるようなイメージ。
要件、設計をまとめて
実装はPython、パッケージ管理は poetry。
Playwright を使うのもいいと思うがお任せします。
ローカルのDBはなんでもOK。LLMはOpenAI のAPIを使って。
まずは、「要件、設計をまとめて」と言ったのだが、Codex は一気に実装を完了させてしまった。
この辺は業務だと制御したいが、今回は日曜日プロジェクトなので、まぁ大丈夫。バイブコーディングでいく。
で、わずか5分〜10分くらいで、仕様通り問題なく動く CLI ができてしまった。
これだけだと呆気なさすぎるので、もう改善を加えて公開まで持っていった。
開発は、次のような感じで進めた。
-
コマンドラインオプションの修正
--sitemap-url
を--url
に変えて、サイト配下絞り込み+サイトマップ自動検出するようにした -
差分インデックス導入
2回目以降のダウンロード時に、すでにDBに保存されていたらインデックスを更新しないようにした -
Playwright でクローリングするようにした。
-
レート制限を追加
-
サイトダウンロードやインデックス化の進行がわかるようにログを改善
-
作成したインデックスの現状がわかるコマンドを追加
-
ローカルのDBをクリーンアップスルコマンドの追加
-
コマンド配布用の GitHub Actions ワークフロー追加
出来上がったもの
下のように Claude Code ドキュメントをダウンロードしてインデックス登録
% orb-note crawl --url https://docs.anthropic.com/ja/docs/claude-code/ --index --limit 200
Discovering sitemaps...
36 URLs discovered under the given path. Fetching...
fetch https://docs.anthropic.com/ja/docs/claude-code/amazon-bedrock
fetch https://docs.anthropic.com/ja/docs/claude-code/analytics
ok https://docs.anthropic.com/ja/docs/claude-code/analytics
ok https://docs.anthropic.com/ja/docs/claude-code/amazon-bedrock
....
Saved 36 pages.
Indexing newly crawled pages...
⠧ Embedding 4 chunks: 開発コンテナ - Anthropic ━━━━━━━╸━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 7/36 0:00:09 0:00:39
Indexed 36 pages, 189 chunks.
Claude Code ドキュメントについて質問できる
% orb-note ask "hooks について教えて"
Retrieving and generating...
Answer:
フックは、特定のイベントが発生したときに実行されるコマンドやスクリプトを定義する機能です。これにより、ツールの呼び出し前や後、通知の送信時、セッションの開始や終了時など、さまざまなタイミングで自動的に処理を行うことができます。フックは、プロジ
ェクト全体に適用されることがあり、設定はJSON形式で行います。具体的なフックの例としては、ファイル保護フックやコードフォーマットフックなどがあります。フックの設定やデバッグについては、リファレンスドキュメントを参照することが推奨されています。
Codex の感想
-
Cursor や GitHub Copilot (Agent Mode) と同じような使い勝手で開発できた。
- Codex は若干 UI が使いづらかった。
コンテキストに入れたいファイルをドラッグ&ドロップで指定できなかったり、この辺はリリースされた直後だからだと思う。
- Codex は若干 UI が使いづらかった。
-
Codex CLI を使えば、Claude Code と同じように使えると思うが、ドキュメントを見る限り、まだ機能は Claude Code に劣ってそう。
-
実装の品質は素晴らしかった。
-
ほぼ問題ない実装を指示一発で出力してくれた。
-
勘違いした実装をしたりすることもないし、実行が終わらないとか、間違った方向に進んで修正を繰り返すということもなかった。
-
完成を実行してみてトラブルがあっても、エラーを見せたら対策が確実にできた
-
開発した CLI ツールの概要
- 言語: Python
- パッケージ管理: Poetry
- CLI: Typer で実装
- クローリング: httpx + BeautifulSoup(静的)/ Playwright(動的)
- 解析: tiktoken(任意)で分割、OpenAI Embeddings(
text-embedding-3-small
) - 生成: OpenAI Chat(
gpt-4o-mini
) - DB: SQLite(SQLAlchemy)
- 配布: GitHub Releases に wheel を添付 → pipx 直URLインストール
- CI: GitHub Actions(タグ/Release/手動で発火)、第三者アクション排除、gh CLI でリリース生成
開発した CLI の利用イメージ
# 準備
pipx install https://github.com/nshmura/orb-note/releases/download/v0.1.0/orb_note-0.1.0-py3-none-any.whl
orb-note browser install
export OPENAI_API_KEY=sk-...
# クロール & インデックス化
orb-note crawl --url https://example.com/docs/ --index
orb-note ask "このサイトの返金ポリシーは?"
開発した CLI ツールの詳細
サブコマンド設計
-
crawl
:サイトURLの配下限定でサイトマップを自動検出→取得→保存- 既定で Playwright モード(
--no-use-browser
で静的に切替) - レート制限(
--delay
既定1s /--rpm
既定60)
- 既定で Playwright モード(
-
index
:差分インデックス + 進捗バー表示 -
ask
:RAG(Top-K 類似チャンクをコンテキストに回答)。 -
pages
:一覧(状態: pending/indexed/stale)と詳細表示(pages show
)。 -
cleanup
:.orb_note
データ削除。
実行例
クロール(指定URL配下を対象、既定は Playwright 使用)
# 配下に限定・20件まで・取得後に即インデックス
orb-note crawl --url https://example.com/docs/ --limit 20 --index
# レート制限と並列度を調整
orb-note crawl --url https://example.com/docs/ --concurrency 2 --rpm 20 --index
# 静的取得(高速・負荷低)
orb-note crawl --url https://example.com/docs/ --no-use-browser --index
ページ一覧と詳細確認
# 一覧(状態とチャンク数)
orb-note pages
# URL に 'anthropic.com/ja/docs' を含むページだけ
orb-note pages --search anthropic.com/ja/docs
# 本文を確認(ID指定、先頭800文字)
orb-note pages show --id 123
# URL部分一致でHTMLを全文表示
orb-note pages show --url anthropic.com/ja/docs --html --full
インデックスと質問
# 差分のみ(既定)
orb-note index
# 全ページを再インデックス
orb-note index --reindex-all
# 質問(Top-K とコンテキスト予算を調整可能)
orb-note ask "このサイトの返金ポリシーは?" --top-k 8 --max-context-tokens 3500
クリーンアップ
# ローカルデータ削除
orb-note cleanup
RAG の実装
-
パイプライン(質問時)
- クエリを Embedding 化
- 事前に保存してあるチャンク埋め込みとコサイン類似度でスコアリング
- 上位 Top-K をコンテキストとして連結
- 「コンテキストのみで答える」方針の system プロンプトで Chat 生成
- 実装:
orb_note/rag.py: search_similar
,build_context
,answer_question
-
インデックス(前処理)
- チャンク分割:
orb_note/embedder.py: chunk_text
- 既定は tiktoken(モデル:
gpt-4o-mini
)でトークン数ベース、未導入時は文字数フォールバック - オーバーラップを持たせて文脈を保全
- 既定は tiktoken(モデル:
- 埋め込み生成:
orb_note/embedder.py: embed_texts
- OpenAI
text-embedding-3-small
(環境変数で変更可)
- OpenAI
- ストレージ:
orb_note/db.py
-
embeddings
テーブルにチャンク単位で保存(ベクトルは JSON 文字列、テキスト・トークン数・文字数も保持) -
pages
と 1:N。pages.indexed_hash
で差分インデックスを制御
-
- チャンク分割:
-
検索(類似度計算)
- 全チャンク埋め込みをメモリに読み込み、NumPy でコサイン類似度を一括計算
- 小~中規模のコーパス向け(シンプルで速い)。大規模化は FAISS や SQLite の VSS 拡張に置換可能
- 実装:
orb_note/rag.py: search_similar
(クエリ埋め込み→スコア付け→Top-K 抽出)
-
生成(回答)
- コンテキストは「スコア・タイトル・URL + チャンク本文」を素直に連結
- system: 「与えられたコンテキストのみで答える。なければ知らないと言う」
- model: 既定
gpt-4o-mini
、temperature=0.2
- 実装:
orb_note/rag.py: answer_question
-
トークン管理
-
max_context_tokens
を受け取りつつ、現状は概算(厳密カウント未実装)。必要なら tiktoken で厳密化可能
-
-
留意点と拡張余地
- 現状は単純な Top-K。MMR(多様性重視)やハイブリッド検索(BM25 併用)で品質を上げられる
- セクション見出し等のメタデータを活用したスコア補正、ページレベル埋め込みの段階検索も有効
まとめ
個人的に欲しかったツールをサクッと作れてよかった。
特に困ることなくスムーズに開発が進んでいて、Codex の開発体験がいいというのを実感できたと思う。
Discussion