【Google Colabでハンズオン】LangChain×Chromaで構築するベクターストア検索・QA

## ゴール

- Google Colab 上で簡単に再現できるハンズオン形式で、LangChain + ベクターストア(Chroma)を組み合わせた「自然言語ドキュメント検索 + 回答」の一連の流れを学ぶ
- 最新版のクラス (`OpenAIEmbeddings`, `OpenAI`) を活用するための **`langchain-openai`** の使い方を知る
- 大規模ドキュメントを想定した拡張方針や会話型検索（メモリ機能）の活用イメージをつかむ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/104b32c7-d9a0-ca1b-d12d-64eec01b6ad6.png)

---

## 1. Google Colab の準備

1. [Google Colab](https://colab.research.google.com/) にアクセスし、新規ノートブックを作成
2. Python 3.x の標準環境があれば OK

---

## 2. 必要なライブラリのインストール

以下のようにセルを実行し、関連ライブラリをインストールします。

```bash
!pip install langchain openai langchain_community
!pip install chromadb tiktoken
!pip install -U langchain-openai
```

- **langchain**: Retrieval QA、チェーン構築など LLM アプリ開発を支援  
- **openai**: OpenAI API クライアントライブラリ  
- **langchain_community**: 一部コミュニティ拡張機能が必要な場合に導入  
- **chromadb**: ローカルで動くベクターストア  
- **tiktoken**: OpenAI のトークン管理ライブラリ  
- **langchain-openai**: `OpenAIEmbeddings`, `OpenAI` クラスの最新版（将来的にこちらの利用が推奨）

> **備考**: エラーが出る場合は、`langchain_community` を先にインストールし、ランタイム再起動 → 再度インストールをお試しください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/f3c95a62-7e71-215d-fecb-f0b06092f7ca.png)

---

## 3. OpenAI APIキーのセット

OpenAI の API キーを用意し、以下のように環境変数に設定します。

```python
import os

# ここにAPIキーを入力
os.environ["OPENAI_API_KEY"] = "<Your-OpenAI-API-Key>"

print("OPENAI_API_KEY is set.")
```

> **ヒント**: `dotenv` などを活用してキーを管理する方法や、Colab の「シークレット管理」機能の使用も検討してください。

---

## 4. Chroma x LangChain で Retrieval QA

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/e6b662aa-9272-e5b9-4e55-950ab4515847.png)


### 4.1 Embeddings の用意

```python
from langchain_openai import OpenAIEmbeddings  # ← langchain-openaiを使う
from langchain.vectorstores import Chroma

embeddings = OpenAIEmbeddings(
    openai_api_key=os.environ["OPENAI_API_KEY"]
)
```

- OpenAI が提供する埋め込みモデル（例: `text-embedding-ada-002`）を利用する際に必要

### 4.2 ドキュメントの準備

```python
texts = [
    "LangChainはLLMアプリケーション開発を容易にするためのPythonライブラリです。",
    "Chromaは軽量なオープンソースのベクターストアです。",
    "Pineconeはマネージドなベクターストアサービスです。",
    "ベクターストアを用いることで、類似度検索によるドキュメント検索が可能になります。"
]
```

- 上記はサンプル用の文をリスト化したもの  
- 実際には、PDF やテキストファイルを **段落や文ごとに分割** → リストなどに格納し、埋め込むのが一般的

### 4.3 Chroma へのインデックス作成

```python
chroma_store = Chroma.from_texts(
    texts, 
    embedding=embeddings, 
    collection_name="my_collection"
)
```

- `Chroma.from_texts()` はテキストを埋め込みベクトルに変換し、Chroma のインデックスへ格納
- `collection_name` は任意で指定OK

### 4.4 Retrieval QA の構築

```python
from langchain.chains import RetrievalQA
from langchain_openai import OpenAI  # ← langchain-openaiを使う

# LLM (OpenAI) の用意
llm = OpenAI(
    openai_api_key=os.environ["OPENAI_API_KEY"],
    temperature=0.0
)

# RetrievalQA チェーンを作成
qa = RetrievalQA.from_chain_type(
    llm=llm,
    chain_type="stuff",  # 他にもmap_reduceなどの方法がある
    retriever=chroma_store.as_retriever(search_kwargs={"k": 2}),
)

print("Chroma による RetrievalQA 構築完了")
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/4c46c26c-4058-0b8e-9845-051b1978fe5b.png)


### 4.5 質問してみる

```python
# クエリを実行
query = "Pineconeってどんなサービス？"
result = qa.invoke({"query": query})

print("[User Query]", query)
print("[Answer]\n", result["result"])
```

- 格納されたテキストから関連度の高い部分を検索し、OpenAI モデルが回答を生成
- `k=2` なので、関連度の高い2文が回答生成の元となる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/75832808-b917-1d77-fe0d-d673dfb838f8.png)

---

## 5. 大規模ドキュメント検索・本番運用への拡張

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/b46040b3-37c9-7618-569e-9766f85c0214.png)


- **ドキュメント分割**: 大量の文章はセクションや段落ごとに細かく分割 → Embeddings 化 → ベクターストア登録  
- **マネージドサービス**: Pinecone や Weaviate, Milvus などを利用すると、大規模データも扱いやすい  
- **運用フロー**: 新たなドキュメントが追加された際に、自動で Embeddings を生成 & ベクターストアに追加する仕組みがあると便利

---

## 6. 会話の文脈を活かした検索 (メモリ機能)

LangChain のメモリ機能を組み合わせると、連続した質問に対してコンテキストを活かしながら回答できます。  
例として `ConversationalRetrievalChain` を使用する場合:

```python
from langchain.chains import ConversationalRetrievalChain
from langchain.memory import ConversationBufferMemory

# 会話用のメモリを用意
memory = ConversationBufferMemory(
    memory_key="chat_history",
    return_messages=True
)

retriever = chroma_store.as_retriever(search_kwargs={"k": 2})
conversation_qa = ConversationalRetrievalChain.from_llm(
    llm=llm,
    retriever=retriever,
    memory=memory
)

resp1 = conversation_qa({"question": "ベクターストアって何ですか？"})
print(resp1["answer"])

resp2 = conversation_qa({"question": "ChromaとPineconeはどう違うの？"})
print(resp2["answer"])
```

- 「前回の回答（会話コンテキスト）」+「ベクターストアによる検索結果」を統合して回答する
- ローカルの Chroma 上でも十分試せるので、プロトタイプ開発におすすめ

---

# まとめ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/6b89f122-d01f-17d2-0c4c-b68bb1aa7eee.png)

- **Google Colab** で気軽にハンズオンするなら、**Chroma** を使った Retrieval QA がシンプルで扱いやすい
- 大規模ドキュメントや本番運用を想定する場合、Pinecone や Weaviate などマネージドサービスも選択肢になる
- メモリ機能を活用すれば、会話の文脈を保ちつつ高度な QA システムを構築できる

**ぜひお好みのドキュメントを用意して試してみてください！**