🎍

「あけおめ!」を大切にできるサイトを作ったよ。

に公開

こんにちは。

元エンジニアの正訓(まさくに)です。ジョブチェンジしてここ2年くらいはは事業のことを見るようになりました。ので、実は何やかんや事業のことやマネジメントのことを話した方が見てもらえるのかもなぁーとうっすら感じたりするんですが、アドベントカレンダーは作ったものを書きたい、と緩い自分ルールがあるので、いったんそっちは無視します。

とは言っても便利な時代になりましたね。ZennでもQiitaでも、「コーディングのチップス」みたいなのはみるみる減って、だいたいがAIの話に塗り替えられました。いままさに時代が変わろうとしていて、実際、今回作ったものもほぼだいたいChatGPTが書いてくれたものなので、自分で作ったというか否かは微妙です。

そういうわけで、コーディング上のこの課題はどうした、みたいな話題は僕のなかでも社会的にもきっと減っていて、なぜ、何を作って、どこに困った、みたいな話になると思います。

あけおめを誰かに伝えるということ

最近、「あけおめ」減ってない? (突然の不人気)

いやー、今年始まったときに「あけましておめでとう」LINEとかでもらったの10に満たない気がする。いやいや、いいんですよ、別にいらないんですよ。でもなんか減ってない? 自分だけというよりは社会的に減ってない? 違うの? そうなの。

いまのスケーラブルな時代だとあんましピンと来ない方もいらっしゃるかもしれませんが、古き良き時代、「あけおめメール」みたいなものがあったころにスケールできないメールサーバーのメンテもしなければならないという地獄の役割を持っていたころがあって、1月1日0時、ターミナルの前で祈るみたいな仕事をやってたこともありました。(NEの話じゃないですよ)

そのころから比べると社会的にもなんか、元旦の「特別さ」が薄れた気がするんですが、それは僕が歳取っただけなんでしょうか。まぁとにかく僕の「あけおめ」は毎年如実に減っていって、少し反省したわけです。

ああ、そもそも「あけおめ」を自分が大切にできてないんじゃないかと。なんで待ちの姿勢なのかと。確かに久々に話したひととコミュニケーションができるタイミングなのに「あけおめ」以外何もしてないな、と思ったのでした。

それで作りました。

「あけおめまつり」でできること

前提

  • 静的サイトジェネレータ(Zola)とCloudflareで実現している
  • すべてフロントエンドだけで構築したので動的なデータ保持、生成はできないししない

ユーザーはこのような画像が手に入る

「あけおめ」を言われた方は、このような画像がダウンロードできるようになっています。背景画像は受け手の任意の画像が選べます。なので送信者と、受信者がこのような画像をさらにやり取りすることで、一言の「あけおめ」から始まり、お互いに嬉しい何かを得ることを考えました。

送り手は年始の挨拶を組み合わせて作成できる

上記のページで「あけおめ」の背景画像や、挨拶や本文を選ぶことができ、URLを発行することができます。そのURLを誰かに渡すことで、年始の挨拶を受け手が伝えることができる、が送り手のユーザーのアクションです。

例えば下記のようなURLです。

https://ake-o.me/matsuri/0001/?b=0717&c=0008&i=0003

現状、背景画像が20種、始めの挨拶が20種、本文が1,500種、締めの挨拶が20種、ということで、掛け合わせて 12,000,000パターン を選ぶことができます。どうぞ存分に。

受け手はもらったメッセージをカスタマイズできる

このURLが送られてくると、受け手はそれを開き、リプライとして自分の「言葉を書き込む」ことができます。これで送り手と受け手のコミュニケーションを残すことができると考えました。しかし当然このリプライに関してはサーバー側に残っていません。

また「背景を変更する」ことができ、デフォルトの背景画像から、自分の写真を設定することができます。この画像もサーバー側に残っていません。

ので、それらの要素を「画像ダウンロード」でダウンロードできるようにしました。昨年の思い出の画像を選び、リプライを書き、相手に返す、というループが生まれると素敵ですね。

