見出し画像

「LangChain完全入門」を寄り道写経で完全入門①LangChain概要・Model I/O

書籍の著者 田村悠 先生


この記事は、書籍「LangChain完全入門」の第1章「ChatGPTとLangChain」、第2章「Model I/O - 言語モデルを扱いやすくする」の通称「寄り道写経」を取り扱います。
寄り道の狙いは「最近のライブラリで動かすこと」です。
したがいまして、記事に掲載する内容は「コード」に焦点を絞ります。

今回取り組む章は、大規模言語モデル、ChatGPT との関わりで LangChain が位置づけられる様子をイメージトレーニングできます!
では書籍を開いて言語モデル活用の旅に出発です🚀

旅行に行く家族のイラスト:「いらすとや」さんより

はじめに


書籍「LangChain完全入門」のご紹介

書籍「LangChain完全入門 生成AIアプリケーション開発がはかどる大規模言語モデルの操り方」(以下、テキストと呼びます)は、2023年10月に発売された LangChain の入門書です。

LangChain は大規模言語モデル(LLM)を活用するアプリケーション開発で一般的に用いられる機能を提供する優れたライブラリであり、テキストによると「執筆時点でデファクトスタンダードとなっています」とのことです。
LLM と API でつながるアプリがサクッと出来てしまう魔法のライブラリです。
LangChain公式サイトはこちら。

テキストを実践して「10数行のコードでLLMを使った簡易なチャットボットを作れる!」という衝撃に何度も出会いました。
そして、自分で書いた(テキストから写経した)チャットGPTのようなChatボットの存在がとても愛おしいのです!

織姫と彦星のイラスト(ゆめかわ):「いらすとや」さんより

回想

そう、あれは大規模書店でのことでした。
プログラミング関連棚の一番いい場所に大量に並んだ「LangChain完全入門」が視線に入ってきたのです。
しかも音声付きで…

そろそろ言語モデルの活用に完全入門しませんか

引き寄せられるように私の手がテキストの1冊を掴んでいました。
気持ちが熱くなりました。

テキストは、LangChain の 6つのモジュールの基礎的な利用方法をコードとコード実行結果を用いて「とてもわかりやすく」解説する優れた書籍です。
プログラミング初学者の私にとって、手入力でいいかげん丹念に写経したコードの実行結果を「テキスト掲載の正解」(テキストのコード実行結果)と照らし合わせられることは、「写経コードが正しく動いたんだ!」という安心感と達成感を確保できる「御守り」です。
安心感が「 LangChain 最高~!楽しい~!」に直結するのです。

寄り道写経って?

さて、LLM の分野は秒進分歩で成長し、社会に大きな変化をもたらしています。
この変化の波は、テキストをも飲み込んだのです。
langchain をはじめとするテキスト利用ライブラリのメジャーな変更によって、テキストのコードが動かなくなったり、ワーニングがたくさん出るなどの好ましくない結果をもたらしているのです。
そこでテキストを書いた先生は、GitHubで「テキストのコードが動作するライブラリのインストール方法」を用意して、大波の影響を回避したのです。

いろいろな波のイラスト:「いらすとや」さんより

ですが・・・
これから廃れていく古いライブラリの使い方を勉強することに抵抗を覚えた私は「最近のライブラリで動くコード」に寄り道する決心をしました(そんな大袈裟なことでは無い。。。)
ということで、寄り道写経の結果をブログ化して参ります。
エラーの収拾がつかなかったあの頃を思い出して(遠目)

良書「LangChain完全入門」から LangChain を始めようとする仲間に、この記事を少しでも役立てて頂けたらなら幸いです🍀

そして・・・
「LangChain完全入門2」の出版を心待ちにしております!

引用表記

この記事は、出典に記載の書籍に掲載された文章及びコードを引用し、適宜、掲載文章とコードを改変して書いています。
【出典】
「LangChain完全入門 生成AIアプリケーション開発がはかどる大規模言語モデルの操り方」初版、著者 田村悠、インプレス

