Skills MCP Model 博客 提交 Skills

DeepSeek RAG ナレッジベース構築チュートリアル

ゼロから始める、DeepSeek と LangChain を使ったエンタープライズ向けナレッジベースQ&Aシステム。ドキュメントの読み込み、ベクトル埋め込み、セマンティック検索、ソース引用付きのインテリジェントQ&A、完全なコードですぐに学べます。

学習を始める

RAGとは何か、なぜ必要なのか?

RAG(Retrieval-Augmented Generation、検索拡張生成)は、大規模モデルがプライベートな知識に回答するための核となる技術です。RAGがなければ、大規模モデルはトレーニングデータ内のコンテンツにしか回答できません。RAGがあれば、大規模モデルはあなたのドキュメント、データベース、知識ベースに基づいて正確に回答できます。

RAG原理の概要

RAGの仕組みを理解することは、高品質な知識ベースを構築するための前提条件です。RAGは、検索(Retrieval)、拡張(Augmented)、生成(Generation)の3つの中核的な段階で構成されています。

RAGの3つの中核的な段階

Retrieval(検索) ユーザーの質問をベクトルに変換し、ベクトルデータベース内で最も関連性の高いドキュメントチャンクを見つけます。これはRAGの「検索」段階です。
Augmented(拡張) 検索されたドキュメントチャンクをコンテキストとしてプロンプトに連結します。これはRAGの「知識注入」段階です。
Generation(生成) DeepSeekモデルは拡張されたプロンプトに基づいて回答を生成します。これはRAGの「出力」段階です。

RAGアーキテクチャのフロー

完全なRAGアーキテクチャは、オフライン段階とオンライン段階の2つに分かれます。

オフライン段階(知識ベース構築):

  • ドキュメントの読み込み:PDF、TXT、Markdown、Webページなど、さまざまなドキュメントを読み取ります。
  • テキスト分割:長いドキュメントを適切なサイズのテキストチャンクに分割します。
  • ベクトル埋め込み:テキストチャンクをベクトル表現に変換します。
  • ベクトルストレージ:ベクトルをChromaDBなどのベクトルデータベースに保存します。

オンライン段階(Q&A):

  • ユーザーの質問:ユーザーの入力質問を受け取ります。
  • ベクトル検索:質問をベクトルに変換し、データベース内で最も類似したテキストチャンクを見つけます。
  • コンテキスト連結:検索結果をコンテキストとして使用し、拡張プロンプトを構築します。
  • モデル生成:DeepSeekは拡張プロンプトに基づいて回答を生成し、ソースの引用を含めます。

RAG vs ファインチューニング

比較項目 RAG ファインチューニング
知識の更新 リアルタイム更新。ドキュメントの追加・削除が即座に反映されます。 再トレーニングが必要で、サイクルが長い。
コスト 低い。ベクトルデータベースのみで済みます。 高い。GPUトレーニングリソースが必要です。
解釈可能性 特定のドキュメントにトレース可能です。 ブラックボックスで、知識の出所を追跡するのが難しい。
適用シナリオ 知識ベースQ&A、ドキュメント検索、カスタマーサービスシステム。 スタイル模倣、特定分野の用語、指示追従

ほとんどの企業シナリオでは、RAGとファインチューニングの組み合わせを推奨します:動的な知識にはRAG、モデルの動作最適化にはファインチューニングを使用します。モデルの詳細については、DeepSeekモデルアーキテクチャ解説をご覧ください。

環境準備

必要なPython依存パッケージをインストールします。以下のコマンドで、RAG開発に必要なすべてのライブラリを一度にインストールできます。

コア依存関係のインストール

# 仮想環境の作成(推奨) python -m venv rag-env # Windowsでのアクティベート rag-env\Scripts\activate # macOS/Linuxでのアクティベート # source rag-env/bin/activate # コア依存関係のインストール pip install langchain langchain-community chromadb sentence-transformers openai # ドキュメントローダーのインストール(必要に応じて選択) pip install pypdf # PDF読み込み pip install unstructured # 汎用ドキュメント読み込み(PDF/Word/PPT/HTML) pip install beautifulsoup4 # Webページ解析 pip install lxml # XML/HTML解析の高速化 # インストールの確認 python -c "import langchain; import chromadb; print('RAG環境の準備完了')"

DeepSeek API設定

RAGシステムは生成モデルとしてDeepSeek APIを使用します。APIキーを保存する環境変数を設定します:

# Windows PowerShell $env:DEEPSEEK_API_KEY = "sk-your-api-key-here" # macOS/Linux # export DEEPSEEK_API_KEY="sk-your-api-key-here" # またはPythonコードで直接設定 import os os.environ["DEEPSEEK_API_KEY"] = "sk-your-api-key-here"

