LangChain v0.3　その４ ~LCELの発展２（RunnableLambda）~

# ここまで
LangChain v0.3のその２ではLCELをやってきました。
それもシーケンシャルに実行したり、パラレルにしてみたり。

https://qiita.com/ikedachin/items/7764965d4ea42e92badf



# バージョン関連
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`ファイルに以下を用意しましょうね
```.env
END_POINT="https://<<ほにゃらら>>.openai.azure.com/"
API_KEY="<<ほにゃらら>>"
API_VERSION="2023-05-15"
LANG_SMITH_API = "<<ほにゃらら>>"
```


# 今回のモチベーション
英語と日本語で大喜利させて、その結果から日本語文化と英語文化の違いを出力させたいと思います。
ま、こんなこと実際にはやらないけどね。笑


# ライブラリのインポート
```python
import os
from dotenv import load_dotenv
# from openai import AzureOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import AzureChatOpenAI
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnableSequence, RunnableParallel, RunnableLambda

load_env(',env')
```

# LangSmithのために環境変数を用意
```python
os.environ["LANGCHAIN_TRACING_V2"]="true"
os.environ["LANGCHAIN_ENDPOINT"]="https://api.smith.langchain.com"
os.environ["LANGCHAIN_API_KEY"]=os.getenv("LANG_SMITH_API")
os.environ["LANGCHAIN_PROJECT"]="langchain_test"
```
やっと少し`LangSmith`が理解できたんだけど、ちゃんと環境変数に入れてあげないと動かないんだね。汗

# モデルの準備
```python
model = AzureChatOpenAI(
    model='gpt-4o-mini',
    azure_endpoint=os.getenv("END_POINT"),
    api_version=os.getenv("API_VERSION"),
    api_key=os.getenv("API_KEY")
)
```


# プロンプトテンプレートの用意
```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')
])

```

# outputparserの準備
```python
output_parser = StrOutputParser()
```

# Parallelチェーンの用意
ここで、入力（日本語）をそのまま大喜利させたり、入力（日本語）を英語に変えて、英語で大喜利させる並列に実行させます。
```python
parallel_chain = RunnableParallel({
    'Japanese': prompt_template0 | model | output_parser,
    'English': topic2english | model | oogiri_prompt | model | output_parser
})
```
# RunnableLambdaの用意（今日のメイン）
`parallel`チェーンを取り込むための関数を作ります。
その関数を`RunnableLambda`でチェーンを作ります。
こうすることで、`invoke`や`batch`などの関数が使えるようになるんですね。

```python
def comparison_template_func(result):
    English = result['English']
    Japanese = result['Japanese']
    template = ChatPromptTemplate([
        ('system', 'あなたはお笑いを客観的に分析ができる優秀な評論家です。'),
        ('user', '''以下は英語で書かれたものと、日本語で掛かれた大喜利です。これらの大喜利を英語社会の背景、日本語社会の背景を鑑みてそれぞれの文化の違いを解説してください。
         \n##英語の大喜利##{English}\n##日本語の大喜利##{Japanese}''')
    ])
    return template

comparison_template = RunnableLambda(comparison_template_func)
```

# 再度、全体のChainを作る

```python
comparison_chain = parallel_chain | comparison_template | model | output_parser
```

こんな感じの流れになります
1. 入力
1. parallel_chainで以下の二つを実行する
    - １の入力を使って日本語で大喜利させる（`prompt_template0 | model | output_parser`）
    - １の入力を英語に直して英語で大喜利させる（`topic2english | model | oogiri_prompt | model | output_parser`）
1. 2の結果を受け取り、英語、日本語の結果を渡して、文化の違いを解説させる（`comparison_templete | model`）
1. 出力の処理を行う（`output_parser`）


# 推論
```python
comparison_result = comparison_chain.invoke('靴下')
comparison_result
```

# 結果
めっちゃがっつり解答してくれました。笑

```output:output
'英語の大喜利と日本語の大喜利には、それぞれの文化的背景や言語特性が反映されており、ユーモアのスタイルや内容に違いが見られます。\n\n### 英語の大喜利\n英語のジョークは、特に言葉遊びやダブルミーニングを用いたものが多いです。この例では、「pair-fect」という言葉が「perfect」と「pair（ペア）」の音をかけており、靴下の「ペア」という特性を利用したユーモアが強調されています。英語圏では、こうした言葉遊びが一般的であり、シンプルでありながらも聴衆に考えさせる要素を持っています。また、英語圏では自己表現や個性の重要性が強調されるため、靴下の「選択」や「パートナーシップ」をテーマにしたジョークも、軽いトーンで楽しむことができます。\n\n### 日本語の大喜利\n日本語の大喜利では、言葉遊びや言語の特性を活かしたユーモアが中心になっています。例えば、「片方どこ行った？」という質問は、靴下の特性を利用し、日常的な悩みをユーモラスに表現しています。また、日本語の大喜利は、より具体的な文脈や状況を想起させるような内容が多く、聴衆が共感しやすい傾向にあります。靴下の「香水」や「選挙に出馬する」というアイデアは、 absurd（不条理）な状況を楽しむ日本のユーモアの典型例です。\n\n### 文化的背景の違い\n1. **言葉遊びのスタイル**: 英語の大喜利はしばしば言葉の音や意味の重なりを利用し、シンプルで直接的なユーモアを提供します。一方、日本語の大喜利は、文脈や文化的な背景を踏まえた言葉遊びが多く、より多層的な意味を持つことが多いです。\n\n2. **社会的なテーマ**: 日本の大喜利は、日常生活に根ざしたテーマが多く、聴衆が共感しやすい内容が好まれます。英語のジョークは、個人の感情や選択に焦点を当てることが多く、より個人的な視点が強調されます。\n\n3. **ユーモアのトーン**: 英語のユーモアは、軽快で楽しいトーンから、時には皮肉や風刺を含むこともあります。日本の大喜利は、一般的に和やかで親しみやすいトーンが強調され、聴衆との一体感を大切にする傾向があります。\n\nこのように、英語と日本語の大喜利は、それぞれの文化や社会的背景を反映したスタイルの違いがあり、言語の特性を活かしたユーモアを楽しむことができます。'
```

LangSmithで確認してみましょう

![スクリーンショット 2024-11-08 12.22.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655320/2d7a4cec-6eee-b827-4190-c8c9e56df43d.png)

ちゃんとパラレルになってからマージされて結果が出力されてます。
`LCEL`って結構なんでもできそうですね。そして、`LangSmith`便利。


# 追記
Runnableを作る方法がもう一つありました。はっけん！

```Python
from langchain_core.runnables import chain