記事中のイラストは、「かわいいフリー素材集いらすとや」さんのイラストをお借りしています。
ありがとうございます!


おしながき


記事の書き方

  • テキストの「.pyファイル」ごとに書きます。

  • テキストの掲載順に書きます。

  • インポート文のみの変更で動く場合には、インポート文の箇所のみ書きます。

  • インポート文以外の変更が必要な場合には、コード全文を書きます。
    変更箇所は ★印 をつけるなどして明確にします。

  • テキストのコード行番号とは一致しません。

  • 実行結果は書きません。

コードの変更方針

  • 2024年5月6日から10日までの間に変更後コードを実行して動くことを確認しました。

  • ベストな変更方法では無い可能性があります(可能性が高いです)。

  • テキストのオリジナルコードを実行することで発生するエラーの回避策を試行錯誤して考えます。

  • なるべくテキストのコードを尊重して変更します。
    新しいライブラリが期待する使用方法では無い可能性があります。

  • なるべくワーニング発生を回避します。
    ただし、私の技量でワーニング回避できない部分は文章でお知らせします。
    【緩募】ワーニング対策が分かる方、ぜひ対策を教えて下さい。

  • ところどころにコメント文を入れていますが、私的なメモです。
    気にしないでください。

Pythonと主なライブラリの使用バージョン

$$
\begin{array}{l:l}
ライブラリ等 & バージョン \\
\hline
\text{python} & 3.11.9 \\
\text{langchain} & 0.1.17 \\
\text{langchain-core} & 0.1.50 \\
\text{langchain-community} & 0.0.36 \\
\text{langchain-openai} & 0.1.6 \\
\text{langchain-text-splitters} & 0.0.1 \\
\text{langchainhub} & 0.1.15 \\
\text{openai} & 1.25.2 \\
\text{pymupdf} & 1.24.2 \\
\text{spacy} & 3.7.4 \\
\text{tiktoken} & 0.6.0 \\
\text{chromadb} & 0.5.0 \\
\text{chainlit} & 1.0.506 \\
\text{wikipedia} & 1.4.0 \\
\text{redis} & 5.0.4 \\
\text{bs4} & 0.0.2 \\
\text{google-search-results} & 2.4.2 \\
\end{array}
$$

免責事項

私の使用環境で動いたコードをそのまま掲載しており、別の環境で動かない可能性があります。
記事のコードにより生じる直接的または間接的な損害について、私では一切の責任を負いかねます。予めご了承ください。

第1章 ChatGPTとLangChain 関連


sample.py (40ページ)

### OpenAIのAPIを呼び出して動作確認

# インポート
from openai import OpenAI                    # ★ json不要

# clientの準備
client = OpenAI()                            # ★追加

# 言語モデルの呼び出し、問い合わせ実行
response = client.chat.completions.create(   # ★変更
    model='gpt-3.5-turbo',
    messages = [
        {'role': 'user', 'content': 'iPhone8のリリース日を教えて'},
    ]
)

# 結果表示
print(response.to_json())                    # ★.to_json()

sample.py (44ページ)

### OpenAIのAPIを呼び出して動作確認

# インポート
from openai import OpenAI                    # ★ json不要

# clientの準備
client = OpenAI()                            # ★追加

# 言語モデルの呼び出し、問い合わせ実行
response = client.chat.completions.create(   # ★変更
    model='gpt-3.5-turbo',
    messages = [
        {'role': 'user',
         'content': 'そばの原材料を教えて'},
    ],
    max_tokens=100,
    temperature=1,
    n=2,
)

# 結果表示
print(response.to_json())                    # ★.to_json()

sample_complete.py

### OpenAIのAPIを呼び出して動作確認

# インポート
from openai import OpenAI                    # ★ json不要

# clientの準備
client = OpenAI()                            # ★追加