注意

Ollamaを使用してローカルにデプロイしたDeepSeekモデルを使用する場合、APIキーは不要で、APIベースURLをhttp://localhost:11434/v1に指定するだけです。詳細はDeepSeekローカルデプロイチュートリアルをご覧ください。

ドキュメント読み込み

LangChain は、PDF、TXT、Markdown、Web ページ、CSV など、さまざまな形式をサポートする豊富なドキュメントローダーを提供しています。ドキュメントの形式に合ったローダーを選択してください。

PDF ドキュメントの読み込み

from langchain_community.document_loaders import PyPDFLoader # 単一の PDF を読み込む loader = PyPDFLoader("docs/企業年報2025.pdf") pages = loader.load() # 読み込み結果を確認 print(f"読み込んだページ数: {len(pages)}") print(f"最初のページの最初の200文字: {pages[0].page_content[:200]}") # PDF フォルダを一括読み込み from langchain_community.document_loaders import DirectoryLoader loader = DirectoryLoader( "docs/", glob="**/*.pdf", loader_cls=PyPDFLoader, ) documents = loader.load() print(f"合計 {len(documents)} 件のドキュメントを読み込みました")

TXT と Markdown の読み込み

from langchain_community.document_loaders import TextLoader, UnstructuredMarkdownLoader # プレーンテキストファイルを読み込む txt_loader = TextLoader("docs/製品マニュアル.txt", encoding="utf-8") txt_docs = txt_loader.load() # Markdown ファイルを読み込む md_loader = UnstructuredMarkdownLoader("docs/技術ドキュメント.md") md_docs = md_loader.load()

Web ページの読み込み

from langchain_community.document_loaders import WebBaseLoader # 単一の Web ページを読み込む web_loader = WebBaseLoader("https://platform.deepseek.com/api-docs") web_docs = web_loader.load() # 複数の Web ページを一括読み込み urls = [ "https://example.com/doc1", "https://example.com/doc2", ] web_loader = WebBaseLoader(urls) web_docs = web_loader.load()

Unstructured を使用した汎用ドキュメントの読み込み

Unstructured は、PDF、Word、PPT、Excel、HTML、画像など 20 以上の形式をサポートする強力なドキュメント解析ライブラリです。複雑なドキュメントシナリオに推奨されます:

from langchain_community.document_loaders import UnstructuredFileLoader # ファイルタイプを自動検出して解析 loader = UnstructuredFileLoader("docs/製品提案.docx") docs = loader.load() # 複数形式を一括読み込み loader = DirectoryLoader( "docs/", glob="**/*.*", loader_cls=UnstructuredFileLoader, ) all_docs = loader.load() print(f"読み込んだドキュメント断片: {len(all_docs)}")

テキスト分割

テキスト分割はRAGの品質にとって重要なステップです。分割が大きすぎると検索が不正確になり、小さすぎるとコンテキストが失われます。LangChainのRecursiveCharacterTextSplitterが最も推奨されるテキスト分割器です。

RecursiveCharacterTextSplitterの主要パラメータ

パラメータ 説明 推奨値
chunk_size 各テキストチャンクの最大文字数 500-1000
chunk_overlap 隣接するチャンク間の重複文字数 50-200
separators 分割優先順位:最初に段落、次に文、最後に文字 デフォルトで良い

テキスト分割コード

from langchain.text_splitter import RecursiveCharacterTextSplitter # テキスト分割器を作成 text_splitter = RecursiveCharacterTextSplitter( chunk_size=500, # 各チャンクの最大文字数は500 chunk_overlap=100, # 隣接チャンク間の重複は100文字 length_function=len, separators=["\n\n", "\n", "。", "!", "?", ".", " ", ""], ) # ドキュメントを分割 chunks = text_splitter.split_documents(documents) print(f"元のドキュメント数: {len(documents)}") print(f"分割後のチャンク数: {len(chunks)}") # 分割結果を確認 for i, chunk in enumerate(chunks[:3]):

テキスト分割のベストプラクティス

  • chunk_sizeはコンテンツタイプに応じて調整:技術文書は500-800、長文記事は800-1200、FAQ類は300-500
  • chunk_overlapは省略しない:重複により重要な情報が境界で分割されるのを防ぎます。chunk_sizeの10%-20%を推奨
  • メタデータを保持:分割時にドキュメントの出典やページ番号などのメタデータを保持し、後でトレーサビリティを確保
  • 中国語の分割:separatorsに中国語の句読点(。!?)を追加し、意味的な境界で分割する
  • 先にロードしてから分割:完全なドキュメントをロードしてから一括分割し、繰り返しのロードと分割を避ける
