【Python】LangChain: RAGでLLMと外部データを連携する方法
でLLMと外部データを連携する方法.jpg)
LangChain を用いた RAG(Retrieval-Augmented Generation)で LLM と外部データを連携する方法を解説します。
目次
LangChain による RAG(Retrieval-Augmented Generation)
LangChain は、大規模言語モデル(LLM: Large Language Model)の活用を容易にするオープンソースの Python ライブラリです。
LangChain には、PromptTemplate や OutputParser、Chain などの様々な機能を提供し、LLM の操作を簡略化しますが、その中でも重要な機能が RAG(Retrieval-Augmented Generation)です。この RAG により、LLM と外部データを連携が可能になります。
この記事では、LangChain を用いた RAG で LLM と外部データを連携する方法を紹介します。
RAG(Retrieval-Augmented Generation)
まず、RAG(Retrieval-Augmented Generation)の概要について説明します。
RAG は、外部データから必要な情報を探索し、LLM の生成プロセスに組み込むことで回答の精度を向上させる技術です。Retrieval は「検索、取得」、Augmented は「拡張された、増強された」、Generation は「生成」という意味があります。
ChatGPT が登場した初期の時期は、学習に含まれていない直近情報に関する回答はできませんでしたが、RAG により大幅に回答精度は向上しました。ただし、事実に基づかない情報生成(ハルシネーション)を完全に防ぐことはできないので注意が必要です。
LangChain による RAG の流れ
RAG は、以下の流れで実現されます。

- Load:データソースからデータを取り込みます。
- Transform:テキストを前処理して文や単語などに分割するなど、データをインデックス化しやすいように変換します。
- Embedding:データをベクトル情報に変換し、これを用いて意味的な類似性を計算できるようにします。
- Vector Store:テキストとベクトル情報を検索しやすいように保存します。
- Retrievers:Vector Store からユーザーの問い合わせに関連する情報を検索し、LLMと連携します。
上記の流れにより、質問に最も関連性のある情報を迅速に見つけ出し、LLM と連携して正確な回答を生成します。
例として、社員が部門の残業状況を調べるケースを想定して、回答までの流れを見てみましょう。上記で紹介した流れで A 社の VectorStore が用意されているとします。

このケースで、A 社や XX 部門の情報は個別企業の情報のため、ChatGPT 等の一般的な LLM には情報を含みません。
そこで、A 社の社員から質問があった際に、Retrievers が関連する情報を Vector Store から検索し、質問内容に付与して LLM に問い合わせます。これにより、LLM の回答精度の向上が期待できます。
上記が、RAG を実現するまでの流れでした。以降で、LangChain による RAG 実装の例を紹介していきます。
LangChain における RAG 関連機能と実装
LangChain には、RAG を実現するための各種機能が提供されています。以降では、それぞれの基本的な使い方について説明し、RAG 実装の流れを紹介していきます。
本記事で使用したバージョンは以下の通りです。バージョンが異なる場合は動作しないことがあります。
- Python: 3.11.5
- langchain: 0.2.11
- langchain-core: 0.2.26
- langchain-community: 0.2.10
- langchain-text-splitters: 0.2.2
- langchain-openai: 0.1.16
- langchain-chroma 0.1.2
- pypdf: 4.3.1
- beautifulsoup4: 4.12.3
- wikipedia: 1.4.0
Document Loader
Document Loader は、外部のドキュメントやデータソースからデータを取得するための機能群です。
色々な種類の Loader がありますが、基本的な Loader の使い方を紹介します。

