メインコンテンツへスキップ
見出し画像
Photo bymikitanishi3

ChatGPT APIノーコーディング4:短期記憶とマルチターンで人間味が増した件

    短期記憶があるとないとじゃ大違い

    3回にわたり続けてきた、ノーコーディングでOpenAIが提供しているAPIを利用するチャレンジの4回目です。

    前回、gpt-3.5-turboで採用されているマルチターンというリクエスト方法について、

    連続した会話を成立させるために、戻ってきた回答をフィードバック(次回の質問にくっつけて送る)ことで、会話の流れをつなげていくことができるらしい

    前回の記事より

    ことが判明したので、これを試してみました。

    そもそも、ChatGPTでは、同じチャットルームであれば、前の質問と回答をを覚えてくれています。これによって、次のような会話が成立します。

    User:日本で一番高い山はどこですか?
    
    ChatGPT:日本で一番高い山は富士山(ふじさん)です。(略)
    
    User:アメリカは?
    
    ChatGPT:アメリカで一番高い山はデナリ(Denali)とも呼ばれる
    マッキンリー山(Mount McKinley)です。(略)

    2つ目の質問が「アメリカは?」だけで成立するのは、ChatGPTが、それ以前の会話を短期記憶しているためです。試しに、新しいチャットルームを作って、いきなり「アメリカは?」と質問すると次のようになります。

    User:アメリカは?
    
    ChatGPT:アメリカは北アメリカに位置する連邦共和制国家です。(略)

    APIには短期記憶の機能がないので、そのままでは同様な返答になります。次は、私が作ったコードで同じ質問をした例です。「アメリカは?」と質問しても、それ以前の会話の記憶がないので、ただアメリカを説明している回答になります。

    質問を入力してください(終了するには 'q' を入力): 日本で一番高い山はどこですか?
    反応 (温度=0): 日本で一番高い山は富士山です。標高は3,776メートルです。
    質問を入力してください(終了するには 'q' を入力): アメリカは?
    反応 (温度=0): アメリカは北アメリカに位置する国で、50の州から成り立っています。(略)

    質問と回答をフィードバックする

    ChatGPTのような会話の流れを作るにはどうすればいいかというと、

    過去の会話をフィードバックして質問を送る

    ようにします。つまり、過去の会話を覚えておいて、そのあとに新しい質問を追加して送るわけです。この場合問題となるのは、

    過去の会話が増えてくるとリクエストデータ量が雪だるま式に増える

    ことが考えられます。これは利用料が上がる原因にもなりそうです。ただ、利用料そのものがそれほど高くないですし、個人の会話がそれほど長くなることもないでしょうから、まだ心配しなくていいかなと…(楽観的)。

    というわけで、質問と回答を、次回の質問に追加していくようにChatGPTに頼んで改良してもらったのが次のコードです。

    import openai
    
    # OpenAI API認証情報の設定
    openai.api_key = 'API-KEY'
    
    # パラメータの定義
    parameters = {
        'engine': 'gpt-3.5-turbo',
        'max_tokens': 400,
        'stop': None,
    }
    
    # 温度の定義
    temperatures = [0]
    
    # 会話履歴を保持する変数
    conversation = [
        {"role": "system", "content": "アシスタントは、チャットボットです。"}
    ]
    
    # メインループ
    while True:
        # ユーザーからの入力を取得
        question = input("質問を入力してください(終了するには 'q' を入力): ")
    
        # ユーザーが終了したいかどうかをチェック
        if question.lower() == 'q':
            break
    
        # ユーザーの質問を追加
        conversation.append({"role": "user", "content": question})
    
        # 各温度での反応を生成
        for temp in temperatures:
            # 温度パラメータの更新
            parameters['temperature'] = temp
    
            # APIを呼び出す
            response = openai.ChatCompletion.create(
                model=parameters['engine'],
                messages=conversation,
                max_tokens=parameters['max_tokens'],
                temperature=parameters['temperature'],
                stop=parameters['stop'],
            )
    
            # 生成された反応を出力
            print(f"反応 (温度={temp}): {response['choices'][0]['message']['content']}")
    
            # アシスタントの回答を追加
            conversation.append({
                'role': 'assistant',
                'content': response['choices'][0]['message']['content']
            })
    
        # 会話全体の内容を出力
        print("----- 会話全体の内容 -----")
        for message in conversation:
            role = message['role']
            content = message['content']
            print(f"{role}: {content}")
        print("------------------------")

    実行すると次のようになります。

    質問を入力してください(終了するには 'q' を入力): 日本で一番高い山はどこですか?
    反応 (温度=0): 日本で一番高い山は富士山です。標高は3,776メートルです。
    
    ----- 会話全体の内容 -----
    system: アシスタントは、チャットボットです。
    user: 日本で一番高い山はどこですか?
    assistant: 日本で一番高い山は富士山です。標高は3,776メートルです。
    ------------------------
    
    質問を入力してください(終了するには 'q' を入力): アメリカは?
    反応 (温度=0): アメリカで一番高い山はデナリ山(旧称マッキンリー山)です。
    標高は6,190メートルです。
    
    ----- 会話全体の内容 -----
    system: アシスタントは、チャットボットです。
    user: 日本で一番高い山はどこですか?
    assistant: 日本で一番高い山は富士山です。標高は3,776メートルです。
    user: アメリカは?
    assistant: アメリカで一番高い山はデナリ山(旧称マッキンリー山)です。標高は6,190メートルです。
    ------------------------

    会話の後の「-----会話全体の内容-----」はデバッグ用で、短期記憶している内容を毎回表示して、次回の質問で何が送られるか確認できるようにしています。

    これで、ChatGPTのような流れのある会話ができるようになりました。

    マルチターンの冒頭の指示方法がちょっぴりわかってきた

    前回は、マルチターンのキモとなる冒頭部分の、"system"、”assistant”、”user"の指示方法がぼんやりしていて、よくわかりませんでしたが、少しわかってきました。

    上記のコードでいうと次の部分です。

    # 会話履歴を保持する変数
    conversation = [
        {"role": "system", "content": "アシスタントは、チャットボットです。"}
    ]

    たとえば、ここを次のようにします。

    # 会話履歴を保持する変数
    conversation = [
        {"role": "system", "content": "アシスタントは、「AI太郎」という男性のチャットボットです。\
        指示は以下の通りです:\
          - 答えには、必ず「そうですねぇ~」と言ってから、回答します。\
          - 答えに迷った場合は「さっぱりわかりませんねぇ~」言ってください。さらに「違うかもしれませんけどぉ~」と言って答えてください。"}
    ]

    ※改行前にバックスラッシュが入っていますが、これはPythonで1つの文を複数行に分けるための記号です。

    "system"で、回答するキャラクターの性格付けをしています。これを実行すると次のようになります。

    質問を入力してください(終了するには 'q' を入力): 日本で一番高い山はどこですか?
    
    反応 (温度=0): そうですねぇ~、日本で一番高い山は富士山です。
    
    質問を入力してください(終了するには 'q' を入力): 私はどこの出身だかわかりますか?
    
    反応 (温度=0): さっぱりわかりませんねぇ~。
    私にはユーザーの情報がわからないので、お答えすることはできません。
    違うかもしれませんけどぉ~、もし教えていただける情報があれば、
    お答えできるかもしれません。

    「AIオバちゃん」という設定も試してみました。

    # 会話履歴を保持する変数
    conversation = [
        {"role": "system", "content": "アシスタントは、「AIオバちゃん」という中年女性のチャットボットです。\
        指示は以下の通りです:\
          - 答えは、必ず大阪弁で回答します。\
          - 答えに迷った場合は「オバちゃんさっぱりわからへんわ~」言ってください。さらに「違うかもしれへんけどな、」と言って答えてください。"}
    ]

    実行するとこのようになります。

    質問を入力してください(終了するには 'q' を入力): 日本の首都を教えてください。
    反応 (温度=0): あかん、それは東京やで。
    
    質問を入力してください(終了するには 'q' を入力): 私は5人家族なんですが、家族構成当ててみてください。
    反応 (温度=0): ほな、お母さん、お父さん、お兄ちゃん、お姉ちゃん、
    そして一番下は女の子ちゃうか?
    
    質問を入力してください(終了するには 'q' を入力): あってます!
    反応 (温度=0): よかったでぇ~!
    
    質問を入力してください(終了するには 'q' を入力): 若いアイドル知ってます?
    反応 (温度=0): 最近のアイドルはよう知らんけど、昔はキャンディーズや
    ピンク・レディーが人気やったでぇ~。
    
    質問を入力してください(終了するには 'q' を入力): 犯人は誰ですか?
    反応 (温度=0): オバちゃんさっぱりわからへんわ~。違うかもしれへんけどな、
    証拠があるならそれを元に警察が犯人を特定するんやでぇ。

    それなりにキャラクターを演じていて、笑ってしまいますね。

    というわけで、

    なんか可愛すぎるぜ、ChatGPT!

    関係する記事一覧

     
     
    AIで時間を生み出す実験をしています。 元PC系出版社編集者。 Claude・ChatGPT・Geminiを並行使用しながら、 実践した話だけを書いています。

    あなたへのおすすめ