print(f"--- チャンク {i+1} ({len(chunk.page_content)} 文字) ---") print(chunk.page_content[:200]) print()

テキスト分割のベストプラクティス

ベクトル埋め込み

ベクトル埋め込みは、テキストを高次元ベクトル(数値の配列)に変換するプロセスです。意味的に類似したテキストは、そのベクトルが空間内で近くなります。これはRAG検索の核となる数学的基盤です。

埋め込みモデルの選択

モデル名 次元 中国語サポート モデルサイズ 推奨シナリオ
BAAI/bge-large-zh-v1.5 1024 非常に良い 1.3GB 中国語ドキュメントに最適
BAAI/bge-small-zh-v1.5 512 非常に良い 96MB 軽量中国語
sentence-transformers/all-MiniLM-L6-v2 384 普通 80MB 英語中心
shibing624/text2vec-base-chinese 768 非常に良い 400MB 中国語セマンティックマッチング

ベクトル埋め込みコード

from langchain_community.embeddings import HuggingFaceEmbeddings # BGE中国語埋め込みモデルを使用(推奨) embedding_model = HuggingFaceEmbeddings( model_name="BAAI/bge-small-zh-v1.5", model_kwargs={"device": "cpu"}, # GPUがある場合は "cuda" に変更 encode_kwargs={"normalize_embeddings": True}, # 正規化して検索精度を向上 ) # 埋め込み効果をテスト test_text = "DeepSeekは強力なオープンソース大規模言語モデルです" embedding = embedding_model.embed_query(test_text)

埋め込みモデル選択の推奨事項

中国語ドキュメントにはBGEシリーズのモデルを強くお勧めします。中国語の意味理解において、一般的な英語モデルよりも明らかに優れています。ローカル展開にはHuggingFaceEmbeddingsを使用して読み込むことで、API費用はかかりません。最高のパフォーマンスと安定性を追求する場合は、クラウドベースの埋め込みAPIを使用できます。

print(f"ベクトル次元: {len(embedding)}") print(f"ベクトルの最初の5つの値: {embedding[:5]}") # DeepSeek APIを埋め込みに使用することもできます(公式サポートされている場合) # またはOpenAI互換の埋め込みAPIを使用 from langchain_openai import OpenAIEmbeddings api_embedding = OpenAIEmbeddings( model="text-embedding-3-small", # または他の互換APIを使用 base_url="https://api.deepseek.com/v1", api_key="sk-your-api-key-here", )

ベクトルデータベース — ChromaDB

ChromaDB は、LLM アプリケーション向けに設計された軽量なオープンソースのベクトルデータベースです。独立したサーバーは不要で、データはローカルファイルに保存されるため、小規模から中規模の RAG アプリケーションに最適です。

ベクトルデータベースの作成と保存

from langchain_community.vectorstores import Chroma # 分割したドキュメントを ChromaDB に保存 vectorstore = Chroma.from_documents( documents=chunks, # 分割されたテキストチャンク embedding=embedding_model, # 埋め込みモデル persist_directory="./chroma_db", # 永続化ストレージパス collection_name="deepseek_knowledge", # コレクション名 ) # データは自動的に永続化され、次回は直接ロード print(f"ベクトルデータベースには {vectorstore._collection.count()} 件のレコードがあります")

既存のベクトルデータベースをロード

# ディスクから既存のベクトルデータベースをロード vectorstore = Chroma( persist_directory="./chroma_db", embedding_function=embedding_model, collection_name="deepseek_knowledge", ) print(f"既存のデータベースをロードしました。合計 {vectorstore._collection.count()} 件のレコード")

類似度検索

# 基本的な類似度検索 query = "DeepSeek モデルの API を呼び出すには?" results = vectorstore.similarity_search(query, k=4) for i, doc in enumerate(results): print(f"--- 結果 {i+1} (ソース: {doc.metadata.get('source', '不明')}) ---") print(doc.page_content[:300]) print() # 類似度スコア付き検索 results_with_scores = vectorstore.similarity_search_with_score(query, k=4) for doc, score in results_with_scores: print(f"類似度スコア: {score:.4f} | 内容: {doc.page_content[:100]}...") # MMR 検索(最大限の関連性)— 関連性と多様性のバランス mmr_results = vectorstore.max_marginal_relevance_search( query, k=4, fetch_k=10, lambda_mult=0.7 )

