1週間でLangChainを使えるようになる： Day 1 - LangChainとは

# このシリーズで分かること
近年、OpenAIのChatGPTに代表される大規模言語モデル（以下、LLM）の進化は目覚ましいものがあります。しかし、LLMを単体で利用するだけでは、できることに限界があります。そこで登場するのが、LLMを活用したアプリケーション開発を支援するフレームワーク「LangChain」です。

本記事では、LangChainの概要、主要な機能、できることなどを解説します。

:::note info
この記事は、以下の前提知識がある方を対象とします。
- Pythonコーディング知識
- 仮想環境やライブラリに対する知識
:::

# Day1で分かること
本ページでは、LangChainの何がそんなにすごいのか？を大まかに理解することができます。
また、本ページでは、無料で学習できるようにOllamaを用います。
下記を参考にOllamaサーバーを起動してください。

`ollama run [モデル名]`

https://qiita.com/mkuwan/items/ff84679ed8308ae78979

# そもそもLangChainとは？
LangChainは、LLMを活用したアプリケーション開発を容易にするためのオープンソースフレームワークです。LLM、外部データソース、その他のツールを組み合わせることで、より高度なタスクを実行するアプリケーションを開発できます。
LangChainは、Language（言語）とChain（アクションの連鎖）の二つの意味を持った言葉です。

## LangChainのChainって？
昨今では、問い合わせチャットボットやDeep Reseachなどで、ユーザーからの問い合わせに対して自社の独自データや生成AIの事前学習時には存在しないデータを検索する仕組みが多く作られています。
 > *[大阪万博LINEチャットボット](https://ascii.jp/elem/000/004/163/4163474/)*
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643462/9c2bdd72-88d7-4988-be4b-f34499ce0698.png)

これらの仕組みは、ユーザーからの問い合わせをアクションとして、PDFやネット検索（アクション）を行います。このアクションの連鎖を"**Chain**"と言います。

## LangChainの主要な機能
LangChainは、主に以下の機能を提供します。

- **モデルI/O**： LLMへの入出力の管理
- **プロンプト**： LLMへの指示文の作成と管理
- **チェーン**： 複数のLLMやツールを組み合わせた処理の実行
- **エージェント**： LLMが自律的にタスクを実行するための機能
- **メモリ**： 会話履歴や過去の情報を記憶するための機能
- **Retrieval**： 外部データソースからの情報検索


# LangChainを用いたモデルI/Oの統一
LLMからレスポンスを得るためには、開発会社ごと(OpenAI, Google, Anthoropicなど)にAPI呼び出し方法(SDK)が用意されています。

:::note warn
以下、APIキーやベースURLなどは、各ドキュメントを参照して適切に配置してください。
:::

## OpenAI`s API 
```python
# @ref: https://platform.openai.com/docs/quickstart?api-mode=chat&lang=python
from openai import OpenAI
client = OpenAI()

completion = client.chat.completions.create(
    model="gpt-4o",
    messages=[
        {
            "role": "user",
            "content": "Write a one-sentence bedtime story about a unicorn."
        }
    ]
)

print(completion.choices[0].message.content)
```
https://platform.openai.com/docs/quickstart?api-mode=chat&lang=python

## Google`s API
```python
# @ref: https://ai.google.dev/gemini-api/docs/quickstart?hl=ja&lang=python
from google import genai

client = genai.Client(api_key="YOUR_API_KEY")
response = client.models.generate_content(
    model="gemini-2.0-flash", contents="Explain how AI works"
)
print(response.text)
```
https://ai.google.dev/gemini-api/docs/quickstart?hl=ja&lang=python

## ollama`s Library
```python
from ollama import chat
from ollama import ChatResponse

response: ChatResponse = chat(model='llama3.2', messages=[
  {
    'role': 'user',
    'content': 'Why is the sky blue?',
  },
])
print(response['message']['content'])
# or access fields directly from the response object
print(response.message.content)
```
https://github.com/ollama/ollama-python

