見出し画像

pythonログラムの初歩13/チャット履歴の手動実装

こんにちはmakokonです。今日はgpt-4o-miniを使ったチャットにメモリ機能を手動で実装します。どうして今頃手動実装なのでしょうか?実はここのところLangChainの新しいチャット履歴管理のRunnableWithMessageHistoryを試しているのですが、安心して使えるような芳しい結果が得られていません。そこで、安心して使えるチャット履歴を試してみます。
まあ、そうは言っても難しい実装ではないので、pythonプログラムの初歩シリーズにしておきます。


チャット履歴とは


チャット履歴とは、過去に行われたテキストベースのチャット会話の記録を指します。これには、メッセージの送信者、受信者、送信時間、メッセージ内容などが含まれます。チャット履歴は、ビジネスや個人のコミュニケーションにおいて、情報の追跡や証拠の保持、問題解決のために役立ちます。
はは!ちょっと硬いですね。
実際にチャットアプリに履歴機能があるとき初心者が求める機能は、以下のような2点だけでしょう。

  • 過去の会話の内容を覚えておくことで、スムーズな対話が可能

  • ユーザーが以前の質問や問題を再説明する手間が省ける

例えば、プログラムの相談をAIにしているときに、「現在のプログラムはこうなっています。(プログラムコード) 次はどうしたらいいですか」
「現在のプログラムはこうなっていて、前回12行目を直しました。(プログラムコード) エラーになるのですがどうしてでしょうか」
などと、毎回プログラムコードをコピーして、状況を説明して、質問してではどう考えても無理があります。

Langchain

今更チャット履歴を実装しようとしなくても、世の中には優れたチャット履歴管理ライブラリがあります。代表的なのがLangchainのRunnableWithMessageHistoryですが、最近試しているものの、まだ安心して使えていません。実際上の動作は問題ないのですが、安心できないと思いながら使うのも自分ひとりなら良くても、人に使ってもらう事はできません。
ここは多少性能に目をつぶっても、安心して使えるものを自前で用意したほうがストレスがありません。少なくとも一度作っておいて無駄はないでしょう。

チャット履歴の設計

今回の履歴の形を決めましょう。
とは言っても今回はシンプルな履歴なので、最終的な形は、
[ "人間:質問1"、"AI:回答1","人間:質問2"、"AI:回答2","人間:質問3"、"AI:回答3","人間:質問3"、"AI:回答3",,,,,,,,,,,,,,]
と人間、AI、の発言延々とつづくリストで十分でしょう。
これを具体的に実装します。
LLMは最近流行りのgpt-4o-miniを使用します。
無限メモリを扱う実装は大変なので、LLMが扱う履歴は、最新の回答を含めて、今回は8ターンだけにしましょう。

gpt-4o-miniの準備

ライブラリを最新にしておきます。

pip install --upgrade openai

基本的な使い方は、こんな感じですね。
lesson13-01.py

from openai import OpenAI 
import os

## Set the API key and model name
MODEL="gpt-4o-mini"
client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY", "<your OpenAI API key if not set as an env var>"))
completion = client.chat.completions.create(
  model=MODEL,
  messages=[
    {"role": "system", "content": "You are a helpful assistant. Help me with my math homework!"}, # <-- This is the system message that provides context to the model
    {"role": "user", "content": "Hello! Could you solve 2+2?"}  # <-- This is the user message for which the model will generate a response
  ]
)

print("Assistant: " + completion.choices[0].message.content)

この、使い方を見るとmessagesというリストがあります。
"role":"system"はシステムプロンプト、"role":"user"はユーザープロンプトですね。あとは、AIの応答の書き方は?
completionの中身を見てみると、下のような感じです。
ChatCompletion(id='chatcmpl-9vQnylBqvAtpDyOzs6mr9YgIcHGhK', choices=[Choice(finish_reason='stop', index=0, logprobs=None, message=ChatCompletionMessage(content='Of course! The sum of 2 + 2 is 4.', refusal=None, role='assistant', function_call=None, tool_calls=None))], created=1723474762, model='gpt-4o-mini-2024-07-18', object='chat.completion', service_tier=None, system_fingerprint='fp_48196bc67a', usage=CompletionUsage(completion_tokens=15, prompt_tokens=34, total_tokens=49))

role='assistant',というのがありますね。これでいきましょう。
ずらずらとくっつければできるような気がする。

実装

履歴は2種類用意します。
all_history 人があとから読むための履歴です。
history LLMに引き渡す履歴です。
実際に引き渡すときは最大14個の要素に制限します。
messages = [system_prompt] + history_14 + [user_prompt]
今までの情報を含めてmessagesにまとめます。全てリストにしてから結合することが大切ですね。
gpt-4o-miniを使う場合、最大トークン数は16,384tokensなので、結構簡単に溢れます。8ターンくらいの会話だと大丈夫だと思いますが、質問によっては気をつけてください。

