青春Gemini Pro野郎はChatGPTの夢を見ない ~嘘だらけのメディアFiction Timesの技術紹介~

この記事は[Firebase Advent Calendar 2023](https://qiita.com/advent-calendar/2023/firebase) 25日目の記事です。

こんにちは。[virapture株式会社](https://virapture.com)でCEOしながら[ラグナロク株式会社](https://ragna-rock.com)でもCKOとして働いている[@mogmet](https://twitter.com/mogmet)です。
![mogmet.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42234/7c8dd069-907b-20aa-7030-79851f8cdb68.jpeg)

先程クソアプリの紹介記事を書きました。

https://qiita.com/mogmet/items/411787f3454eb85e60e5

今回の画像はそこで使われた反対側の写真のものになります。2記事で1つてきなノリです。

こちらの記事では、技術の力で日常にユーモアを注入するアプリ[Fiction Times](https://fiction-times.virapture.com/)の開発秘話をご紹介します。

https://fiction-times.virapture.com/

Flutter、Firebase、そしてGemini ProというAIまで、最新技術を駆使して構築されたこのプロジェクトは、ただのクソアプリとは一線を画します。
（そんなことはないか）

## 作ったきっかけ

速攻でGeminiProのアプリを作った方のこんなつぶやきを見受けました。

https://x.com/_serinuntius/status/1735272017145085985?s=20

それなのであればGeminiProちゃんにはハルシネーション（AIが事実に基づかない情報を生成する現象）という名の夢を見てもらおうということで嘘だらけのメディアをGemini Proに作ってもらうことにしました。
（タイトル回収）

## 構成

このサービスは下記のような構成となっております。

- Frontend: Flutter web
- Backend: CloudFunctions
- Database: firestore
- Hosting: firebase hosting
- AI: gemeni pro

![architecture.001.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42234/08d549ad-4151-6af3-ffba-27a6f1dfe329.jpeg)


これらの構成についてそれぞれ紹介させていただきます。

## Frontend: Flutter Web

まずは、ユーザーインターフェースです。
フロントエンドにはFlutter Webを採用しました。

なぜか？

それは、一貫したユーザーエクスペリエンスをモバイルとWebの両方で提供できるからです。
Hot Reloadが支える迅速な開発サイクルで、ユーザーがまさに求めるユーモア溢れるインタラクションを瞬時に形にしました。

ちなみにUIの殆どはChat GPTに考えてもらったのであまり作り込んでないです。

![CleanShot 2023-12-25 at 11.03.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42234/d2a6b7f1-fd49-fd49-3c72-9e21513ab591.png)

## Backend: Cloud Functions

バックエンドでは、FirebaseのCloud Functionsを活用しています。
サーバレスアーキテクチャを採用することで、インフラ管理の負担を軽減し、開発者がビジネスロジックに集中できる環境を実現しました。
今回のケースでは、タレコミによるとarticleが生成され、それをトリガーとして、Cloud Functionsが起動してGeminiProにニュースを考えてもらい、articleを更新することで記事を生成しています。

実装をするためには、先にAPI Keyの取得をします。

https://makersuite.google.com/app/apikey

Gemini Proへの通信はライブラリを使うと簡単に実装することができます。

```ts
    const ai = new GoogleGenerativeAI(GEMINI_API_KEY)
    const model = ai.getGenerativeModel({model: 'gemini-pro'})
    // $sourceにはユーザからのタレコミ情報が入る
    const prompt = `あなたは情報提供者から寄せられた情報をもとにユーモラスな記事を書き上げる編集長です。ユーザからの情報は文字数が少ないこともありますが、その情報を元に勝手に想像を膨らませて800文字前後の空想の記事を書いてください。レスポンスはJSON形式で、titleとbodyの要素を元に返却してください。下記が提供情報です。
\`\`\`
${source}
\`\`\`
`
    const response = await model.generateContentStream(prompt)
    let responseText = ''
    for await (const chunk of response.stream) {
      responseText += chunk.text() // JSONで値がはいってくる
    }
```



ただ、markdownと、改行入りのレスポンスが返ってくるのでJSONになるように整形の必要がありました。

```ts
function createJson(responseText: string) {
  const jsonMatch = responseText.match(/{[\s\S]*}/s)
  if (!jsonMatch) {
    logger.error(text)
    throw new Error('Invalid JSON response')
  }
  try {
    // ""内の改行を\nに変換する
    const data = jsonMatch[0].replace(/"([^"]*)"/g, (match) => {
      return match.replace(/\n/g, '\\n')
    })
    return JSON.parse(data)
  } catch (e) {
    logger.error(jsonMatch[0])
    throw e
  }
}
```

## Database: Firestore

データベースにはFirestoreを選択。

リアルタイムでのデータ同期と、強力なクエリ言語により、非同期的なデータフローをスムーズに処理。
Firestoreのスケーラブルな性質は、Fiction Timesが急成長するユーザーベースに対応する上で欠かせません。

Firestoreの大きな特徴の一つであるListenerの機能はCloud Functionsによるarticleの上書きを待機する為に使用しています。
これにより、ネタ投稿後、記事更新を待機して記事詳細ページに遷移するような処理を実現しています。

```article_repository.dart
  Future<Article> createArticle({
    required String reporter,
    required String source,
  }) async {
    final article = Article(
      reporter: reporter,
      source: source,
      body: '',
      title: '',
      createdAt: const FirestoreTimestamp.serverTimestamp(),
      updatedAt: const FirestoreTimestamp.serverTimestamp(),
    );
    final reference = getCollectionReference().doc();
    await reference.set(article);
    final completer = Completer<Article>();
    StreamSubscription<DocumentSnapshot>? subscription;
    subscription = reference.snapshots().listen(
      (snapshot) {
        final updatedArticle = snapshot.data();
        if (updatedArticle == null || snapshot.metadata.hasPendingWrites) {
          return;
        }
        if ((updatedArticle.error.isNotEmpty) ||
            (updatedArticle.body.isNotEmpty &&
                updatedArticle.title.isNotEmpty)) {
          // エラーがあるか、updateされたら処理を終える
          completer.complete(updatedArticle);
          subscription?.cancel();
        }
      },
      onError: completer.completeError,
    );
    // Return the future that completes when the article is updated
    return completer.future;
  }