などの様々な開発会社ごとに様々なAPIが用意されています。
しかし、上の２つの例でも分かると思いますが、各社で統一したAPI使用方法は用意されていません。そこでLangChainでは、これらのAPIを以下のように呼び出すことができます。

## LangChainを用いたLLM API
```python
def res_langchain(user_question: str) -> str:
    # OpenAI API
    # lib: pip install langchain-openai
    from langchain_openai import ChatOpenAI
    llm = ChatOpenAI(model="gpt-4o")

    # # Gemini API
    # # lib: pip install -U langchain-google-genai
    from langchain_google_genai import ChatGoogleGenerativeAI
    llm = ChatGoogleGenerativeAI(model="gemini-pro")

    # ollama Lib
    # lib: pip install -U langchain-ollama
    from langchain_ollama import ChatOllama
    llm = ChatOllama(model = "llama3")

    res = llm.invoke(user_question)
    return res.content
```
https://python.langchain.com/docs/integrations/providers/

と書くことができます。
ここで効果的な点は以下の２点です。
1. **I/Oの統一**：応答を１つの関数で完結させること
1. **モデル利用の抽象化**：LLMモデルの動作抽象度が向上

特に、LLMのAPIを使用したことがある人は、LLMモデルI/Oのスマートさに驚くと思います。これによって、LangChainはアクションの連鎖という抽象度の高い動作を実現することが可能になっています。


# LangChainのプロンプトテンプレートを使う
LangChainのプロンプトテンプレートは、LLMへの入力（プロンプト）を効率的に管理・生成することができます。プロンプトテンプレートを使用することで、以下のようなメリットがあります。

* **プロンプトの再利用性向上**: 頻繁に使用するプロンプトをテンプレート化し、再利用できる
* **プロンプトの可読性向上**: 変数を使用してプロンプトを構造化し、可読性を高める。
* **プロンプトの動的な生成**: ユーザーの入力や外部データに基づいてプロンプトを動的に生成できる。

## プロンプトテンプレートの基本

プロンプトテンプレートは、文字列と変数（プレースホルダー）を組み合わせて定義します。変数は`{変数名}`のように波括弧で囲んで記述します。

```python
from langchain_core.prompts import PromptTemplate

template = PromptTemplate.from_template("あなたは{role}です。{input}について{target_language}で説明してください。")

prompt = template.format(role="スウィッチ", input="LangChain", target_language="日本語")

print(prompt)
# 出力：あなたはAIアシスタントです。LangChainについて日本語で説明してください。
```

上記の例では、`{role}`, `{input}`, `{target_language}`という3つの変数を定義しています。`template.format()`メソッドに各変数の値を渡すことで、実際のプロンプトが生成されます。

## プロンプトテンプレートの種類

LangChainには、さまざまな用途に対応したプロンプトテンプレートが用意されています。

* **StringPromptTemplate**: 基本的な文字列テンプレート。
* **ChatPromptTemplate**: チャット形式のプロンプトテンプレート。
* **MessagesPlaceholder**: 会話の履歴を動的に管理するテンプレート。

https://python.langchain.com/docs/concepts/prompt_templates/

# LangChainの`chain`とは？
LangChainの`chain`は、複数の処理（アクション）を連結して、一つの処理フローとして実行するための仕組みです。これにより、複雑なタスクを、より小さな、再利用可能なコンポーネントに分割し、それらを組み合わせて実行できます。

Ollamaと組み合わせる場合には、**モデルI/O**と**プロンプトテンプレート**を`chain`を用いて行います。以下に、OllamaとLangChainのプロンプトテンプレートを組み合わせた例を示します。

```python
from langchain_ollama import ChatOllama
from langchain_core.prompts import PromptTemplate

def res_langchain(user_question: str) -> str:
    # プロンプトテンプレートの作成
    template = PromptTemplate.from_template("あなたは{role}です。次の質問に答えてください。{prompt}")
    
    # LLMの初期化
    llm = ChatOllama(model = "llama3")

    # アクションの連鎖（Chain）
    chain = template | llm

    res = chain.invoke(input = {"role": "AIアシスタント", "prompt":user_question})
    return res.content
```



