Claude Codeの会話履歴をデータベース化してナレッジ検索可能にする方法

Claude Code 会話履歴のイメージ画像

Claude Codeの会話ログをSupabase+pgvectorでDB化すると、数百セッションの知識がセマンティック検索で即座に引き出せるナレッジベースになります。

  • 要点1: 会話ログはJSONL形式で ~/.claude/projects/ 以下に保存されており、Pythonスクリプトで容易に読み込み可能
  • 要点2: Supabaseの無料プランでpgvectorを有効化するだけでベクトル検索基盤が構築できる
  • 要点3: chat-syncスキル+cronジョブで会話の自動同期を実現し、常に最新の知識を検索できる状態を維持できる

対象: Claude Codeを日常的に使っており、蓄積した会話を活用したいエンジニア・DX推進担当者

今日やること: ~/.claude/projects/ フォルダを確認し、会話ログのJSONLファイルが溜まっているかを確認する

この記事の著者
川島陸

株式会社Nexa 代表取締役川島 陸

一橋大学経済学部卒業後、フォーティエンスコンサルティング株式会社(旧 株式会社クニエ)にて法人向けAI導入支援等を経験。独立後、AI系メディア運営やDify/n8nの導入支援を経て、株式会社Nexaを創業。法人向けAI研修・AI導入支援・AI関連メディア運営を手掛ける。

Claude Codeの会話ログをSupabase+pgvectorでデータベース化することで、過去の手順・トラブル解決策・設計判断をセマンティック検索で瞬時に引き出せるナレッジベースが構築できます。

「あのGSCスキルの設定、どのセッションに書いてあったっけ」——Claude Codeを使い込むほど、こうした課題に直面します。会話数が数十を超えたあたりから、必要な情報を後から探すことが難しくなります。

この記事では、あるメディアサイト運営者が実際に構築したClaude Code会話履歴DB化の手順と、遭遇したトラブルの解決策を具体的に解説します。

「あの手順、どのセッションに書いてあった?」という課題

Claude Codeを毎日使う人ほど、膨大な会話ログが蓄積されます。その中には、実装の全プロセス、コンテキスト付きの解決策、アーキテクチャ判断の経緯など、再利用価値の高い情報が詰まっています。問題は、これらを後から効率よく取り出す手段がないことです。

会話ログが価値ある知識の宝庫である理由

Claude Codeとの会話には、検索エンジンでは見つからない「自分固有の文脈」が含まれています。

  • どのAPIを使ったか、なぜそのアプローチを選んだか
  • エラーが発生した際の原因と具体的な解決コマンド
  • 特定のサービスとの連携設定の細かい手順
  • 試行錯誤の末に辿り着いた最適解

こうした情報は、「あのとき解決した」という記憶があっても、どのセッションで扱ったかを特定するのが難しくなります。特に複数のPCやマシンで作業している場合は、会話が分散してさらに探しにくくなります。

キーワード検索では限界がある理由

単純なキーワードのgrepや全文検索では、「Bing Webmaster APIの連携」について調べたいときに「Bing」「API」のキーワードが含まれる会話しか引っかかりません。しかし「検索ボリューム取得の設定」「キーワード調査の自動化」といった意味的に近い表現で書かれた会話は取り出せません。

これを解決するのが、セマンティック検索(意味での検索)です。テキストをベクトルに変換して意味的な近さで検索することで、表現が異なっていても関連する会話を引き出せます。

Claude CodeのセッションデータはJSONL形式で保存されている

Claude Codeとの会話履歴は、JSONL(JSON Lines)形式でローカルに保存されています。JSONL形式とは、1行が1つのJSONオブジェクトからなるテキストファイル形式で、追記が容易で大容量でも扱いやすいのが特徴です。

セッションファイルの場所と構造

会話ログは以下のパスに保存されています。

~/.claude/projects/[プロジェクトID]/[セッションID].jsonl

