Introduction to LangChain

LangChainとは?

LangChainは、大規模言語モデル(LLM)を活用したアプリケーション開発のためのフレームワークで、LLMアプリケーションのライフサイクルを簡素化できます。

  • Developments:LangChainのオープンソースの構成要素、コンポーネント、サードパーティ連携を使用してアプリケーションを構築できます。LangGraphを使えば、ストリーミングとHuman-in-the-Loopをサポートするエージェントを作成できます。
  • Productionization:LangSmithを使用してチェーンの検査、監視、評価を行い、継続的な最適化と安全なデプロイを実現できます。
  • Deployment:LangGraph Cloudを使用して、LangGraphアプリケーションを本番環境に対応したAPIやアシスタントに変換できます。

具体的には、以下のオープンソースライブラリで構成されています。

  • langchain-core:基本的な抽象化とLangChain表現言語。
  • langchain-community:サードパーティ連携。
    • パートナーパッケージ(例:langchain-openailangchain-anthropicなど):一部の連携機能は、langchain-coreのみに依存する軽量なパッケージとして分離されています。
  • langchain:アプリケーションの認知アーキテクチャを構成するチェーン、エージェント、検索戦略。
  • LangGraph:グラフのエッジとノードをモデル化することで、LLMを使用した堅牢で状態を持つマルチアクターアプリケーションを構築します。LangChainとシームレスに統合できますが、単独でも使用可能です。
  • LangServeLangChainのチェーンをRESTful APIとしてデプロイします。
  • LangSmith:LLMアプリケーションのデバッグ、テスト、評価、モニタリングを可能にする開発者向けプラットフォームです。

事始め

LangChainは、AIアプリケーション開発を支援するPythonライブラリです。表現言語LCELと、OpenAI APIとの連携機能を提供します。

LangChainのインストールは、pipコマンドで行います。

$ pip install langchain==0.2.7 langchain-community==0.2.7 langchain-openai==0.1.12 unstructured==0.14.10 markdown==3.6
注意: LangChainはv0.1リリースでlangchainとlangchain_openaiの2つに分割されました。OpenAI APIとの連携機能はlangchain_openaiパッケージで提供されています。

また、OpenAI APIを使用するには、APIキーが必要です。

  1. OpenAIアカウントの作成: https://platform.openai.com/ からアカウントを作成します。
  2. APIキーの発行: ログイン後、画面右上の「User API keys」からAPIキーを発行します。

発行されたAPIキーは、環境変数OPENAI_API_KEYに設定します。

import os
os.environ["OPENAI_API_KEY"] = "YOUR-API-KEY"

LangChainの主要モジュール

LangChainは、以下の6つの主要モジュールから構成されています。これらのモジュールを組み合わせることで、複雑なAIアプリケーションを効率的に構築することができます。

  1. Models: 言語モデル(LLM)やチャットモデルとのインターフェースを提供します。OpenAI、Hugging Face、その他のAIプロバイダーのモデルを統一的に扱うことができます。
  2. Prompts: モデルへの入力を構造化し、テンプレート化する機能を提供します。動的なプロンプト生成やプロンプトの最適化を行うツールも含まれます。
  3. Chains: 複数のコンポーネント(モデル、プロンプト、その他の処理)を連結して、より複雑なタスクを実行するためのフレームワークです。
  4. Indexes: 大量のテキストデータを効率的に検索・クエリするための機能を提供します。ベクトルデータベースやその他の検索手法を利用できます。
  5. Memory: 会話の文脈や過去の情報を保持し、それを後続の処理に活用する機能を提供します。チャットボットなどの対話システムで特に重要です。
  6. Agents: 特定のタスクを自律的に実行する「エージェント」を作成するためのフレームワークです。エージェントは与えられた目標に向けて、適切なツールを選択し実行します。

以降で、これらの6つのモジュールの簡単な使い方を解説します。

Models (LLMs, Chat Models)

Modelsは、LangChainで使用する機械学習モデルを指定するものです。Modelsには、以下の3種類があります。

  • LLMs: OpenAI の Completions API (gpt-3.5-turbo-instruct)などの大規模言語モデル
  • Chat Models: OpenAI の Chat API (gpt-4gpt-3.5-turbo)のためのモジュール
  • Text Embedding Models: テキストをベクトル化するためのモデル

