LangChain v0.3　その３ ~LCELの発展１（RunnableSequenceとRunnableParallel）~

# LCEL
LangChainのLCEL。ま、コアな部分の一つだよね。
きっとこれらを使うと便利なものが作れるんだろうね。
ということでもう少し踏み込んでみました。
実は「ドキュメントを読む練習」というのは内緒である。
ということで直線的な処理と並行させる処理をやらせてみました

https://python.langchain.com/docs/how_to/#langchain-expression-language-lcel

# バージョン関連
Python 3.10.8
langchain==0.3.7
python-dotenv
langchain-openai==0.2.5
langgraph>0.2.27
langchain-core

※LLMのAPIはAzureOpenAIのgpt-4o-miniを使いました

# 準備
`.env`ファイルに以下を用意しましょうね
END_POINT="https://<<ほにゃらら>>.openai.azure.com/"
API_KEY="<<ほにゃらら>>"
API_VERSION="2023-05-15"
LANG_SMITH = "<<ほにゃらら>>"



# やらせてみたこと
大喜利。それも英語で


# 復習
前回のもみてね

https://qiita.com/ikedachin/items/26f98436648bc74e2ba7

# シーケンシャルな処理の場合
`langchain_core`の`runnables`から`RunnableSequence`をインポートして使います。

```Python
from langchain_core.runnables import RunnableSequence
```

#### プロンプトを準備

```python
prompt_template0 = ChatPromptTemplate([
    ('system', 'あなたは偉大な漫才師です。'),
    ('user', '{topic}について大喜利してください。')
])

topic2english = ChatPromptTemplate([
    ('system', 'あなたは優秀な英語への翻訳者です。翻訳した内容だけを回答してください。'),
    ('user', '{topic}を翻訳してください')
])

oogiri_prompt = ChatPromptTemplate([
    ('system', 'You are a great comedian'),
    ('user', 'Please create OOGIRI about {topic} in English')
])
```


#### シーケンシャルに組む
```python
chain_oogiri_english = RunnableSequence(
    topic2english, 
    model, 
    oogiri_prompt, 
    model, 
    output_parser
    )
```

実はこれ、以下と同じ
```python
chain_oogiri_english = topic2english | model | oogiri_prompt | model | output_parser
```
なんやねんて。
ま、直線的に組むだけですからそうなりますわな。


実行してプリント

```python
result = chain_oogiri_english.invoke('靴下')
print(result)
```


```output:output
Sure! Here’s an OOGIRI (a form of Japanese comedy) themed around socks:

---

**Q:** Why did the sock break up with the shoe?  
**A:** Because it found someone more supportive!

---

**Q:** What do you call a sock that can do magic?  
**A:** A sock-cerer!

---

**Q:** Why did the sock refuse to play hide and seek?  
**A:** Because it always got lost in the wash!

---

Hope that brings you a chuckle!

```


# 並列処理の場合

```python
from langchain_core.runnables import RunnableParallel
```
今度はRunnalbleParallelを使います。



```python
parallel_chain = RunnableParallel({
    'Japanese': prompt_template0 | model | output_parser,
    'English': topic2english | model | oogiri_prompt | model | output_parser
})
```
こんな風に辞書形式でそれぞれぶっ込みます。
今回は辞書の中はパイプ「`|`」で繋いでます


実行！

```python
result_parallel = parallel_chain.invoke('靴下')
```


結果を表示。まず日本語から。
```python
print(result_parallel['Japanese'])
```

```
もちろん、靴下についての大喜利いきますよ！

1. 靴下が出会い系アプリを始めた理由は？
   - 「足元から恋を育てたい！」

2. 短い靴下と長い靴下、どっちが人気？
   - 「短い靴下、いつも『あたしも見て！』ってアピールしてるから！」

3. 靴下が自分の存在意義を考えた結果？
   - 「やっぱり、足元を支えるのが私の使命だと気づいた！」

4. 靴下が言う「これが私のスタイル！」とは？
   - 「色んな柄で、足元から個性を主張！」

5. 靴下が選ぶ理想のデートコースは？
   - 「靴屋巡り。だって、いい靴にはいい靴下が必要でしょ！」

どうでしょう？靴下の魅力を引き出しつつ、笑いを誘う感じになりましたか？
```
まぁまぁ大喜利しやがる！さすがGPT-4o-mini



</br>
</br>
</br>

今度は英語
```python
print(result_parallel['English'])
```


```output:output
Sure! Here’s an OOGIRI (a Japanese form of comedic wordplay) about socks:

**Q:** Why did the sock break up with the shoe?  
**A:** Because it found someone who really "fits" its sole! 

Feel free to ask for more jokes or a different topic!

```
えっと、英語は苦手なんでぇ・・・よくわからん。w
日本語は5個も作ってくれたけど、英語はひとつなのね。笑


# 大事だと思うこと
これらのライブラリ、継承しているクラスを遡っていくとRunnableクラスにたどり着きます。
ここで、RunnableクラスのDocStringsを和訳してもらいました。（GPT-4o）
以下の108行目


https://github.com/langchain-ai/langchain/blob/master/libs/core/langchain_core/runnables/base.py



```text:和訳
LangChainのRunnableクラスは、データを処理するための作業単位を表す概念で、さまざまな方法での呼び出しや構成が可能です。このクラスには以下の主要なメソッドや特徴が備わっています。

主なメソッド

	•	invoke/ainvoke：1つの入力を1つの出力に変換します。ainvokeは非同期対応。
	•	batch/abatch：複数の入力を効率的に処理し、出力を得ます。
	•	stream/astream：1つの入力に対し出力をストリーミングとして返します。
	•	astream_log：出力と中間結果をリアルタイムでストリーミングします。

組み込みの最適化

	•	バッチ処理：デフォルトではスレッドプールを使って並列実行し、効率化を図ります。
	•	非同期処理：メソッド名に「a」がついているものは非同期処理で、標準的には同期版を非同期化していますが、オーバーライドしてネイティブな非同期対応も可能です。

各メソッドにはオプションのconfig引数があり、実行設定やデバッグのためのメタデータ追加に利用されます。また、input_schemaやoutput_schemaを使って入出力形式の情報を提供します。

LangChain Expression Language（LCEL）と構成

LCELは、Runnableをチェーン状に構成するための宣言的な方法です。これにより、同期・非同期・バッチ・ストリーミングの全機能を持つチェーンを簡単に構築できます。主な構成方法には以下があります。

	•	RunnableSequence：複数のRunnableを順に呼び出し、一つの出力を次の入力として利用します。|演算子やリストで構成できます。
	•	RunnableParallel：複数のRunnableを並列に呼び出し、同じ入力を渡して異なる出力を取得します。辞書リテラルを使って構成可能です。
```

つまり、Runnableの機能として`invoke/ainvoke`、`batch/abatch`、`stream/astream`、`astream_log`これらの機能がとても重要なんですね。
あとで少しコードを読んでみようかな。



# まとめ
ということでパラレルに動かすってことをやってみたかったので、`RunnableSequence`と`RunnableParallel`を比較してみました。
使ってみると挙動がよくわかります。
 sosite,
 LCELの発展２に続きます。