spinny:~/writing $ vim rag-langchain-deep-dive.md
1~2GPT-4やClaudeのような大規模言語モデル(LLM)は非常に強力ですが、根本的な制限があります。それは、知識がトレーニング時点で凍結されていることです。社内ドキュメント、データベース、リアルタイム情報にアクセスすることができません。**検索拡張生成(RAG)** は、LLMの生成能力と外部ソースから情報を取得する能力を組み合わせることで、まさにこの問題を解決します。3~4## 問題:LLMの限界5~6RAGについて話す前に、なぜそれが必要なのかを理解することが重要です。7~81. **静的な知識**:LLMはトレーニング中に見たものだけを知っています。カットオフ日以降に起きた出来事について質問しても、答えることができません。92. **ハルシネーション**:LLMが答えを知らない場合、もっともらしいが完全に誤った情報を生成する傾向があります。103. **プライベートデータへのアクセス不可**:汎用LLMは、企業の社内ドキュメント、チケット、コードベースにアクセスできません。11~12RAGは、クエリ時に外部ソースから取得した**関連コンテキスト**をモデルに提供することで、これら3つの問題すべてに対処します。13~14## RAGとは?15~16検索拡張生成は、外部のナレッジベースから取得した情報でLLMに送信するプロンプトを強化するアーキテクチャです。モデルのパラメトリックな知識のみに頼るのではなく、RAGはまず関連情報を**検索**し、それをプロンプトに**注入**することで、正確で根拠のある回答を生成できるようにします。17~18```mermaid19graph LR20 User["ユーザー"] -- "質問" --> Retriever21 Retriever -- "関連ドキュメントを\n検索" --> VectorStore["ベクトルストア"]22 VectorStore -- "関連\nドキュメント" --> Retriever23 Retriever -- "コンテキスト + 質問" --> LLM24 LLM -- "根拠のある\n回答" --> User25```26~27## RAGの詳細な仕組み28~29RAGアーキテクチャは2つの主要フェーズで構成されます:**インデキシング**(オフライン)と**検索 + 生成**(オンライン)。30~31### フェーズ1:インデキシング(ドキュメントの取り込み)32~33インデキシングフェーズは、セマンティック検索のためにドキュメントを準備します。4つのステップで構成されます。34~35```mermaid36graph TD37 A["ドキュメント\n(PDF, HTML, MD, DB)"] --> B["ドキュメントローダー"]38 B --> C["テキストスプリッター"]39 C --> D["テキストチャンク"]40 D --> E["エンベディングモデル"]41 E --> F["数値ベクトル"]42 F --> G["ベクトルストア\n(ChromaDB, Pinecone, FAISS)"]43```44~45#### 1. ドキュメントの読み込み46~47ドキュメントはPDFファイル、Webページ、データベース、Markdownファイル、APIなど、あらゆるソースから取得できます。**ドキュメントローダー**はこれらのドキュメントを読み取り、構造化テキストに変換します。48~49#### 2. テキスト分割(チャンキング)50~51LLMのコンテキストウィンドウは限られており、ドキュメントは非常に長くなる可能性があります。**テキストスプリッター**はドキュメントを*チャンク*と呼ばれる小さな断片に分割します。チャンキングの品質は非常に重要です。小さすぎるチャンクはコンテキストを失い、大きすぎるチャンクは関連性を薄めます。52~53最も一般的な戦略は:54- **再帰的文字分割**:`\n\n`、`\n`、`. ` などのセパレータを使用してテキストを再帰的に分割し、ドキュメント構造を尊重します。55- **セマンティック分割**:エンベディングを使用してテキスト内の自然な区切り点を見つけます。56- **チャンクオーバーラップ**:連続するチャンク間にオーバーラップを含め、境界でのコンテキストを保持します。57~58#### 3. エンベディング59~60各チャンクはエンベディングモデル(OpenAIの`text-embedding-3-small`など)を通じて**数値ベクトル**(エンベディング)に変換されます。これらのベクトルはテキストのセマンティックな意味を捉えます。類似した意味を持つ文は、多次元空間で近いベクトルを持ちます。61~62#### 4. ベクトルストア63~64ベクトルはChromaDB、Pinecone、Weaviate、FAISSなどの**ベクトルストア**(ベクトルデータベース)に保存されます。このデータベースは**類似性検索**に最適化されています。クエリが与えられると、最も類似したベクトル(つまり最も関連性の高いテキストチャンク)を見つけます。65~66### フェーズ2:検索 + 生成67~68ユーザーが質問すると:69~701. 質問が同じエンベディングモデルを使用してエンベディングに変換されます。712. ベクトルストアが**類似性検索**(通常はコサイン類似度またはユークリッド距離)で最も類似したチャンクを見つけます。723. 取得されたチャンクがコンテキストとしてプロンプトに挿入されます。734. LLMが提供されたコンテキストに基づいて回答を生成します。74~75## LangChainでRAGパイプラインを構築する76~77**LangChain**は、LLMベースのアプリケーションを構築するための最も人気のあるPython(およびJavaScript)フレームワークです。RAGパイプラインのすべてのコンポーネントに対する高レベルの抽象化を提供します。78~79### インストール80~81```bash82pip install langchain langchain-openai langchain-community chromadb83```84~85### ステップ1:ドキュメントの読み込み86~87LangChainは、さまざまなデータソース用の数十のドキュメントローダーを提供しています。88~89```python90from langchain_community.document_loaders import (91 PyPDFLoader,92 WebBaseLoader,93 DirectoryLoader,94 TextLoader,95)96~97# PDFを読み込む98pdf_loader = PyPDFLoader("docs/manual.pdf")99pdf_docs = pdf_loader.load()100~101# Webページを読み込む102web_loader = WebBaseLoader("https://docs.example.com/guide")103web_docs = web_loader.load()104~105# ディレクトリからすべての.mdファイルを読み込む106dir_loader = DirectoryLoader("./knowledge_base", glob="**/*.md", loader_cls=TextLoader)107md_docs = dir_loader.load()108~109all_docs = pdf_docs + web_docs + md_docs110```111~112### ステップ2:ドキュメントをチャンクに分割113~114```python115from langchain.text_splitter import RecursiveCharacterTextSplitter116~117text_splitter = RecursiveCharacterTextSplitter(118 chunk_size=1000,119 chunk_overlap=200,120 separators=["\n\n", "\n", ". ", " ", ""],121)122~123chunks = text_splitter.split_documents(all_docs)124print(f"元のドキュメント: {len(all_docs)}, チャンク: {len(chunks)}")125```126~127`chunk_overlap`パラメータは重要です。連続するチャンク間にオーバーラップを作成し、境界でコンテキストが失われないようにします。128~129### ステップ3:エンベディングとベクトルストアの作成130~131```python132from langchain_openai import OpenAIEmbeddings133from langchain_community.vectorstores import Chroma134~135embedding_model = OpenAIEmbeddings(model="text-embedding-3-small")136~137vectorstore = Chroma.from_documents(138 documents=chunks,139 embedding=embedding_model,140 persist_directory="./chroma_db",141)142```143~144### ステップ4:レトリーバーの作成145~146レトリーバーは、クエリが与えられるとベクトルストアから最も関連性の高いチャンクを取得するコンポーネントです。147~148```python149retriever = vectorstore.as_retriever(150 search_type="similarity",151 search_kwargs={"k": 4},152)153~154relevant_docs = retriever.invoke("認証はどのように機能しますか?")155for doc in relevant_docs:156 print(doc.page_content[:200])157 print("---")158```159~160### ステップ5:RAGチェーンの構築161~162LLMとプロンプトテンプレートですべてを組み合わせます。163~164```python165from langchain_openai import ChatOpenAI166from langchain_core.prompts import ChatPromptTemplate167from langchain_core.runnables import RunnablePassthrough168from langchain_core.output_parsers import StrOutputParser169~170llm = ChatOpenAI(model="gpt-4o", temperature=0)171~172prompt = ChatPromptTemplate.from_template("""173提供されたコンテキストのみに基づいて質問に回答してください。174コンテキストに十分な情報がない場合は、分からないと言ってください。175~176コンテキスト:177{context}178~179質問:{question}180~181回答:182""")183~184def format_docs(docs):185 return "\n\n".join(doc.page_content for doc in docs)186~187rag_chain = (188 {"context": retriever | format_docs, "question": RunnablePassthrough()}189 | prompt190 | llm191 | StrOutputParser()192)193~194response = rag_chain.invoke("システムの認証はどのように機能しますか?")195print(response)196```197~198## 高度なRAGテクニック199~200基本的なパイプラインは十分に機能しますが、回答の品質を大幅に向上させるいくつかのテクニックがあります。201~202### マルチクエリ検索203~204ユーザーのクエリが曖昧だったり、ドキュメントで使用されている言語と一致しない場合があります。**マルチクエリレトリーバー**は、複数の視点を捉えるために元の質問のバリエーションを自動的に生成します。205~206```python207from langchain.retrievers import MultiQueryRetriever208~209multi_retriever = MultiQueryRetriever.from_llm(210 retriever=vectorstore.as_retriever(),211 llm=llm,212)213~214docs = multi_retriever.invoke("セキュリティのベストプラクティスは何ですか?")215```216~217### コンテキスト圧縮218~219チャンクのすべてのコンテンツがクエリに関連しているわけではありません。**コンテキスト圧縮レトリーバー**は、LLMを使用して各取得チャンクから関連部分のみを抽出します。220~221```python222from langchain.retrievers import ContextualCompressionRetriever223from langchain.retrievers.document_compressors import LLMChainExtractor224~225compressor = LLMChainExtractor.from_llm(llm)226compression_retriever = ContextualCompressionRetriever(227 base_compressor=compressor,228 base_retriever=retriever,229)230```231~232### ハイブリッド検索233~234純粋なセマンティック検索が常に最適とは限りません。**ハイブリッド検索**はセマンティック検索(エンベディング)と字句検索(BM25、キーワードマッチング)を組み合わせて、より良い結果を得ます。235~236```python237from langchain.retrievers import EnsembleRetriever238from langchain_community.retrievers import BM25Retriever239~240bm25_retriever = BM25Retriever.from_documents(chunks)241bm25_retriever.k = 4242~243semantic_retriever = vectorstore.as_retriever(search_kwargs={"k": 4})244~245hybrid_retriever = EnsembleRetriever(246 retrievers=[bm25_retriever, semantic_retriever],247 weights=[0.4, 0.6],248)249```250~251### 会話型RAG(メモリ付き)252~253会話のコンテキストを記憶するRAGチャットボットを構築するには、会話履歴を考慮してユーザーの質問を再定式化するメモリを追加する必要があります。254~255```python256from langchain.chains import create_history_aware_retriever257from langchain_core.prompts import MessagesPlaceholder258~259contextualize_prompt = ChatPromptTemplate.from_messages([260 ("system", "チャット履歴とユーザーの最新の質問を考慮して、"261 "履歴なしでも理解できるように質問を再定式化してください。"),262 MessagesPlaceholder("chat_history"),263 ("human", "{input}"),264])265~266history_aware_retriever = create_history_aware_retriever(267 llm, retriever, contextualize_prompt268)269```270~271## ベストプラクティス272~2731. **適切なチャンクサイズを選択する**:さまざまなサイズ(500〜1500トークン)で実験してください。精確な回答には小さいチャンク、広いコンテキストには大きいチャンクを使用します。2742. **ドキュメントのメタデータを使用する**:ソース、日付、カテゴリをチャンクのメタデータとして追加します。これにより、検索時に結果をフィルタリングできます。2753. **品質を評価する**:[RAGAS](https://docs.ragas.io/)などのフレームワークを使用して、*faithfulness*、*relevancy*、*context precision*などのメトリクスを測定します。2764. **ドキュメントの更新を管理する**:ベクトルストアをデータソースと同期させるための再取り込みパイプラインを実装します。2775. **リランカーを追加する**:初期検索後、リランキングモデル(Cohere Rerankなど)を使用して、実際の関連性に基づいて結果を並べ替えます。278~279## まとめ280~281RAGは、特定の最新知識へのアクセスが必要なAIアプリケーションを構築するための標準アーキテクチャになりました。LangChainは、パイプラインのすべてのコンポーネントに対する抽象化を提供することで、実装を大幅に簡素化します。282~283**次のステップ:**284- **ローカルで実験する**:ChromaDBと少数のドキュメントから始めて、パイプラインに慣れましょう。285- **LangSmithを探索する**:[LangSmith](https://smith.langchain.com/)を使用して、本番環境でチェーンを監視およびデバッグしましょう。286- **さまざまなエンベディングモデルを試す**:`text-embedding-3-small`、`text-embedding-3-large`、Sentence Transformersのオープンソースモデルを比較しましょう。287- **ドキュメントを参照する**:[LangChainドキュメント](https://python.langchain.com/docs/)は優れた、常に更新されるリソースです。288~
NORMAL · rag-langchain-deep-dive.md [readonly]288 lines · :q to close