# openaiはcompletionsをレガシーに位置づけ、chat.completions APIの活用を促している
response = client.completions.create(        # ★completions
    model='gpt-3.5-turbo-instruct',
    prompt= '今日の天気がとても良くて、気分が',
    stop='。',
    max_tokens=100,
    n=2,
    temperature=0.5,
)
print(response.to_json())                    # ★.to_json()

第2章 Model I/O 関連


sample.py

### OpenAIの言語モデルを呼び出し

# インポート
from openai import OpenAI                    # ★openai.OpenAI

# clientの準備
client = OpenAI()                            # ★追加

# 言語モデルの呼び出し、問い合わせ実行
response = client.chat.completions.create(   # ★変更
    model='gpt-3.5-turbo',
    messages = [
        {'role': 'user',
         'content': 'iPhone8のリリース日を教えて'},
    ],
)

# 結果表示
print(response.choices[0].message.content)   # ★変更(contentのみ抽出)

language_models.py

## チャットモデル ※言語モデルごとにインターフェイスが異なる

# インポート
from langchain_openai import ChatOpenAI               # ★_openai
from langchain_core.messages import HumanMessage      # ★_core

# chatの準備
chat = ChatOpenAI(model='gpt-3.5-turbo')              # ★追加

# messageの準備
message = [HumanMessage(content='こんにちは!')]       # ★追加

# chatの実行
response = chat.invoke(message)                       # ★invoke
print(response.content)

language_models_ai_message_sample.py

## チャットモデル ※言語モデルごとにインターフェイスが異なる
# テキストのコードに動かすためのコードを追加(ただし返答は意図していない結果になる)

# インポート
from langchain_openai import ChatOpenAI                      # ★_openai
from langchain_core.messages import HumanMessage, AIMessage  # ★_core

# chatの準備
chat = ChatOpenAI(model='gpt-3.5-turbo')                     # ★追加

# メッセージの設定                                              ★追加
message = [
    HumanMessage(content='茶碗蒸しの作り方を教えて'),
    AIMessage(content='{ChatModelからの返答である茶碗蒸しの作り方}'),
    HumanMessage(content='英語に翻訳して')
]

# チャットの実行
result = chat.invoke(message)                                # ★invoke
print(result.content)

language_models_chat_anthropic_sample.py
掲載省略

prompt.py
インポートのみ変更

# インポート
from langchain_core.prompts import PromptTemplate   # ★_core

(以下省略)

prompt_and_language_model.py

## 言語モデルを指定してプロンプトテンプレートを利用

# インポート
from langchain_core.prompts import PromptTemplate    # ★_core
from langchain_openai import ChatOpenAI              # ★_openai
from langchain_core.messages import HumanMessage     # ★_core

# chatの準備
chat = ChatOpenAI(model='gpt-3.5-turbo')

# promptの準備
prompt = PromptTemplate(
    template='{product}はどこの会社が開発した製品ですか?',
    input_variables=[
        'product'
    ]
)

# messageの準備                                        ★追加
message = [
    HumanMessage(content=prompt.format(product='iPhone')),
]

# chatの実行
result = chat.invoke(message)                        # ★invoke
print(result.content)

prompt_template_from_template_save_sample.py
インポートのみ変更

# インポート
from langchain_core.prompts import PromptTemplate      # ★_core

(以下省略)

prompt_template_from_template_load_sample.py
インポートのみ変更

# インポート
from langchain_core.prompts import load_prompt               # ★_core

(以下省略)

list_output_parser.py

### リスト形式で結果を受取

# インポート
from langchain_openai import ChatOpenAI              # ★_openai
from langchain_core.output_parsers import CommaSeparatedListOutputParser # ★_core
from langchain_core.messages import HumanMessage     # ★_core

# chatの準備
chat = ChatOpenAI(model='gpt-3.5-turbo')

# 出力パーサーの準備
output_parser = CommaSeparatedListOutputParser()

