プロンプトエンジニアリングの無料コースを受講したのでチャットボットをサクッと作ってみた

Deeplearning.AI社がChatGPTのプロンプトエンジニアリングのコースを無料公開していたので、受講して、試しにStreamlitを駆使してチャットボットを作ってみました。
コードを示しながら、かいつまんで解説します。

![openai-0.27.6](https://img.shields.io/badge/openai-0.27.6-brightgreen) ![Streamlit-1.22.0](https://img.shields.io/badge/Streamlit-1.22.0-brightgreen) ![Python-3.11.2](https://img.shields.io/badge/Python-3.11.2-brightgreen)

## 作ったもの

作ったチャットボットはこんな感じです。

![chatGPT_chatbot.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2818/aa16036d-835b-f8ab-9664-b04f72e5c321.gif)

ソースコードはこちらです。100行に満たないです。
https://github.com/Vermee81/play-chatGPT/blob/main/app.py

## 前準備

OpenAIからAPIのkeyを発行してください。
https://platform.openai.com/

発行したら、.envファイルに以下のように鍵のパスフレーズを入力して保存します。

```.env
OPENAI_API_KEY = 'ここにKeyを入力'
```

必要なライブラリをインストールします。
インストールしたのは、以下の3つ
- OpenAIのライブラリ: OpenAIのAPIを使うので必須
- Streamlit: 簡単にGUIを作れます
- python-dotenv: ソースコードにAPIのkeyを書かないで、.envファイルから読み込むため

virtualenvでのセットアップ例。virtualenvでなくて、poetryでもpipenvでもなんでも好きなのを使ってください。
```bash
python -m venv venv
source venv/bin/activate
pip install streamlit
pip install openai
pip install python-dotenv
```

## 解説

主に使い倒すAPIは、チャットに特化したChatCompletionAPIです。
https://platform.openai.com/docs/guides/chat

ざっくりとやることは
1. 言語モデルを指定
2. チャットボットの振る舞い（会話の枠組み）を指定
3. ユーザーとチャットボットとの会話の履歴をチャットボットにインプットする

### 言語モデルの指定

言語モデルの指定では、言語モデルとパラメーターを設定します。
`model`では、自然言語やコードを理解してくれるgtp-3.5-turboを指定しました。
`temperature`は、チャットボットが返す返事のばらつきです。値が大きいと、同じインプットでも返す返事がバラつきます。0にした方がばらつきが少なく誤った発言も減るようです。

以下のところが言語モデルを指定している箇所です。
処理としては、APIにリクエストしてレスポンスからテキスト部分を取り出しています。

```Python
def get_completion_from_messages(
    messages: list[dict[str, str]], model: str = "gpt-3.5-turbo", temperature: int = 0
):
    response = openai.ChatCompletion.create(
        model=model,
        messages=messages,
        temperature=temperature,
    )
    return response.choices[0].message["content"]
```

### チャットボットの振る舞い（会話の枠組み）を指定

ざっくりというと会話の前提知識や前提条件を指定することです。
今回は、ホットドッグの注文を受けるチャットボットなので、ホットドッグやドリンクのメニューの情報や、オーダーの取り方についてインプットしました。

APIにインプットするときに、"role"キーの値として"user", "assistant", "system"の3つがあります。
"user"は、チャットボットの利用者（人間）
"assistant"はチャットボット
"system"はこのチャットボットの振る舞いになります。

コードは以下の通りです。メニューは一部省略しています。

```Python
@st.cache_resource
def initialize_chat_log():
    context = [
        {
            "role": "system",
            "content": """\
あなたはホットドッグ店の注文受付用のチャットボットです。\
最初に挨拶をして、注文を受けます。\
全ての注文を受けてから、注文を繰り返して、追加に注文がないか確認します。\
追加の注文がないことを確認できたら、料金を計算して、金額を伝えます。\
全てのオプションやサイズを明確にして、メニューからアイテムを一意に識別できるようにしてください。\
注文を受けるときは、短く、親しみのあるスタイルで応答します。\
メニューは以下の通りです \
通常のホットドッグ 小: 500円, 中: 700円, 大: 900円 \
チーズホットドッグ 小: 650円, 中: 850円, 大: 1050円 \
トッピング: \
チーズ追加 200円 \
スパイシーソース追加 100円 \
ドリンク: \
コーラ 小: 100円, 中: 200円, 大: 300円 \
ボトルウォーター 500円 \
""",
        }
    ]
    return context

```

日本語で書いていますが、英語で書き直しています。
英語でインプットする場合は、日本語で返事してくださいという文言を追加しています。

書き直した理由としては、英語の方が忠実に話している印象をもったからです。
一例を挙げると英語でインプットするとちゃんとオーダーが一意になるようにサイズやトッピングを聞いてくれます。

ちなみに最後の合計金額は、英語でも日本語でも間違えます・・・
一品オーダーを受けるたびに品数と単価をかけあわせた金額を計算する。みたいに具体的な指示をすると改善するかもしれません。
指示の仕方の工夫で、返事の品質が変わるところが、プロンプトエンジニアリングの工夫のしどころ（楽しさ）の一つかなと思います。
コード書かないで、ここまでできるのってすごいなぁーと思います。

`@st.cache_resource`は、送信ボタンを押すとリフレッシュして、これまでの会話の履歴が全部消えてしまうので、Streamlitの機能を使って会話の履歴をキャッシュさせています。

詳しい解説はここでは公式ドキュメントにゆずります。
https://docs.streamlit.io/library/advanced-features/caching#stcache_resource

## 実際のソースコード

100行に満たないソースコードなので、のぞいてみてください。
https://github.com/Vermee81/play-chatGPT/blob/main/app.py

## 受講したコース

受講したコースはこちらです。
英語ですが1時間くらいの尺なので、丁寧に追いかけても2時間くらいで聴き終えると思います。
左にJupyter notebook、右に講義動画という形式なので、実行しつつ、自分でもコードを変更しながら受講できて楽しいです。

https://www.deeplearning.ai/short-courses/chatgpt-prompt-engineering-for-developers/