## アクションの連鎖（Chain）の概念

例えば、以下のような処理を考えてみましょう。

1.  ユーザーからの質問を受け取る。
2.  質問に基づいて、関連する情報をデータベースから検索する。
3.  検索結果と質問を組み合わせて、LLMに回答を生成させる。
4.  生成された回答をユーザーに返す。

これらの処理を個別に実行するのではなく、`chain`を使って連結することで、一つの連続した処理として実行できます。

## パイプライン演算子`|`の役割

LangChainでは、パイプライン演算子`|`を使って、処理（アクション）を連結します。この演算子は、UNIXのパイプラインと同様に、左側の処理の出力を右側の処理の入力として渡す役割を果たします。

## コード例の解説

```python
chain = template | llm
res = chain.invoke(input = {"role": "AIアシスタント", "prompt":user_question})
```

上記のコード例では、以下の処理が行われています。

1.  `template | llm`: `template`（プロンプトテンプレート）と`llm`（LLM）を`|`で連結し、新しい`chain`を作成します。これにより、プロンプトテンプレートの出力がLLMの入力として渡されるようになります。
2.  `chain.invoke(input = {"role": "AIアシスタント", "prompt":user_question})`: `chain`を実行し、`input`を渡します。`input`は、プロンプトテンプレートの変数に値を設定するために使用されます。
3.  `template`への入力: `chain.invoke()`によって与えられた`input`は、`template`に渡されます。`template`は`input`から必要な値（`role`と`prompt`）を取り出し、プロンプトを生成します。
4.  `template`からの出力: 生成されたプロンプトは、`|`によって`llm`に渡されます。
5.  `llm`への入力: `llm`は、受け取ったプロンプトに基づいて回答を生成します。
6.  `llm`からの出力: 生成された回答が`res`に格納され、関数の戻り値として返されます。

### パイプライン演算子の利点

* **可読性の向上**: 処理フローを視覚的に表現できるため、コードの可読性が向上します。
* **柔軟性**: 処理の順番や組み合わせを柔軟に変更できます。
* **再利用性**: 複数の`chain`で共通の処理を再利用できます。

# まとめ
この記事では、LangChainの基本的な概念から、主要な機能、そして実際のコード例を交えながら、その核心部分である「Chain」（アクションの連鎖）について解説しました。

LangChainは、LLMを活用したアプリケーション開発を効率化し、その可能性を最大限に引き出すための強力なツールです。モデルI/Oの統一、プロンプトテンプレートによる管理、そしてChainによる複雑な処理フローの構築など、LangChainが提供する機能は、開発者がより高度なアプリケーションを開発するための強力な武器となります。

特に、Ollamaとの連携によってローカル環境での開発が可能になる点は、開発者にとって大きなメリットです。これにより、APIキーの管理や外部APIの制限を気にすることなく、自由な発想でLLMを活用したアプリケーションを開発できます。

LangChainはまだ発展途上のフレームワークであり、今後も様々な機能が追加されていくことが期待されます。この記事が、LangChainに興味を持ち、実際に使ってみようと考えている方々の参考になれば幸いです。

## 次回予告
Day2は現在執筆中です。
予定では、LLM Agentの考え方について書く予定です。

## 参考資料
- [LangChain- Develop LLM powered applications with LangChain](https://www.udemy.com/course/langchain/?couponCode=ST11MT170325G2)
    世界一わかりやすいLangChain勉強教材です。詳しく知りたい方は、講座の購入を心からお勧めします。
- LangChain公式ドキュメント：[https://python.langchain.com/docs/get_started/introduction](https://python.langchain.com/docs/get_started/introduction)
- Ollama公式ドキュメント：https://ollama.ai/

## コメント
記事に関する質問や感想、間違いの指摘などがあれば、お気軽にコメント欄にご記入ください。
もしくはTwitter（X）まで。

https://x.com/switch_techLAB/status/1902675523425481167