# messageの準備                                       # ★追加
message = [
    HumanMessage(content='Appleが開発した代表的な製品を3つ教えて下さい'),
    HumanMessage(content=output_parser.get_format_instructions()),
]

# chatの実行
result = chat.invoke(message)                         # ★invoke

# 出力結果を解析してリスト形式に変換し、回答を1件ずつ表示
for item in output_parser.invoke(result):             # ★invoke
    print('代表的な製品 => ' + item)

model_io_llm.py

### LLMモデルの実行

# インポート
from langchain_openai import OpenAI               # ★_openai

# llmの準備
llm = OpenAI(model='gpt-3.5-turbo-instruct')

# llmの実行
result = llm.invoke(                              # ★invoke
    '美味しいラーメンを',
    stop='。'
)
print(result)

chat_model_cache.py

### 回答のキャッシュ

# インポート
import time
import langchain
from langchain.cache import InMemoryCache
from langchain_openai import ChatOpenAI             # ★_openai
from langchain_core.messages import HumanMessage    # ★_core

# llm_cacheにInMemoryCacheを設定
langchain.llm_cache = InMemoryCache()

# chatの準備
chat = ChatOpenAI()

# 1回目の実行
start = time.time()
message = [HumanMessage(content='こんにちは!')]     # ★追加
result = chat.invoke(message)                       # ★invoke
end = time.time()
print(result.content)
print(f'実行時間: {end - start}秒')

# 2回目の実行
start = time.time()
result = chat.invoke(message)                       # ★invoke
end = time.time()
print(result.content)
print(f'実行時間: {end - start}秒')

chat_model_streaming.py

### streaming callbackを使わない方法
# https://python.langchain.com/docs/modules/model_io/chat/streaming/

# インポート
from langchain_openai import ChatOpenAI              # ★_openai
from langchain_core.messages import HumanMessage     # ★_core

# chatの準備
chat = ChatOpenAI()

# messageの準備
message = [HumanMessage(content='おいしいステーキの焼き方を教えて')] # ★追加

# リクエストを送信して結果をストリーミング(逐次)表示
for chunk in chat.stream(message):                   # ★chat.stream
    print(chunk.content, end='', flush=True)

model_io_few_shot.py

### Few-shot プロンプトを使用
# https://python.langchain.com/docs/modules/model_io/prompts/few_shot_examples/

# インポート
from langchain_openai import OpenAI                              # ★_opanai
from langchain_core.prompts.few_shot import FewShotPromptTemplate  # ★_core
from langchain_core.prompts.prompt import PromptTemplate         # ★_core

# examplesの準備
examples = [
    {'input': 'LangChainはChatGPT・Large Language Model(LLM)の実利用を'
              'より柔軟に簡易に行うためのツール群です',
     'output': 'LangChainは、ChatGPT・Large Language Model(LLM)の実利用を'
               'より柔軟に、簡易に行うためのツール群です。',
    },
]

# promptの準備
prompt = PromptTemplate(
    input_variables=['input', 'output'],
    template='入力: {input}\n出力: {output}',
)

# Few-shot Promptの準備
few_shot_prompt = FewShotPromptTemplate(
    examples=examples,
    example_prompt=prompt,
    prefix='以下の句読点の抜けた入力に句読点を追加してください。'
           '追加して良い句読点は「、」「。」のみです。'
           '他の句読点は追加しないでください',
    suffix='入力: {input_string}\n出力: ',
    input_variables=['input_string'],
)

# LLMの準備
llm = OpenAI()

# Few-shotプロンプトの作成
formatted_prompt = few_shot_prompt.format(
    input_string='私はさまざまな機能がモジュールとして提供されている'
                 'LangChainを使ってアプリケーションを開発しています'
)

# LLMの実行
result = llm.invoke(formatted_prompt)                              # ★invoke

# プロンプトとLLMの回答を表示
print('formatted_prompt: ', formatted_prompt)
print('result: ', result)

datetime_output_parser.py

