🦜

【連載】LangChainの公式チュートリアルを1個ずつ地味に、地道にコツコツと【Basic編#1】

2024/08/31に公開

こんにちは!今回から唐突に始まりました連載記事として新シリーズ「LangChainの公式チュートリアルを1個ずつ地味に、地道にコツコツと」では、LangChainの公式チュートリアルを一つずつ丁寧に解説していきます。初心者の方にも分かりやすいように、LangChainの基本的な使い方から応用まで、少しずつ理解を深めていきましょう。まずは【Basic編#1】として、LangChainの公式チュートリアルにあるBuild a Simple LLM Application with LCELを題材にシンプルなLLM(Large Language Model)アプリケーションを構築する方法を学びます。

公式チュートリアル 対応する連載記事 記事の有無
Build a Simple LLM Application with LCEL Basic編#1(本記事) 公開中
Build a Chatbot Basic編#2 公開中
Build vector stores and retrievers Basic編#3 公開中
Build an Agent Basic編#4 予定
Build a Retrieval Augmented Generation (RAG) Application Working with external knowledge編#1 予定
Build a Conversational RAG Application Working with external knowledge編#2 予定
Build a Question/Answering system over SQL data Working with external knowledge編#3 予定
Build a Query Analysis System Working with external knowledge編#4 予定
Build a local RAG application Working with external knowledge編#5 予定
Build a Question Answering application over a Graph Database Working with external knowledge編#6 予定
Build a PDF ingestion and Question/Answering system Working with external knowledge編#7 予定
Build an Extraction Chain Specialized tasks編#1 予定
Build a PDF ingestion and Question/Answering system Specialized tasks編#2 予定
Classify text into labels Specialized tasks編#3 予定
Summarize text Specialized tasks編#4 予定

LangChainとは?

最初に、LangChainとは何かについて簡単に説明しましょう。

LangChainは、大規模言語モデル(LLMs)を活用したアプリケーションを開発するためのフレームワークです。LLMアプリケーションのライフサイクル全体を簡素化するために設計されており、次のような3つの主要なステージでサポートを提供します。

1. 開発(Development)

LangChainは、オープンソースのビルディングブロック、コンポーネント、サードパーティの統合を使用してアプリケーションを構築するための基盤を提供します。特に、LangGraphを利用することで、ストリーミングや人間を介したインタラクション(Human-in-the-loop)のサポートを備えた、ステートフルなエージェントの構築が可能です。これにより、ユーザーとの対話や動的なデータ処理がスムーズに行えます。

2. 本番化(Productionization)

アプリケーションを本番環境で運用する際には、LangSmithを利用して、チェーン(LLMモデルの処理フロー)を検査、監視、評価します。LangSmithを使用することで、アプリケーション内で何が起こっているのかを詳細に追跡し、チェーンやエージェントの内部動作を把握することができます。特に複雑なアプリケーションになるほど、こうした可観測性は重要になります。

LangSmithのトレース機能を有効にするためには、LangSmithにサインアップし、以下のように環境変数を設定します。

export LANGCHAIN_TRACING_V2="true"
export LANGCHAIN_API_KEY="..."

Jupyter Notebookを使用している場合は、以下のコードで設定できます。

import getpass
import os

os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_API_KEY"] = getpass.getpass()

3. デプロイメント(Deployment)

構築したLangGraphアプリケーションを、LangGraph Cloudを使って本番対応のAPIやアシスタントに変換し、デプロイできます。これにより、エンタープライズレベルのアプリケーションをスケーラブルに展開することが可能です。


[1]

LangChainの構成要素