@chain
def add_test(text: str) -> str:
    return text + '\nこれはLangChainの勉強のために出力されました。'
```
なんと、デコレータを先頭につけるだけ。
<br>

これだけ
さ、もう一度チェーンを作って出力してみましょ。

```Python
comparison_chain_2 = parallel_chain | comparison_template | model | output_parser | add_text
```
<br>

```Python
comparison_result = comparison_chain_2.invoke('靴下')
print(comparison_result)
```
<br>

```text
英語と日本語の大喜利の文化的背景には、いくつかの重要な違いがあります。それぞれのユーモアのスタイルや表現方法が、社会や文化の影響を受けているためです。

### 英語の大喜利の分析

1. **スタイルと構造**: 英語の大喜利は、シンプルで直接的な質問とその後に続くパンチラインの形式が特徴です。質問に対する答えとして、意外性や言葉遊びを用いることが多く、特に「Sole-ful Disappeared」といった言葉のダブルミーニングが強調されています。このような言葉遊びは、英語圏のユーモアの一つの重要な要素です。

2. **テーマの普遍性**: 洗濯機で靴下が消えるという現象は、世界中の人々が共感できる普遍的な経験です。この共感性が、笑いを生む要因となっています。英語圏では、日常生活の中の小さな不満や疑問をユーモアにすることが好まれます。

3. **文化的背景**: 英語圏では、個人主義が強く、ユーモアもまた個々の視点や体験から生まれることが多いです。したがって、こうした大喜利は個人の視点を強調し、独自性を持たせる傾向があります。

### 日本語の大喜利の分析

1. **スタイルと構造**: 日本語の大喜利は、質問と回答の形式が多様で、特にストーリー性やユーモラスな状況設定が重視されることがあります。たとえば、「靴下が語る『私の人生』」という形で、靴下に人格を与え、その視点からのストーリーを展開することで、より深い共感を引き出しています。

2. **文化的要素**: 日本のユーモアは、しばしば集団意識や共感を重視します。「片方だけ」と言われる靴下の孤独感や、夢を持つ靴下の姿は、日常の中の小さなドラマを描いており、多くの人が共感できるテーマです。また、靴下の夢や好きな音楽など、キャラクターに感情を持たせることで、観客との距離を縮めています。

3. **言葉遊びと含意**: 日本語の大喜利は、言葉遊びやダブルミーニングが豊富で、言葉の音や響きを楽しむ要素が強いです。「ソックス・ロック」などの表現は、その一例で、音楽と靴下を掛け合わせたユーモアが見られます。

### 結論

このように、英語と日本語の大喜利には、それぞれ異なる文化的背景やユーモアのスタイルが反映されています。英語圏では直接的な言葉遊びが好まれる一方で、日本ではストーリー性やキャラクターの感情を重視する傾向があります。これらの違いを理解することで、各文化におけるユーモアの深さや多様性を楽しむことができます。
これはLangChainの勉強のために出力されました。
```
最後の1行を付け足すようにRunnableを組んでみました。
ね〜、いろんな方法があって楽しいわぁ。
と、書いた一時間後です。
え、デコレータなくてもええの？
もうわからん。www
**デコレータ、あってもなくてもええんか〜〜〜〜いっ！**

<br>
<br>

---

ということで、今度はLangGraphに挑戦予定。
ドキュメント読むのに時間がかかりそうなので夜は寝て待て！w