### 結果を日時形式で受取り
# ワーニングを解消できない。
# LangChainDeprecationWarning: Importing GuardrailsOutputParser ...
# https://python.langchain.com/docs/modules/model_io/output_parsers/types/datetime/

# インポート
from langchain_openai import ChatOpenAI                     # ★_openai 
from langchain_core.messages import HumanMessage            # ★_core
from langchain_core.prompts.prompt import PromptTemplate    # ★_core
from langchain.output_parsers import DatetimeOutputParser

# 出力パーサの準備
output_parser = DatetimeOutputParser()

# chatの準備
chat = ChatOpenAI(model='gpt-3.5-turbo')

# promptの準備
prompt = PromptTemplate.from_template(
    template='{product}のリリース日を教えて',
)

# messageの準備                                              # ★追加
message = [
    HumanMessage(content=prompt.format(product='iPhone8')),
    HumanMessage(content=output_parser.get_format_instructions()),
]

# chatの実行
result = chat.invoke(message)                                # ★invoke
print('result: ', result)                                    # ★検証用

# 出力結果を解析して日時形式に変換
output = output_parser.parse(result.content)
print(output)

pydantic_outputparser.py (83ページ)

### 誤った回答に対して修正を指示する
# ワーニングを解消できない。
# LangChainDeprecationWarning: Importing GuardrailsOutputParser ...

# インポート
from langchain_openai import ChatOpenAI                         # ★_openai
from langchain_core.messages import HumanMessage                # ★_core
from langchain_core.output_parsers import PydanticOutputParser  # ★_core
from pydantic import BaseModel, Field, field_validator

# chatの準備
chat = ChatOpenAI()

# Pydanticのモデルを定義:スマートフォンの情報を表現するデータモデル
class Smartphone(BaseModel):
    release_date: str = Field(description='スマートフォンの発売日')
    screen_inches: float = Field(
        description='スマートフォンの画面サイズ(インチ)')
    os_installed: str = Field(
        description='スマートフォンにインストールされているos')
    name_model: str = Field(description='スマートフォンのモデル名')

    @field_validator('screen_inches')
    def validate_screen_inches(cls, field):
        if field <= 0:
            raise ValueError('Screen inches must be a positive number')
        return field

# Pydanticを用いたパーサの準備
parser = PydanticOutputParser(pydantic_object=Smartphone)

# メッセージの準備                                                 # ★追加
message = [
    HumanMessage(content='Androidでリリースしたスマートフォンを1個挙げて'),
    HumanMessage(content=parser.get_format_instructions()),
]

# chatの実行
result = chat.invoke(message)                                     # ★invoke
print('result: ', result)                                         # ★検証用

# 出力結果をPydanticモデルで解析してモデル名を表示
parsed_result = parser.parse(result.content)
print('parsed_result: ', parsed_result)                           # ★検証用
print(f'モデル名: {parsed_result.name_model}')

pydantic_outputparser.py (86ページ)

### 誤った回答に対して修正を指示する
# ワーニングを解消できない。
# LangChainDeprecationWarning: Importing GuardrailsOutputParser ...

# インポート
from langchain_openai import ChatOpenAI                         # ★_openai
from langchain.output_parsers import OutputFixingParser
from langchain_core.output_parsers import PydanticOutputParser  # ★_core
from langchain_core.messages import HumanMessage                # ★_core
from pydantic import BaseModel, Field, field_validator

# chatの準備
chat = ChatOpenAI()

# Pydanticのモデルを定義:スマートフォンの情報を表現するデータモデル
class Smartphone(BaseModel):
    release_date: str = Field(description='スマートフォンの発売日')
    screen_inches: float = Field(
        description='スマートフォンの画面サイズ(インチ)')
    os_installed: str = Field(
        description='スマートフォンにインストールされているos')
    name_model: str = Field(description='スマートフォンのモデル名')

    @field_validator('screen_inches')
    def validate_screen_inches(cls, field):
        if field <= 0:
            raise ValueError('Screen inches must be a positive number')
        return field