プロジェクトIDはプロジェクトのディレクトリパスをハイフン区切りに変換したもので、例えば ~/Desktop/myproject/ で作業していた場合は -Users-yourname-Desktop-myproject- のようなフォルダになります。

各セッションは独立したJSONLファイルで、1行が1メッセージに対応しています。

{"type":"message","message":{"role":"user","content":"Bing Webmaster APIの設定方法を教えて"},"timestamp":"2026-03-20T09:15:00Z"}{"type":"message","message":{"role":"assistant","content":"まずAPIキーを取得します..."},"timestamp":"2026-03-20T09:15:05Z"}

メッセージ・ロール・コンテンツの読み方

Pythonで読み込む場合、以下のような処理になります。

import jsondef read_session(filepath):    messages = []    with open(filepath) as f:        for line in f:            line = line.strip()            if not line:                continue            data = json.loads(line)            msg = data.get('message', {})            role = msg.get('role', '')            content = msg.get('content', '')            # contentがlistの場合(ツール呼び出し等)はテキスト部分を抽出            if isinstance(content, list):                for c in content:                    if isinstance(c, dict) and c.get('type') == 'text':                        messages.append({'role': role, 'text': c.get('text', '')})            elif isinstance(content, str) and content:                messages.append({'role': role, 'text': content})    return messages

ツール呼び出しやシステムメッセージなど、会話以外のノイズをフィルタリングすることで、本質的なQ&Aの文脈だけを抽出できます。

\ Claude Codeの導入、何から始めればいいかわかります /

法人向けClaude Code個別指導の無料相談はこちら

Supabase+pgvectorでナレッジ検索基盤を構築する

Supabaseはオープンソースのバックエンドプラットフォームで、PostgreSQLをベースにしています。pgvectorはPostgreSQLのベクトル拡張機能で、テキストを数値ベクトルとして保存し、意味的な近さで検索(コサイン類似度検索)できます。この2つを組み合わせると、会話ログのナレッジ検索基盤が比較的少ない工数で構築できます。

Supabaseの準備とpgvector有効化

  1. Supabaseでプロジェクトを作成(無料プランで開始可能)
  2. SQL Editorで以下を実行してpgvectorを有効化する
-- pgvector拡張を有効化create extension if not exists vector;
  1. テーブルを作成する
-- セッション情報テーブルcreate table chat_sessions (  id text primary key,  project_path text,  created_at timestamptz,  message_count int,  machine_id text);-- メッセージとベクトルのテーブルcreate table chat_messages (  id serial primary key,  session_id text references chat_sessions(id),  role text,  content text,  embedding vector(1536),  -- OpenAI text-embedding-3-small の次元数  created_at timestamptz default now());-- ベクトル検索用インデックスcreate index on chat_messagesusing ivfflat (embedding vector_cosine_ops)with (lists = 100);

テーブル設計(sessionsとmessagesの分離)

セッション情報とメッセージを分離することで、「このセッションのどのメッセージがヒットしたか」を効率的に取得できます。また machine_id フィールドを持たせることで、MacBookとMac miniなど複数マシンからの会話を区別して管理できます。

OpenAI Embeddings APIでベクトル化する

OpenAI の text-embedding-3-small モデルを使ってテキストをベクトルに変換します。このモデルは1536次元のベクトルを生成し、精度とコストのバランスが良好です。

import openaiclient = openai.OpenAI()def get_embedding(text):    response = client.embeddings.create(        input=text,        model="text-embedding-3-small"    )    return response.data[0].embedding

Claude Codeの導入や活用方法について、個別にご相談いただけます。「どの機能から使えばいいか」「自社業務への適用方法を知りたい」といった段階から対応しています。

Claude Code個別指導の無料相談はこちら →


chat-syncスキルでMac ↔ DBを自動同期する

ある個人事業主のメディア運営者は、MacBookとMac miniの2台でClaude Codeを使用しており、それぞれで溜まっていく会話ログを一元管理するためのchat-syncスキルを構築しました。

