アクセスするたびにAIが画面を作るブログを作った

:::note info
本記事は[みすてむず いず みすきーしすてむず その2 Advent Calendar 2024](https://adventar.org/calendars/10269)の5日目記事です。
:::

# 結論
アクセスのたびにAIへプロンプトを投げて、画面を作ってもらうという動作をするブログを作成しました。
https://blog.shinamonlifelog.uk/

# 概要
メインのフローは図にするとこんな感じです。
```mermaid
sequenceDiagram
    actor User
    participant Server
    participant Static
    participant OldStatic
    participant AI
    
    User->>Server: ページリクエスト
    
    Server->>Static: HTMLファイル確認
    Static-->>Server: HTMLファイル
    Server->>User: HTMLレスポンス
    
    Static-->>OldStatic: ファイル移動
    Server->>AI: 新規HTML生成リクエスト
    AI-->>Static: 生成完了・保存
```
StaticとOldStaticはディレクトリです。
使用しているAIはOpenAIのgpt4o,4o-mini, Geminiの1.5flashです。
また、お金と心に余裕があるときは、ClaudeのSonnetも利用したりしています。

事前準備として、StaticにAIが生成したHTMLをいくつかプールしておいてあります。
アクセスがあるたびにこのプールされている画面から適当に1つ返すと同時にAIに新しい画面生成をリクエストし、生成されたらstaticに追加します。
また、使用されたstatic内のHTMLはOldStaticに移動されます。OldStatic内のHTMLは、Staticが枯渇してなおかつAI生成も(投入資金不足などで)失敗したときに最終手段として使用されます。

# がんばったところ
- 画面をプールするようにした
- AIがHTMLを生成した後で、本文を埋め込むようにした
- バリエーション豊かかついい感じの画面をつくってもらうためのプロンプトを工夫した

これら3点について以下で感想を書きます。

# 画面をプールするようにした
公開時はプールなしの単純な実装で、画面の表示まで20秒くらいかかっておりました。
当初はコレでいいやと思っていましたが、ブログとしての利便性が非常に損なわれており、AIが画面生成している間に、こっちではストレスが生成されるようになってしまったため、プールするように変更しました。
現在はアクセスが集中したり、画面が枯渇していなければ、静的ファイルを配信するだけなので、一瞬で表示できます。

# AIがHTMLを生成した後で、本文を埋め込むようにした
ブログの内容すべてをAIへのプロンプトに含めると、以下のようなデメリットがあります。
- トークン数が増えて、コストが高くなる
- 勝手に本文が要約、変更される
- プロンプト中の指示をAIが忘れて、想定外の回答が返ってくる

これらのデメリットをなるべく回避するために図のような方法で画面を生成するようにしました。
```mermaid
graph LR
    A["template.md"] --> B["AI Request"]
    C["prompt.txt"] --> B
    B --AIが生成--> D["生成されたHTML"]
    E["blog.yml"]
    D --> F["完成したHTML"]
    E --本文埋め込み--> F
```

prompt.txtには、ブログ記事全体共通のプロンプトが記載されています。
template.mdは、ブログ内容が変数の形で埋め込まれたmarkdownファイルになっており、このtemplateとpromptを合体させたものをAIにリクエストとして投げています。
blog.ymlには、変数とそれに対応した文章が記載されており、これを参照しながら、AIから返ってきたHTMLの変数部分を置換することで最終的なHTMLを作っています。

このようにすることで、上で挙げたデメリットをほぼ回避しています。
ただ、たまに変数が勝手に変えられたり、抜け落ちたりする事象も確認しており、これにはもう少し対策が必要だと思っております。(脱落を検出して再度生成リクエストする等)
# バリエーション豊か、かついい感じの画面をつくってもらうためのプロンプトを工夫した
AIに適当に「画面作って」とか「ブログ画面作って」とかでも十分それらしいものはできるのですが、あまりバリエーションがなく、つまらなかったため、プロンプトを工夫することで対策しました。

prompt.txt中の見た目に関わる箇所がこちらです。
```
# Design Requirements
I want you to create a very rich website. Rich animations and CSS.
I want it to have a luxurious look, like it was made by a professional.
You can use both CSS and JavaScript, but I want everything embedded in a single HTML file.
I want a look that is more like a rich corporate website, not limited to the blog style.
I want various components to move, change colors, and interactively operate. It would also be nice if they followed the mouse.
I will list modern techniques, so implement them all:
Card layout
Parallax scrolling
Micro-interactions
Neumorphism
Glassmorphism
Abstract shapes
Gradients
3D design
Vaporwave
Cyberpunk
Claymorphism
Scroll-telling
Neo-Brutalism
```
前半部分に結構具体的な指示をしてしまっている箇所がいくつかあります。これらを取り除きたいとおもっているのですが、「あなたの個性をだして」、「独創的な見た目にして」などの抽象的な指示では、なぜか虹色背景の画面になるだけであまり面白くないかつバリエーションもないので、現状はこのようなプロンプトになってしまっています。

プロンプト後半に、「モダンテクニック列挙するから全部実装しろ」という無茶な要求があります。この列挙部分はChatGPTに作ってもらったのですが、これがかなり効いており、想定外の挙動かつ、見た目にもキレイな画面が生成できるようになりました。当然、列挙した全部が実装されることはなく、適度にサボってくれるので、結果的にランダム性もいい感じに生まれております。

また、ブログとしての利便性のため、こちらでかなり細かい画面要求を行ってる箇所もあります。
```
# Content Requirements
Include a favicon:
<link rel='icon' href='https://drive.blog.shinamonlifelog.uk/favicon.ico' type='image/x-icon'>
Display the blog title: "デンシナブログ!!!"
Display a self-introduction somewhere on the page. The content of the self-introduction will be provided later.
Chose Emoji then use it in site notice.
Display a site notice: "このサイトは雰囲気で毎回生成されます。ぶっ壊れてたらリロードしてみてね。" 改行 "今回のデザイン担当者は、{Emoji here} $creator"
Display the article content. The content will be provided later.

Create a pagination feature. You can navigate to pages by accessing /page_index, for example, /0 for the first page.
At least show links to the previous and next pages, if they exist. If not, don't show them.
Access method for links: /0, /1, etc. (A wrong example would be /paget_index/0).
INFO page_index:{} and the maximum page number max_page:{}.
```
faviconの設定、ブログタイトルや自己紹介、サイト注意事項、ページネーション機能などは、細かく注文をつけています。当然壊れていたりすることもよくありますが、おおよそ満足の行くクオリティに仕上がっております。

# 課題と今後
現状、OldStaticがほぼ使われない状態になっております。少々もったいないので、なにか面白いことに利用できないかなと思っていますが、私の脳のパラメータ数が足りずうまくいってません。
また、現状の実装では、HTMLは1枚使用するたびに1枚あるいは0枚生成されます。AIが文章生成に失敗したり(資金不足など)、想定していない形式で生成する確率があるためです。するとプール内のHTMLは少しずつ減っていってしまいます。これを回避する機能を実装したいのですが、これも私のパラメータ数が足りず未実装です。