「あけおめまつり」で考えたこと

  • 自分の「あけおめ」の課題感を緩和できること
    • 「あけおめが少ない」という僕の情けなさじゃなくて、「あけおめから生まれるコミュニケーションを多くしたい」のほうね。
  • 全般的にAIで開発をすること
    • そもそも年末まで時間はないわ、もうコーディングを仕事でしてないわで、AIを全面的に頼ることが確定的でした。
    • たまたまいまプライベートでパッと使えるのがCodexだったのでCodexにコーディングしてもらいました。
  • セキュリティの懸念を減らすこと
    • 頑張れば裏側でデータ持ってどうこう、もできたのかもしれませんが、今回はサーバーを持たず、そちら側のセキュリティ懸念を捨てたかったのでスパッとフロントエンドだけで何ができるかを考えました。
    • なのでできるだけ「個人がコンテンツを作る」じゃなく、選べるようにする、という方向に倒しています。
  • バリエーション豊かで誰かが使えると思ってくれること
    • とは言っても、選べる要素は増やしたいと思って、種類を増やすことにしました。文章もそうですし、背景画像とかOGPとかもそうです。作ったのはAIですが。
  • それなりに大規模なアクセスに耐えうること
    • もしかしたら中高生にバカウケするかもしれないので、一定のリクエスト負荷に耐えられて、かつ無料って考えて、Cloudflare Pagesの選択になりました。
  • 押し付けがましくないコミュニケーションにすること
    • 「返事をくれ」とう「あけおめ」を出せるサービスを作ってしまったら本末転倒なので、送り手受け手のどちらにも押し付けがましくならないように考えています。
    • 例えば名前で「あけおめシェア」などを初手は考えたのですが、この数年で「シェア」は何だかんだ押し付けがましい言葉になりました。(個人の感想)ので、「0時にあけおめいっぱい」のことを考えて「あけおめまつり」にしました。
  • 実はずいぶん前から持っていたドメインを使うこと
    • ake-o.me を10年以上前になんか買っていて、こういう機を狙っていました。

「あけおめまつり」の開発ハイライト

1,500個のJSONをまとめて作り出す

static/assets/contents/ja-JP/bodyのjsonが0001から1500になるように変更、追加をしてください。

* contentの文字数はそれぞれ50文字付近とし、原則として1文のみとする。
* 5歳〜75歳の、あらゆる年賀状を送り合う関係性(例えば親と子、恋人同士、先生と生徒、他にもあらゆる相互の関係性)を創造し、それぞれがお互いに温かな心でメッセージを送るというスタンスのcontentにし、語尾や文体や敬語もそれに一致するようにすること。
* 新年の挨拶(あけましておめでとう)などは含めず、また、その関係性を文章に入れる必要はない。(◯◯から✗✗へ、みたいなのは不要)
* 秘密情報に類するものは禁止とし、固有名詞や、商標登録されているものを極力排し、しかし、2025年の社会的な流行なども加味し、それぞれのこの1年を親しい人に伝えるリアルな文章を作成する。
* 重複する体験を作成するのは禁止、またパターンをスクリプトなどで機械的に増やさず、すべて生成AIが文章を構築する。
* 関係性と一年の出来事(例えば一緒にどこかに行ったこと、感じたこと、など)を絡めてリアルなメッセージになるように留意するが、多くの状況で使用できるように一定抽象度は高くすること。
  * OK:去年のライブ最高すぎたのでまた今年も行こう!
  * NG:去年の9月に二人で行ったライブの3曲目最高すぎたのでまた今年も行こう!

どーーーーん!!!

正直これがやりたかったからやったのかもしれません。Fakerでもできない、意味が通る1,500個のファイルを一括で作成するバカをAIにやらせました。本当はもっと作りたかったのですが、Cloudflareのファイル数上限に引っかかるのでやめました。

ただ、これだけのファイルを作ろうとするとかなりChatGPTも手を抜こうとして、Pythonを書いてパターンで対応しようとしてきます。また、自分で考えているうちにそちらの話題方面でしかアイディアが出なくなるようで、本当はもっと複数回にバラして実行した方が良いものが生まれるのかもしれません。