LangChainフレームワークは、以下のオープンソースライブラリで構成されています。

  • langchain-core: 基本的な抽象化とLangChain Expression Language(LCEL)の提供。
  • langchain-community: サードパーティの統合。
  • パートナーパッケージ(例: langchain-openai, langchain-anthropic など): 一部の統合は、langchain-coreのみに依存する軽量パッケージとして分割されています。
  • langchain: アプリケーションのコグニティブアーキテクチャを構成するチェーン、エージェント、およびリトリーバル戦略。
  • LangGraph: グラフ内のエッジとノードとしてステップをモデル化することで、LLMを使った堅牢でステートフルなマルチアクターアプリケーションを構築します。LangChainとスムーズに統合できますが、独立して使用することも可能です。
  • LangServe: LangChainのチェーンをREST APIとしてデプロイするためのツール。
  • LangSmith: LLMアプリケーションのデバッグ、テスト、評価、監視を行うための開発者向けプラットフォーム。

ハンズオン:シンプルな翻訳アプリケーションを作ってみよう

では、具体的にLangChainを使ってどのようにアプリケーションを作るのかをハンズオンしながら見ていきましょう。今回作るのは、英語のテキストを指定された言語に翻訳するシンプルなアプリケーションです。このハンズオンを通して、LangChainの基本的な使い方を学んでいきます。

このチュートリアルは、LangChainの公式チュートリアルの最初のステップ、「Build a Simple LLM Application with LCEL」に基づいています。これから、この公式チュートリアルをもとにしたハンズオンを丁寧に解説しながら、少しずつ進めていきますので、どうぞお楽しみに!

ハンズオンを実施するために必要な準備

まず、LangChainを使用するための環境を整えます。今回のチュートリアルでは、Jupyter Notebookを使用します。Jupyter Notebookは、インタラクティブにコードを実行しながら学ぶことができるため、とても便利です。では、早速インストールしてみましょう。

pip install langchain==0.2.14 langserve==0.2.2

このコマンドを実行すると、LangChainを利用するために必要なパッケージがインストールされます。

言語モデルの基本操作

LangChainでは、多くの言語モデルを簡単に操作することができます。ここでは、Azure OpenAIのGPT-4o miniモデルを使って、基本的な操作方法を学びましょう。
Azure OpneAIのセットアップ方法は、以下の記事の2章をご参照ください。

https://zenn.dev/chips0711/articles/f4795ffa2376f0

他のモデルを試したい場合は、Build a Simple LLM Application with LCELに例(GeminiやClaudeなど)が複数ありますのでご覧ください。

さて、今回は、APIを実行するうえで必要な環境変数は.envファイルから読み込みましょう。
これから始めるハンズオンで作成するPython or Jupyter Notebookファイルがあるディレクトリ内に.envファイルを作成し配置しましょう。

以下は.envファイルのサンプルです。適宜、値は各自変更してください。

.env例
LANGCHAIN_TRACING_V2=true #ハンズオンでは任意
LANGCHAIN_API_KEY=<LangSmithで作成したAPIキー> #ハンズオンでは任意

AZURE_OPENAI_API_KEY=<あなたが所有するモデルのAPIキー>
AZURE_OPENAI_ENDPOINT=<あなたが所有するモデルのエンドポイント>
OPENAI_API_VERSION="2024-07-01-preview" 
AZURE_OPENAI_DEPLOYMENT="gpt-4o-mini" #Azure OpenAI上のモデルデプロイ名

さて、ではこれで準備は整ったと思いますので、早速コーディングしていきましょう!
以下は、AzureChatOpenAIクラスのインスタンスを作成し、翻訳するためのプロンプトを定義し、英語から日本語への翻訳を行うための基本的なコードを示しています。

import os

from dotenv import load_dotenv, find_dotenv
from langchain_core.messages import HumanMessage, SystemMessage
from langchain_openai import AzureChatOpenAI

load_dotenv(find_dotenv())

# Azure OpenAIのGPT-4o miniモデルを使用してAzureChatOpenAIインスタンスを作成します。
model = AzureChatOpenAI(
    azure_deployment=os.environ["AZURE_OPENAI_DEPLOYMENT"],
)

# 翻訳指示とユーザーからのメッセージを定義します。
messages = [
    SystemMessage(content="Translate the following from English into Japanese"),
    HumanMessage(content="Hi!"),
]