他のベクトルデータベースオプション

  • FAISS: Meta がオープンソース化したベクトルインデックスライブラリ。純粋なインメモリ演算で、検索速度が非常に速く、数百万件のデータに適しています。
  • Milvus: エンタープライズ向け分散ベクトルデータベース。10億件規模のベクトルをサポートし、大規模な本番環境に適しています。
  • Weaviate: オープンソースのベクトルデータベース。ベクトル化とハイブリッド検索を内蔵し、GraphQL API をサポートしています。
  • Pinecone: 商用ベクトルデータベースサービス。運用不要で、インフラ管理を避けたいチームに適しています。
  • Qdrant: Rust で書かれた高性能ベクトルデータベース。フィルタリングとグループ検索をサポートしています。

検索チェーンの構築

ベクトル検索とDeepSeekモデルを接続し、完全なRAG QAチェーンを構築します。LangChainのRetrievalQAチェーンは、検索から生成までの全プロセスをカプセル化します。

RetrievalQAチェーンの構築

from langchain_openai import ChatOpenAI from langchain.chains import RetrievalQA from langchain.prompts import PromptTemplate # DeepSeekモデルの初期化(OpenAI互換インターフェース経由) llm = ChatOpenAI( model="deepseek-chat", base_url="https://api.deepseek.com/v1", api_key="sk-your-api-key-here", temperature=0.3, # RAGシナリオでは精度のため低い温度を推奨 max_tokens=2048, ) # カスタムプロンプトテンプレート prompt_template = """あなたは知識ベースに基づく専門的なQAアシスタントです。以下のドキュメント内容に基づいてユーザーの質問に答えてください。 要件: 1. 提供されたドキュメント内容のみに基づいて回答し、情報を捏造しないこと 2. ドキュメント内容が質問に答えるのに不十分な場合は、「既存のドキュメントに基づいて、この質問には回答できません」と明確に述べること 3. 回答には具体的なドキュメントの出典を引用すること 4. 中国語で回答し、専門的かつ明確に保つこと ドキュメント内容: {context} ユーザーの質問:{question} 回答:""" PROMPT = PromptTemplate( template=prompt_template, input_variables=["context", "question"], ) # RetrievalQAチェーンの構築 qa_chain = RetrievalQA.from_chain_type( llm=llm, chain_type="stuff", # すべての検索結果をプロンプトに投入 retriever=vectorstore.as_retriever( search_kwargs={"k": 4} # 最も関連性の高い4つのドキュメントを取得 ), chain_type_kwargs={"prompt": PROMPT}, return_source_documents=True, # ソースドキュメントを返す )

QAテスト

# 質問して回答を取得 question = "DeepSeek APIの料金体系は?" result = qa_chain.invoke({"query": question}) print("質問:", question) print("回答:", result["result"]) print("\n参照元:") for i, doc in enumerate(result["source_documents"]): source = doc.metadata.get("source", "不明") print(f" {i+1}. {source}")

chain_typeパラメータの説明

タイプ 説明 適用シナリオ
stuff すべての検索結果を一度にプロンプトに入れる ドキュメントが少ない場合(k <= 4)、最も一般的
map_reduce 各ドキュメントを個別に要約し、その後要約を結合する ドキュメントが多く長い場合、全体の理解が必要
refine ドキュメントごとに回答を反復的に改善する 高品質な回答が必要で、遅延を気にしない場合
map_rerank 各ドキュメントにスコアを付け、最高スコアの回答を選択する 特定のドキュメントを正確にマッチングする必要がある場合

ソース引用付きQ&A

RAGの核となる利点の1つはトレーサビリティです。各回答は具体的なドキュメントソースに遡ることができ、ユーザーは情報の正確性を検証できます。以下では、UIでソース引用付きのQ&A結果を表示する方法を示します。

ソース引用のフォーマット

def format_qa_response(result): """RAGのQ&A結果をソース引用付きでフォーマット""" answer = result["result"] sources = result["source_documents"] # 一意のソースを抽出 unique_sources = [] seen = set() for doc in sources: source = doc.metadata.get("source", "不明なソース") page = doc.metadata.get("page", None) source_key = f"{source}_{page}" if source_key not in seen: seen.add(source_key) unique_sources.append({ "source": source, "page": page, "preview": doc.page_content[:150] + "..." }) return { "answer": answer, "sources": unique_sources, "source_count": len(unique_sources), } # 使用例 result = qa_chain.invoke({"query": "DeepSeek R1モデルの特徴は何ですか?"}) formatted = format_qa_response(result) print("=" * 60) print(formatted["answer"]) print("\n--- 参照ソース (", formatted["source_count"], "件) ---") for i, s in enumerate(formatted["sources"]): page_info = f"{s['page']}ページ" if s['page'] else "" print(f"{i+1}. {s['source']} {page_info}") print(f" {s['preview']}")