ZolaがCloudflare Pagesで動かない

Zola はRust製の静的サイトジェネレータです。マークダウンでコンテンツ作りができて、なんとなく他でもこれを使っているので今回も採用しました。

他ではNetlifyと組み合わせて使っていたので今回のCloudflare+Zolaは初めてのことでした。しかし、なぜかCloudflareでビルドができません。

https://www.getzola.org/documentation/deployment/cloudflare-pages/

https://developers.cloudflare.com/pages/framework-guides/deploy-a-zola-site/

両社ともに公式対応を謳っているのに、実際にGithubと組み合わせてデプロイしようとすると以下の2種のエラーなどが発生します。

/bin/sh: 1: zola: not found
zola: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.33' not found (required by zola)

ビルドシステムのバージョンを上げればzolaが見つからないといい、公式的に対応していはずのビルドシステムの1を使えばGLIBCがないという……。なんでや、というところで下記の方法を発見して、ビルドシステムは最新のものにし、ビルドコマンドでzolaをインストールしたらうまくいきました。

https://www.getzola.org/documentation/deployment/cloudflare-pages/#or-use-unstable-pre-build-environment-variable-asdf

asdf plugin add zola https://github.com/salasrod/asdf-zola && asdf install zola 0.21.0 && asdf global zola 0.21.0 && zola build

まぁ全然スマートじゃないスよね……。

20枚の背景画像の作り方

id category prompt
1 元気 【元気】年賀状の背景画像に使える明るいビビッドカラーの抽象的な光の軌跡が中央に集まり、年始の活力を象徴する縦長構図。中心に要素が寄るようにぼかしと光彩を調整したダイナミックな背景。文字は一文字も使わない。
2 元気 【元気】年賀状の背景画像に使えるポップな幾何学模様(円・三角・星)が中央に寄るように配置された縦長デザイン。鮮やかな赤・黄・青を用い、祝いの高揚感を表現した背景。文字は一文字も使わない。
3 元気 【元気】年賀状の背景画像に使える中央に向かって放射状に広がるカラフルなストロークのアニメ調エネルギー背景。縦長で、勢いや新年のスタートを象徴する構成。文字は一文字も使わない。
4 元気 【元気】年賀状の背景画像に使える来年の干支・午をモチーフにした、跳ねる馬のシルエットが中央に大きく配置された縦長構図。背景は明るく躍動的な色のグラデーションで爽快感を演出。文字は一文字も使わない。
5 可愛い 【可愛い】年賀状の背景画像に使える淡いパステルカラーの丸い模様が中央に寄るように散りばめられた縦長構図。柔らかく温かみのある雰囲気の年賀背景。文字は一文字も使わない。
6 可愛い 【可愛い】年賀状の背景画像に使える手描き風の動物キャラクターの足跡が中央に向かうように配置された縦長デザイン。シンプルで愛らしさを感じる背景。文字は一文字も使わない。

・・・・・・こういうのをプロンプトで作ってからGeminiで作りました。画像生成はChatGPTと比べちゃうと圧倒的にGeminiの方が上でした。なので、右下にウォーターマークが入っています。バナナすげーよバナナ。

URL生成ページはワンターンで作れた

url生成ページを作ります。

* 「画像ダウンロード」チップスの下に「新しく作る」チップスを追加。
* 「新しく作る」ページを /matsuri/new/ として作成する。
*  /matsuri/new/ ではロゴが/matsuri/0001/と同位置に存在し、にreadonlyのテキストボックス(URLテキストボックス)が存在し、その下に下記のセクションがアコーディオンで閉じた状態で表示されている。
  - 背景画像を選ぶ
  - 新年の挨拶を選ぶ
  - 新年の本文を選ぶ
  - 新年の締めを選ぶ
