Claude にあなたの会社、メモ、ファイルについて質問しても、何も答えられません。それらを見たことがないからです。Claude はトレーニングで学んだことしか知らず、あなたの情報はその一部ではありません。
RAG システムがこれを解決します。Claude は記憶から答える代わりに、まずあなたのドキュメントを検索し、関連する部分を取得して、実際に見つけた情報から回答します。あなたのデータをソースとし、推測は行いません。
ファイルをチャットに貼り付けるより優れている理由:
スケーラブルです。 知識ベース全体を 1 つのチャットに収めることはできません。RAG はすべてを保存し、各質問に必要なものだけを取得します。
コスト効率が良いです。 ファイルを貼り付けると、Claude は質問のたびに全体を読み直します。RAG は一度読んで、関連部分だけを取得します。毎回 10,000 トークンのマニュアルを送る代わりに、必要なセクションの 500 トークンだけを送る可能性があります。実際の設定ではトークン使用量が 80% 以上削減されます。
精度が高いです。 巨大なテキストの壁をモデルに与えると、途中の詳細を見失います。正確なチャンクをいくつか渡すと、回答の精度が上がります。
最新の状態を保ちます。 ファイルを一度更新すれば、システムは新しいバージョンを使用します。再貼り付けは不要です。
このガイドを終えると、自分のファイルで動作する RAG システムを手順を追って構築できます。博士号は必要ありません。
必要なもの
コードに触れる前に、完全なリストを紹介します。朗報です:このバージョンでは API キーが 1 つだけで、他はすべて自分のマシンで無料で動作します。
- Python 3.9 またはそれ以降。 インストールされているか確認するには、ターミナル(Mac の場合は Terminal、Windows の場合はコマンドプロンプト)を開き、python --version と入力します。3.11 のような表示があれば問題ありません。ない場合は、python.org からダウンロードしてインストーラを実行してください。Windows の場合は、セットアップ中に「Add Python to PATH」にチェックを入れてください。そうしないと、以下のコマンドが動作しません。
- 1 つの Claude API キーと少額のクレジット残高。 これがこのガイド全体で必要な唯一のキーであり、唯一のお金です。正確な手順は以下の通りです:
platform.claude.com にアクセスし、ログイン(またはサインアップ)してください。
API を実行するには残高が必要なので、まず資金を追加します。プロンプトが表示されたら、クレジットが自分用か会社用かを選択し、支払い画面に進みます。$5 の「Starting out」 オプションを選択してください。それで十分です:このガイドの他のすべては無料でローカルで動作するため、Claude だけが費用がかかり、各質問は 1 セントの何分の 1 です。クレジットは購入から 1 年間有効です。
支払い後、コンソールのダッシュボードに移動します。左上の「Organization credits」の下に残高(例:$5.00)が表示されるはずです。
次にキーを取得します。Get API key(右上)をクリックし、Create Key をクリックします。任意の名前(例:my-rag-key)を付け、ワークスペースは Default のままにします。作成をクリックし、表示された文字列をコピーします。これは sk-ant- で始まり、一度しか表示されないので、安全な場所に一時的に貼り付けてください。
これでセットアップは完了です。
ステップ 1: キーを追加してファイルを読み込む
- プロジェクトフォルダを作成します。 デスクトップに新しいフォルダを作成し、rag-project という名前を付けます。すべてはここに置きます。
- ターミナルを開きます。 Mac の場合:Cmd+Space を押し、Terminal と入力して Enter。Windows の場合:スタートボタンを押し、cmd と入力して Enter。
- ターミナルをフォルダに移動します。 cd とスペースを入力し、rag-project フォルダをターミナルウィンドウにドラッグして Enter を押します。以下のコマンドはすべてこのフォルダ内で実行します。
1cd Desktop/rag-project
- ツールをインストールします。 これをターミナルに貼り付けて Enter を押します(初回実行は 1 分ほどかかる場合があります):
1pip install anthropic chromadb sentence-transformers pypdf python-dotenv
pip: command not found と表示された場合は、pip の代わりに pip3 を使用してください。ターミナルに新しい行が表示され、赤いエラーがなければ完了です。
- コードファイルを作成します。 rag-project 内に、rag.py という名前の空のファイルを作成します。任意のテキストエディタで開いてください。
- キーファイルを作成します。 同じフォルダ内に、 .env という名前のファイルを作成します(ドットで始まり、前に名前はありません)。中に以下を貼り付け、セットアップ時に作成した実際のキーを = の後に、スペースや引用符なしで記述します:
1ANTHROPIC_API_KEY=sk-ant-paste-your-real-key-here
キーをコードではなく .env に保存することで、スクリプトを共有したり GitHub に置いたりしても漏洩しません。
- キーを読み込みます。 これを rag.py の先頭に追加します:
1import os2from dotenv import load_dotenv34load_dotenv() # reads your .env file5api_key = os.getenv("ANTHROPIC_API_KEY")
- ナレッジベースを作成します。 rag-project 内に documents という名前のフォルダを作成します。.txt、.md、.pdf ファイルをそこに置きます:メモ、製品ドキュメント、会議の要約など、何でも構いません。
8.1. まだファイルがない場合は、このテストファイルを使用します。 documents フォルダ内に notes.txt を作成し、以下を貼り付けます:
プロジェクト Northstar は、顧客フィードバックを追跡するための社内ツールです。2026 年 3 月に開始され、プラットフォームチームが管理しています。主任エンジニアは Dana Reyes です。フィードバックは毎週金曜日にレビューされます。Northstar は、2025 年まで使用していた古いスプレッドシートシステムに代わるものです。
最後に、Claude に Northstar について質問し、このファイルから回答するのを確認します。
- ファイルを読み込むコードを追加します。 ステップ 7 のコードの下、rag.py に:
1from pathlib import Path2from pypdf import PdfReader34def load_documents(folder="documents"):5 docs = []6 for file in Path(folder).iterdir():7 if file.suffix in [".txt", ".md"]:8 text = file.read_text(encoding="utf-8")9 docs.append({"source": file.name, "text": text})10 elif file.suffix == ".pdf":11 reader = PdfReader(str(file))12 text = "\n".join(page.extract_text() or "" for page in reader.pages)13 docs.append({"source": file.name, "text": text})14 return docs1516documents = load_documents()17print(f"Loaded {len(documents)} document(s).")
- 実行します。 rag.py を保存し、ターミナルで:
1python rag.py
次のように表示されるはずです:
1 件のドキュメントを読み込みました。
Loaded 0 document(s) と表示された場合、documents フォルダが空か、間違った場所にあります。rag-project の中に直接、rag.py の隣に配置する必要があります。
ステップ 2: ファイルをチャンクに分割する
現在、各ファイルは 1 つの大きなテキストブロックです。検索可能にするには、小さな断片(チャンク)に分割する必要があります。その理由:質問があった場合、システムは一致するチャンクを見つけ、それだけを Claude に送信します。チャンクが 50 ページのドキュメント全体だと、送信量が多すぎます。単一の文だと、コンテキストが失われます。小さな段落が最適です。
- チャンク化コードを追加します。 ステップ 10 のコードの下、rag.py に:
1def chunk_text(text, chunk_size=500, overlap=100):2 words = text.split()3 chunks = []4 start = 05 while start < len(words):6 end = start + chunk_size7 chunk = " ".join(words[start:end])8 chunks.append(chunk)9 start = end - overlap # step back a little so chunks overlap10 return chunks
ここで理解すべき 2 つの数値について、平易に説明します:
- chunk_size=500 は、各チャンクが約 500 単語であることを意味します。完全なアイデアを保持するのに十分大きく、精度を保つのに十分小さいサイズです。
- overlap=100 は、各チャンクが前のチャンクの最後の 100 単語を繰り返すことを意味します。これは、回答が 2 つのチャンクの境界に位置する可能性があるため重要です。オーバーラップがないと、途中で分割された文が失われる可能性があります。オーバーラップにより、アイデアが隙間から落ちるのを防ぎます。
- すべてのドキュメントをチャンクに変換します。 以下を追加します:
1all_chunks = []2for doc in documents:3 for chunk in chunk_text(doc["text"]):4 all_chunks.append({"source": doc["source"], "text": chunk})56print(f"Created {len(all_chunks)} chunk(s) from {len(documents)} document(s).")
各チャンクには ソース(元のファイル名)が付いていることに注意してください。これをずっと保持しておくことで、後で Claude が回答する際に、回答がどのファイルから来たかを教えてくれます。
- 実行します。 rag.py を保存し、ターミナルで:
1python rag.py
次のような表示がされるはずです:
1 件のドキュメントを読み込みました。
1 件のドキュメントから 1 つのチャンクを作成しました。
小さなテストファイルは短いため、1 つのチャンクになります。実際のドキュメントでは多数のチャンクが生成されます。長い PDF をフォルダに置いた場合、数十から数百のチャンクが表示されるでしょう。それが望ましい状態です。
ステップ 3: チャンクを埋め込みに変換する
このステップにより、コンピュータは正確な単語ではなく意味で検索できるようになります。各チャンクは、その内容を捉えた数値のリスト(埋め込み)に変換されます。意味が似ているチャンクは、類似した数値になります。後で質問が来たら、質問も数値に変換し、最も近い一致を見つけます。
この処理を行うモデルは、あなたのマシン上でローカルに実行されます。一度ダウンロードすれば、その後はオフラインで無料で動作し、ファイルがコンピュータの外に出ることはありません。
- 埋め込みモデルを読み込みます。 ステップ 2 のコードの下、rag.py に:
1from sentence_transformers import SentenceTransformer23print("Loading the embedding model (first run downloads it, about 90 MB)...")4embedder = SentenceTransformer("all-MiniLM-L6-v2")
初回実行時にはモデルをダウンロードするため、少し時間がかかります。2 回目以降は、マシン上に既にあるため即座に実行されます。
- すべてのチャンクを埋め込みに変換します。 以下を追加します:
1chunk_texts = [chunk["text"] for chunk in all_chunks]2embeddings = embedder.encode(chunk_texts)34print(f"Created {len(embeddings)} embedding(s).")5print(f"Each embedding is a list of {len(embeddings[0])} numbers.")
embedder.encode(...) はチャンクテキストのリストを受け取り、チャンクごとに 1 つの埋め込みを返します。これだけです。
- 実行します。 rag.py を保存し、ターミナルで:
1python rag.py
初回実行時はモデルのダウンロード中に一時停止し、その後次のような表示がされるはずです:
1 件のドキュメントを読み込みました。
1 件のドキュメントから 1 つのチャンクを作成しました。
埋め込みモデルを読み込み中(初回は約 90 MB をダウンロードします)...
1 つの埋め込みを作成しました。
各埋め込みは 384 個の数値のリストです。
この「384 個の数値」という行が、アイデアを可視化しています:テキストがコンピュータで比較可能な数値の行になりました。自分でそれらの数値を読んだり理解したりする必要はありません。次のステップのデータベースがすべての比較を処理します。
接続エラーでダウンロードが失敗した場合は、もう一度コマンドを実行してください。中断したところから再開します。
ステップ 4: すべてをベクターデータベースに保存する
次に、チャンクとその埋め込みを Chroma(ローカルデータベース)に保存します。これにより検索が高速になります:毎回手動で質問をすべてのチャンクと比較する代わりに、Chroma が準備済みのチャンクを保存し、マッチングを行います。マシン上のフォルダに保存されるため、一度構築するだけです。
- データベースをセットアップします。 ステップ 3 のコードの下、rag.py に:
1import chromadb23client = chromadb.PersistentClient(path="chroma_db")4collection = client.get_or_create_collection("my_documents")
PersistentClient(path="chroma_db") は、Chroma に chroma_db という名前のフォルダに保存するよう指示します(スクリプトの隣に自動的に作成されます)。ディスクに保存されるため、スクリプト終了後もデータは保持されます。collection は、チャンクが格納される名前付きの箱です。
- チャンクをデータベースに追加します。 以下を追加します:
1collection.add(2 ids=[str(i) for i in range(len(all_chunks))],3 embeddings=[emb.tolist() for emb in embeddings],4 documents=[chunk["text"] for chunk in all_chunks],5 metadatas=[{"source": chunk["source"]} for chunk in all_chunks],6)78print(f"Stored {collection.count()} chunk(s) in the database.")
各ラインが Chroma に渡す内容を平易に説明します:ids は各チャンクに一意のラベル(0, 1, 2...)を付け、embeddings はステップ 3 の数値、documents は実際のチャンクテキスト、metadatas はファイル名を保持し、後でソースを表示できるようにします。Chroma はこれら 4 つを関連付けて保持します。
- 実行します。 rag.py を保存し、ターミナルで:
1python rag.py
次のように表示されるはずです:
データベースに 1 つのチャンクを保存しました。
後で知っておくべきこと。 現在、スクリプトを実行するたびにチャンクが再度追加されるため、繰り返し実行するとカウントが増加します(1、2、3...)。構築中の間は問題ありません。クリーンな状態から始めるには、chroma_db フォルダを削除してもう一度実行してください。最終バージョンでは、重複しないように適切に処理します。
ステップ 5: ドキュメントを検索する
これが RAG の「検索」部分、つまり R です。質問を受け取り、チャンクと同じ方法で埋め込みに変換し、Chroma に意味が最も近いチャンクを要求します。一致したチャンクを次のステップで Claude に渡します。
- 検索関数を追加します。 ステップ 4 のコードの下、rag.py に:
1def search(question, n_results=3):2 question_embedding = embedder.encode([question])[0]3 results = collection.query(4 query_embeddings=[question_embedding.tolist()],5 n_results=n_results,6 )7 return results
これが行うことを平易に説明します:チャンクに使用したのと同じモデルで質問を数値に変換し(両方が同じ「数値言語」を話す必要があるため重要)、Chroma に最も近い一致を要求します。n_results=3 は「最も関連性の高い 3 つのチャンクを返す」ことを意味します。3 は適切なデフォルトです:十分なコンテキストを提供し、トークンを無駄にしすぎません。
- 検索を試してみます。 以下を追加してテストします:
1question = "Who runs Northstar and when is feedback reviewed?"2results = search(question)34for i, doc in enumerate(results["documents"][0]):5 source = results["metadatas"][0][i]["source"]6 print(f"\n--- Match {i+1} (from {source}) ---")7 print(doc)
これにより、データベースに対して実際の質問を実行し、見つかったチャンクをそれぞれのファイル名とともに表示します。
- 実行します。 rag.py を保存し、ターミナルで:
1python rag.py
Northstar のテストファイルを使用すると、一致するチャンクが次のように返されるはずです:
--- 一致 1 (notes.txt から) ---
プロジェクト Northstar は、顧客フィードバックを追跡するための社内ツールです。2026 年 3 月に開始され、プラットフォームチームが管理しています。主任エンジニアは Dana Reyes です。フィードバックは毎週金曜日にレビューされます。Northstar は、2025 年まで使用していた古いスプレッドシートシステムに代わるものです。
何が起こったかに注目してください:あなたの質問は「who runs」と「reviewed」という単語を使用しましたが、ファイルには「lead engineer」と「reviewed every Friday」とあります。それでも一致しました。なぜなら検索は意味で動作し、正確な単語ではないからです。これが埋め込みの要点であり、ファイル内の単純なキーワード検索(Ctrl+F)よりも優れている理由です。
さらにファイルがある場合、すべてのファイルから上位 3 つのチャンクが、一致度の高い順に表示されます。
ステップ 6: Claude が見つけた情報から回答を得る
これが RAG の「生成」部分、つまり G です。ステップ 5 のチャンクを取得し、質問とともに Claude Opus 4.8 に渡し、そのコンテキストのみを使用して回答するよう指示します。これにより推測が防止されます:Claude は自身の記憶ではなく、ファイルから回答し、使用したファイルを教えてくれます。
- 回答関数を追加します。 ステップ 5 のコードの下、rag.py に:
1import anthropic23claude = anthropic.Anthropic(api_key=api_key)45def answer(question):6 results = search(question)7 chunks = results["documents"][0]8 sources = [m["source"] for m in results["metadatas"][0]]910 context = ""11 for i, chunk in enumerate(chunks):12 context += f"[From {sources[i]}]\n{chunk}\n\n"1314 message = claude.messages.create(15 model="claude-opus-4-8",16 max_tokens=1024,17 system=(18 "You answer questions using only the context provided. "19 "If the answer is not in the context, say you don't know. "20 "Always mention which file your answer came from."21 ),22 messages=[23 {24 "role": "user",25 "content": f"Context:\n{context}\nQuestion: {question}",26 }27 ],28 )29 return message.content[0].text
ここで行われていることを平易に説明します:関連するチャンクを検索し、それらを 1 つの context ブロック(それぞれにファイル名がラベル付けされています)にまとめ、そのブロックと質問を Claude に送信します。system 指示が重要な部分です。Claude に 3 つのことを指示します:コンテキストのみから回答すること、回答がない場合は認めること、ソースファイルを指定すること。これらの 3 つのルールにより、回答は作り話ではなく信頼できるものになります。
model="claude-opus-4-8" は正確なモデル名です(ドットではなくダッシュ)。max_tokens=1024 は回答の最大長を制限します。
- 質問をします。 以下を追加します:
1question = "Who runs Northstar and when is feedback reviewed?"2print(answer(question))
- 実行します。 rag.py を保存し、ターミナルで:
1python rag.py
ファイルから構築された実際の回答が次のように表示されるはずです:
Dana Reyes がプロジェクト Northstar を担当する主任エンジニアであり、フィードバックは毎週金曜日にレビューされます。(出典:notes.txt)
これで完全な RAG システムが動作しています。Claude はトレーニング中にこのファイルを見たことがなく、Dana Reyes が誰かを知りませんが、正しく回答し、回答の出典を正確に示しました。ファイルにないことを質問すると、回答をでっち上げるのではなく、知らないと言います。その「わかりません」は機能であり、欠陥ではありません:信頼できるツールと推測するツールの違いです。
ステップ 7: 実際に使えるものにする
現在、質問するたびにコードを編集してスクリプト全体を再実行する必要があります。さらに悪いことに、実行ごとにファイルを再読み込みしてデータベースに再追加するため、チャンクが蓄積されます。両方を修正しましょう:データベースを一度だけ構築し、ループで質問を入力できるようにし、ターミナルに直接入力できるようにします。
- 重複追加を修正します。 ステップ 4 のチャンクを追加するブロック(collection.add(...) の部分)を見つけ、データベースが空の場合のみ構築するこのバージョンに置き換えます:
1if collection.count() == 0:2 collection.add(3 ids=[str(i) for i in range(len(all_chunks))],4 embeddings=[emb.tolist() for emb in embeddings],5 documents=[chunk["text"] for chunk in all_chunks],6 metadatas=[{"source": chunk["source"]} for chunk in all_chunks],7 )8 print(f"Stored {collection.count()} chunk(s) in the database.")9else:10 print(f"Database already has {collection.count()} chunk(s), skipping rebuild.")
これで、重い処理(ファイルの読み込み、埋め込みの作成、データベースへの追加)は初回のみ行われます。以降の実行は直接回答にスキップします。
- 質問ループを追加します。 rag.py の最下部で、ステップ 6 の単一テスト質問を以下に置き換えます:
1print("\nAsk a question about your documents (or type 'quit' to exit).\n")23while True:4 question = input("You: ")5 if question.lower() in ["quit", "exit"]:6 break7 print("\nClaude: " + answer(question) + "\n")
input("You: ") は、質問を入力して Enter を押すのを待ちます。while True はそれを続け、好きなだけ質問できます。quit と入力すると停止します。
- 実行します。 rag.py を保存し、ターミナルで:
1python rag.py
これで、ファイルと会話できます:
ドキュメントについて質問してください(終了するには 'quit' と入力)。
あなた:Northstar の主任エンジニアは誰ですか?
Claude:プロジェクト Northstar の主任エンジニアは Dana Reyes です。(出典:notes.txt)
あなた:それに代わるものは何ですか?
Claude:Northstar は、2025 年まで使用されていた古いスプレッドシートシステムに代わるものです。(出典:notes.txt)
あなた:quit
これで完成した RAG システムです。ファイルを一度読み込み、記憶し、オンデマンドで質問に回答し、毎回ソースを提示します。
新しいファイルを追加する際に知っておくべきこと。 データベースは一度だけ構築されるため、documents に新しいファイルを追加しても自動的には反映されません。新しいファイルを読み込むには、chroma_db フォルダを削除してスクリプトを一度実行してください。フォルダ内のすべてのファイルから最初から再構築されます。
オプション:ブラウザにチャットウィンドウを追加する
ターミナルでも動作しますが、実際のチャットウィンドウが必要な場合は、Streamlit で約 20 行で追加できます。
- インストールします。 ターミナルで:
1pip install streamlit
- 同じフォルダに [app.py**](https://app.py/ を作成し、以下を貼り付けます。rag.py の answer 関数を再利用します:
1import streamlit as st2from rag import answer34st.title("Chat with your documents")56if "history" not in st.session_state:7 st.session_state.history = []89question = st.chat_input("Ask about your files...")1011if question:12 reply = answer(question)13 st.session_state.history.append((question, reply))1415for q, a in st.session_state.history:16 st.chat_message("user").write(q)17 st.chat_message("assistant").write(a)
- 実行します。 ターミナルで(注意:streamlit run を使用し、python ではありません):
1streamlit run app.py
自動的にブラウザにチャットウィンドウが開きます。質問を入力し、ソース付きの回答を取得できます。ターミナルと同様ですが、見た目が良くなります。
注意点:これを機能させるには、ステップ 7 の質問ループがインポート時に実行されないようにする必要があります。rag.py の下部にあるループを if __name__ == "__main__": でラップし、rag.py を直接実行したときのみ動作し、app.py がインポートしたときは動作しないようにします。
一般的な質問にも回答できるようにする
一般的な質問にも回答させたい場合。 現在、システムはファイルからのみ回答するため、「ベネズエラの首都は?」という質問に対して、Claude が答えを知っていても「ドキュメントにありません」と返します。自身の知識にフォールバックさせたい場合は、rag.py を開き、ステップ 6 の system=(...) ブロックを見つけ、次の行を置き換えます:
1"答えがコンテキストにない場合は、わからないと言ってください。"
次の場合:
1"答えがコンテキストにない場合は、自分の一般的な知識から答えるが、その旨を伝えてください。"
保存して再実行します。これで、まずファイルから回答し、ファイルに情報がない場合は一般的な知識にフォールバックし、どちらを使用したかを教えてくれます。
まとめ
これで、動作する RAG システムが完成しました。自分のファイルを読み込み、関連する部分を見つけ、Claude がそれらから常に正確なソースを添えて回答する仕組みです。この同じ設定は、ちょっとしたメモから知識ベース全体までスケールします。
ここからは、あなた次第です。Obsidian の保管庫、仕事のドキュメント、保存した研究資料など、どこにでも向けられます。ファイルを置き、一度再構築して、質問を始めるだけです。ここで学んだすべて、チャンク、埋め込み、検索、回答は、あなたがこれまで見てきた「ドキュメントとチャット」ツールの背後にある同じ基盤です。
これが役に立ったなら、私のプロフィールにアクセスしてフォローしてください。テクノロジー、AI、そして実際に動作するシステムについて書いています。
チャオ、
[@undefinedKi]([https://x.com/@undefinedKi](https://x.com/@undefinedKi))