プロンプトでのソース引用の形式

プロンプトで、引用時にソース番号を明記するようにモデルに要求すると、トレーサビリティを効果的に向上させることができます:

# 番号付き引用のプロンプトテンプレート prompt_with_citation = """あなたは知識ベースに基づく専門的なQ&Aアシスタントです。以下のドキュメント内容に基づいてユーザーの質問に回答してください。 要件: 1. 提供されたドキュメント内容のみに基づいて回答し、情報を捏造しないでください 2. 回答内で具体的なソースを引用し、形式は [ソースX] とします 3. ドキュメント内容が質問に答えるのに不十分な場合は、その旨を明確に述べてください 4. 中国語で回答し、専門的で明確に保ってください ドキュメント内容: {context} ユーザーの質問:{question} 回答してください(引用元は [ソース1]、[ソース2] などでマーク):"""

Web Q&A APIの構築

FastAPIを使用してRAGシステムをWeb APIとしてラップします:

# pip install fastapi uvicorn from fastapi import FastAPI from pydantic import BaseModel app = FastAPI(title="DeepSeek RAG API") class Question(BaseModel): text: str class Answer(BaseModel): question: str answer: str sources: list @app.post("/ask", response_model=Answer) async def ask_question(q: Question): result = qa_chain.invoke({"query": q.text}) formatted = format_qa_response(result) return Answer( question=q.text, answer=formatted["answer"], sources=[s["source"] for s in formatted["sources"]], ) # サービス起動: uvicorn main:app --reload --port 8000

高度なRAGテクニック

基本的なRAGで80%のシナリオを解決できますが、複雑なシナリオではより高度な検索戦略が必要です。以下のテクニックは、RAGシステムの回答品質を大幅に向上させるのに役立ちます。

マルチクエリ検索(Multi-Query Retrieval)

大規模言語モデルを使用してユーザーの質問を自動的に異なる角度から複数のクエリに書き換え、それぞれ検索して結果を統合し、再現率を向上させます:

from langchain.retrievers.multi_query import MultiQueryRetriever # マルチクエリ検索器を作成 multi_query_retriever = MultiQueryRetriever.from_llm( retriever=vectorstore.as_retriever(), llm=llm, ) # ユーザーの質問: "モデルはどうデプロイしますか?" # 自動的に複数のクエリを生成: # 1. "DeepSeekモデルのローカルデプロイ方法" # 2. "OllamaでDeepSeekをデプロイするチュートリアル" # 3. "DeepSeekモデルのサーバーデプロイ手順" unique_docs = multi_query_retriever.invoke("モデルはどうデプロイしますか?") print(f"マルチクエリ検索で {len(unique_docs)} 件の関連ドキュメントを取得")

親ドキュメント検索器(Parent Document Retriever)

検索には小さなテキストチャンクを使用し(精度を向上)、返却には大きなテキストチャンクを使用します(コンテキストを保持)。小さなチャンクでコンテキストが失われる問題を解決します:

from langchain.retrievers import ParentDocumentRetriever from langchain.storage import InMemoryStore # 2つのスプリッターを作成:子ドキュメントは小さなチャンク、親ドキュメントは大きなチャンク child_splitter = RecursiveCharacterTextSplitter(chunk_size=200) parent_splitter = RecursiveCharacterTextSplitter(chunk_size=1000) # 親ドキュメント検索器 parent_retriever = ParentDocumentRetriever( vectorstore=Chroma( collection_name="parent_docs", embedding_function=embedding_model, ), docstore=InMemoryStore(), child_splitter=child_splitter, parent_splitter=parent_splitter, ) parent_retriever.add_documents(documents) # 検索時は子ドキュメントでマッチングし、完全な親ドキュメントを返す results = parent_retriever.invoke("DeepSeek API 価格")

コンテキスト圧縮(Contextual Compression)

ドキュメントを取得した後、圧縮器を使用して質問に最も関連する部分を抽出し、冗長な情報を除去します:

from langchain.retrievers import ContextualCompressionRetriever from langchain.retrievers.document_compressors import LLMChainExtractor # 圧縮器を作成:LLMを使用して質問に関連するドキュメントの部分を抽出 compressor = LLMChainExtractor.from_llm(llm) compression_retriever = ContextualCompressionRetriever( base_compressor=compressor, base_retriever=vectorstore.as_retriever(search_kwargs={"k": 6}), ) # ドキュメントを取得して圧縮 compressed_docs = compression_retriever.invoke( "DeepSeek V3とR1の違いは何ですか?" ) for doc in compressed_docs: print(f"圧縮後のコンテンツ長: {len(doc.page_content)} 文字")

