まだコードリーディングに時間かけてるの？ GitHubリポジトリから最速でRAGを構築する方法 ― LangChain & GitLoader & Chroma解説

  - [はじめに](#はじめに)
  - [使用技術](#使用技術)
  - [サンプルコードの概要](#サンプルコードの概要)
  - [コード解説](#コード解説)
    - [1. GitLoaderでのドキュメント取得](#1-gitloaderでのドキュメント取得)
    - [2. LLMおよび埋め込みモデルの設定](#2-llmおよび埋め込みモデルの設定)
    - [3. Chromaによるベクトルストア構築](#3-chromaによるベクトルストア構築)
    - [4. リトリーバーとパイプラインチェーンの構築](#4-リトリーバーとパイプラインチェーンの構築)
    - [5. クエリの実行](#5-クエリの実行)
  - [まとめ](#まとめ)


## はじめに
レポジトリ内のコード全てを読んでいる暇は無い！コードの全容を手っ取り早く把握したい！
そんな願いを叶えるための技術がGitLoaderとLangChainには備わっています。
本記事を読めば、コード理解をサポートするRAGシステムを構築可能です。

## 使用技術
- **LangChain**  
  LLMを活用したアプリケーション開発や、複雑なデータフローの管理をシンプルにするためのフレームワークです。  
- **GitLoader**  
  Gitリポジトリ内のドキュメントを簡単にロードでき、ソースコードやドキュメントの情報を効率的に取り込めます。  
- **Chroma**  
  ベクトルデータを格納するためのベクトルストアです。  

## サンプルコードの概要

以下は、今回のRAGシステム構築におけるPythonコードのサンプルです。  
今回はOpenAIのLLMやEmbeddingモデルを使用しましたが、Azureにデプロイしたモデルなどを用いることも可能です。

```python
from langchain_community.document_loaders import GitLoader
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_chroma.vectorstores import Chroma
from langchain_core.documents import Document
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnableParallel, RunnablePassthrough

import os
os.environ["OPENAI_API_KEY"] = "OpenAIのAPIキーを入力"

# Gitリポジトリからドキュメントをロード
loader = GitLoader(repo_path="./sample", clone_url="https://github.com/GenerativeAgents/agent-book.git")
documents: list[Document] = loader.load()

# LLMとEmbeddingモデルの設定
llm = ChatOpenAI(model="gpt-4o-mini")
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")

# ベクトルストアの作成とドキュメントの追加
vector_store = Chroma(
    collection_name="sample_collection",
    embedding_function=embeddings,
    persist_directory="./vector_store"
)
vector_store.add_documents(documents)

# ドキュメント検索用リトリーバーの構築
retriever = vector_store.as_retriever()

# プロンプトテンプレートの定義
template = ChatPromptTemplate.from_template(
    """以下のコンテキストを基にユーザクエリに回答してください\n
    コンテキスト: {context}\n
    ユーザクエリ: {user_query}\n
    """
)

# LCELを用いたChainの実装
chain = (
    RunnableParallel(
        {
            "user_query": RunnablePassthrough(),
            "context": retriever
        }
    )
    | template
    | llm
    | StrOutputParser()
)

# クエリの実行例
res = chain.invoke("AIエージェントのコードを簡潔に解説してください")
print(res)
```

## コード解説
### 1. GitLoaderでのドキュメント取得
```Python
loader = GitLoader(repo_path="./sample", clone_url="https://github.com/GenerativeAgents/agent-book.git")
documents: list[Document] = loader.load()
```

**ポイント：**  
* GitLoaderは、指定したリポジトリからコードやドキュメントを自動で抽出します。
* `Document`はLangChain 内で利用されるドキュメントの型として標準的に採用されているデータ構造です。

### 2. LLMおよび埋め込みモデルの設定
```Python
llm = ChatOpenAI(model="gpt-4o-mini")
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
```
**ポイント：**
* modelには使用したいOpenAIのモデルを指定します。
* 埋め込みモデルはテキストなどのベクトル化をサポートします。

### 3. Chromaによるベクトルストア構築
```Python
vector_store = Chroma(
    collection_name="sample_collection",
    embedding_function=embeddings,
    persist_directory="./vector_store"
)
vector_store.add_documents(documents)
```
**ポイント：**
* Chromaは、取り込んだドキュメントをベクトル化し、迅速な検索を可能にするベクトルストアとして機能します。
* ドキュメントを効率的に管理し、ユーザクエリに対して最も関連性の高い情報を即座に抽出します。
* `persist_directory`を指定することでベクトルストアを永続化することがきます。


### 4. リトリーバーとチェーンの構築

```Python
retriever = vector_store.as_retriever()
```

**ポイント：**
* リトリーバーは、ベクトルストアから関連情報を抽出するためのインターフェースです。
* ChromaはLangChainの基底クラス`VectorStore`を継承しており、as_retriever()を用いることでLangChainのコンポーネントとして用いることができます。

続いて、LCELを用いたChainの構築です。

LCELについての解説は下記の記事で説明しています。
[LangChain Expression Language (LCEL) 記法について解説(LangChain 0.3系)](https://qiita.com/Koggle/items/c08ba4a7b1ba2d430a9b)

```Python
chain = (
    RunnableParallel(
        {
            "user_query": RunnablePassthrough(),
            "context": retriever
        }
    )
    | template
    | llm
    | StrOutputParser()
)
```

**ポイント：**
* RunnableParallelを利用することで、ユーザクエリを元にベクトル検索した結果をcontextに入れて、user_query, contextを次の処理に流します。


### 5. クエリの実行

```Python
res = chain.invoke("self_reflectionのコードを簡潔に解説してください")
print(res)
```

**出力**
```
`self_reflection`モジュールのコードは、ユーザーが与えたタスクを処理し、その結果を内省（自己反省）するためのエージェントシステムを実装しています。以下に各クラスとその役割を簡潔に述べます。

1. **ReflectionManager**:
   - リフレクション（反省）のデータを管理します。
   - リフレクションの読み込み、保存、および関連するリフレクションの取得を行います。

2. **Reflection**:
   - 反省の内容を表すデータモデルで、タスクの内容、反省の内容、判断情報（リトライが必要かどうかなど）を持ちます。

3. **TaskReflector**:
   - 与えられたタスクを実行した結果を分析し、内省します。
   - LLM（大規模言語モデル）を使用してリフレクションを生成し、保存します。

4. **ReflectiveAgent**:
   - 内省エージェント全体の管理を行います。
   - タスクを実行し、結果に基づいて反省し、最終的な出力を生成します。

5. **main関数**:
   - コマンドライン引数を介してタスクを受け取り、エージェントを初期化して実行します。

このシステムは、課題解決能力の向上を目的としており、過去のリフレクションに基づいてタスクの実行やその結果の評価を行います。また、再試行が必要な場合はその判断も行います。
```

**ポイント：**
* コード全てに目を通す必要はなく、特定のコードについての解説などを頼むことで、必要な情報が瞬時に得られる点が大きなメリットです。
* 今回は実装の概要を示すことを目的としたので、プロンプトはかなり雑に作成しましたが、プロンプトを改良することでより良いRAGが構築できると思います。

## まとめ
本記事では、GitLoaderとLangChain、Chromaを組み合わせたRAGシステムの構築手法を解説しました。
このアプローチにより、GitHubレポジトリ全体を読み込む手間を省き、必要なコード情報を自動で抽出することで、コード理解にかかる時間を大幅に短縮できます。

* GitLoader：リポジトリから自動でドキュメントを抽出
* LangChain：LLMと連携し、柔軟なパイプラインを構築
* Chroma：高速なベクトル検索を実現し、情報抽出を最適化

開発現場でのコード理解時間を短縮し、効率的に情報を活用するための一助として、ぜひこのシステムの構築を検討してみてください。