【生成AIアプリ100チャレンジ】（22）競馬予想アプリ

![icon_jra_ai.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398694/60d1769a-95d6-084e-c402-15eb0f897af2.png)

https://ai.nuevo.jp/jra/ 

# 開発環境

|  |  |
|:---:|:---:|
| Server | lightSail |
| Language | Python3.11 |
| Framework | Django |
| DB | sqlite3 |


ローカル環境ではPythonのvenvを使用。エディタはvs codeです。

# 目的

 競馬予想アプリです。競馬サイトから出走馬データをスクレイピングして予想させてみようという試みです。最初、スクレイピングして予想という処理にしたのですが、サーバにchromeをインストールして、スクレイピングさせたら、どうやってもサーバタイムアウトになってしまうので断念。
　スクレイピングは別アプリにして、後ほど、スピーディーに必要なデータだけを取得できるようにリファクタリングすることにする。とりあえずデータを取得したCSVファイルをアウトプットする機能だけは実装できているので、そのデータを予想に使用しようと思います。
　まだまだ荒削りなので、プロンプトの改修、モデルの追加などを随時していきバージョンアップさせていきたいと思います。10個の予想を出してもらって100円ずつ1000円使って、いつの日か黒字になればいいなって考えています。まずは競馬からですが、株価予測などもしていきたいって思っています。



# コード
```python:qiita.rb

try:
    OPENAI_API_KEY = os.environ['OPENAI_API_KEY']
except KeyError:
    return HttpResponse("APIキーが設定されていません。", status=500)
chat_results = ""
if request.method == "POST":
    form = ChatForm(request.POST, request.FILES)
    if form.is_valid():
        # レース場の情報
        rase = form.cleaned_data['rase']
        if 'date' in form.cleaned_data and form.cleaned_data['date'] is not None:
            date = form.cleaned_data['date']
            date = date.strftime('%Y年%-m月%-d日')
        else:
            date = ''

        distance = form.cleaned_data['location']
        course = form.cleaned_data['course']
        orientation = form.cleaned_data['orientation']
        place = form.cleaned_data['place']
        weather = form.cleaned_data['weather']
        condition = form.cleaned_data['condition']
        betting = form.cleaned_data['betting']

        csv_file = request.FILES['csv_file']
        data_set = csv_file.read()
        result = chardet.detect(data_set)
        encoding = result['encoding']
        data_set = data_set.decode(encoding)
        io_string = io.StringIO(data_set)
        races_info = pd.read_csv(io_string)
        # フィルタリング条件を指定
        race_number = form.cleaned_data['race_number']
        location = form.cleaned_data['location']
        horses_info = races_info[
            (races_info['date'] == date) & 
            (races_info['round'] == race_number) & 
            (races_info['location'].str.contains(location))
        ]
        horse_names = races_info['horse'].tolist()
        # フィルタリング結果をコンソールに出力
        try:
            client = OpenAI(
                api_key = OPENAI_API_KEY,
            )
            result = client.chat.completions.create(
                model="gpt-4o",
                messages=[
                    {
                        "role": "system",
                        "role": "system",
                        "content": f"""
                        あなたは競馬予想の専門家です。ユーザーが提供する詳細なレース情報および出走馬のデータを基に、最適な予想を提供してください。
                        {date}に開催される{rase}の結果を予想してください。
                        過去の{rase}の情報は下記サイトで調べてください。
                        https://dir.netkeiba.com/
                        競馬場は{place}になります。距離は{distance}、コースの種類は{course}です。コースの回りは{orientation}です。
                        天候は{weather}で、馬場の状態は{condition}になります。
                        馬券の種類は{betting}です。馬券の種類あわせた{betting}の予想のみ出力してください。
                        出走馬の情報を下記に記載します。
                        {races_info}
                        なお、下記のリストの情報は合わせて検索してください。
                        検索用馬名リスト: {horse_names}
                        検索用サイト:https://dir.netkeiba.com/
                        馬券の種類は{betting}です。馬券の種類あわせた{betting}の予想の組み合わせを10パターンを出力してください。
                        当選確率が高いと思われる順に並べてください。
                        その場合、馬名と馬番で表示してください。解説も予想の後にしてください。
                        例
                        馬券の種類
                        馬番-馬番-馬番
                        馬番-馬番-馬番
                        馬番-馬番-馬番
                        馬番-馬番-馬番
                        馬番-馬番-馬番
                        馬番-馬番-馬番
                        馬番-馬番-馬番
                        馬番-馬番-馬番
                        馬番-馬番-馬番
                        馬番-馬番-馬番
                        ーーーーーーーーーーーーーー
                        解説
                        """
                    }
                ],
            )
            result = result.choices[0].message.content
            chat_results = result.replace("\n", "<br>")

        except Exception as e:
            return HttpResponse(f"API呼び出し中にエラーが発生しました: {str(e)}", status=500)
    else:
        return HttpResponse("フォームのデータが無効です。", status=400)
else:
    form = ChatForm()

```

各フレームワークの課題に合わせて出力されます。

# アプリ画面
![スクリーンショット 2024-05-28 20.04.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398694/05e59e50-3545-19cf-0722-6ece69f9dbdd.png)



# 感想
早速、日本ダービーで予想をしてもらいましたが、9番人気のダノンデサイルを当てることは難しかった。徐々にデータを増やしていって的中率を上げていきたい。