リランキング(Re-ranking)

まずベクトル検索で候補ドキュメントを取得し、次にリランキングモデルで候補ドキュメントを再ランク付けし、検索精度を大幅に向上させます:

# pip install sentence-transformers from sentence_transformers import CrossEncoder # リランキングモデルをロード reranker = CrossEncoder("BAAI/bge-reranker-large") # ステップ1: ベクトル検索で候補ドキュメントを取得(多めに取得) candidate_docs = vectorstore.similarity_search(query, k=20) # ステップ2: リランキングモデルで再スコアリング pairs = [[query, doc.page_content] for doc in candidate_docs] scores = reranker.predict(pairs) # ステップ3: スコアでソートし、上位K件を取得 sorted_docs = sorted( zip(candidate_docs, scores), key=lambda x: x[1], reverse=True ) top_docs = [doc for doc, score in sorted_docs[:4]] print(f"リランキング後、{len(top_docs)}件の最も関連性の高いドキュメントを保持")

高度なテクニックのまとめ

テクニック 解決する問題 コスト
マルチクエリ検索 ユーザーの質問の表現が不正確で検索に失敗する 追加のLLM呼び出し
親ドキュメント検索 小さなチャンクでコンテキストが失われる ストレージが2倍
コンテキスト圧縮 検索結果に無関係な内容が多く含まれる 追加のLLM呼び出し
再ランキング ベクトル検索の精度が不十分 追加のモデル推論

完全な実践プロジェクト

これまでのすべてのステップを統合して、完全なドキュメントQ&Aシステムを構築します:PDFフォルダの読み込み、ベクトルデータベースの作成、対話型Q&A、ソース引用付きの出力。

完全なコード:deepseek_rag.py

"""DeepSeek RAG 知識ベースQ&Aシステム — 完全実装""" import os from langchain_community.document_loaders import DirectoryLoader, PyPDFLoader, TextLoader from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain_community.embeddings import HuggingFaceEmbeddings from langchain_community.vectorstores import Chroma from langchain_openai import ChatOpenAI from langchain.chains import RetrievalQA from langchain.prompts import PromptTemplate # ========== 設定 ========== DEEPSEEK_API_KEY = os.getenv("DEEPSEEK_API_KEY", "sk-your-api-key") DOCS_DIR = "./documents" CHROMA_DIR = "./chroma_db" EMBEDDING_MODEL = "BAAI/bge-small-zh-v1.5" CHUNK_SIZE = 500 CHUNK_OVERLAP = 100 RETRIEVAL_K = 4 # ========== 1. ドキュメントの読み込み ========== def load_documents(docs_dir): """docs_dir 配下のすべてのPDFおよびTXTファイルを読み込む""" loaders = { "**/*.pdf": PyPDFLoader, "**/*.txt": TextLoader, } all_docs = [] for pattern, loader_cls in loaders.items(): loader = DirectoryLoader(docs_dir, glob=pattern, loader_cls=loader_cls) try: docs = loader.load() all_docs.extend(docs) print(f" [OK] {pattern}: {len(docs)} 個のドキュメント") except Exception as e: print(f" [SKIP] {pattern}: {e}") return all_docs # ========== 2. ドキュメント分割 ========== def split_documents(documents): splitter = RecursiveCharacterTextSplitter( chunk_size=CHUNK_SIZE, chunk_overlap=CHUNK_OVERLAP, separators=["\n\n", "\n", "。", "!", "?", ".", " ", ""], ) chunks = splitter.split_documents(documents) print(f" 元ドキュメント: {len(documents)}, 分割後: {len(chunks)} チャンク") return chunks # ========== 3. ベクトルデータベース作成 ========== def create_vectorstore(chunks, force_rebuild=False): embedding = HuggingFaceEmbeddings( model_name=EMBEDDING_MODEL, model_kwargs={"device": "cpu"}, encode_kwargs={"normalize_embeddings": True}, ) if os.path.exists(CHROMA_DIR) and not force_rebuild: print(" 既存のベクトルデータベースを読み込み中...") vectorstore = Chroma( persist_directory=CHROMA_DIR, embedding_function=embedding, ) else: print(" 新しいベクトルデータベースを作成中...") vectorstore = Chroma.from_documents( documents=chunks, embedding=embedding, persist_directory=CHROMA_DIR, ) print(f" ベクトルデータベース: {vectorstore._collection.count()} 件のレコード") return vectorstore # ========== 4. QAチェーン構築 ========== def create_qa_chain(vectorstore): llm = ChatOpenAI( model="deepseek-chat", base_url="https://api.deepseek.com/v1", api_key=DEEPSEEK_API_KEY, temperature=0.3, max_tokens=2048, ) prompt = PromptTemplate( template="""あなたは知識ベースに基づく専門的なQ&Aアシスタントです。以下のドキュメント内容に基づいてユーザーの質問に回答してください。 要件: 1. 提供されたドキュメント内容のみに基づいて回答し、情報を捏造しないこと 2. ドキュメント内容が質問に答えるのに不十分な場合は、明確に述べること 3. 回答にはドキュメントの出典を引用し、形式は [出典X] 4. 中国語で回答し、専門的かつ明確に保つこと ドキュメント内容: {context} ユーザーの質問:{question} 回答:""", input_variables=["context", "question"], ) qa_chain = RetrievalQA.from_chain_type( llm=llm, chain_type="stuff", retriever=vectorstore.as_retriever( search_kwargs={"k": RETRIEVAL_K} ), chain_type_kwargs={"prompt": prompt}, return_source_documents=True, ) return qa_chain # ========== 5. 対話型Q&A ========== def interactive_qa(qa_chain): print("\n" + "=" * 60) print(" DeepSeek RAG 知識ベースQ&Aシステム") print(" 'quit' で終了、'sources' で前回の出典を表示") print("=" * 60) last_result = None while True: try: question = input("\nあなたの質問: ").strip() except (EOFError, KeyboardInterrupt): break if not question: continue if question.lower() == "quit": break if question.lower() == "sources" and last_result: print("\n--- 前回の回答の参照出典 ---") for i, doc in enumerate(last_result["source_documents"]): source = doc.metadata.get("source", "不明") print(f" [出典{i+1}] {source}") continue print(" 考え中...", end="\r") result = qa_chain.invoke({"query": question}) last_result = result print("\n" + "-" * 60) print(result["result"]) print("-" * 60) # 出典サマリーの表示 sources = set() for doc in result["source_documents"]: sources.add(doc.metadata.get("source", "不明")) print(f"参照元 ({len(sources)} 件): {', '.join(list(sources)[:3])}") print("\n さようなら!") # ========== メインプログラム ========== if __name__ == "__main__": print("[1/4] ドキュメントを読み込み中...") documents = load_documents(DOCS_DIR) if not documents: print(" エラー: ドキュメントが見つかりません。./documents ディレクトリに PDF/TXT ファイルを配置してください") exit(1) print("\n[2/4] ドキュメントを分割中...") chunks = split_documents(documents) print("\n[3/4] ベクトルデータベースを作成中...") vectorstore = create_vectorstore(chunks) print("\n[4/4] QAチェーンを構築中...") qa_chain = create_qa_chain(vectorstore) interactive_qa(qa_chain)

