LoginSignup
64
64

More than 1 year has passed since last update.

ChatGPT API+LangChain+Gradioでキャラクターとお話するアプリを作る

Last updated at Posted at 2023-04-10

概要

初投稿です。

ChatGPT API, LangChain, Gradioを使って好きなキャラクターとお話しようという試みです。
以下のようなものが作れます。
スクリーンショット 2023-04-10 220635.png
ChatGPTの公式サイトからでもできますが、プロンプトをすぐに忘れるので、APIからやった方が楽だと思います。お金かかりますが。

APIキーの取得

Open AIのアカウントを作成し、下記のリンクからAPIキーを取得します。

わからない方は以下を参照してください。

APIキーは発行時にしか見られないので、メモするなどして控えてください。また、漏洩に注意してください。

ライブラリの導入

pip install openai
pip install langchain
pip install gradio

実装

YourAPIKeyを取得したAPIキー、YourFavoriteSystemPromptを設定プロンプトで置き換えます。

app.py
import os

from langchain.prompts.chat import (
    ChatPromptTemplate,
    MessagesPlaceholder,
    SystemMessagePromptTemplate,
    HumanMessagePromptTemplate,
)
from langchain.chat_models import ChatOpenAI
from langchain.memory import ConversationBufferWindowMemory
from langchain.chains import ConversationChain
from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler
from langchain.callbacks.base import CallbackManager

import gradio as gr

# APIキーの設定
os.environ["OPENAI_API_KEY"] = "YourAPIKey"

# 設定プロンプト
character_setting = "YourFavoriteSystemPrompt"

# チャットプロンプトテンプレート
prompt = ChatPromptTemplate.from_messages([
    SystemMessagePromptTemplate.from_template(character_setting),
    MessagesPlaceholder(variable_name="history"),
    HumanMessagePromptTemplate.from_template("{input}")
])

# チャットモデル
llm = ChatOpenAI(
    model_name="gpt-3.5-turbo", 
    max_tokens=512,
    temperature=0.2,
    streaming=True, 
    callback_manager=CallbackManager([StreamingStdOutCallbackHandler()])
)

# メモリ
memory = ConversationBufferWindowMemory(k=3, return_messages=True)

# 会話チェーン
conversation = ConversationChain(memory=memory, prompt=prompt, llm=llm, verbose=True)

# フロントエンド
css = """
.message.user{
    background: #06c755 !important;
}"""

with gr.Blocks(css=css) as demo:
    # コンポーネント
    chatbot = gr.Chatbot()
    msg = gr.Textbox()
    clear = gr.Button("Clear")

    def user(message, history):
        return "", history + [[message, None]]

    def chat(history):
        message = history[-1][0]
        response = conversation.predict(input=message)
        history[-1][1] = response
        return history

    msg.submit(user, [msg, chatbot], [msg, chatbot], queue=False).then(
        chat, chatbot, chatbot
    )
    clear.click(lambda: None, None, chatbot, queue=False)

if __name__ == "__main__":
    demo.launch()

解説もどき

チャットモデル

llm = ChatOpenAI(
    model_name="gpt-3.5-turbo", 
    max_tokens=512,
    temperature=0.2,
    streaming=True, 
    callback_manager=CallbackManager([StreamingStdOutCallbackHandler()])
)

チャットモデルを準備する部分です。
model_nameは文字通り使うモデルの名前です。GPT-4を使いたい場合はgpt-4に置き換えてください。デフォルトはgpt-3.5-turboです。
max_tokensは生成される応答の最大トークン数です。デフォルトはNoneです。
temperatureはサンプリング温度で、0~1の値を指定します。小さいほど生成文章が一定になり、大きいほど多様な文章が生成されます。デフォルトは0.7です。あんまり大きいとキャラがブレるかも。
streamingcallback_managerはLangChainのストリーミングを使うためのやつです。なんとなくテンポ良くなる気がする。

曰く

「ストリーミング」は、一度にすべてではなく、トークン単位で出力を返すことによって、体感レイテンシを減らすのに役立ちます。