* 背景画像を選ぶをクリックするとアコーディオンが表示され、背景画像がサムネイルサイズで横にスクロールできるようになっていて、クリックで選ぶことができる。
  - そのクリックした画像のidが追加された状態で 上部のURLテキストボックスにクエリが追加される(i={選ばれた背景画像番号})
  - なにかを選んだり、セクション外をクリック(タップ)タップしたらアコーディオンを閉じる
* 新年の挨拶を選ぶ、新年の本文を選ぶ、新年の締めを選ぶをそれぞれ選んだら、アコーディオンが開き、その中にはテキストボックス(フィルタリングテキストボックス)と、それぞれの要素で選べるものがリストで表示される。
  - 選べる要素は下記のjsonにそれぞれ入っているので、アコーディオンが開くときに非同期で取得して描画する
    - /assets/contents/ja-JP/opening/aoo-all.json
    - /assets/contents/ja-JP/body/aob-all.json
    - assets/contents/ja-JP/closing/aoc-all.json
  - フィルタリングテキストボックスにユーザーが文字列を入力したら、json内の文字列と部分比較し、一覧には一致するものだけ残す。
  - 選ばれた文字のキーをURLのパス、クエリとして生成してURLテキストボックスに追加する。
  - なにかを選んだり、セクション外をクリック(タップ)タップしたらアコーディオンを閉じる

さすがにこのプロンプトが一発で概ね通ったのは感動を覚えました。まぁその前段として1,500種のjsonを結合しておく、などは別のシェルスクリプトで実施したのですが。

「あけおめまつり」の課題とTODO

言葉の抽象度がよくない

1,500種を作ったわけですが、そのほとんどは使えないでしょう。無駄に具体的だったり、ほぼ繰り返しだったりするからです。どうもAIもその辺は手を抜くようで、ここはちゃんと書き直すべきだと思っています。

ダウンロードできる画像が暗い

一番の推しポイントである画像のダウンロードで、ダウンロードできる画像が実際のWeb画面より暗いです。これは優先度高く直したいと思っています。

送り手の言葉を入力させるか否か

ここが実はいま一番迷っているポイントで、受け手の言葉は受け手が画像にすることで画像にすることができるのですが、送り手の言葉も1,500種からチョイスするだけじゃなくて、直に書きたいもんだよなぁ、と思っています。のでクエリパラメータに文字を入れることで自分の言葉で作り込むことができるようになるでしょう。

懸念はユーザーの使い方の問題で、自由記入をURLに含めてしまって良いのか否か、です。原理的にユーザー作成のコンテンツがここだけ通信に載るようになるためです。うーん、どーしよっかなー。

まとめに代えて

エージェント型の生成AIは1ターン目が最も賢く、「そうじゃないそうじゃない」と2ターン目以降はもっとチンプンカンプンになっていきます。そうなるとタスクをどれだけ細かく区切れるかを考えるようになって、1ターンでどれだけ正確にゴールに近づけるかを考えるようになりました。

また具体のコードはレビューするにせよ、その詳細は任せた方が早いので、詳細仕様は任せるようになって、どうすればゴールなのか、どういう使い方をしたいのか、を伝えるようになりました。

さらに途中のものを何度かに分けてゴールを目指すよりも、1ターン1ターンで価値を作ってもらって、その価値をふくらませた方が文脈が多くなるので、賢く動いてくれるように思います。

――アジャイルやこれ!

というのが最近生成AIを使ってて、とみに思うことです。おそらくここからは一人がアジャイルのチームを指揮する感覚でエピック単位でサービス改善が高速に動いていく、と思っています。

そんな時代ではエンジニアの仕事はどうなるのでしょうか。そのころは「エンジニア」という呼ばれ方をするのでしょうか。仕事がなくなる、とまことしやかに言われてますが、それは望むところです。ただ、なくならないでしょう。


年末にかけてまだまだ追加、調整していきますが、使ってみてね、あけおめまつり。「あけおめ」を来年は大切にしよう。おしまい。本年も大変お世話になりました。残りも踏ん張っていくぞ。

NE株式会社の開発ブログ

Discussion