プロジェクトの実行

# 1. ドキュメントディレクトリを作成 mkdir documents # 2. PDF/TXTファイルを配置 # 企業ドキュメントや技術マニュアルなどを documents/ ディレクトリに配置 # 3. APIキーを設定 # Windows: # $env:DEEPSEEK_API_KEY = "sk-your-api-key" # macOS/Linux: # export DEEPSEEK_API_KEY="sk-your-api-key" # 4. 実行 python deepseek_rag.py

Difyとの統合

コードを書きたくない場合は、DifyプラットフォームがビジュアルなRAGナレッジベース機能を提供します。上記のRAGコンセプトをDifyに適用すれば、コーディングなしでエンタープライズレベルのナレッジベースQ&Aシステムを構築できます。

DifyでのRAG対応関係

RAG段階 コード実装 Difyの対応機能
ドキュメント読み込み PyPDFLoader / TextLoader ナレッジベース → ドキュメントアップロード(PDF/Word/TXT/Web対応)
テキスト分割 RecursiveCharacterTextSplitter ナレッジベース → チャンク設定(チャンク長とオーバーラップをカスタマイズ)
ベクトル埋め込み HuggingFaceEmbeddings モデルプロバイダー → 埋め込みモデル設定
ベクトルストレージ ChromaDB Dify組み込みベクトルデータベース(Qdrant/Weaviate/ChromaDB)
検索 vectorstore.similarity_search() ナレッジベース → リコール設定(TopK、スコアしきい値)
生成 ChatOpenAI + RetrievalQA アプリケーションオーケストレーション → DeepSeekモデル + ナレッジベースノードを追加