# # モデルにメッセージを渡して翻訳を実行します。
response = model.invoke(messages)
print(response)
実行結果
content='こんにちは!' response_metadata={'token_usage': {'completion_tokens': 2, 'prompt_tokens': 20, 'total_tokens': 22}, 'model_name': 'gpt-4o-mini', 'system_fingerprint': 'fp_80a1bad4c7', 'prompt_filter_results': [{'prompt_index': 0, 'content_filter_results': {'hate': {'filtered': False, 'severity': 'safe'}, 'jailbreak': {'filtered': False, 'detected': False}, 'self_harm': {'filtered': False, 'severity': 'safe'}, 'sexual': {'filtered': False, 'severity': 'safe'}, 'violence': {'filtered': False, 'severity': 'safe'}}}], 'finish_reason': 'stop', 'logprobs': None, 'content_filter_results': {'hate': {'filtered': False, 'severity': 'safe'}, 'protected_material_code': {'filtered': False, 'detected': False}, 'protected_material_text': {'filtered': False, 'detected': False}, 'self_harm': {'filtered': False, 'severity': 'safe'}, 'sexual': {'filtered': False, 'severity': 'safe'}, 'violence': {'filtered': False, 'severity': 'safe'}}} id='run-〇〇〇〇' usage_metadata={'input_tokens': 20, 'output_tokens': 2, 'total_tokens': 22}

このコードは、「Hi!」という英語のテキストを日本語に翻訳します。言語モデルの操作は、レストランでシェフに注文するようなものです。注文(プロンプト)を出すと、シェフ(モデル)が料理(翻訳)を準備してくれます。

OutputParserの使用方法

次に、モデルからの出力を効率的に扱うためにOutputParserを使用します。これは、モデルからの出力を文字列として簡単に扱えるようにするためのツールです。例えば、料理が運ばれてきたときに、その料理を見やすく整えるようなものです。

from langchain_core.output_parsers import StrOutputParser

# 出力を文字列として取得するためのOutputParserをインスタンス化します。
parser = StrOutputParser()

# モデルからのレスポンスを文字列としてパースします。
parsed_result = parser.invoke(response)
print(parsed_result)
実行結果
こんにちわ!

このコードでは、モデルのレスポンスを文字列形式で取得しています。

PromptTemplatesの導入

PromptTemplatesを使用すると、ユーザーの入力を簡単にプロンプトに変換し、言語モデルに渡すことができます。これにより、アプリケーションのロジックをシンプルに保つことができます。例えば、注文書を使ってシェフに具体的な指示を出すようなものです。

from langchain_core.prompts import ChatPromptTemplate

# システムメッセージのテンプレートを定義します。
system_template = "Translate the following into {language}:"

# ユーザーからの入力とシステムメッセージを基にプロンプトテンプレートを作成します。
prompt_template = ChatPromptTemplate.from_messages([
    ("system", system_template),
    ("user", "{text}")
])

# テンプレートを使ってプロンプトをフォーマットします。
input_data = {"language": "Japanese", "text": "Hi!"}
formatted_prompt = prompt_template.invoke(input_data)
print(formatted_prompt)
実行結果
messages=[SystemMessage(content='Translate the following into Japanese:'), HumanMessage(content='Hi!')]

このコードは、ユーザーの入力を基に、動的にプロンプトを生成します。テンプレートは、ユーザーの要求に基づいてシェフに注文を伝えるための注文書のようなものです。

LangChain Expression Language (LCEL)の紹介

さて、ここで一歩進んで、LangChainのもう一つの強力なツールであるLangChain Expression Language(LCEL)について紹介しましょう。

LCELは、LangChainのコンポーネントを宣言的にチェーンするための方法です。これは、シンプルな「プロンプト + LLM」チェーンから、何百ものステップを持つ複雑なチェーンまで、コード変更なしでプロトタイプ