langchain_openai.OpenAIの使い方

from langchain_openai import OpenAI

def try_llm(prompt: str, model_name: str = "gpt-3.5-turbo-instruct") -> str:
    llm = OpenAI(model_name=model_name, temperature=0)
    result = llm.invoke(prompt)
    return result

if __name__ == "__main__":
    import os 
    os.environ["OPENAI_API_KEY"] = "YOUR-OPENAI-KEY"

    try_llm(prompt="深層学習について教えて下さい。")

langchain_openai.ChatOpenAIの使い方

from langchain_openai import ChatOpenAI

def try_chat(prompt: str, model_name: str = "gpt-3.5-turbo") -> None:
    chat = ChatOpenAI(model_name=model_name, temperature=0)
    result = chat.invoke(prompt)
    print(result)

if __name__ == "__main__":
    import os 
    os.environ["OPENAI_API_KEY"] = "YOUR-OPENAI-KEY"

    try_chat(prompt="深層学習について教えて下さい。")

Prompts (Prompt Template)

Promptsは、モデルの入力を組み立てるためのモジュールで、大きく以下の4つの要素が存在します。

  • Prompt Template: テキスト生成のためのテンプレートを作成する機能です。変数を含む文字列テンプレートを定義し、実行時に具体的な値を挿入してプロンプトを生成します。これにより、動的で再利用可能なプロンプトの作成が可能になります。
  • Chat Prompt Template: チャットベースのモデル用に最適化されたプロンプトテンプレートです。複数のメッセージを含む会話形式のプロンプトを作成できます。システムメッセージ、ユーザーメッセージ、AIアシスタントのメッセージなど、異なる役割のメッセージを組み合わせることができます。
  • Example Selectors: Few-shot Learningのために、プロンプトに含める例を動的に選択する機能です。タスクや入力に応じて最も関連性の高い例を選び、モデルの性能を向上させることができます。
    • RandomExampleSelector: ランダムに例を選択
    • LengthBasedExampleSelector: 入力の長さに基づいて例を選択
    • SemanticSimilarityExampleSelector: 意味的類似性に基づいて例を選択
  • Output Parsers: モデルの出力を構造化されたデータに変換する機能です。テキスト出力を特定のフォーマット(JSON、リスト、カスタム構造体など)に解析し、プログラムで扱いやすい形式に変換します。

langchain.prompts.PromptTemplateの実装例

from langchain.prompts import PromptTemplate

def try_template(keyword: str) -> str:
    template = """
    次の用語の概要を箇条書きで説明して下さい。
    
    用語: {keyword}
    """
    
    prompt = PromptTemplate(input_variables=["keyword"], template=template)
    result = prompt.format(keyword=keyword)
    return result

if __name__ == "__main__":
    import os 
    os.environ["OPENAI_API_KEY"] = "YOUR-OPENAI-KEY"
    
    print(try_template("深層学習"))

langchain.output_parsers.PydanticOutputParserの実装例

import textwrap
from typing import List
from langchain_openai import ChatOpenAI
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
from langchain.output_parsers import PydanticOutputParser
from pydantic import BaseModel, Field, validator

class Recipe(BaseModel):
    ingredients: List[str] = Field(description="ingredients of the dish")
    steps: List[str] = Field(description="steps to make the dish")

def create_chat(model_name: str = "gpt-3.5-turbo") -> ChatOpenAI:
    return ChatOpenAI(model_name=model_name, temperature=0)

def create_template() -> PromptTemplate:
    template = textwrap.dedent("""
    料理のレシピを教えてください。

    {format_instructions}

    料理名: {dish}
    """)
    parser = PydanticOutputParser(pydantic_object=Recipe)
    return PromptTemplate(
        template=template,
        input_variables=["dish"],
        partial_variables={"format_instructions": parser.get_format_instructions()},
    )

def try_chain(dish: str) -> str:
    chain = LLMChain(
        llm=create_chat(), 
        prompt=create_template()
    )
    return chain.invoke(dish)