sync.pyスクリプトの仕組み

スクリプトは以下の処理を順に実行します。

  1. ~/.claude/projects/ 以下のすべての .jsonl ファイルを走査
  2. Supabase上の chat_sessions テーブルを確認し、未同期のセッションを特定
  3. 新規セッションのメッセージを読み込み、ノイズを除去
  4. OpenAI Embeddings APIでベクトル化
  5. Supabaseに登録
def sync_all_sessions(claude_projects_dir, supabase_client, openai_client):    """~/.claude/projects/ 以下の全セッションをDBに同期する"""    for project_dir in Path(claude_projects_dir).iterdir():        if not project_dir.is_dir():            continue        for jsonl_file in project_dir.glob("*.jsonl"):            session_id = jsonl_file.stem  # ファイル名がセッションID            # 既にDB登録済みならスキップ            if is_already_synced(supabase_client, session_id):                continue            # メッセージを読み込みDBに登録            messages = read_session(jsonl_file)            register_session(supabase_client, openai_client, session_id, messages)

初回同期では数百セッションのベクトル化が必要なため、30分〜1時間程度かかる場合があります。OpenAIのEmbeddings APIはレートリミットがあるため、リクエスト間に適度な間隔(0.5秒程度)を挟む実装が必要です。

cronジョブとの組み合わせで自動同期

OpenClawのcronジョブと組み合わせることで、毎日深夜に新しい会話を自動的にDBへ同期する仕組みが実現できます。

{  "id": "chat-history-sync",  "schedule": "0 2 * * *",  "prompt": "chat-syncスキルを使って、今日の新規会話セッションをDBに同期して。",  "maxConcurrentRuns": 1}

この設定により、毎朝起動時には前日の会話がすでに検索可能な状態になっています。

また、MachineIDを活用することで、Mac miniとMacBookの両方の会話を同じDBに統合しつつ、「どのマシンの会話か」を区別して管理できます。初期同期後の増分同期は通常数秒〜数十秒で完了します。

\ 業務自動化のお悩み、プロが30分で整理します /

法人向けClaude Code個別指導の無料相談はこちら

chat-searchスキルで「あの会話」を検索する

DBが構築できたら、検索インターフェースを整備します。前述の運営者は chat-search スキルとしてClaude Codeから直接DBを検索できる仕組みを構築しました。

セマンティック検索(意味での検索)

def semantic_search(query, supabase_client, openai_client, limit=5):    """意味的に近いメッセージを検索する"""    # クエリをベクトル化    query_embedding = get_embedding(query)    # Supabaseでコサイン類似度検索    result = supabase_client.rpc('search_messages', {        'query_embedding': query_embedding,        'match_count': limit    }).execute()    return result.data

SupabaseのSQL関数として検索ロジックを定義します。

create or replace function search_messages(  query_embedding vector(1536),  match_count int default 5)returns table (  session_id text,  role text,  content text,  similarity float)language sqlas $$  select    session_id,    role,    content,    1 - (embedding <=> query_embedding) as similarity  from chat_messages  order by embedding <=> query_embedding  limit match_count;$$;

全文検索との組み合わせ(ハイブリッド)

セマンティック検索だけでは、コマンド名や固有名詞のような「正確に一致させたい」検索が苦手です。全文検索と組み合わせたハイブリッド検索を実装することで、両方の強みを活かせます。

検索方法 得意なクエリ
セマンティック検索 意味・文脈での検索 「エラーが出たときの解決方法」
全文検索 固有名詞・コマンド名 「WP-CLI post create
ハイブリッド 両方をカバー 「WP-CLIで記事公開するときのエラー対応」

検索クエリの実例