```

## Hosting: Firebase Hosting

静的ファイルと動的コンテンツのホスティングにはFirebase Hostingを使用。
SSL暗号化によるセキュアな配信はもちろんのこと、CDN経由での高速なローディングを実現しています。
開発からデプロイメントまで、Firebaseのエコシステム内で完結することが、生産性を大きく向上させました。

`flutter build web`を実施すると`build/web`ディレクトリが生成されるのでそれをhostingにデプロイするように設定しています。

```firebase.json
...
  "hosting": {
    "public": "apps/app/build/web",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "rewrites": [
      {
        "source": "**",
        "destination": "/index.html"
      }
    ]
...
```

## AI: Gemini Pro

そして、Fiction Timesの核となるのがAI技術、ここではGemini Proを採用しています。
このAIは、与えられたプロンプトから創造的でユーモアあふれるコンテンツ(という名の夢)を生成します。
ユーザーからの入力を受けて、即座に架空のニュース記事を作り出す能力は、このアプリの最大の売りです。
ChatGPTとは違い、きっとたくさんの夢（ハルシネーション）を見てくれることでしょう。

実装はCloud Functionsの章で紹介しましたが、API経由でレスポンスを取得することも可能です。

```sh
curl \
  -H 'Content-Type: application/json' \
  -d '{"contents":[{"parts":[{"text":"Write a story about a magic backpack"}]}]}' \
  -X POST https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent?key=YOUR_API_KEY

```

## まとめ

技術は日々進化していますが、それを使って何を生み出すかは、我々開発者次第です。Fiction Timesでは、これらの先端技術を組み合わせることで、日常に新たな笑いと発見を提供しています。
技術の真価は、それを通じて人々の生活にどれだけプラスの影響を与えられるかにあります。
Fiction Timesの場合、それはユーザーの日常にユーモアの一瞬をもたらすことでした。

技術を通じて笑顔を作り出す、そんな未来を、皆さんも一緒に創り上げませんか？


今回のソースについては下記にアップしているのでよかったらご参考ください。

https://virapture.com/fiction-times-architecture/


最後に、スノボの楽しく滑るコツを掴める [cotsume](https://cotsume.page.link/blog) というアプリを作ってます！よかったらDLしてみてね！

https://cotsume.page.link/blog

また、[ワンナイト人狼オンライン](https://online.1nite-jinro.com/link/blog)というゲームを作ってます！よかったら遊んでね！

https://online.1nite-jinro.com/link/blog

他にも[Camecon](https://camecon.me/)や[Offcha](https://offcha.com/)、問い合わせ対応が簡単にできる[CSmart](https://csmart.jp/lp)、フリーランスのコミュニティの[NextFreelance](https://next-freelance.net/)といったサービスも作ってるのでよかったら使ってね！

https://camecon.me/

https://offcha.com/

https://csmart.jp/lp

https://next-freelance.net/

また、チームビルディングや技術顧問、Firebaseの設計やアドバイスといったお話も受け付けてますので御用の方は弊社までお問い合わせください。

https://virapture.com/

https://menta.work/user/20565

ラグナロクでもエンジニアやデザイナーのメンバーを募集しています！！楽しくぶち上げたい人はぜひお話ししましょう！！

https://ragna-rock.com