を本番環境に移行することをサポートするように設計されています。LCELの主な特徴は以下の通りです:

  • ストリーミングサポート:LCELでチェーンを構築すると、最初のトークンが出力されるまでの時間が最適化されます。これにより、LLMからのトークンをストリーミングOutputParserに直接送信し、LLMプロバイダーがトークンを出力する速度でインクリメンタルにパースされた出力を得ることができます。

  • 非同期サポート:LCELで構築されたチェーンは、同期API(例:Jupyterノートブックでのプロトタイピング)と非同期API(例:LangServeサーバー内)で呼び出すことができます。同じコードを使用してプロトタイプと本番環境で高性能を発揮し、多くの同時リクエストを処理することができます。

  • 最適化された並列実行:LCELチェーン内で並列に実行できるステップがある場合(例:複数のリトリーバーからドキュメントを取得する場合)、それを自動的に行い、同期インターフェースと非同期インターフェースの両方で最小の遅延を実現します。

  • リトライとフォールバック:LCELチェーンの任意の部分にリトライとフォールバックを設定できます。これにより、チェーンを大規模により信頼性の高いものにすることができます。

  • 中間結果へのアクセス:複雑なチェーンの場合、最終出力が生成される前に中間ステップの結果にアクセスすることが非常に便利です。これにより、ユーザーに進行状況を通知したり、チェーンのデバッグに使用することができます。中間結果は、すべてのLangServeサーバーで利用可能です。

  • 入力と出力のスキーマ:入力と出力のスキーマは、チェーンの構造から推論されたPydanticおよびJSONSchemaスキーマを各LCELチェーンに提供します。これを入力および出力の検証に使用することができ、LangServeの重要な部分です。

  • LangSmithとのシームレスなトレース:チェーンがますます複雑になるにつれて、各ステップで何が起こっているのかを理解することがますます重要になります。LCELを使用すると、すべてのステップが自動的にLangSmithにログ記録され、最大限の観察性とデバッグ性を提供します。

LCELを使うことで、複雑なアプリケーションをより柔軟で効率的に構築することができます。それでは、LCELを使ったコンポーネントのチェーン化を見てみましょう。

LCELを使ったコンポーネントのチェーン化

LCELを使用すると、異なるコンポーネントを簡単にチェーン化できます。これにより、モデルの呼び出しから出力の取得までを一つの流れで処理できます。LCELは、いくつかの異なる材料を一緒に混ぜて料理を作る料理法のようなものです。

# プロンプトテンプレート、モデル、出力パーサーを組み合わせてチェーンを作成します。
chain = prompt_template | model | parser

# チェーンに入力を渡して結果を取得します。
result = chain.invoke({"language": "Japanese", "text": "Hi!"})
print(result)
実行結果
messages=[SystemMessage(content='Translate the following into Japanese:'), HumanMessage(content='Hi!')]

この例では、プロンプトテンプレート、言語モデル、OutputParserを組み合わせて、簡単な翻訳チェーンを作成しています。これにより、ユーザーの要求が直接シェフに伝わり、シェフが料理を準備し、それを美しく盛り付けて提供するまでの一連のプロセスが効率的に行われます。

LangServeを使ったアプリケーションのデプロイ

最後に、作成したアプリケーションをデプロイする方法を見ていきましょう。LangServeを使用すると、LangChainのチェーンをREST APIとして簡単に提供できます。LangServeは、FastAPIと統合されており、Pydanticを使用したデータバリデーションもサポートしています。これにより、エンドポイントの入力と出力スキーマが自動的に推論され、すべてのAPIコールで強制されるため、エラーメッセージが豊富になります。

サーバーのセットアップ

まず、FastAPIを使用してアプリケーションをサーバーとして提供します。以下のコードをserve.pyとして保存してください。

import os

from dotenv import find_dotenv, load_dotenv
from fastapi import FastAPI
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import AzureChatOpenAI
from langserve import add_routes

load_dotenv(find_dotenv())