実際にこの仕組みを使った検索例を紹介します(固有名詞は伏せています)。

  • クエリ: 「Bing Webmaster APIのキーワードボリューム取得の設定手順」
  • ヒット: 数ヶ月前に構築したBing API連携スキルの作成セッション。APIキーのKeychain登録方法まで含む会話が引き出された

  • クエリ: 「SSH over Tailscaleでファイル同期する方法」

  • ヒット: 複数PC環境構築セッション。rsyncコマンドとTailscaleのIP設定が具体的に書かれた会話

  • クエリ: 「OpenClawが返信しなくなったときの対処法」

  • ヒット: Slack無応答のトラブルシューティングセッション。stale-socketの解決方法まで含む手順

セマンティック検索があることで、表現が微妙に異なる会話でも関連するものが引き出せます。

情報ハブとの連携 — AIエージェントに「過去の自分」を参照させる

この会話DBをさらに発展させると、AIエージェント(OpenClaw)が過去の会話を参照しながら提案や作業を行う仕組みが構築できます。ある運営者が取り組んだ構想では、個人の事業情報・クライアント情報・スケジュールを管理するプライベートリポジトリ(情報ハブ)と会話DBを組み合わせる設計が考えられました。

情報ハブリポジトリとの連携構想

GitHubのプライベートリポジトリに以下のような構造で情報を管理します。

info-hub/├── businesses/           # 事業ごとのフォルダ│   ├── [事業A]/│   │   └── clients/     # クライアント情報│   └── [事業B]/├── STATUS.md             # 現在の優先アクション└── CONTEXT.md            # 私に関する基本情報

OpenClawのcronジョブが毎朝このリポジトリと会話DBを参照し、「未回答の問い合わせがある」「先週調べたKWに関連する記事提案」などのアクションを提示する仕組みです。

OpenClawがDBを参照して提案する実例

実際の活用シーンでは次のような動作が期待できます。

  • OpenClawがSlackに「先週のキーワード調査結果を参照したところ、[KW名]の記事がまだ未着手です。今日書きますか?」と提案する
  • 「○○スキルの設定方法がわからない」というSlackからの質問に対して、過去の関連セッションを検索してから回答する
  • 毎朝の未回答メール確認時に、「このクライアントとは先月のセッションでXの件を話し合っていました」と文脈を補足する

会話DBは「AIエージェントの長期記憶」として機能し、毎回ゼロから文脈を提供しなくても済む状態が実現します。

\ AI活用の「次の一手」を一緒に考えませんか /

法人向けClaude Code個別指導の無料相談はこちら

構築時に遭遇したトラブルと解決策

実際に構築を進めた際にいくつかのトラブルが発生しました。同じ問題で詰まる人のために共有します。

Supabase接続認証エラーの対処

SupabaseのサービスロールキーとURLは環境変数として管理するのが推奨ですが、Claude Codeの場合はmacOSのKeychainを使うと安全です。

import subprocessdef get_keychain_value(account_name, key_name):    result = subprocess.run(        ['security', 'find-generic-password',         '-a', account_name,         '-s', key_name,         '-w'],        capture_output=True, text=True    )    return result.stdout.strip()# 使用例supabase_url = get_keychain_value('chat-history', 'SUPABASE_URL')supabase_key = get_keychain_value('chat-history', 'SUPABASE_SERVICE_ROLE_KEY')

.env ファイルに直書きするよりも安全で、Claude Codeが誤って読み込む心配がありません。

テキストが長すぎる場合のチャンク分割

一部のアシスタントメッセージは非常に長く(数千文字に及ぶコードブロック含む)、そのままEmbedding APIに渡すとトークン制限(8,191トークン)を超えてエラーになります。長いメッセージは500〜1000文字程度にチャンク分割して、複数レコードとして登録する処理が必要です。

def chunk_text(text, chunk_size=800, overlap=100):    """テキストをオーバーラップ付きで分割する"""    chunks = []    start = 0    while start < len(text):        end = min(start + chunk_size, len(text))        chunks.append(text[start:end])        start += chunk_size - overlap    return chunks

Mac miniとMacBookで会話IDが重複する問題