# OutputFixingParserを用いたパーサの準備
parser = OutputFixingParser.from_llm(
    parser=PydanticOutputParser(pydantic_object=Smartphone),
    llm=chat,
)

# messageの準備
message = [                                                        # ★追加
    HumanMessage(content='Androidでリリースしたスマートフォンを1個挙げて'),
    HumanMessage(content=parser.get_format_instructions()),
]

# chatの実行
result = chat.invoke(message)                                      # ★invoke
print('result: ', result)                                          # ★検証用

# 出力結果をPydanticモデルで解析して表示
parsed_result = parser.parse(result.content)
print('parsed_result: ', parsed_result)                            # ★検証用
print(f'モデル名: {parsed_result.name_model}')
print(f'画面サイズ: {parsed_result.screen_inches}インチ')
print(f'OS: {parsed_result.os_installed}')
print(f'発売日: {parsed_result.release_date}')

第1章、第2章の寄り道写経は以上です。


シリーズの記事

次の記事

前の記事

この記事からシリーズははじまります

目次

ブログの紹介


note で7つのシリーズ記事を書いています。
ぜひ覗いていってくださいね!

1.のんびり統計

統計検定2級の問題集を手がかりにして、確率・統計をざっくり掘り下げるブログです。
雑談感覚で大丈夫です。ぜひ覗いていってくださいね。
統計検定2級公式問題集CBT対応版に対応しています。
Python、EXCELのサンプルコードの配布もあります。

2.実験!たのしいベイズモデリング1&2をPyMC Ver.5で

書籍「たのしいベイズモデリング」・「たのしいベイズモデリング2」の心理学研究に用いられたベイズモデルを PyMC Ver.5で描いて分析します。
この書籍をはじめ、多くのベイズモデルは R言語+Stanで書かれています。
PyMCの可能性を探り出し、手軽にベイズモデリングを実践できるように努めます。
身近なテーマ、イメージしやすいテーマですので、ぜひぜひPyMCで動かして、一緒に楽しみましょう!

3.実験!岩波データサイエンス1のベイズモデリングをPyMC Ver.5で

書籍「実験!岩波データサイエンスvol.1」の4人のベイジアンによるベイズモデルを PyMC Ver.5で描いて分析します。
この書籍はベイズプログラミングのイロハをざっくりと学ぶことができる良書です。
楽しくPyMCモデルを動かして、ベイズと仲良しになれた気がします。
みなさんもぜひぜひPyMCで動かして、一緒に遊んで学びましょう!

4.楽しい写経 ベイズ・Python等

ベイズ、Python、その他の「書籍の写経活動」の成果をブログにします。
主にPythonへの翻訳に取り組んでいます。
写経に取り組むお仲間さんのサンプルコードになれば幸いです🍀

5.RとStanではじめる心理学のための時系列分析入門 を PythonとPyMC Ver.5 で

書籍「RとStanではじめる心理学のための時系列分析入門」の時系列分析をPythonとPyMC Ver.5 で実践します。
この書籍には時系列分析のテーマが盛りだくさん!
時系列分析の懐の深さを実感いたしました。
大好きなPythonで楽しく時系列分析を学びます。

6.データサイエンスっぽいことを綴る

統計、データ分析、AI、機械学習、Pythonのコラムを不定期に綴っています。
統計・データサイエンス書籍にまつわる記事が多いです。
「統計」「Python」「数学とPython」「R」のシリーズが生まれています。

7.Python機械学習プログラミング実践記

書籍「Python機械学習プログラミング PyTorch & scikit-learn編」を学んだときのさまざまな思いを記事にしました。
この書籍は、scikit-learnとPyTorchの教科書です。
よかったらぜひ、お試しくださいませ。

最後までお読みいただきまして、ありがとうございました。

この記事が参加している募集

新生活をたのしく

この記事が気に入ったらサポートをしてみませんか?