from openai import OpenAI
import os

# Set the API key and model name
MODEL = "gpt-4o-mini"
client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY", "<your OpenAI API key if not set as an env var>"))

def chat(MODEL, client, s_prompt, prompt, history):
    system_prompt = {"role": "system", "content": s_prompt}
    history_14 = history[-14:]  # 最後の14件の履歴を取得
    user_prompt = {"role": "user", "content": prompt}
    
    # メッセージリストを構築
    messages = [system_prompt] + history_14 + [user_prompt]
    
    # API呼び出し
    completion = client.chat.completions.create(
        model=MODEL,
        messages=messages,
        temperature=0.7,
        max_tokens=14000
    )

    return completion.choices[0].message.content

# main
all_history = []
history = []
s_prompt = "あなたは各分野の専門家として、userの入力に対し、必要ならば履歴を参照して、日本語で簡潔に答えてください。"

while True:
    user = input('あなた:')
    if user == "exit":
        break
    
    response = chat(MODEL, client, s_prompt, user, history)
    print("AI:", response)
    
    all_history.append("人間:" + user)
    all_history.append("AI:" + response)
    history.append({"role": "user", "content": user})
    history.append({"role": "assistant", "content": response})

# end phase
for message in all_history:
    print(message)

では、実行結果です。all_historyの内容を示します。
継続的な会話をしており、今までの会話を覚えていることが確認できました。

人間:自己紹介してください
AI:私はAIアシスタントです。さまざまな分野の情報を提供し、質問にお答えするために設計されています。あなたの疑問や興味についてお手伝いすることができますので、何でも聞いてください!
人間:どんなテーマが得意なのですか
AI:私は幅広いテーマに対応できますが、特に以下の分野が得意です:

1. 科学と技術
2. 健康と医療
3. 教育と学習
4. ビジネスと経済
5. 歴史と文化
6. 旅行と観光
7. 趣味やライフスタイル

その他の質問やテーマについてもお気軽にどうぞ!
人間:政治や宗教についてはどうですか
AI:政治や宗教についてもお答えできますが、これらのテーマはしばしばデリケートで多様な意見が存在します。そのため、客観的な情報や一般的な知識を提供することに重点を置いています。具体的な質問があればお聞かせください。
人間:デリケートの意味がわかりません。
AI:「デリケート」という言葉は、繊細で扱いに注意が必要な状態や状況を指します。特に、感情や意見に影響を与えやすいテーマについて使われることが多いです。例えば、政治や宗教の話題は人々の感情に触れることがあるため、「デリケート」と言われます。
人間:なるほど。今までの会話を10行で要約してください。
AI:1. 私はAIアシスタントです。
2. 幅広いテーマに対応できます。
3. 得意な分野には科学、健康、教育などがあります。
4. 政治や宗教についてもお答え可能です。
5. これらのテーマはデリケートであることが多いです。
6. デリケートとは、扱いに注意が必要な状態を指します。
7. 特に感情や意見に影響を与えやすいテーマです。
8. 質問には客観的な情報を提供します。
9. 具体的な質問があればお答えします。
10. 何でもお気軽に聞いてください。

実行結果

まとめ

今回は、gpt-4o-miniを使ったチャットボットに手動でチャット履歴機能を実装しました。LangChainのチャット履歴管理機能を試してみたものの、まだ完全に安心して使える結果が得られなかったため、自前で信頼できる履歴管理を実装することにしました。

具体的な実装方法については、Pythonの基本的な知識を使って簡単に行えるもので、履歴を管理するリストを用意し、必要なメッセージを追加するだけです。今回の実装では、チャット履歴を最新の8ターンに制限し、履歴を利用してスムーズな対話を行うことができました。

この方法を使えば、履歴を管理しながらスムーズな会話を続けることができます。皆さんもぜひ試してみてください。

タイトル画の説明 by gpt-4o


この画像は、古代アジア風のスタイルで描かれた芸術作品です。一人の男性が左側に座り、手に灯火を持ち、右側には二つの頭を持つ蛇がいます。蛇は傘を持ち、男性の前で何かを説明しているように見えます。背景には山々と寺院が描かれており、空には蝶が飛んでいます。男性の椅子の上には鳥がとまっています。この画像は、神話やファンタジーの要素を含んだ落ち着いた雰囲気を醸し出しています
makokon パイソンと知性的な会話を楽しむ人を書かせたのだが、難しいな。

#Python
#GPT4
#チャットボット
#AI開発
#プログラミング
#オープンソース
#LangChain
#AIチャット
#人工知能
#プログラム学習
#会話履歴
#リストび

いいなと思ったら応援しよう!