Dify設定手順

  1. Difyをデプロイ:Docker Composeを使用してワンクリックデプロイ(詳細はDeepSeekエコシステムツールのDifyセクションを参照)
  2. モデルを設定:「設定 → モデルプロバイダー」でDeepSeekを追加(OpenAI-API互換方式)
  3. ナレッジベースを作成:ドキュメントをアップロードし、チャンクパラメータを設定(chunk_size 500, chunk_overlap 100)
  4. アプリケーションを構築:「チャットアプリ」または「エージェント」を作成し、オーケストレーションキャンバスにナレッジベースノードを追加
  5. 公開して使用:ワンクリックでWebアプリまたはAPIとして公開、既存システムへの埋め込みをサポート

Difyの利点

  • ビジュアル操作、コーディング不要
  • 組み込みベクトルデータベース、インデックスを自動管理
  • ハイブリッド検索をサポート(ベクトル検索 + キーワード検索)
  • 引用トレーサビリティを内蔵、回答の出典を自動的に注釈
  • 会話ログと分析、Q&Aパフォーマンスを継続的に最適化
  • ワンクリックでAPIとして公開、ビジネスシステムへの統合が容易

Difyの活用術とDeepSeek統合ソリューションの詳細は、DeepSeekエコシステムツールDeepSeek導入チュートリアルをご覧ください。

DeepSeek RAGナレッジベースFAQ

RAGとファインチューニング(Fine-tuning)はどちらを選ぶべきですか? +
RAGは動的な知識シナリオ(知識が頻繁に更新される、トレーサビリティが必要、予算が限られている)に適しており、ファインチューニングは静的な能力シナリオ(スタイル模倣、特定用語、指示追従)に適しています。ほとんどの企業シナリオでは、RAGを主とし、ファインチューニングを補助とする組み合わせが推奨されます。RAGは低コストで効果が早いため、まずRAGから始めることをお勧めします。
chunk_sizeとchunk_overlapはどう設定すればよいですか? +
chunk_sizeは500〜1000文字を推奨し、chunk_overlapはchunk_sizeの10%〜20%に設定します。技術文書では500、長文記事では800〜1000、FAQタイプでは300〜500を使用します。chunk_overlapは省略できません。境界で重要な情報が失われないようにするためです。まずデフォルト値でテストし、検索結果に応じて微調整することをお勧めします。
中国語文書にはどの埋め込みモデルが推奨されますか? +
BAAI/bge-small-zh-v1.5(96MB、軽量で効率的)とBAAI/bge-large-zh-v1.5(1.3GB、最高精度)を強くお勧めします。これらの2つのモデルは、中国語の意味理解において一般的な英語モデルよりも明らかに優れています。Difyプラットフォームを使用する場合は、モデルプロバイダーでこれらの埋め込みモデルを直接設定できます。ローカルデプロイでは、HuggingFaceEmbeddingsでロードするだけです。
ChromaDBとFAISSはどう選べばよいですか? +
ChromaDBは中小規模(数万〜数十万ベクトル)に適しており、データは自動的にディスクに永続化され、使い方が簡単です。FAISSは大規模(数百万以上)に適しており、純粋なインメモリ演算で非常に高速ですが、永続化を手動で管理する必要があります。個人プロジェクトや小規模チームにはChromaDBで十分であり、本番環境の大規模データにはMilvusまたはQdrantを使用します。
検索結果が不正確な場合はどうすればよいですか? +
1) chunk_sizeとchunk_overlapパラメータを調整する。2) マルチクエリ検索(Multi-Query)を使用し、ユーザーの質問を自動的に書き換える。3) リランキング(Re-ranking)を追加し、検索結果を再ランク付けする。4) Promptテンプレートを最適化し、モデルにドキュメントに基づいて回答するよう明示的に要求する。5) ハイブリッド検索(ベクトル検索+キーワード検索)を使用する。まずチャンクパラメータの調整から始めることをお勧めします。最も効果が顕著です。
OllamaでローカルデプロイしたDeepSeekをRAGに使用できますか? +
完全に可能です。ChatOpenAIのbase_urlをhttp://localhost:11434/v1に変更し、modelをdeepseek-r1:8b(または使用するモデル名)に設定し、api_keyを任意の非空文字列に設定するだけです。埋め込みモデルもローカルのHuggingFaceEmbeddingsを使用でき、RAGシステム全体が完全にオフラインで動作し、データは外部に送信されません。詳細はDeepSeekローカルデプロイチュートリアルをご覧ください。

DeepSeek 関連チュートリアル

DeepSeek モデルの使用方法、デプロイ、エコシステムツールを深く学びます。

每日精选 Skill 推荐,免费送到你邮箱

输入邮箱,每天接收一个精选 AI Agent 技能推荐。完全免费,持续更新。

完全免费,取消任意时间。我们不会发送垃圾邮件。