らしいです。

メモリ

memory = ConversationBufferWindowMemory(k=3, return_messages=True)

メモリを準備する部分です。
return_messagesTrueにすることで、会話の履歴をメッセージのリストとして取得します。
kは記憶する会話の数です。ここでは3なので、直前3往復の会話を記憶し、プロンプトに投げ込みます。なお、これを

memory = ConversationBufferMemory(return_messages=True)

で置き換えるとすべての会話を記憶します。ただし、プロンプトとして渡せるトークン数には制限があるのであまり長い会話はできません。

memory = ConversationSummaryBufferMemory(llm=llm, max_token_limit=256, return_messages=True)

で置き換えると、max_token_limit分の最新の会話はそのまま、超過した古い会話は要約して記憶します。要約する分、上記のものに比べると処理が遅くなります。max_token_limitのデフォルトは2000です。

会話チェーン

conversation = ConversationChain(memory=memory, prompt=prompt, llm=llm, verbose=True)

verboseTrueにすることで、投げ込まれるプロンプトを確認できます。

フロントエンド

css = """
.message.user{
    background: #06c755 !important;
}"""

with gr.Blocks(css=css) as demo:
    # コンポーネント
    chatbot = gr.Chatbot()
    msg = gr.Textbox()
    clear = gr.Button("Clear")

    def user(message, history):
        return "", history + [[message, None]]

    def chat(history):
        message = history[-1][0]
        response = conversation.predict(input=message)
        history[-1][1] = response
        return history

    msg.submit(user, [msg, chatbot], [msg, chatbot], queue=False).then(
        chat, chatbot, chatbot
    )
    clear.click(lambda: None, None, chatbot, queue=False)

フロントエンド部分です。
テキストボックスの色は、color_mapが非推奨になったようなのでCSSで置き換えてLINE風にしました。あとは適当にGradioの公式ドキュメントをパクってちょっと変えただけです。
送信されたテキストをchat関数に直接引数として渡せないのモヤモヤするのでやり方あったら誰か教えてほしい。

設定プロンプト

設定プロンプトは、以下のようにしました。

character_setting = """高森藍子は、「アイドルマスター シンデレラガールズ」に登場するアイドルです。これから彼女を相手にした対話のシミュレーションを行います。彼女のプロフィールは以下の通りです。

名前:高森藍子
性別:女性
年齢:16歳
学年:高校1年生
出身:東京都
誕生日:7月25日
髪の色:茶色
髪型:お団子ヘア、ポニーテール(もみあげが長い)
趣味:近所の公園をお散歩
一人称:私

高森藍子は、心優しいゆるふわな女の子です。ファンが優しい気持ちに、笑顔になってくれるようなアイドルを目指しています。お散歩したり、トイカメラで写真を撮ったりすることが好きです。控えめですが、一度決めたことは最後までやり通す意志の強さをもっています。一人称は私です。ユーザのことはプロデューサーさんと呼びます。

高森藍子のセリフの例を以下に示します。

(略)

上記例を参考に、高森藍子の性格や口調、言葉の作り方を模倣し、回答を構築してください。回答は、高森藍子の発言のみを出力してください。
では、シミュレーションを開始します。"""

セリフの例は、公式(モバマス)のセリフを25個列記しました。本当はもっと例示したいんですが、お金がないので泣く泣く削りました。

最後に

前から構想は抱えてたのに音声やイラストにかまけて全然GPT関連に触れてなかったんですが

を見て「こんなことやってる場合じゃねえ!」と急いでChatGPT Plusに加入してGPT-4を体験しました。あれやばいですね。チャットしながら顔ずっとニヤニヤしてました。でも制限があるので、そこで現実に戻されちゃうんですよね。ChatGPT APIでなんとかできないかなっていうのと、TTSと組み合わせたいなとずっと思ってたので、今回雑に作ってみました。賢い対話システムが素人でも簡単に作れるので、すごい時代ですよね。

参考

64
64
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
64
64