GPT-4Vで設計図からプログラムを生成する

# はじめに
Gpt-4-Turbo with Vision で画像を解析できるようになったので、設計図(フロー図っぽいなにか）の画像からプログラムのせめて土台は生成できないかなということで試してみました。
ただ画像だけだと事前に用意したAPIとかを当然考慮しない実装になってしまうので、設計画像に加えて参考できるコードも併せて入力としてみました。（下図）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/977156/cc39b8b7-0731-42b8-b75a-c2ad14e88d4d.png)

# 設計図
設計図としては以下の画像を作成しました。日本語は残念ながらOCRの精度があまりよくないので、英語で記載していますが、なるべく単純化しています。この画像中のwebassist.pyが今回生成したいコードになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/977156/dbc8a954-9065-5892-03e4-973a0eab4ac0.png)

処理の概要
1. リクエストを元にBingの検索クエリを生成する
2. Bing検索を実行する
3. Bing検索結果のURLからHTMLを取得する
4. HTMLの内容をChatGPTにまとめさせる
5. ChatGPTに回答をレビューさせる
5. もしレビュー結果がNGなら再度Bing検索クエリを生成して、2.に戻る（3回まで）
5. もしレビュー結果がOKなら回答を返答する

# 参考コード
画像だけでもプログラムを生成してくれますが、フォーマットスタイルや使ってほしいAPIの情報は当然入ってこないので、そこを参考コードをいれることで補完させます。

参考コードは以下の処理をしています。今回作成したいプログラムからレビューチェックなどがないものになります。
- Bingの検索クエリを生成する
- Bing検索を実行する
- Bing検索結果のURLからHTMLを取得する
- HTMLの内容をChatGPTにまとめさせる

とりあえずVをつかった動作の検証がしたいだけなので、参考コードの詳細は割愛します。

# GPT-4-V のプロンプト
システムプロンプトは以下です。日本語だとGPT-4-Vの挙動が安定しなかったので英語で試しました。内容としては、「**pythonプログラムのエキスパートとして、参考コードをもとにwebassist.pyを作成してください。API設計書などはないので参考コードと同じように実装してください。**」といった感じです。
ref_textには参考コードをそのまま突っ込みます。

```python
system_prompt = f"""\
You are an expert in Python programming. \
Please create a Python code (webassist.py) based on the content described.

Since there is no API specification document or system prompt design document, \
please refer to the following bingsearch.py \
and implement it in a similar manner as much as possible.

{ref_text}
"""
```

# GPT-4-V の作成コード

結果、以下のコードが作成されました。
内容としてはそれなりにいい感じです。が、やっぱりAPIの設計書とかが入っていないので、そのあたりがちょっと適当な感じです。でも全体の処理はそれっぽく出ていますね。

ただちょっとダメなポイントは、
- システムプロンプトが抜けている（ChatGPTはChatGPTのプロンプトを作らせるのは苦手）
- 再帰処理のところがコメントで省略されている（設計が曖昧、参考コードが悪い？）
- クエリ再作成が同じ作成処理になっている（設計が悪い）
- レビューチェックが"Sufficient"文字列になっているところ（設計が悪い）
あたりでしょうか。

やっぱり設計が曖昧で、かつ参考コードにない部分はやっぱり雰囲気コードになっちゃってますね。ただ、このあたりはきちんと設計があって、参考コードがきちんと揃っていれば大丈夫そうな部分ではあります。

```python
# FILENAME_PATH: routers/webassist.py
from fastapi import APIRouter
from fastapi.responses import JSONResponse
from pydantic import BaseModel
from typing import List

import requests

from call_chatgpt import call_chatgpt
from call_bingapi import call_bingapi
from analyze_html import analyze_html

from logger import logger

router = APIRouter()

class Message(BaseModel):
    role: str
    content: str

@router.post("/webassist")
def webassist(messages: List[Message]):
    messages = messages[-10:]  # Keep only the last 10 messages

    # Generate Bing search query
    system_prompt_query = """..."""  # Define the system prompt for generating Bing search query
    messages_for_query = [Message(role="system", content=system_prompt_query)] + messages
    logger.debug(messages_for_query)
    query = call_chatgpt([m.dict() for m in messages_for_query])

    # Execute Bing search
    bingsearchurl = "https://api.bing.microsoft.com/v7.0/search?q="
    querystring = "%20".join(eval(query))
    option = "&count=5&offset=0"
    bing_results = call_bingapi(bingsearchurl, querystring, option)

    # Analyze Bing result
    analyzed_results = []
    for result in bing_results:
        url = result["url"]
        if url.endswith(".pdf"):
            continue
        try:
            title, content = analyze_html(url)
            analyzed_results.append({"url": url, "title": title, "content": content})
        except Exception as e:
            logger.error(f"Error analyzing HTML: {e}")

    # Summarize contents based on request
    system_prompt_summary = """..."""  # Define the system prompt for summarizing contents
    messages_for_summary = [Message(role="system", content=system_prompt_summary)] + messages
    summary = call_chatgpt([m.dict() for m in messages_for_summary])

    # Review Answer and possibly remake Bing query if not sufficient
    review_count = 0
    while review_count < 3:
        review_prompt = """..."""  # Define the system prompt for reviewing the answer
        review_messages = [Message(role="system", content=review_prompt)] + [Message(role="user", content=summary)]
        review = call_chatgpt([m.dict() for m in review_messages])
        if "sufficient" in review:
            break
        else:
            # Remake Bing query and review result
            query = call_chatgpt([m.dict() for m in messages_for_query])
            querystring = "%20".join(eval(query))
            bing_results = call_bingapi(bingsearchurl, querystring, option)
            # Repeat the analysis and summarization process
            # ...
        review_count += 1

    data = {
        "message": summary,
        "query": query,
        "searchResult": analyzed_results,
    }
    return JSONResponse(content=data)
```

# まとめ
今回GPT-4-Vに、設計図（画像）と参考コードをいれてプログラムコードを作成させてみましたが、**土台として**は使えそうなレベルのコードが生成できたかと思ます。この土台コードをもとにGitHub Copilot なんかも使ってコーディングを実施していけば結構な省力化になりそうです。

特にプロジェクトが大きくなればなるほど、参考にできるコードや設計図や規約なんかも増えてくると思いますので、GPT-4-Vをつかってコードの土台部分を設計画像から生成するやり方なんかはとてもアリじゃないかなと思います。

もっと良いものにするためには、
- 設計図により詳細な処理を記載する
- API設計書など関連するドキュメントも入れる
- 規約や処理パターンのお手本コードも入れる
等があると思います。これらを全部やると相当いいものがでそうです。

なお、今回はGPT-4-Vの検証をしたかったのであえて画像を用意しましたが、テキストベースの設計書でも十分な精度が出るかと思いますので、あえて画像を用意する必要はないかもしれません。けど処理フローは画像にした方がいかんせん見やすいので、そういうケースに使えるかもしれないということで。