👋

OpenAIのGPT-4oを使って、音声と画像からDjangoコードを生成させてみた

2024/06/03に公開

OpenAIのGPT-4oを使って、音声と画像からDjangoコードを生成させてみた

はじめに

こんにちは!
前回は、以下の記事でAzureOpenAIGPT-4oを使った簡単な検証を行ってみましたが、今回は、OpenAIGPT-4oのAPIを使って、ローカルで動くマルチモーダルなアプリを作ってみました。

https://zenn.dev/acntechjp/articles/92178ecfe44a82

アプリの概要

下図のように画像と音声による指示出しで、アウトプットを出力させるようなマルチモーダルアプリにしてみました。

取り込む画像は、「Windowsマーク+Shift+S」で範囲指定して画面キャプチャしておくと、自動でアプリがクリップボード領域から画像データを取得するという仕組みにしてみました。

まず、インプットに使う画像を用意します。

生成AIのプロンプトを管理するDjangoアプリを開発する」というお題で、今回は以下のようなフロー図(mermaidフロー図)を生成AIに作成していもらいました。

このフロー図と、音声による指示出しをテキスト変換した情報をGPT-4oに与えてアウトプット(Djangoコード)を出力させるといった感じです。

実際に動かしてみたデモ動画
https://youtu.be/bIVAdZQoVX8

指示出しの音声が入ってませんが、音声で「このフローズを満たすPythonのDjangoコードを作成してください」としゃべって指示だししています。

使用したライブラリとAPI

このアプリでは以下の機能を利用しています。

ライブラリ/API 説明
eel PythonとHTML/JS/CSSを使ってデスクトップアプリを作るためのライブラリ
pyaudio Pythonでオーディオ入力を扱うためのライブラリ
webrtcvad 音声アクティビティ検出(VAD)を行うためのライブラリ
PIL 画像処理を行うためのライブラリ
OpenAI Whisper API 音声をテキストに変換するためのAPI
OpenAI GPT-4 API OpenAIの最新の大規模言語モデル(2024/6/2時点)

実装の流れ

  1. eelを使ってデスクトップアプリの基本構成を作成
  2. pyaudiowebrtcvadを使ってユーザーの音声入力を録音し、無音区間を検出して録音を停止
  3. 録音された音声をOpenAI Whisper APIを使ってテキストに変換
  4. PILを使ってクリップボードから画像を取得し、base64エンコードして画像データを準備
  5. 音声から変換されたテキストと画像データをOpenAI GPT-4o APIに送信し、回答を得る
  6. 生成された回答をeelを通してフロントエンドに表示

実験結果

インプットは先ほどのフロー図と、音声で「このフローズを満たすPythonのDjangoコードを作成してください」と指示だししてみました。

以下が実際に回答された全内容ですが、モデル、ビュー、フォーム、テンプレート、URLパターン等、最低限必要と思われるベースのコードとしては結構いい感じに提案してくれています。

そのまま動かすとエラーが出てところどころ修正は必要ですが、1から作るより爆速で開発できますね。

このボリュームのコードが30秒かからず出てくるわけですから、凄い時代になったなーと感じます。

===============================

ここから下が生成された回答内容

================================

このフロー図に基づいて、Djangoプロジェクトを作成するための基本的なコードを提供します。以下のコードは、ユーザー認証、プロンプトの作成、検索、 編集、削除、お気に入り追加、評価などの機能を含んでいます。

1. プロジェクトのセットアップ

まず、Djangoプロジェクトを作成します。

django-admin startproject myproject
cd myproject
django-admin startapp prompts

2. settings.pyの設定

INSTALLED_APPSpromptsアプリを追加します。