# プロンプトテンプレート、モデル、出力パーサーを定義します。
system_template = "Translate the following into {language}:"
prompt_template = ChatPromptTemplate.from_messages([("system", system_template), ("user", "{text}")])
model = AzureChatOpenAI(
    azure_deployment=os.environ["AZURE_OPENAI_DEPLOYMENT"],
)
parser = StrOutputParser()

# チェーンを定義します。
chain = prompt_template | model | parser

# appの定義としてFastAPIアプリケーションのインスタンスを作成します。
app = FastAPI(
    title="LangChain Server",
    version="1.0",
    description="A simple API server using LangChain's Runnable interfaces",
)

# チェーンをエンドポイントに追加します。
add_routes(app, chain, path="/chain")

if __name__ == "__main__":
    import uvicorn

    # サーバーを起動します。
    uvicorn.run(app, host="localhost", port=8000)

アプリケーションの実行

次に、ターミナルを開き、serve.pyを保存したディレクトリに移動して以下のコマンドを実行します。

python serve.py

サーバーが起動したら、ブラウザで http://localhost:8000/chain にアクセスすることで、APIエンドポイントを通じてアプリケーションを利用することができます。例えば、http://localhost:8000/chainのエンドポイントに{"language": "italian", "text": "hi"}というJSONをPOSTリクエストとして送信すると、翻訳されたテキストを受け取ることができます。

豆知識:APIドキュメントの確認

LangServeを使用すると、自動的にOpenAPIドキュメントが生成されます。サーバーを起動した後、ブラウザで http://localhost:8000/docs にアクセスすると、APIの仕様やエンドポイントの詳細を確認できます。

アプリケーションの利用例

requestsライブラリでの例

以下のようにクライアント側でリクエストを送信することもできます。Pythonのrequestsライブラリを使用して、エンドポイントにリクエストを送信してみましょう。

import requests

# APIエンドポイントに送信するデータ
data = {"language": "Japanese", "text": "Today is a beatuiful day."}

# POSTリクエストを送信して結果を取得
response = requests.post("http://localhost:8000/chain/invoke", json={'input': data})

# エラーチェック
if response.status_code == 200:
    print(response.json())
else:
    print(f"Error {response.status_code}: {response.text}")

実行結果
{'output': '今日は美しい日です。', 'metadata': {'run_id': '〇〇〇〇〇〇', 'feedback_tokens': []}}

langserve.RemoteRunnableクライアントでの例

from langserve import RemoteRunnable

remote_chain = RemoteRunnable("http://localhost:8000/chain/")
remote_chain.invoke({"language": "Japanese", "text": "Today is a beautiful day."})
実行結果
'今日は美しい日です。'

LangServe組み込みのUIでの例

http://localhost:8000/chain/playground/にアクセスしてみますと、以下のような画面UIからAPIを実行できます。この画面UIでは、ストリーミング出力と中間ステップを使用してlangserve.RemoteRunnableを構成および呼び出すためのシンプルな UI が公開されています。

これらの方法によって、FastAPIを用いたLangServeサーバーを介して翻訳された結果を取得できるようになります。これにより、簡単にLangChainアプリケーションをデプロイして、実際のプロジェクトで利用することが可能になります。

まとめ

今回の【Basic編#1】では、LangChainの基本的な使い方について学びました。シンプルな翻訳アプリケーションを通して、言語モデルの使い方、プロンプトテンプレートの設定方法、LCELを使ったチェーン化、そしてLangServeによるデプロイまでの一連の流れを解説しました。

このブログを通じて、LangChainの基本をしっかりと理解し、今後の開発に役立てていただければと思います。

次回は、公式チュートリアルの次のステップ、「Build a Chatbot」に進んで、よりインタラクティブなアプリケーションの作成方法について学んでいきます。公式チュートリアルの詳細については、こちらからご覧いただけますので、次回もお楽しみに!

参考文献

これからもLangChainの公式チュートリアルを1個ずつ、地味に地道にコツコツと解説していきますので、ぜひフォローしてください!

脚注
  1. Introduction ↩︎

Discussion