異なるマシンで独立して生成されたUUIDが偶然衝突することは極めてまれですが、セッションIDに {machine_id}-{session_uuid} の形式を使うことで完全に回避できます。machine_id はmacOSのシリアル番号などから生成する方法が確実です。

import subprocessdef get_machine_id():    """macOSのシリアル番号を機械IDとして使用"""    result = subprocess.run(        ['system_profiler', 'SPHardwareDataType'],        capture_output=True, text=True    )    for line in result.stdout.split('\n'):        if 'Serial Number' in line:            return line.split(':')[1].strip()    return 'unknown'

よくある質問

Q. Claude Codeの会話履歴はどこに保存されていますか?

macOSの場合、~/.claude/projects/[プロジェクトID]/ 以下に .jsonl 形式で保存されています。プロジェクトIDはプロジェクトのディレクトリパスから自動生成されます。ls ~/.claude/projects/ で確認できます。

Q. Supabaseの無料プランで運用できますか?

無料プランでも十分スタートできます。無料プランの制限は500MBのデータベース容量と50,000行。数千セッションの会話であれば余裕を持って収まります。埋め込みベクトルは1行あたり1536次元×4バイト=約6KBなので、1万メッセージで約60MBです。本格的に使い込んで容量が増えてきたらProプラン($25/月)への移行を検討してください。

Q. セマンティック検索とキーワード検索の違いは何ですか?

キーワード検索は「指定した単語が含まれているか」で検索するのに対して、セマンティック検索は「意味的に近いか」で検索します。例えば「Webhook設定」と検索した場合、キーワード検索では「Webhook」という単語が含まれる文書しかヒットしませんが、セマンティック検索では「HTTPリクエストのエンドポイント設定」や「イベント通知のURL設定」など、類似した意味を持つ会話も引き出せます。

Q. OpenClaw(AIエージェント)との連携は難しいですか?

chat-searchスキルとしてClaude Codeに登録しておけば、OpenClaw経由でも自然に呼び出せます。「Slack連携の設定方法を過去のセッションから検索して」というSlack上のメッセージで、OpenClawがDBを検索して回答する仕組みが構築できます。OpenClawのセットアップ自体は別途必要ですが、スキルの追加は数行の設定ファイル修正で完了します。(OpenClawでClaude Codeを24時間Slack常駐させる完全ガイド →

まとめ

Claude Codeの会話ログを活用するポイントをまとめます。

  • 会話ログはJSONL形式でローカルに存在する: ~/.claude/projects/ 以下を確認すれば、これまでの会話の全量が把握できます
  • Supabase+pgvectorで低コストにDB化できる: 無料プランでも十分機能するベクトルDB基盤が構築可能です
  • chat-syncスキルで自動同期: cronジョブと組み合わせれば、毎日の会話を自動でDBに取り込めます
  • 情報ハブとの連携でAIエージェントの記憶に: 会話DBはOpenClawなどのAIエージェントの長期記憶として機能し、文脈を持ったアシスタントが実現します

Claude Codeを使うほど会話が蓄積され、検索基盤の価値が高まります。「どのセッションだったか」と悩む時間を減らし、過去の知識をより効果的に活用するための一歩として、まずはJSONLファイルの場所の確認から始めてみてください。


Claude Codeの導入・活用をサポートします

株式会社Nexaでは、Claude Codeを活用した業務自動化の個別指導・企業研修を提供しています。会話履歴のDB化をはじめ、スキル設計・OpenClaw連携・自動化ワークフロー構築まで、非エンジニアの方でも3ヶ月で実務に活用できるプログラムです。「何から始めればいいかわからない」という段階からご支援いたします。詳しい設定方法やカスタマイズは無料相談でお伝えしています。

Claude Code個別指導の詳細・無料相談はこちら →




関連記事

AIの力で、ビジネスを次のステージへ

まずはお気軽にご相談ください。貴社に最適なAI活用プランをご提案します。

Claude Codeのプロに無料相談 30秒で日程調整完了