openai APIとLangChainを比較したい

# はじめに
langchainはええよ～って話も聞くけど「いや、openai api素直に使ったらええやないですか」っていう心の声もあり
どっちがいいのかを比べてみた

# 先に結論
* OpenAI APIでできることは当然LangChainでもできる
* LangChainがなくても同じようなプログラミングはできるけど、OpenAI APIだとわざわざ作りこまないといけないところをLangChainは先まわって作ってくれるのでストレスが少ない
* 今回は比較のために初歩的なところの比較しかできないけどLangChainには様々な便利クラスがある
* ちょろいAIプログラミングならOpenAI APIでもいいけど、しっかりとしたものを作るならLangChainを使ったほうがいいなって思いました

# Chat Completions API vs Chat models
## Chat Completions(OpenAI API)

```
import openai

response = openai.ChatCompletion.create(
  model="gpt-3.5-turbo",
  messages=[
    {"role": "system", "content": "You are a helpful assistant."},
    {"role": "user", "content": "Hello! I'm John."}
  ]
)

print(response)
```

出力結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416263/0b39360e-783d-6949-02df-6709889d653f.png)

出力結果はエスケープされたUnicodeエンコードとなっているので変換されていた。
変換結果は

```
はい、先ほどジョンさんと名乗られましたので、お名前が分かりました。
私は会話の中でお客様の情報を記憶することができます。どのようにお手伝いできますか？
```

と書いてました。
参考：https://tech-unlimited.com/escape-unicode.html



## Chat models(LangChain)

```
from langchain.chat_models import ChatOpenAI
from langchain.schema import AIMessage, HumanMessage, SystemMessage

chat = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)

messages = [
    SystemMessage(content="You are a helpful assistant."),
    HumanMessage(content="こんにちは！私はジョンと言います！"),
    AIMessage(content="こんにちは、ジョンさん！どのようにお手伝いできますか？"),
    HumanMessage(content= "私の名前が分かりますか？")
]

result = chat(messages)
print(result.content)
```

出力結果。
回答内容にほぼ差はないけど、何より「Unicodeエンコード」されてるのをちゃんと変換した状態で出してくれるのがイイ…！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416263/a930c072-b9ca-0935-dd34-8d90d3abe444.png)

# ストリーミング
## OpenAI API

```
response = openai.ChatCompletion.create(
  model="gpt-3.5-turbo",
  messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "Hello! I'm John."}
  ],
  stream=True
)

for chunk in response:
  choice = chunk["choices"][0]
  if choice["finish_reason"] is None:
    print(choice["delta"]["content"])
```

出力結果。
１文字ずつ出力しててほぼ縦書き。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416263/e9a7f531-778b-1ed2-9f4a-bd699a7c3fd5.png)


## LangChain

```
from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler
from langchain.chat_models import ChatOpenAI
from langchain.schema import HumanMessage

chat = ChatOpenAI(
    model_name="gpt-3.5-turbo",
    temperature=0,
    streaming=True,
    callbacks=[StreamingStdOutCallbackHandler()],
)

messages = [HumanMessage(content="自己紹介してください")]
result = chat(messages)
```

リアルタイム出力するところは一緒だけど勝手に改行しないのでそこは結構嬉しかったりする
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416263/45bf381a-e665-079e-4823-722fbf8f5ba1.png)

# PromptTemplate
## OpenAI API

```
def generate_recipe(dish_name):
    prompt_template = f"""
以下の料理のレシピを考えてください。

料理名: {dish_name}
"""

    openai.api_key = api_key  # APIキーを設定

    response = openai.Completion.create(
        engine="text-davinci-002",  # 使用するエンジンを選択
        prompt=prompt_template,
        max_tokens=100,  # 応答の最大トークン数
    )

    return response.choices[0].text

dish_name = "カレー"
recipe = generate_recipe(dish_name)
print(recipe)
```

作り方次第でテンプレート作れる。ちょっと作りこみが大変かなって。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416263/b5e54f1d-14f2-fd79-db33-b9543f152475.png)

## LangChain

```
from langchain.prompts import PromptTemplate

template = """
以下の料理のレシピを考えてください。

料理名: {dish}
"""

prompt = PromptTemplate(
   input_variables=["dish"],
   template=template,
)

result = prompt.format(dish="カレー")
print(chat.predict(result))
```

出力結果。
やたらとしっかりした文章になってるのは気になるけど問題はそこじゃなくて…
PromptTemplateってクラスを独自で作ってるおかげですごい直感的で作りやすくなってる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416263/76491242-9274-b7ba-9ed2-6b375847d96c.png)

### LangChainにはほかのTemplateクラスある
ChatPromptTemplateクラスだと、SystemMessageとHumanMessageのそれぞれにテンプレートパターンを作れる。
LangChainを勉強してると自然とAIプログラミングができる気分になってきたー

```
from langchain.prompts import (
    ChatPromptTemplate,
    PromptTemplate,
    SystemMessagePromptTemplate,
    HumanMessagePromptTemplate,
)
from langchain.schema import HumanMessage, SystemMessage

chat_prompt = ChatPromptTemplate.from_messages([
    SystemMessagePromptTemplate.from_template("あなたは{country}料理のプロフェッショナルです。"),
    HumanMessagePromptTemplate.from_template("以下の料理のレシピを考えてください。\n\n料理名: {dish}")
])

messages = chat_prompt.format_prompt(country="イギリス", dish="肉じゃが").to_messages()

print(messages)

from langchain.chat_models import ChatOpenAI

chat = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)
result = chat(messages)
print(result.content)
```

出力結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416263/dd93f6ea-3a50-700a-6c55-6e891a627b48.png)