背景
- chatGPTを使って業務でラクしたり、色々実験したりしたいが、実行に時間がかかるし、やりとりが学習に使われる可能性があり機密情報に気を使わないといけない、サイトに訪問するのも面倒臭い(たまに繋がらないし)
- gpt-3.5-turbo のAPIが公開されたので、これを使えばchatGPTをCLI上でも使える。しかしこれだけでは色々問題があった。具体的には
- いちいちHTTPリクエスト書くのは面倒だし、実験の速度が重要なので、API実行速度以外の障壁をなるべく無くしたい
- 出力をmarkdown記法としてレンダリングしてほしい
- 過去のやり取りを読み込んだり、スレッドを複数作成して並行して話をしたり、気に入らない回答の再生成をしたり、本家サイトと同様の機能が欲しい。リアルタイム出力は遅くなるならいらない。
- そこで、"pythonなら書けるし、きっとjupyterなら出力をmarkdownとしてレンダリングしてくれるだろう"と思い至り、「ぼくだけのChat-GPT環境」を作ってみることにした。
前提
筆者はM1MacBookを使っており、バージョンはMonterey(12.6.2)です。
python3が最初から使える状態でスタートします
APIKeyの発行
まずはAPIKeyの発行です。APIKeyは、 OpenAIにログインの後、View APIKeyページのCreate new secret keyというボタンから発行することができます。(発行したキーを保管しておきましょう)
ライブラリのインストール
pythonのパッケージ管理ツールであるpipを使って、必要なライブラリをインストールします
$ python3 -m pip install openai
$ python3 -m pip install jupyterlab
openaiは、その名の通りopenAIのAPIを扱うためのpythonライブラリで
jupyterlabは、ブラウザで動くpythonの開発環境です。
(IPythonをつかってmarkdownを扱うため、SpyderでもPyCharmでも、VSCodeでも、IPython環境さえあれば問題ないです。)
jupyterlabの起動
python3 -m jupyterlab -port 8888
これでおそらくブラウザが立ち上がり
こんな感じの画面になると思います。
ここで、 まずOtherのPython Fileをクリックして、 gptAPIを使うための自作モジュールを作ります
gpt.pyの作成
import openai
from IPython.display import Markdown, display
openai.api_key = "apikey" # ここに前工程で作成したAPIKeyをいれる
def role_content(content, role="user"):
return {"role": role, "content": content}
class Thread:
content_tree = []
latest_response = None
# APIのstopパラメータに入れても良いのだが、出力が途中で切れるのが悲しいので、プロンプトにしてみた。
system_prompt = role_content("質問には最長でも2000文字程度で回答してください","system")
max_depth = 10
def set_system_prompt(self, content):
self.system_prompt = role_content(content, "system")
def question(self, content=None):
if self.latest_response:
self.content_tree.append(self.latest_response)
self.latest_response = None
if content:
self.content_tree.append(role_content(content, "user"))
if len(self.content_tree) > self.max_depth:
self.content_tree = self.content_tree[-selfmax_depth:]
response = openai.ChatCompletion.create(model="gpt-3.5-turbo", messages=[self.system_prompt]+self.content_tree)
self.latest_response = role_content(response['choices'][0]["message"]["content"], "assistant")
display(Markdown(self.latest_response["content"]))
return(self.latest_response["content"])
def regenelate(self):
self.latest_response = None
response = openai.ChatCompletion.create(model="gpt-3.5-turbo", messages=[self.system_prompt]+self.content_tree)
self.latest_response = role_content(response['choices'][0]["message"]["content"], "assistant")
display(Markdown(self.latest_response["content"]+"\n(再生成)"))
return(self.latest_response["content"])
def q(content, prompt=None, thread=None):
if not thread:
thread = Thread()
if prompt:
thread.set_system_prompt(prompt)
thread.question(content)
return thread
def r(thread):
thread.regenelate()
untitled.pyとしてファイルが作成されるはずなので、 これをgpt.pyなど分かりやすい名前にしておく
いざ実行
これで下準備は全て終わり。
使い方
上タブから、ランチャーを開き、一番上のNotebook(python3)をクリック
まず
import gpt
で先ほど作ったモジュールを読み込み、
t1 = gpt.q("質問文字列")
で出力される というのがメインの使い方です。 この続きを出力したいときは
t1 = gpt.q("つづきの質問", thread=t1)
とでき、 またプロンプトを与えたい時は
t1 = gpt.q("つづきの質問", prompt="あなたは哲学者です", thread=t1)
のような形で使えます。
この場合 t1
がスレッドのインスタンスなので、 別のスレッドで会話を始めたい時は
t2 = gpt.q("別の質問")
とすれば、別スレッドが作成されるし、t1での最後のやり取りが気に入らなかったら
gpt.r(t)
で再度出力を吐き出します
別スレッドは別notebookを作成して実行するのがグラフィカルにもよさそうですね。
実行例
こんな感じで、ちゃんとマークダウン出力をレンダリングできました!
さいごに
結構細かい操作ができるGPT環境ができて個人的には満足です。
やっていることはpythonコードの実行なので、pythonが苦痛なく使える人は、結構使いやすいんじゃないかなと思います。
また今回はサボりましたが、APIのパラメータ類ももっと入れられるはずなので、実験が進んだらそこらへんも調整しやすい仕組みを作ろうかと思います。