if __name__ == "__main__":
    import langchain
    import os

    # APIキーの設定
    os.environ["OPENAI_API_KEY"] = "YOUR-OPENAI-KEY"

    # LangChainの出力を冗長にする
    langchain.verbose = True

    # Chainの実行
    result = try_chain("カレー")
    print(result)
    
    # 結果のパース
    parser = PydanticOutputParser(pydantic_object=Recipe)
    print(parser.parse(result))

Chains

Chainsは、各モジュール(ModelsTemplatesなど)を連結するために使用します。SimpleSequentialChainを使うと、Chain同士を直列につなぐことができます。

langchain.chains.LLMChainの実装例

import textwrap
from langchain_openai import ChatOpenAI
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate

def create_chat(model_name: str = "gpt-3.5-turbo") -> ChatOpenAI:
    return ChatOpenAI(model_name=model_name, temperature=0)

def create_template() -> PromptTemplate:
    template = textwrap.dedent("""
    次の専門用語を説明して下さい。ただし、書式に準拠した内容でまとめて下さい。

    専門用語: {keyword}
    書式: 箇条書き
    """)
    return PromptTemplate(input_variables=["keyword"], template=template)

def try_chain(keyword: str) -> str:
    chain = LLMChain(
        llm=create_chat(), 
        prompt=create_template()
    )
    return chain.invoke(keyword)

if __name__ == "__main__":
    import langchain
    import os

    # APIキーの設定
    os.environ["OPENAI_API_KEY"] = "YOUR-OPENAI-KEY"

    # LangChainの出力を冗長にする
    langchain.verbose = True

    # Chainの実行
    print(try_chain("地球温暖化"))

langchain.chains.SimpleSequentialChainの実装例

import textwrap
from langchain_openai import ChatOpenAI
from langchain.chains import LLMChain, SimpleSequentialChain
from langchain.prompts import PromptTemplate

def create_chat(model_name: str = "gpt-3.5-turbo") -> ChatOpenAI:
    return ChatOpenAI(model_name=model_name, temperature=0)

def create_cot_template() -> PromptTemplate:
    template = textwrap.dedent("""
    以下の質問に回答して下さい。

    ### 質問 ###
    {question}
    ### 質問終了 ###

    ステップバイステップで考えましょう。
    """)
    return PromptTemplate(input_variables=["question"], template=template)

def create_summarize_template() -> PromptTemplate:
    template = textwrap.dedent("""
    入力を結論だけ一言で要約して下さい。

    ### 入力 ###
    {input}
    ### 入力終了 ###
    """)
    return PromptTemplate(input_variables=["input"], template=template)

def create_cot_chain(chat: ChatOpenAI) -> LLMChain:
    return LLMChain(llm=chat, prompt=create_cot_template())

def create_summarize_chain(chat: ChatOpenAI) -> LLMChain:
    return LLMChain(llm=chat, prompt=create_summarize_template())

def try_sequential_chain(question: str) -> str:
    chat = create_chat()
    simple_chain = SimpleSequentialChain(chains=[
        create_cot_chain(chat),
        create_summarize_chain(chat)
    ])

    return simple_chain(question)

if __name__ == "__main__":
    import langchain
    import os

    # APIキーの設定
    os.environ["OPENAI_API_KEY"] = "YOUR-OPENAI-KEY"

    # LangChainの出力を冗長にする
    langchain.verbose = True

    # SimpleSequentialChainの実行
    question = textwrap.dedent("""
    私は市場に行って10個のリンゴを買いました。隣人に2つ、修理工に2つ渡しました。
    それから、5つのリンゴを買って1つ食べました。残りは何個ですか?
    """)
    print(try_sequential_chain(question))

Indexes

様々なテーマに対して的確に応答する大規模言語モデルですが、学習に使用されていない情報については正確に回答できません。そのため、ハルシネーション(Hallucination, 幻覚) と呼ばれる間違った情報を生成することがあります。

そこで、この問題を解決するため、Vector Storeと呼ばれる機能を活用します。大規模言語モデルが未学習の情報(テキスト)をベクトル化して Vector Storeに保存しておき、入力と近いベクトルの文章をVector Storeから検索してコンテキストに含める手法です。

langchain_community.document_loaders.DirectoryLoaderの実装例

