そうだ、年末大掃除にAIを取り入れよう

こんにちは。medibaでバックエンドエンジニアをしている@yyyです。
本記事は [mediba Advent Calender 2023](https://qiita.com/advent-calendar/2023/mediba) の13日目の記事です。

## 年末大掃除をAIに手伝ってもらいませんか？
皆さんこんにちは、もう年末ですね。お部屋の隅々まで掃除は進んでいますか？
もし「まだ…」という方、今回はあなたの大掃除に猫の手を。

## 今どきの断捨離はメルカリ、だけど...
メルカリの出品フローを見てみましょう
1. 写真の撮影
2. 商品のタイトルを書く
3. 商品の説明文を書く
...


これってAIが成長を続ける今なら簡略化できるのでは？

## OpenAI (ChatGPT) が写真の認識をできるように
OpenAIは画像に含まれる情報を解釈し理解できる画像認識に特化したモデルを一般公開しました
参考：https://platform.openai.com/docs/guides/vision

これをつかって断捨離したいものからタイトルや説明文を考えてもらおう

## 実際にChatGPTからテスト
ChatGPT Plusに加入していれば誰でも簡単にテストできます
とりあえずタイトルや説明文を考えてくれるようなプロンプトを投げかけてみます

<img width="200" alt="df6703c660b1aa52_1702389593.jpeg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59148/19d2e774-5530-ee97-f43c-6dd07227fd79.jpeg"> 

```
添付する画像の商品をメルカリに出品しようとしています
売れやすいような商品のタイトルと説明文を考えてください
ただし、以下の制約に注意してください

・商品のタイトルは最大130文字まで
・商品の説明文は最大3,000文字まで
・誤解のないよう商品の正確な商品名がわかるように書いてください
・説明文には検索されるであろうワードをハッシュタグ形式で最低10個末尾につけてください
```
### 結果
<img width="300" alt="S__47743083.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59148/a907d2ed-7ec0-2cbb-7a43-e0f753695685.jpeg">

<b>思ったよりめちゃくちゃ有能！</b>

## WEBアプリ化してみた
OpenAIが提供しているAPIを使って叩いて、スマホから簡単に同じようなことが簡単にできるWEBアプリを作って見る

### OpenAI APIを叩くイメージ
画像を認識して欲しいので `gpt-4-vision-preview` を利用します
また、`max_tokens`の値はレスポンスの応答速度に応じて書き換えましょう
```PHP
<?php
$data = [
    'model' => 'gpt-4-vision-preview',
    'messages' => [
        [
            'role' => 'user',
            'content' => [
                [
                    'type' => 'text',
                    'text' => $prompt,
                ],
                [
                    'type' => 'image_url',
                    'image_url' => $image_url
                ]
            ]
        ]
    ],
    'max_tokens' => 4000
];

$ch = curl_init('https://api.openai.com/v1/chat/completions');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    "Authorization: Bearer {$openai_api_key}"
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
$response = curl_exec($ch);
```

### アップロード画像が見れるURLを発行する
画像のバイナリなどをPOSTするのではなく、
OpenAIが見れるように画像のURLを提示してあげる必要があります

以下簡易的なアップロード画像を処理するコード、公開するにはもっと厳密にチェックする必要あり
```PHP
$imageFileType = strtolower(pathinfo(basename($_FILES["image"]["name"]), PATHINFO_EXTENSION));
if (!in_array($imageFileType, ['jpeg', 'jpg', 'png'])) {
    $error = '不正なファイル形式です。JPEGまたはPNGファイルのみアップロード可能です。';
}

$target_file = generateRandomFileName($imageFileType);
move_uploaded_file($_FILES["image"]["tmp_name"], $target_file)
$image_url = (isset($_SERVER['HTTPS']) ? "https" : "http") . "://$_SERVER[HTTP_HOST]" . dirname($_SERVER['PHP_SELF']) . "/$target_file";
```

### そんなこんなで完成！

<img width="200" alt="S__47743085.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59148/dcda4320-0715-24c0-19ee-c407821b4d9d.jpeg"> 

## 精度を高めたい
現行の`GPT-4 Vision`は十分すごいが人間が思っていることとは違うものが返されることも多々あった

AirPodsの本体を売りたい、だけど中に何が入っているかは画像からは読み取れない
誤植例：ケースだけの出品と間違えられる
```
タイトル:
「Apple純正AirPods充電ケース 第1・2世代対応 - 使用感あり」

説明文:
ご覧いただきありがとうございます。
こちらはApple純正のAirPods充電ケースで、第1世代および第2世代のAirPodsに対応しています。
使用に伴う小傷や擦れがありますが、機能的には問題なくしっかりと動作します。
本体のみの出品で、AirPodsは含まれておりません。また、充電ケーブルも同梱しておりませんので、ご自身でご用意ください。
配送前にクリーニングをしてからお送りしますが、細かな傷や使用感が気になる方はご購入をお控えください。何かご質問があればお気軽にどうぞ！

ハッシュタグ:
#Apple #AirPods充電ケース #純正アクセサリー #第1世代対応 #第2世代対応 #ワイヤレスイヤホン #Bluetooth #音楽 #充電器 #ガジェット
```

そこで <b>中身ちゃんと入ってますよ！</b> っていう意思表示ができるようにしてみた

### 補足情報をテキストで入れれるようにした
どうしても画像だけでは伝えきれない部分は文字で伝えるしかなさそうなので
補足情報を入れれるようにして柔軟性を高めてみました
```HTML
<div class="form-group">
    <label for="additional_info">補足情報（任意）:</label>
    <textarea name="additional_info" id="additional_info" class="form-control" rows="3" placeholder="商品についての補足情報を入力してください。"></textarea>
</div>
```

```PHP
$prompt = "添付する画像の商品を..."
if (!empty($additional_info)) {
    $prompt .= PHP_EOL . "画像の捕捉情報：{$additional_info}";
}
```

## 出来上がったもの
年末大掃除で断捨離しないといけないものがたくさんある方、ぜひ真似てみてください

https://github.com/5l/advent-cleaning

## さいごに

AirPodsは売ってません。ばりばり現役です。