ChatGPT API function callingで作る ~ふわっと画像生成
はじめに
おはこんにちばんは、えるです❢
ChatGPT APIのアップデート「Function calling」が楽しそうだったので、せっかくですし画像生成に絡めたものを作って遊ぶことにしました
Function calling and other API updates (openai.com)
遊んだ結果としては、ふわっとした入力で画像を生成する機能をdiscord botに追加できそうです こんな感じです
実装結果のみ気になる方は、以降の前置きは飛ばしてもよいかもです
前置き ~Function calling
まずは参考に公式のドキュメント通りにやってみま、、
Function calling and other API updates (openai.com)
ある程度わかった今読むとわかりますが、初見のえるさんには無理だったので素直に参考記事を探します このあたりかな?
OpenAIがChatGPTを更にパワーアップ!「Function Calling」の使い方とは? - Qiita
なるほどわかったようなわからないような。。
ひとまず大きく分けて処理は
①-1: ユーザーinputを受けてChatGPTがFunction callするかどうか決める
①-2: Functionに渡すinputをChatGPTが生成する
②: Functionが実行される
③: Function実行結果をChatGPTにinputして、回答を生成
っぽいことがわかりました。
当初にとても凄く謎だったのは、①-1の部分で、
「どんなFunctionを準備して、どんなinputだとChatGPTがFunction Callするか」といった点でしたが、そのあたりは色々試してみました ここでは省略
前置き ~discord bot
過去noteを参考に実装します
Discord bot with chatGPT 作成日記|える (note.com)
前置き ~画像生成API(Webui API)
過去noteを参考に実装します
webui(AUTOMATIC1111) APIを使ってみた|える (note.com)
前置き ~作りたいものイメージ
作りたいものの流れは以下の感じです
1) Discord上で「~のイラストを生成したい」といったメッセージを送信
2) Discord botで拾って、ChatGPTにinput
3) Function Call, イラストのテーマを抽出
4) イラストのテーマをChatGPTにinputして、生成用プロンプトに膨らませる
5) 生成用プロンプトをwebui APIにinput、画像生成
6) 生成した画像をDiscordへ送信
書き出してみてわかりましたが、Function Callingのお手本には沿っておらず、ちょっと自己流入っちゃってる気がしますね、、まあいいでしょう。。
実装方法とか結果
最近は私のdiscord botの実行環境がcolabでは無くなったので、今回はcolabで実行可能な形式にはしていません
要望があればcolab形式でも作ろうかと思っていますので、その場合はコメント頂ければ…!
①初期化
import asyncio
import json
import aiohttp
import os
import discord
import openai
import requests
import base64
import random
import io
from PIL import Image, PngImagePlugin
bot_token = os.environ['KEMI_BOT_KEY']
openai_key = os.environ['GPT_KEY']
bot_tokenやopenai_keyはDiscordトークンとOpenAIのAPI Keyを入れます
私の場合は環境変数に設定しておいて入力するようにしています
importに必要なinstallは環境によって異なるので省略します
いろいろ追加機能を試していたので、無駄なものも入っちゃってるかも 気にしないでください
②Discord bot記述部
### discord initial
intents = discord.Intents.default()
intents.message_content = True
client = discord.Client(intents=intents)
message_chunks = []
@client.event
async def on_ready():
print(f'{client.user.name} has connected to Discord!')
@client.event
async def on_message(message):
print(message.content)
if message.author == client.user:
return
state,data = gpt_msg(message.content)
await message.channel.send(state)
if state == "--generate--": await message.channel.send(file=discord.File("image.png"))
else: await message.channel.send(data)
client.run(bot_token)
実装の概要としては、
discord上でメッセージを受け取ったら何かしら処理(gpt_msg)を実施、処理の戻り値のstateが指定の文字列であれば画像送信、それ以外であればdata送信、という感じです
③gpt_msg部
def gpt_msg(question):
model_name = "gpt-3.5-turbo-0613"
response = openai.ChatCompletion.create(
model=model_name,
messages=[
{"role": "user", "content": question},
],
functions=functions,
function_call="auto",
)
message = response["choices"][0]["message"]
if message.get("function_call"):
function_name = message["function_call"]["name"]
if function_name == "generate_image": #
arguments = json.loads(message["function_call"]["arguments"])
prompt = arguments.get("prompt")
second_response = openai.ChatCompletion.create(
model=model_name,
messages=[
{"role": "user", "content": prompt+"がモチーフの詳細な美しい画像を作りたいので、その詳細な描写を画像生成AIに伝えるためのプロンプトを提案してください。回答は英語が嬉しいです。プロンプトのみの回答が嬉しいです。以下はプロンプトの例です。 intricate detail, dreamy lighting, girl"},
],
)
input_text = second_response.choices[0]["message"]["content"].strip()
webui_api(input_text)
return "--generate--", "dummy data"
else:
print(response.choices[0]["message"]["content"].strip())
return "----", response.choices[0]["message"]["content"].strip()
ここの概要は以下の感じです
・受け取ったメッセージ(question)をChatGPTにinput
・ChatGPTがfunctionに入るか判断、入らない場合ChatGPTの回答を戻す
・functionに入る場合、functionを実行
・funcitionの実行結果(prompt)を受け取り、ここでは生成テーマが入っています
・ChatGPTに生成テーマ(prompt)を渡して、画像生成用プロンプト(input_text)を生成
・webui_apiにプロンプトを渡す
function callingの便利な点はfunctionに入る場合とfunctionに入らない場合をChatGPTが判断してくれる部分です
これはかなり自由度が高く、工夫すればいろいろできて面白そうと個人的には思っています
④function部
functions=[
{
"name": "generate_image",
"description": "イラスト生成を指示する文の場合、イラスト生成用のキーワードを作成する。",
"parameters": {
"type": "object",
"properties": {
"prompt": {
"type": "string",
"description": "生成用キーワードなど",
},
},
"required": ["prompt"],
},
}
]
※ここは前置き参照
⑤webui API部
def webui_api(prompt):
url = "xxx"
neg_prompt = "xxx"
vae = "xxx"
width = 512
height = 768
cfg = 8
step = 20
sampler = "DPM++ 2M SDE Karras"
#"DPM++ 2M Karras"
#"Euler a"
payload = {
"prompt": prompt,
"negative_prompt" : neg_prompt,
#"sd_model_checkpoint": "sd_model名",
"sd_vae": vae,
"width": width,
"height": height,
"cfg_scale": cfg,
"sampler_index": sampler,
"steps": step,
"n_iter": 1,
"enable_hr": False,
"seed": -1
}
response = requests.post(url=f'{url}/sdapi/v1/txt2img', json=payload)
r = response.json()
for i in r['images']:
image = Image.open(io.BytesIO(base64.b64decode(i.split(",",1)[0])))
png_payload = {
"image": "data:image/png;base64," + i
}
response2 = requests.post(url=f'{url}/sdapi/v1/png-info', json=png_payload)
pnginfo = PngImagePlugin.PngInfo()
pnginfo.add_text("parameters", response2.json().get("info"))
image.save('image.png', pnginfo=pnginfo)
※ここは前置き参照
結果
①~⑥を1つのファイルに張り付けて以下の感じにpython実行します
python file.py
すると、disocord botにふわっとしたお願いをすると、画像を生成してくれるはずです!多分きっと以下の具合に…❢
こんな感じ