from langchain_openai import ChatOpenAI
from langchain_openai import OpenAIEmbeddings
from langchain_community.document_loaders import DirectoryLoader
from langchain_community.vectorstores import Chroma
from langchain.indexes import VectorstoreIndexCreator
from langchain.indexes.vectorstore import VectorStoreIndexWrapper

def create_chat(model_name: str = "gpt-3.5-turbo") -> ChatOpenAI:
    return ChatOpenAI(model_name=model_name, temperature=0)

def create_index() -> VectorStoreIndexWrapper:
    embeddings = OpenAIEmbeddings()
    loader = DirectoryLoader("./diffusers/docs/source/ja", glob="**/*.md")
    index = VectorstoreIndexCreator(vectorstore_cls=Chroma, embedding=embeddings).from_loaders([loader])
    return index

def try_vector_store(question: str) -> str:
    chat = create_chat()
    index = create_index()
    return index.query(question, llm=chat)

if __name__ == "__main__":
    import os
    import langchain
    
    # APIキーの設定
    os.environ["OPENAI_API_KEY"] = "YOUR-OPENAI-KEY"

    # LangChainの出力を冗長にする
    # langchain.verbose = True

    # 動作確認用の環境を作成する
    if not os.path.exists("diffusers"):
        os.system("git clone https://github.com/huggingface/diffusers")

    # Vector Storeの動作確認
    print(try_vector_store("diffusersの基本的な使い方を教えてください。"))

Memory

大規模言語モデルを利用したアプリケーションでは、対話インターフェースが一般的で、会話内の以前の情報を参照することが重要となります。このときに使用されるのがMemoryで、Memoryは以前の対話情報を保存する機能を提供します。また、読み取りと書き込みの2つの基本アクションをサポートし、対話型AIは実行前に読み取り、実行後に書き込むことで過去の情報を利用可能にできます。

langchain.memory.ConversationBufferMemoryの実装例

from langchain_openai import ChatOpenAI
from langchain.chains import ConversationChain
from langchain.memory import ConversationBufferMemory

def create_chat(model_name: str = "gpt-3.5-turbo") -> ChatOpenAI:
    return ChatOpenAI(model_name=model_name, temperature=0, max_tokens=100)

def try_memory(max_iter: int = 3) -> None:
    chat = create_chat()
    conversation = ConversationChain(llm=chat, memory=ConversationBufferMemory())

    for _ in range(max_iter):
        user_message = input("You: ")
        response = conversation.predict(input=user_message)
        print(f"ChatGPT: {response}")

if __name__ == "__main__":
    import os
    
    # APIキーの設定
    os.environ["OPENAI_API_KEY"] = "YOUR-API-KEY"

    # Memoryの動作確認
    try_memory()

Agents

  • Agentsを使用すると、LLMが様々なツールを選択して使用しながら動作します。
    • 実際には、LLMがツールを使用するわけではなく、ツールを使用しているように動作します。
  • ツールの例
    • Bash
    • Google検索
    • Pythonインタプリタ
    • ウィキペディアのAPI
    • etc…
  • Agentsは、MRKLReActといった仕組みのプロンプトで動作します。
    • MRKL (Multi-Round Knowledge Loop): AIエージェントが複数のツールやアプローチを組み合わせてタスクを解決するための枠組みです。
    • ReAct: LLMが外部ツールと対話して追加情報を取得し、より信頼性の高い事実に基づく回答を生成するための機能です。

Linux Terminalを使用した例

from langchain_openai import ChatOpenAI
from langchain.agents import load_tools, initialize_agent
from langchain.agents.agent import AgentExecutor

def create_chat(model_name: str = "gpt-3.5-turbo") -> ChatOpenAI:
    return ChatOpenAI(model_name=model_name, temperature=0)

def create_agent() -> AgentExecutor:
    chat = create_chat()
    tools = load_tools(["terminal"], llm=chat)
    return initialize_agent(tools, chat, agent="zero-shot-react-description")

def try_agent() -> None:
    agent_chain = create_agent()
    result = agent_chain.run("What is your current directory ?")
    print(result)

今回は、LangChainの基本的な使い方を紹介しましたが、この他にも様々な機能が実装されています。詳しくは、公式のドキュメント等をご確認ください。