LangChain で RAG Agent を構築

前の2つの記事では、ベクトルストアとリトリーバーを準備しました。

この記事では、それらを Agent に統合し、完全な RAG Agent——プライベートナレッジベースに基づいて質問に答えるスマートアシスタントを構築します。


Retriever ツールを作成

リトリーバーをツールとしてラップすると、Agent が必要に応じて自動的にナレッジベースを検索できます。

OpenAI の key がない場合は、阿里百炼の Embedding サービスを利用できます。設定を参照してください。https://www.example.com/langchain/langchain-alibailian.html:

# OpenAI 的文本嵌入模型
# 将文本转换为向量(一组浮点数)

# 使用阿里云百炼(DashScope)的通义千问 Embedding 服务
embeddings = OpenAIEmbeddings(
    model="text-embedding-v4",
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
    check_embedding_ctx_length=False,
    chunk_size=10,
)

例(OpenAI)

import os
from dotenv import load_dotenv
load_dotenv()

from langchain.tools import tool
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langchain.messages import HumanMessage
from langchain_openai import OpenAIEmbeddings
from langchain_chroma import Chroma
from langchain_text_splitters import RecursiveCharacterTextSplitter

# ----- ステップ 1: ナレッジベースの準備 -----

# 菜鳥教程 サンプル の知識ドキュメントを模擬
knowledge_docs = [
    "菜鳥教程(サンプル)は 2013 年に設立され、完全無料のプログラミング学習プラットフォームです。",
    "プラットフォームは 300+ のチュートリアルを公開しており、フロントエンド、バックエンド、データベース、モバイル開発などの分野をカバーしています。",
    "Python3 基礎チュートリアルはプラットフォームで最も人気のあるコースで、延べ学習者数は 500 万人を超えています。",
    "Python3 基礎チュートリアルは全 30 章で、環境構築、基本構文、関数、クラス、例外処理などの内容を含みます。",
    "HTML 基礎チュートリアルは全 25 章で、HTML の基本構造からフォームとマルチメディア要素まで説明しています。",
    "菜鳥教程はオンラインでのコード実行をサポートしており、学習者はソフトウェアをインストールすることなくコードを作成・実行できます。",
    "プラットフォームはモバイル対応しており、ユーザーはスマートフォンでいつでもどこでもプログラミングを学べます。",
    "菜鳥教程の会員サービスは、ビデオコース、プロジェクト演習、1対1の質疑応答などの付加価値サービスを提供します。",
]

# 分割
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=200, chunk_overlap=30
)
chunks = text_splitter.create_documents(knowledge_docs)

# ベクトル化ストレージ -- OpenAI の key がない場合は阿里百炼に変更できます
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")


vector_store = Chroma.from_documents(
    documents=chunks,
    embedding=embeddings,
)
retriever = vector_store.as_retriever(search_kwargs={"k": 3})

# ----- ステップ 2: 検索ツールの作成 -----

@tool
def search_knowledge_base(query: str) -> str:
    """菜鳥教程 サンプル ナレッジベース内の関連情報を検索します。

ユーザーが菜鳥教程の具体的な情報(コース数、プラットフォームの歴史、機能特性など)を尋ねた場合、
正確な情報を得るために、このツールを使用してナレッジベースを検索する必要があります。

    Args:
query: 検索キーワードまたは質問
    """

    docs = retriever.invoke(query)
    if not docs:
        return "ナレッジベースに関連情報が見つかりませんでした。"

    results = []
    for i, doc in enumerate(docs, 1):
        results.append(f"[{i}] {doc.page_content}")

    return "\n\n".join(results)


# ----- ステップ 3: RAG Agent の作成 -----

model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
agent = create_agent(
    model=model,
    tools=[search_knowledge_base],
    system_prompt="""あなたは菜鳥教程 サンプル のスマートカスタマーサービスアシスタントです。

## ルール
1. ユーザーが菜鳥教程の具体的な情報を尋ねた場合は、必ず search_knowledge_base ツールを使用して検索します
2. 検索された情報に基づいて回答し、ナレッジベースにない内容を捏造しない
3. ナレッジベースに関連情報がない場合は、正直にユーザーに伝える
4. 回答は親しみやすく、簡潔で、正確であること"""
,
)

# ----- ステップ 4: テスト -----

questions = [
    "菜鳥教程はいつ設立されましたか?",
    "Python3 基礎チュートリアルは何章ありますか?",
    "菜鳥教程は全部で何セットのチュートリアルがありますか?",
]

for q in questions:
    result = agent.invoke({"messages": [HumanMessage(content=q)]})
    print(f"Q: {q}")
    print(f"A: {result['messages'][-1].content}")
    print("-" * 60)

実行結果:

Q: サンプル是什么时候创立的?
A: サンプル创立于 2013 年,是一个完全免费的编程学习平台。
------------------------------------------------------------
Q: Python3 基础教程有多少章?
A: Python3 基础教程共 30 章,包含环境搭建、基本语法、函数、类、异常处理等内容。
------------------------------------------------------------
Q: サンプル一共有多少套教程?
A: サンプル已上线 300+ 套教程,涵盖前端、后端、数据库、移动开发等多个领域。
------------------------------------------------------------

RAG Agent の実行フロー

上記の3番目の質問「菜鳥教程は全部で何セットのチュートリアルがありますか?」に対する Agent の実行フローは次のとおりです:

  1. ユーザーが質問
  2. モデルがナレッジベースの検索が必要と判断 → search_knowledge_base("菜鳥教程 チュートリアル数") を呼び出す
  3. リトリーバーがベクトルデータベースから意味的に最も類似したドキュメントチャンクを検索
  4. 検索結果をモデルに返す
  5. モデルが検索結果に基づいて正確な回答を生成

引用元を追加

専門的な RAG システムは通常、引用元を付けて、ユーザーが情報の出所を知ることができるようにします:

例

from langchain_core.documents import Document


@tool
def search_with_sources(query: str) -> str:
    """菜鳥教程ナレッジベースを検索し、出典付きの結果を返します。

    Args:
query: 検索キーワード
    """

    docs = retriever.invoke(query)
    if not docs:
        return "関連情報が見つかりませんでした。"

    results = []
    for i, doc in enumerate(docs, 1):
        source = doc.metadata.get("source", "菜鳥教程ナレッジベース")
        results.append(f"[出典 {i}: {source}]\n{doc.page_content}")

    return "\n\n".join(results)


# ドキュメントに出典情報を残すには、作成時にメタデータを追加します
doc_with_meta = Document(
    page_content="Python3 基礎チュートリアルは全 30 章...",
    metadata={"source": "Python3 基礎チュートリアル-コース紹介", "url": "https://www.example.com/python3/"}
)

ベクトルストアの永続化

実際のプロジェクトでは、毎回ベクトルインデックスを再構築する必要はありません。Chroma はローカルへの永続化をサポートしています:

例

# 永続化ベクトルストアを作成(初回実行)
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vector_store = Chroma.from_documents(
    documents=chunks,
    embedding=embeddings,
    persist_directory="./サンプル_vector_db",  # 永続化ディレクトリ
)

# 後続の実行では直接ロード
loaded_store = Chroma(
    persist_directory="./サンプル_vector_db",
    embedding_function=embeddings,
)
retriever = loaded_store.as_retriever()

# ベクトルを再計算する必要はありません!

ベクトルストアの永続化により、起動速度を大幅に向上できます。ドキュメント数が多い場合(数千から数万件)、すべてのベクトルの Embedding を再計算すると数十分かかることがあります。永続化しておけば、ロードするだけで済みます。

その他の拡張