LangChainのLCELとAzure OpenAIのJSON Modeを試してみる。

# LangChain Expression Language (LCEL)
LangChainのチェーン記法がリニューアルされていたので試してみた。

https://python.langchain.com/docs/expression_language/


これまではLLMChainを作成してプロンプト、モデルを渡す書き方だったが、
```python
from langchain import LLMChain
llm_chain = LLMChain(　prompt=prompt,　llm=model)
llm_chain("ice cream")
```

LCELでは　”|”　で連結してChainを実装できる。（直感的にわかりやすくなっている）
またoutput_parserを繋げてレスポンスの後処理もできる。
```python
chain = prompt | model | output_parser
chain.invoke({"topic": "ice cream"})
```

# JSON Mode
ChatGPTからの応答がJSONで返ってくるのを保証するモード。2023/11/6に本家でリリース、11/17にAzure OpenAIでリリースされた模様。
LLM応答でJSON”だけ”返して欲しいのに、余計な文言がついてしまうようなケースを回避できるらしい。

# LCELコード例
まず通常の利用。こちらはこれまでのgpt-35-turbo, gpt-4 （0613）、APIバージョン2023-07-01-previewで動く。

```python
from langchain.chat_models import AzureChatOpenAI
from langchain.prompts import PromptTemplate


prompt = PromptTemplate.from_template("""ショートジョークを言ってください。 お題： {topic}""")

model = AzureChatOpenAI(
    openai_api_base=BASE_URL,
    openai_api_version="2023-07-01-preview",
    deployment_name=DEPLOYMENT_NAME,
    openai_api_key=API_KEY,
    openai_api_type="azure",
    temperature=0)
```

LCELでのチェーン定義部とチェーンの実行。

```python
chain = prompt | model

result = chain.invoke({"topic": "カレー"})

print(result.content)
```

実行結果　（面白いかは別。。）
```
なぜカレーがパーティーに行きたがらなかったの？

だって、いつもスパイスが多すぎて汗ばんでしまうからさ！
```

# output_parser
次にoutput parserを使ってみる。こちらはJSON Modeを使う必要がある。
JSON Modeを使用するにはgpt-35-turbo-1106とgpt-4-1106-previewのデプロイが必要で、2023/12/1時点では、オーストラリア東部、インド南部、アメリカ東部のリージョンに限定されるので注意。

output parserで扱うオブジェクトをBaseModelを継承して作成する。出力項目（例の場合は材料と手順）をフィールド定義する。

```python
from langchain.output_parsers import PydanticOutputParser
from pydantic import BaseModel, Field

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

output_parser = PydanticOutputParser(pydantic_object=Recipe)

```

テンプレート設定。（ここらは参考サイトのコードを拝借させていただいた）
AOAIモデルはJSON Modeを利用するため、上記のリージョン作成のgpt-35-turbo、バージョン1106を使用。APIバージョンは2023-10-01-preview。
.bindとしてresponse_formatを指定してJSON Modeを有効化する。

```python
prompt = PromptTemplate.from_template(
    """料理のレシピを考えてください。

{format_instructions}

料理名: {dish}""",
    partial_variables={"format_instructions": output_parser.get_format_instructions()},
)

model = AzureChatOpenAI(
    openai_api_base=BASE_URL,
    openai_api_version="2023-10-01-preview",
    deployment_name=DEPLOYMENT_NAME,
    openai_api_key=API_KEY,
    openai_api_type="azure",
    temperature=0).bind(
        response_format={"type": "json_object"}
    )
```

チェーンの作成と実行。output parserをパイプする。

```python
chain = prompt | model | output_parser

result = chain.invoke({"dish": "肉じゃが"})
print(type(result))
print(result)
```

実行結果。Recipeクラスで定義した、ingredients, stepsのリストとして応答を取得することができる。応答のJSONをパースしているのがわかる。
```
<class '__main__.Recipe'>
ingredients=['牛肉', 'じゃがいも', 'にんじん', '玉ねぎ', 'しょうが', 'にんにく', '醤油', 'みりん', '砂糖', '水'] steps=['1. 牛肉を食べやすい大きさに切る。', '2. じゃがいも、にんじん、玉ねぎを食べやすい大きさに切る。', '3. 鍋に油を熱し、しょうがとにんにくを炒める。', '4. 牛肉を加えて炒める。', '5. じゃがいも、にんじん、玉ねぎを加えて炒める。', '6. 醤油、みりん、砂糖、水を加えて煮る。', '7. 全体に味がなじんだら完成。']
```

# 参考

https://python.langchain.com/docs/expression_language/

https://zenn.dev/microsoft/articles/azure-openai-jsom-mode

https://zenn.dev/os1ma/articles/acd3472c3a6755