INSTALLED_APPS = [
    ...
    'prompts',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

3. モデルの作成

prompts/models.pyにモデルを定義します。

from django.db import models
from django.contrib.auth.models import User

class Prompt(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    owner = models.ForeignKey(User, on_delete=models.CASCADE)
    created_at = models.DateTimeField(auto_now_add=True)

class Favorite(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    prompt = models.ForeignKey(Prompt, on_delete=models.CASCADE)

class Rating(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    prompt = models.ForeignKey(Prompt, on_delete=models.CASCADE)
    score = models.IntegerField()

4. URLの設定

prompts/urls.pyを作成し、ビューをマッピングします。

from django.urls import path
from . import views

urlpatterns = [
    path('', views.dashboard, name='dashboard'),
    path('login/', views.login_view, name='login'),
    path('prompt/<int:prompt_id>/', views.view_prompt, name='view_prompt'),
    path('prompt/new/', views.create_prompt, name='create_prompt'),
    path('prompt/<int:prompt_id>/edit/', views.edit_prompt, name='edit_prompt'),
    path('prompt/<int:prompt_id>/delete/', views.delete_prompt, name='delete_prompt'),
    path('prompt/<int:prompt_id>/favorite/', views.add_favorite, name='add_favorite'),
    path('prompt/<int:prompt_id>/rate/', views.rate_prompt, name='rate_prompt'),
]

5. ビューの作成

prompts/views.pyにビューを定義します。

from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.contrib.auth import authenticate, login
from .models import Prompt, Favorite, Rating
from .forms import PromptForm, RatingForm

def login_view(request):
    if request.method == 'POST':
        username = request.POST['username']
        password = request.POST['password']
        user = authenticate(request, username=username, password=password)
        if user is not None:
            login(request, user)
            return redirect('dashboard')
    return render(request, 'prompts/login.html')

@login_required
def dashboard(request):
    prompts = Prompt.objects.filter(owner=request.user)
    return render(request, 'prompts/dashboard.html', {'prompts': prompts})

@login_required
def create_prompt(request):
    if request.method == 'POST':
        form = PromptForm(request.POST)
        if form.is_valid():
            prompt = form.save(commit=False)
            prompt.owner = request.user
            prompt.save()
            return redirect('dashboard')
    else:
        form = PromptForm()
    return render(request, 'prompts/create_prompt.html', {'form': form})

@login_required
def view_prompt(request, prompt_id):
    prompt = get_object_or_404(Prompt, id=prompt_id)
    is_owner = prompt.owner == request.user
    return render(request, 'prompts/view_prompt.html', {'prompt': prompt, 'is_owner': is_owner})

@login_required
def edit_prompt(request, prompt_id):
    prompt = get_object_or_404(Prompt, id=prompt_id)
    if request.user != prompt.owner:
        return redirect('dashboard')
    if request.method == 'POST':
        form = PromptForm(request.POST, instance=prompt)
        if form.is_valid():
            form.save()
            return redirect('dashboard')
    else:
        form = PromptForm(instance=prompt)
    return render(request, 'prompts/edit_prompt.html', {'form': form})

@login_required
def delete_prompt(request, prompt_id):
    prompt = get_object_or_404(Prompt, id=prompt_id)
    if request.user == prompt.owner:
        prompt.delete()
    return redirect('dashboard')

@login_required
def add_favorite(request, prompt_id):
    prompt = get_object_or_404(Prompt, id=prompt_id)
    Favorite.objects.get_or_create(user=request.user, prompt=prompt)
    return redirect('view_prompt', prompt_id=prompt_id)

@login_required
def rate_prompt(request, prompt_id):
    prompt = get_object_or_404(Prompt, id=prompt_id)
    if request.method == 'POST':
        form = RatingForm(request.POST)
        if form.is_valid():
            rating = form.save(commit=False)
            rating.user = request.user
            rating.prompt = prompt
            rating.save()
            return redirect('view_prompt', prompt_id=prompt_id)
    else:
        form = RatingForm()
    return render(request, 'prompts/rate_prompt.html', {'form': form})

6. フォームの作成

prompts/forms.pyにフォームを定義します。

from django import forms
from .models import Prompt, Rating

class PromptForm(forms.ModelForm):
    class Meta:
        model = Prompt
        fields = ['title', 'content']

class RatingForm(forms.ModelForm):
    class Meta:
        model = Rating
        fields = ['score']

7. テンプレートの作成

prompts/templates/prompts/ディレクトリに以下のテンプレートファイルを作成し ます。

login.html

<form method="post">
    {% csrf_token %}
    <label for="username">Username:</label>
    <input type="text" name="username" id="username">
    <label for="password">Password:</label>
    <input type="password" name="password" id="password">
    <button type="submit">Login</button>
</form>

dashboard.html

<h1>Dashboard</h1>
<a href="{% url 'create_prompt' %}">Create New Prompt</a>
<ul>
    {% for prompt in prompts %}
        <li><a href="{% url 'view_prompt' prompt.id %}">{{ prompt.title }}</a></li>
    {% endfor %}
</ul>

create_prompt.html

<h1>Create New Prompt</h1>
<form method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">Save</button>
</form>

view_prompt.html

<h1>{{ prompt.title }}</h1>
<p>{{ prompt.content }}</p>
{% if is_owner %}
    <a href="{% url 'edit_prompt' prompt.id %}">Edit</a>
    <a href="{% url 'delete_prompt' prompt.id %}">Delete</a>
{% else %}
    <a href="{% url 'add_favorite' prompt.id %}">Add to Favorites</a>
    <a href="{% url 'rate_prompt' prompt.id %}">Rate</a>
{% endif %}

edit_prompt.html

<h1>Edit Prompt</h1>
<form method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">Save</button>
</form>

rate_prompt.html

<h1>Rate Prompt</h1>
<form method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">Submit</button>
</form>

8. マイグレーションの実行

最後に、データベースのマイグレーションを実行します。

python manage.py makemigrations
python manage.py migrate

9. サーバーの起動

python manage.py runserver

これで、フロー図に基づいたDjangoアプリケーションが完成しました。ユーザーはロ グインし、プロンプトを作成、編集、削除、お気に入り追加、評価することができます。

===============================

ここまでが生成された回答内容

================================

GPT-4oの可能性

GPT-4oの登場によって、マルチモーダルな生成AIの活用が一段と加速しそうだなと感じさせられました。

現時点ではまだ一般公開されてませんが、GPT-4o自体には音声を扱える機能も搭載されているようなので、音声機能がリリースされれば、今回のようにわざわざWhisper APIを使って音声をテキストに変換するような処理を繰み込まなくても直接音声をインプットできるようになるんじゃないかなーと思っています。

実務レベルで使えそうな精度に近づいている気がするので、今後は音声と画像の組み合わせのようなマルチモーダルなインターフェースの活用が増えていきそうですね。

Accenture Japan (有志)

Discussion