ChatGPTと同じように会話できるコンソールアプリケーションをPythonで作ってみた

こんにちは。サービス部の武田です。ChatGPTの履歴機能がunavailableとなっていたので、履歴を残せる会話コンソールアプリケーションを作ってみました。
2023.03.23

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは。サービス部の武田です。

ChatGPTが話題になってはや2ヵ月といったところでしょうか。いろいろな活用方法が試され、DevelopersIOでもたくさんのブログが書かれています。

ChatGPTはアカウントを作成しブラウザでアクセスするだけで無料で試せます。そして会話の履歴は自動的に保存され、過去の会話は自由に見られました。ところがここ数日、その履歴部分が機能しなくなってしまい困っていました。

そんなわけで、会話を残すために、会話を残せるコンソールアプリケーションを書いてみました。

なお、このエントリを書く段階になってSaveGPTというChrome拡張があることを知りました。

これを使えば今回のアプリケーションは正直いらなかったのですが、OpenAIを使う練習になったのでよかったとします。

書いてみた

事前にopenaiのインストールと、OPENAI_API_KEY環境変数にOpenAIのAPIキーを設定しておく必要があります。

$ pip install openai
$ export OPENAI_API_KEY=xxxx

今回書いてみたコードは次のようなものです。

import openai
import os
import json

FILENAME = "chat_history.txt"
MODEL_ENGINE = "gpt-3.5-turbo"

openai.api_key = os.environ["OPENAI_API_KEY"]

messages = []

if os.path.exists(FILENAME):
    with open(FILENAME, "r") as f:
        messages.extend([json.loads(j) for j in f.read().splitlines()])

while True:
    user_input = input("\nuser: ")

    if user_input == "exit":
        break

    user_message = {"role": "user", "content": user_input}
    messages.append(user_message)
    with open(FILENAME, "a") as f:
        f.write(json.dumps(user_message, ensure_ascii=False) + "\n")

    print("ChatGPT: ")
    response = openai.ChatCompletion.create(
        model=MODEL_ENGINE,
        messages=messages,
    )

    bot_response = response["choices"][0]["message"]["content"].strip()

    bot_message = {"role": "assistant", "content": bot_response}
    messages.append(bot_message)
    with open(FILENAME, "a") as f:
        f.write(json.dumps(bot_message, ensure_ascii=False) + "\n")

    print(bot_response)

さて、動かしてみたのがこちらです。

user: 1から10まで出力するRubyプログラムを書いてください。
ChatGPT:
```ruby
(1..10).each do |i|
  puts i
end
```

もしくは

```ruby
10.times do |i|
  puts i + 1
end
```

どちらでもOKです。

user: やっぱりHaskellでお願いします。
ChatGPT:
もちろんです。以下が1から10まで出力するHaskellプログラムです。

```haskell
main = mapM_ print [1..10]
```

`[1..10]`は1から10までのリストを表します。`mapM_`は各要素に関数を適用するための関数で、`print`関数を適用することで各要素を出力しています。`main`関数はHaskellのプログラムのエントリーポイントであるため、この関数内に処理を記述することでプログラムを実行できます。

user: exit

ちゃんとコンテキストを理解してますね。この状態のchat_history.txtは次のようになっています。

{"role": "user", "content": "1から10まで出力するRubyプログラムを書いてください。"}
{"role": "assistant", "content": "```ruby\n(1..10).each do |i|\n  puts i\nend\n```\n\nもしくは\n\n```ruby\n10.times do |i|\n  puts i + 1\nend\n```\n\nどちらでもOKです。"}
{"role": "user", "content": "やっぱりHaskellでお願いします。"}
{"role": "assistant", "content": "もちろんです。以下が1から10まで出力するHaskellプログラムです。\n\n```haskell\nmain = mapM_ print [1..10]\n```\n\n`[1..10]`は1から10までのリストを表します。`mapM_`は各要素に関数を適用するための関数で、`print`関数を適用することで各要素を出力しています。`main`関数はHaskellのプログラムのエントリーポイントであるため、この関数内に処理を記述することでプログラムを実行できます。"}

いわゆるJSON Lines形式ですね。次回プログラムを起動すると、このファイルが読み込まれ、会話を再開できます。

まとめ

今回は次のような流れでプログラムに機能追加しました。

  1. OpenAI APIを使って単発の会話
  2. ループを導入して繰り返し会話(コンテキスト保持なし)
  3. コンテキストを保持した会話
  4. ファイルに履歴保存・読み込み

自分はアイデアマンではないので、Open AIを使ったおもしろそうなことっていうのをあまり作れません。ですが、まずはこれを足がかりに何か作ってみたいなと思っています。

参考URL