TextLoader:テキストファイルを読み込む
テキストファイルを読み込む場合には、TextLoader を使用します。
from langchain_community.document_loaders import TextLoader
# TextLoaderを用意する
loader = TextLoader("../data/index.txt", encoding="utf-8")
# データを読み込む
document = loader.load()
print(document)
【実行結果】
[Document(metadata={'source': '../data/index.txt'}, page_content='LangChainとは\nLangChainとは、大規模言語モデル(LLM: Large Language Model)を活用するためのオープンソースPythonライブラリです。公式サイトは、こちらを参照してください。\n\nLangChainは、LLMを用いた複雑なタスクを実行するア
...(途中省略)...
した。ただし、アップデート時には、これらのパッケージ間での依存関係や整合性を考慮し、各バージョンの互換性にも注意が必要です。各パッケージがどのように連携するかは公式ドキュメントを参照するなどして確認するようにしてください。')]
TextLoader は、インスタンス化の際にテキストファイルのパスやエンコーディングを指定し、データの読み込みには load メソッドを使用します。
返却値は、Document クラスのインスタンスで、メタデータは metadata に、テキスト内容は page_content に格納されます。
DirectoryLoader:ディレクトリ内のファイルを読み込む
ディレクトリ内のファイルをまとめて読み込みたい場合には DirectoryLoader を使用します。
from langchain_community.document_loaders import DirectoryLoader, TextLoader
# DirectoryLoaderを用意する
loader = DirectoryLoader(
"../data",
glob="*.txt",
recursive=True,
loader_cls=TextLoader,
loader_kwargs={"encoding": "utf-8"},
)
# データを読み込む
documents = loader.load()
print(documents)
【実行結果】
[Document(metadata={'source': '..\\data\\index.txt'}, page_content='LangChainとは\nLangChainとは、大規模言語モデル(LLM: Large Language Model)を活用するためのオープンソースPythonライブラリです。公式サイトは、こちらを参照してください。\n\nLangChainは、LLMを用いた複雑なタスクを実行する
...(途中省略)...
Document(metadata={'source': '..\\data\\index1.txt'}, page_content='LangChainの基本的な使い方\nここでは、LangChainの基本的な使い方を紹介したいと思います。\n\n本記事で紹介する環境は以下バージョンでの動作結果です。バージョン更新が速いため最新バージョンではうまく動作しない可能性があります
...(途中省略)...
LangChainにはさまざまな機能があります。これらの機能についても、今後の記事更新や新たな記事で詳しく解説したいと考えています。\n\n皆さんも、ぜひ色々と調べてLangChainを使ってみてください。')]DirectoryLoader は、指定したディレクトリ内のファイルを一括で読み込みます。今回例では、data ディレクトリ内の index.txt と index1.txt を対象としています。
DirectoryLoader の引数は以下のようなものがあります。
| 引数 | 概要説明 |
|---|---|
glob | 読み込むファイルのパターンを指定します。例えば「*.txt」とすることで、指定ディレクトリ内のすべてのテキストファイルが対象になります。 |
recursive | ディレクトリの検索を再帰的に行うかどうかを指定します。 |
loader_cls | 各ファイルを読み込む際に使用する Document Loader クラスを指定します。 |
loader_kwargs | loader_cls で指定した Document Loader クラスに渡す追加の引数を設定します。 |
読み込みは load メソッドで行い、結果は Document のリストとして返されます。
PyPDFLoader:PDFファイルを読み込む
PDF ファイルを読み込む場合には PyPDFLoader を使用します。事前に pypdf パッケージをインストールしてください。
pip install pypdf
PyPDFLoader は、以下のように使用します。
from langchain_community.document_loaders import PyPDFLoader
# PDFLoaderを用意する
loader = PyPDFLoader("../data/sample.pdf")
# データを読み込む
document = loader.load()
print(document)
【実行結果】
[Document(metadata={'source': './data/sample.pdf', 'page': 0}, page_content='LangChain とは \nLangChain とは、大規模言語モデル (LLM: Large Language Model) を活用するためのオー\nプンソース Pythonライブラリです。公式サイトは、 こちらを参照してください。 \nLangChain は、 LLMを用いた複雑
...(途中省略)...
の研究チームによる「 Attention is All You Need 」という非常に有名な論文で紹介されまし\nた。 \n従来の再帰的ニューラルネットワーク (RNN)やLSTM(Long Short Term Memory) などで\n不得意であった長い依存関係の問題を解決し、その後の BERTやGPT等の現在の主流技\n術の登場に貢献しています。 \n ')]PyPDFLoader の使い方は基本的に TextLoader と同様です。
WebBaseLoader:Webサイトの情報を取得する
Web サイトのデータを読み込む場合には WebBaseLoader を使用します。Web スクレイピングには、Beautiful Soup が必要ですので、事前にインストールしてください。
pip install beautifulsoup4
WebBaseLoader は、以下のように使用します。
from langchain_community.document_loaders import WebBaseLoader
# WebBaseLoaderを用意する
loader = WebBaseLoader("https://tech.nkhn37.net/profile/")
# データを読み込む
document = loader.load()
print(document)
【実行結果】
[Document(metadata={'source': 'https://tech.nkhn37.net/profile/', 'title': 'ホッシーの自己紹介|Python Tech', 'description': 'ホッシーのプロフィール プロフィール ※キャラクターデザイン ゼイルン様 愛知県生まれの30代サラリーマンのホッシーとい', 'language': 'ja'}, page_content='\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nホッシーの自己紹介|Python Tech\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nPython Tech ~Python学習サイト~
...(途中省略)...
\nエキスパートPythonプログラミング\nPythonデータサイエンスハンドブック\nPythonによるディープラーニング\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\nHOME\n\n\n\n\n\n\n自己紹介\n\n \n\n\n \n\n\n\n\n\nプライバシーポリシー\n免責事項\n2021–2024\xa0\xa0Python Tech\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n')]WebBaseLoader には、読み込み対象となる Web サイトの URL を指定します。今回は私のサイトのプロフィールページを指定してみました。データを取得手順は、TextLoader などの他の Document Loader と同様です。
WikipediaLoader:Wikipedia の情報を取得する
Wikipedia からデータを読み込むには WikipediaLoader を使用します。事前に wikipedia パッケージをインストールしてください。
pip install wikipedia
WikipediaLoader は、以下のように使用します。
from langchain_community.document_loaders import WikipediaLoader # WikipediaLoaderを用意する loader = WikipediaLoader(query="langchain") # データを読み込む documents = loader.load() # 結果を表示 print(len(documents)) print([d.page_content[:100] for d in documents])
【実行結果】 5 ['LangChain is a framework designed to simplify the creation of applications using large language mode', 'Retrieval augmented generation (RAG) is a type of information retrieval process. It modifies interac', 'In natural language processing, a sentence embedding refers to a numeric representation of a sentenc', 'DataStax, Inc. is a real-time data for AI company based in Santa Clara, California. Its product Astr', 'Prompt injection is a family of related computer security exploits carried out by getting a machine ']
今回は "langchain" に関連する Wikipedia の情報を取得してみました。WikipediaLoader は、検索クエリを query 引数に指定してデータを取得します。
デフォルトでは英語の記事(lang="en")が対象です。結果として 5 つの記事が取得されました。言語を日本語に変更するには、lang="ja" を指定します。
from langchain_community.document_loaders import WikipediaLoader # WikipediaLoaderを用意する loader = WikipediaLoader(query="langchain", lang="ja") # データを読み込む documents = loader.load() # 結果を表示 print(len(documents)) print([d.page_content[:100] for d in documents])
【実行結果】 1 ['LangChain(ラングチェイン)は、大規模言語モデル(LLM)を使ったアプリケーションソフトウェアの作成を簡素化するように設計されたフレームワークである。言語モデル統合フレームワークとして、Lan']
例では、日本語 Wikipedia には "langchain" の関連ページが 1 つ見つかりました。
Text Splitter
Text Splitterは、読み込んだドキュメントを分割し、Embedding で扱いやすく加工するツールです。これは、RAG における「Transform」に対応します。

LangChain には、基本クラスとして TextSplitter があり、これを継承した様々なクラスが提供されています。以下で主要なクラスの使い方を紹介します。
【プロパティ】
| 名称 | 説明 |
|---|---|
chunk_size | チャンクの最大サイズ |
chun_overlap | チャンク間の重複文字数 |
length_function | チャンクサイズを計測する関数(デフォルトは len) |
【メソッド】
| 名称 | 説明 |
|---|---|
text_split | 文字列を分割してリストを返す |
split_documents | ドキュメントリストを分割してリストを返す |
chunk_size は分割する際の最大サイズで、chunk_overlap はチャンク間の重複部分のサイズです。length_function は、チャンクサイズの計測に使用する関数で、カスタム関数を渡すことも可能です。
CharacterTextSplitter:文字数で分割する
CharacterTextSplitter は、文字数を基準にドキュメントを分割します。
from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import CharacterTextSplitter
# TextLoaderを用意する
loader = TextLoader("../data/index.txt", encoding="utf-8")
# データを読み込む
document = loader.load()
# Character Text Splitterを用意する
text_splitter = CharacterTextSplitter(
chunk_size=200,
chunk_overlap=100,
add_start_index=True,
separator="。",
)
# 読み込んだデータを分割する
split_document = text_splitter.split_documents(document)
print(len(split_document))
print(split_document)
【実行結果】
21
[Document(metadata={'source': '../data/index.txt', 'start_index': 0}, page_content='LangChainとは\nLangChainとは、大規模言語モデル(LLM: Large Language Model)を活用するためのオープンソースPythonライブラリです。公式サイトは、こちらを参照してください'), Document(metadata={'source': '../data/index.txt', 'start_index': 88}, page_content='公式サイトは、こちらを参照してください。\n\nLangChainは、LLMを用いた複雑なタスクを実行するアプリケーションを構築することを目的としており、様々なツールとの統合、タスク指向の設計、拡張性とカスタマイズ性といったことを特徴としています。\n\nまた、オープンソースプロジェクトとして、多くの開発者や研究者がコミュニティを通じて貢献し、ライブラリは常に改善されています'),
...(途中省略)..
Document(metadata={'source': '../data/index.txt', 'start_index': 2232}, page_content='バージョン0.1.0以降では、上記のようにパッケージ分割されることで開発や保守がしやすく、ユーザーにとってもパッケージ選択をしやすく柔軟性が増しました。ただし、アップデート時には、これらのパッケージ間での依存関係や整合性を考慮し、各バージョンの互換性にも注意が必要です。各パッケージがどのように連携するかは公式ドキュメントを参照するなどして確認するようにしてください')]CharacterTextSplitter の使用には、chunk_size や chunk_overlap を設定します。add_start_index=True により、分割時の開始インデックスがメタデータに含まれます。また、分割の基準となるセパレータを separator 引数を使って指定できます。
今回の例では 21 個のチャンクに分割されました。今回は、chunk_size=200、chunk_overlap=100 として半分ぐらい重複するように設定してみました。実際の結果を見ても一部重複部分が表れていることが分かります。
RecursiveCharacterTextSplitter:複数の区切り文字で分割する
RecursiveCharacterTextSplitter は、複数のセパレータを指定して分割します。
from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
# TextLoaderを用意する
loader = TextLoader("../data/index.txt", encoding="utf-8")
# データを読み込む
document = loader.load()
# Recursive Character Text Splitterを用意する
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=200,
chunk_overlap=100,
add_start_index=True,
separators=[",", "、", "。"],
)
# 読み込んだデータを分割する
split_document = text_splitter.split_documents(document)
print(len(split_document))
print(split_document)
【実行結果】
22
[Document(metadata={'source': '../data/index.txt', 'start_index': 0}, page_content='LangChainとは\nLangChainとは、大規模言語モデル(LLM: Large Language Model)を活用するためのオープンソースPythonライブラリです。公式サイトは、こちらを参照してください。\n\nLangChainは、LLMを用いた複雑なタスクを実行するアプリケーションを構築することを目的としており、様々なツールとの統合、タスク指向の設計'), Document(metadata={'source': '../data/index.txt', 'start_index': 94}, page_content='、こちらを参照してください。\n\nLangChainは、LLMを用いた複雑なタスクを実行するアプリケーションを構築することを目的としており、様々なツールとの統合、タスク指向の設計、拡張性とカスタマイズ性といったことを特徴としています。\n\nまた、オープンソースプロジェクトとして、多くの開発者や研究者がコミュニティを通じて貢献し、ライブラリは常に改善されています。LangChainを用いることで'),
...(途中省略)...
Document(metadata={'source': '../data/index.txt', 'start_index': 2246}, page_content='、上記のようにパッケージ分割されることで開発や保守がしやすく、ユーザーにとってもパッケージ選択をしやすく柔軟性が増しました。ただし、アップデート時には、これらのパッケージ間での依存関係や整合性を考慮し、各バージョンの互換性にも注意が必要です。各パッケージがどのように連携するかは公式ドキュメントを参照するなどして確認するようにしてください。')]RecursiveCharacterTextSplitter では、separators 引数で複数の区切り文字リストを指定します。複数のセパレータを指定することで、より柔軟な分割が可能です。今回の例では 22 個のチャンクに分割されました。
TokenTextSplitter:トークン数で分割する
TokenTextSplitter は、トークン数を基準に分割します。トークン数は使用するモデルごとに決まります。
from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import TokenTextSplitter
# TextLoaderを用意する
loader = TextLoader("../data/index.txt", encoding="utf-8")
# データを読み込む
document = loader.load()
# Token Text Splitterを用意する
text_splitter = TokenTextSplitter(
chunk_size=200,
chunk_overlap=100,
add_start_index=True,
encoding_name="cl100k_base",
)
# 読み込んだデータを分割する
split_document = text_splitter.split_documents(document)
print(len(split_document))
print(split_document)
【実行結果】
19
[Document(metadata={'source': '../data/index.txt', 'start_index': 0}, page_content='LangChainとは\nLangChainとは、大規模言語モデル(LLM: Large Language Model)を活用するためのオープンソースPythonライブラリです。公式サイトは、こちらを参照してください。\n\nLangChainは、LLMを用いた複雑なタスクを実行するアプリケーションを構築することを目的としており、様々なツールとの統合、タスク指向の設計、拡張性とカスタマイズ性といったことを特徴としています。\n\nまた、オープンソースプロジェクトとして、多くの開発者や研究者'), Document(metadata={'source': '../data/index.txt', 'start_index': 148}, page_content='構築することを目的としており、様々なツールとの統合、タスク指向の設計、拡張性とカスタマイズ性といったことを特徴としています。\n\nまた、オープンソースプロジェクトとして、多くの開発者や研究者がコミュニティを通じて貢献し、ライブラリは常に改善されています。LangChainを用いることで、開発者はLLMを最大限に活用してアプリケーションを構築できます。\n\nLangChainの理解に必要な概念\nLangChainを扱っていく'),
...(途中省略)...
Document(metadata={'source': '../data/index.txt', 'start_index': -1}, page_content='�では、上記のようにパッケージ分割されることで開発や保守がしやすく、ユーザーにとってもパッケージ選択をしやすく柔軟性が増しました。ただし、アップデート時には、これらのパッケージ間での依存関係や整合性を考慮し、各バージョンの互換性にも注意が必要です。各パッケージがどのように連携するかは公式ドキュメントを参照するなどして確認するようにしてください。')]TokenTextSplitter は、encoding_name 引数でトークン分割方法を指定します。例えば、"cl100k_base" は OpenAI の GPT モデルのトークン化方法に対応しています。今回の例では 19 個のチャンクに分割されました。
Embedding
Embedding は、テキストを数値ベクトルへ変換し、Vector Store に蓄積するための機能です。Document Loader で読み込んだデータを Text Splitter で分割した後にベクトル化します。

LangChain では、基本クラスとして Embedding クラスがあり、これを継承した様々なクラスが提供されています。
【メソッド】
| 名称 | 説明 |
|---|---|
embed_query | 単一の文字列を対象にベクトル化する |
embed_documents | 複数の文字列を対象にベクトル化する |
embed_query は、単一のテキストを、embed_documents は、複数のテキストを対象にします。
OpenAIEmbeddings:OpenAI の埋め込みモデル
Embedding のモデルには様々ありますが、ここでは OpenAIの OpenAIEmbeddings を使用します。使用には langchain-openaiパッケージが必要です。
pip install langchain-openai
OpenAI の API 使用にはキーが必要です。APIキーについては「OpenAI APIの使い方の基本」や「LangChainで大規模言語モデル(LLM)を活用する」を参考にしてください。今回は、config.ini に設定して使用します。
config.ini
[OPENAI] key = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
OpenAIEmbeddings を使用した埋め込み(ベクトル化)は以下のようにします。
import configparser
from langchain_community.document_loaders import TextLoader
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import TokenTextSplitter
# コンフィグファイルからキーを取得する
config = configparser.ConfigParser()
config.read("config.ini")
# TextLoaderを用意する
loader = TextLoader("../data/index.txt", encoding="utf-8")
# データを読み込む
document = loader.load()
# Token Text Splitterを用意する
text_splitter = TokenTextSplitter(
chunk_size=200,
chunk_overlap=100,
add_start_index=True,
encoding_name="cl100k_base",
)
# 読み込んだデータを分割する
split_documents = text_splitter.split_documents(document)
# 埋め込みモデルを用意する
embeddings_model = OpenAIEmbeddings(api_key=config["OPENAI"]["key"])
# 読み込んだデータをベクトル化する
embed = embeddings_model.embed_documents([d.page_content for d in split_documents])
print(len(embed))
print(embed)
【実行結果】 19 [[-0.007674966007471085, -0.0046662986278533936, -0.011643846519291401, -0.0388936847448349, 0.013503627851605415, 0.004646083805710077, -0.012209867127239704, 0.03091549128293991, 0.007937761023640633, ...(途中省略)... -0.02785574644804001, -0.01018467452377081, 0.012009980157017708, -0.010409531183540821, -0.01772397942841053, -0.010144994594156742, -0.012261290103197098]]
OpenAIEmbeddings は、API キーを渡してモデルを作成し、embed_documents メソッドで分割したドキュメントをベクトル化します。結果として、各ドキュメントがベクトル化されていることが確認できます。
Vector Stores(Chroma)
ベクトル化したデータを蓄積する Vector Store としては、Chroma、Pinecone、FAISS、Lance などがあります。ここでは、手軽で汎用性の高い Chroma を使用したベクトルデータの蓄積方法を紹介します。

Chromaにベクトル情報を保存する(InMemory)
Chroma は、ベクトル(高次元の数値配列)を効率的に保存・検索するためのベクトルデータベースです。コサイン類似度やドット積を使用してベクトル間の類似性を計算することができます。
Chroma の使用にはインストールが必要です。
pip install langchain-chroma
以下は、Embedding で生成したベクトル情報を Chroma に蓄積する例です。InMemory(メモリ上)に Vector Store を構築しています。
import configparser
from langchain_chroma import Chroma
from langchain_community.document_loaders import TextLoader
from langchain_openai.embeddings import OpenAIEmbeddings
from langchain_text_splitters import TokenTextSplitter
# コンフィグファイルからキーを取得する
config = configparser.ConfigParser()
config.read("config.ini")
# TextLoaderを用意する
loader = TextLoader("../data/index1.txt", encoding="utf-8")
# データを読み込む
document = loader.load()
# Token Text Splitterを用意する
text_splitter = TokenTextSplitter(
chunk_size=200,
chunk_overlap=100,
add_start_index=True,
encoding_name="cl100k_base",
)
# 読み込んだデータを分割する
split_documents = text_splitter.split_documents(document)
# 埋め込みモデルを用意する
embeddings_model = OpenAIEmbeddings(api_key=config["OPENAI"]["key"])
# ChromaにInMemoryで格納する
db = Chroma.from_documents(split_documents, embedding=embeddings_model)
# Chromaを検索する
query = "LangChainとは何ですか?"
docs = db.similarity_search(query)
print(len(docs))
print(docs)
【実行結果】
4
[Document(metadata={'source': '../data/index1.txt', 'start_index': -1}, page_content='デルの簡単な使い方について紹介しました。LangChainにはさまざまな機能があります。これらの機能についても、今後の記事更新や新たな記事で詳しく解説したいと考えています。\n\n皆さんも、ぜひ色々と調べてLangChainを使ってみてください。'), Document(metadata={'source': '../data/index1.txt', 'start_index': 5241}, page_content='することで、APIコールが省略され、コスト節約とレスポンス向上が期待できます。\n\nLangChainの様々な機能\nLangChainには他にも様々な機能があります。以下のページも必要に応じて参考にしてください。\n\nMemoryで情報を保存・管理する\nPromptTemplateで柔軟にプロンプトを定義する\nOutputParserで出力形式を制御する\nChainを使用してワークフローを実行する\n\nまとめ\nPythonで大規模言語モデルを活用する際に使用できるLangChainの概要について解説しました。\n\nLangChainは、Model(モデル'), Document(metadata={'source': '../data/index1.txt', 'start_index': 0}, page_content='LangChainの基本的な使い方\nここでは、LangChainの基本的な使い方を紹介したいと思います。\n\n本記事で紹介する環境は以下バージョンでの動作結果です。バージョン更新が速いため最新バージョンではうまく動作しない可能性がありますがご了承ください。\n\nPython: 3.11.5\nlangchain: 0.2.11\nlangchain-core: 0.2.26\nlangchain-community: 0.2.10\nlangchain-openai: 0.1.16\nLangChainのインストール\nLangChainは、pipを使用してインストールすることができます。以下のpip installでライブラリをインストールしてください。\n\npip install langchain lang'), Document(metadata={'source': '../data/index1.txt', 'start_index': 5517}, page_content=')、Prompt(プロンプト)、Memory(メモリー)、Index(インデックス)、Chain(チェイン)、Agent(エージェント)といった構成要素で成り立っており、LLMを用いた複雑なタスクを開発することを便利にしてくれます。\n\nこの記事では、概要とOpenAIのモデルの簡単な使い方について紹介しました。LangChainにはさまざまな機能があります。これらの機能についても、今後の記事更新や新たな記事で詳しく解説したいと考えています。\n\n皆さんも、ぜひ色々と調べてLangChainを使ってみ')]Chroma では、from_documents メソッドに分割したベクトル化対象のドキュメントと、埋め込みモデルを指定します。これにより、対象ドキュメントをモデルでベクトル化しつつ蓄積できます。
検索時には、similarity_search に検索対象の文字列を指定します。今回の例では、指定した「"LangChainとは何ですか?"」に類維持するドキュメントとして 4 件ヒットしました。
ベクトル情報をファイル出力して利用する(persist_directory)
InMemory の Vector Store はプログラム終了時に消えてしまいます。永続的に保存するには、persist_directory を使用して保存先を指定します。
# Chromaにファイルに格納する
db = Chroma.from_documents(
split_documents,
embedding=embeddings_model,
persist_directory="./chroma_db",
)persist_directory に保存先を指定すると、chroma_db ディレクトリが生成され、sqlite のデータやバイナリファイルが生成されます。

保存したデータを再利用する際は以下のように読み込みます。
import configparser
from langchain_chroma import Chroma
from langchain_openai.embeddings import OpenAIEmbeddings
# コンフィグファイルからキーを取得する
config = configparser.ConfigParser()
config.read("config.ini")
# Chromaを読み込む
db = Chroma(
persist_directory="./chroma_db",
embedding_function=OpenAIEmbeddings(openai_api_key=config["OPENAI"]["key"]),
)
# Chromaを検索する
query = "LangChainとは何ですか?"
docs = db.similarity_search(query)
print(len(docs))
print(docs)
【実行結果】
4
[Document(metadata={'source': '../data/index1.txt', 'start_index': -1}, page_content='デルの簡単な使い方について紹介しました。LangChainにはさまざまな機能があります。これらの機能についても、今後の記事更新や新たな記事で詳しく解説したいと考えています。\n\n皆さんも、ぜひ色々と調べてLangChainを使ってみてください。'), Document(metadata={'source': '../data/index1.txt', 'start_index': 5241}, page_content='することで、APIコールが省略され、コスト節約とレスポンス向上が期待できます。\n\nLangChainの様々な機能\nLangChainには他にも様々な機能があります。以下のページも必要に応じて参考にしてください。\n\nMemoryで情報を保存・管理する\nPromptTemplateで柔軟にプロンプトを定義する\nOutputParserで出力形式を制御する\nChainを使用してワークフローを実行する\n\nまとめ\nPythonで大規模言語モデルを活用する際に使用できるLangChainの概要について解説しました。\n\nLangChainは、Model(モデル'), Document(metadata={'source': '../data/index1.txt', 'start_index': 0}, page_content='LangChainの基本的な使い方\nここでは、LangChainの基本的な使い方を紹介したいと思います。\n\n本記事で紹介する環境は以下バージョンでの動作結果です。バージョン更新が速いため最新バージョンではうまく動作しない可能性がありますがご了承ください。\n\nPython: 3.11.5\nlangchain: 0.2.11\nlangchain-core: 0.2.26\nlangchain-community: 0.2.10\nlangchain-openai: 0.1.16\nLangChainのインストール\nLangChainは、pipを使用してインストールすることができます。以下のpip installでライブラリをインストールしてください。\n\npip install langchain lang'), Document(metadata={'source': '../data/index1.txt', 'start_index': 5517}, page_content=')、Prompt(プロンプト)、Memory(メモリー)、Index(インデックス)、Chain(チェイン)、Agent(エージェント)といった構成要素で成り立っており、LLMを用いた複雑なタスクを開発することを便利にしてくれます。\n\nこの記事では、概要とOpenAIのモデルの簡単な使い方について紹介しました。LangChainにはさまざまな機能があります。これらの機能についても、今後の記事更新や新たな記事で詳しく解説したいと考えています。\n\n皆さんも、ぜひ色々と調べてLangChainを使ってみ')]Collection を使用して効率的にデータを扱う
Chroma では、データを整理するために Collection を使用します。Collection 名を collection_name 引数に指定してデータを管理することで、効率的にデータを検索できます。なお、デフォルトでは collection_name="langchain" となっています。
# Chromaにファイルに格納する
db = Chroma.from_documents(
split_documents,
embedding=embeddings_model,
collection_name="collection_1",
persist_directory="./chroma_db",
)検索時では、collection_nameを指定することで対象 Collection を検索可能です。
# Chromaを読み込む
db = Chroma(
persist_directory="./chroma_db",
collection_name="collection_1",
embedding_function=OpenAIEmbeddings(openai_api_key=config["OPENAI"]["key"]),
)このように collection_name を指定することで、複数のデータセットを整理して管理できます。
Retriever
これまで外部データを取得し、Vector Store を蓄積する方法を紹介しました。ここからは、RAG の中心となる Retriever の使い方について説明します。

今回は、架空の会社「あいうえおカンパニー」の社長プロフィールを使用して Vector Store を構築し、LLM がその情報を使って質問に答えられるかを確認します。
外部データの準備
以下は、架空の会社「あいうえおカンパニー」の社長プロフィールデータです。このデータも ChatGPT を使用して生成しています。
profile.md
# 社長プロフィール ## 名前 **山田 太郎 (Yamada Taro)** ## 役職 **代表取締役社長** ## 会社名 **あいうえおカンパニー** ## 生年月日 **1978年5月10日** ## 出身地 **東京都渋谷区** ## 学歴 - **2000年** 東京大学 経済学部 卒業 - **2005年** ハーバードビジネススクール MBA 取得 ## 経歴 - **2000年:** 大手商社に入社し、営業部門でキャリアをスタート。国内外のビジネス展開をリードし、業績を大幅に向上させる。 - **2005年:** MBA取得後、外資系コンサルティング会社に転職し、戦略コンサルタントとして活躍。複数の業界において企業変革を支援。 - **2010年:** あいうえおカンパニーに参画し、マーケティング部門の統括に任命される。革新的なプロモーション戦略を展開し、ブランドの認知度を大幅に向上させる。 - **2015年:** 取締役に昇進し、経営戦略全般を担当。新規事業の立ち上げやM&Aに積極的に取り組む。 - **2020年:** 代表取締役社長に就任。デジタルトランスフォーメーションとグローバル展開を推進し、会社の成長を加速させる。 ## 専門分野 - 経営戦略 - グローバルマーケティング - デジタルトランスフォーメーション - M&A ## 趣味 - 読書(ビジネス書、歴史書) - ゴルフ - 旅行(特にヨーロッパと東南アジア) ## 座右の銘 **「挑戦なくして成長なし」** 山田太郎氏は、革新的なリーダーシップとグローバルな視野を持ち、あいうえおカンパニーを次の成長ステージへと導く原動力となっています。
以下のコードで Chroma の Vector Store を構築します。
import configparser
from langchain_chroma import Chroma
from langchain_community.document_loaders import TextLoader
from langchain_openai.embeddings import OpenAIEmbeddings
from langchain_text_splitters import TokenTextSplitter
# コンフィグファイルからOpenAI用のキーを取得する
config = configparser.ConfigParser()
config.read("config.ini")
# TextLoaderを用意する
loader = TextLoader("./profile.md", encoding="utf-8")
# データを読み込む
document = loader.load()
# Token Text Splitterを用意する
text_splitter = TokenTextSplitter(
chunk_size=100,
chunk_overlap=20,
add_start_index=True,
encoding_name="cl100k_base",
)
# 読み込んだデータを分割する
split_documents = text_splitter.split_documents(document)
# 埋め込みモデルを用意する
embeddings_model = OpenAIEmbeddings(api_key=config["OPENAI"]["key"])
# Chromaにファイルに格納する
db = Chroma.from_documents(
split_documents,
collection_name="AIUEO_Company",
embedding=embeddings_model,
persist_directory="./chroma_db",
)
上記までで外部データから構築した Vector Store の準備が整いました。
Retriever を使用した RAG の実装
Retriever を使用して RAG を実装する方法を以下に示します。
import configparser
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain.chains.retrieval import create_retrieval_chain
from langchain_chroma import Chroma
from langchain_core.prompts import (
ChatPromptTemplate,
HumanMessagePromptTemplate,
SystemMessagePromptTemplate,
)
from langchain_openai import ChatOpenAI
from langchain_openai.embeddings import OpenAIEmbeddings
# コンフィグファイルからOpenAI用のキーを取得する
config = configparser.ConfigParser()
config.read("config.ini")
# llmの設定
llm = ChatOpenAI(openai_api_key=config["OPENAI"]["key"])
# Chromaを読み込む
db = Chroma(
persist_directory="./chroma_db",
collection_name="AIUEO_Company",
embedding_function=OpenAIEmbeddings(openai_api_key=config["OPENAI"]["key"]),
)
# Retrieverの設定
retriever = db.as_retriever()
# プロンプトテンプレートを作成する(システムプロンプトにRetrieverを質疑応答に組み込む)
prompt_template = ChatPromptTemplate.from_messages(
[
SystemMessagePromptTemplate.from_template(
"あなたは質疑応答タスクのアシスタントです。"
"以下のコンテキストを使用して質問に答えてください。"
"\n\n"
"{context}"
),
HumanMessagePromptTemplate.from_template("{input}"),
]
)
# Chain作成
question_answer_chain = create_stuff_documents_chain(llm=llm, prompt=prompt_template)
rag_chain = create_retrieval_chain(
retriever=retriever, combine_docs_chain=question_answer_chain
)
# 問い合わせの実行
result = rag_chain.invoke(
{"input": "あいうえおカンパニーの社長の名前を教えてください。"}
)
print(result)
print(result["answer"])
print("=====")
result = rag_chain.invoke(
{"input": "山田太郎氏の経歴と専門分野、座右の銘を教えてください。"}
)
print(result)
print(result["answer"])
print("=====")
result = rag_chain.invoke({"input": "山田太郎氏の趣味について教えてください。"})
print(result)
print(result["answer"])
【実行結果】 あいうえおカンパニーの社長の名前は山田太郎(Yamada Taro)です。 ==================== 山田太郎氏は、東京都渋谷区出身で、2000年に東京大学経済学部を卒業後、大手商社に入社し営業部に配属されました。その後、2005年にハーバードビジネススクールでMBAを取得しました。 山田太郎氏の専門分野はM&A(合併・買収)であり、その分野での豊富な経験を持っています。 また、山田太郎氏の座右の銘は「挑戦なくして成長なし」という言葉です。この言葉からも、彼の革新的なリーダーシップや成長志向がうかがえます。 ==================== 山田太郎氏の趣味は、読書(ビジネス書、歴史書)、ゴルフ、旅行(特にヨーロッパと東南アジア)です。
上記実行結果を確認してみると、外部ドキュメントとして渡した情報をもとに LLM が回答を生成できていることが分かります。
RAG のための Chainは、create_retrieval_chain で Retriever と LLM を接続し、質問に対する回答を行っています。なお、Retriever は上記で作成した Chromaの as_retriever メソッドを使用して取得しています。
PromptTemplate の SystemMessage として、以下のように Retriever の文脈 {context} を参照するように指定します。
# プロンプトテンプレートを作成する(システムプロンプトにRetrieverを質疑応答に組み込む)
prompt_template = ChatPromptTemplate.from_messages(
[
SystemMessagePromptTemplate.from_template(
"あなたは質疑応答タスクのアシスタントです。"
"以下のコンテキストを使用して質問に答えてください。"
"\n\n"
"{context}"
),
HumanMessagePromptTemplate.from_template("{input}"),
]
)create_retrieval_chain の内部では、Retriever を使って質問に関連する情報をVector Store から検索し、{context} 部分に埋め込んで LLM へ渡されます。これにより、外部ドキュメントに関連する回答が得られるわけです。
実際の実行時には、invoke メソッドで {input} に設定する質問を渡し、結果は result["answer"] で取得します。
以上が、LangChain を用いた RAG 実装の基本的な流れです。
まとめ
LangChain を 用いたRAG(Retrieval-Augmented Generation)でLLM と外部データを連携する方法を解説しました。
RAG は、外部データから必要な情報を検索し、それを質問に組み込むことで回答の精度を向上させる技術です。これにより、一般の LLM で対応できない特定の情報に関する AI を実装できます。
RAG を実現するには、外部データから Vector Store を作成し、Retriever で質問に関連する情報を検索して LLM と連携します。この記事では、その一連の手順を追って説明しました。
企業や個人のデータを LLM と連携させ、固有の回答を生成できる RAGは、今後様々な場面で重要となる技術です。生成 AI の技術は急速に進化していますので RAG の基礎を理解しておいてもらいたいと思います。


-を活用する.jpg)




