Zenn
✍️

Gemini APIを使ってXの投稿を評価・添削するアプリを作ってみた!

2025/03/28に公開
1

はじめに

今回は、Gemini APIを使ってSNSの投稿を改善するシンプルなアプリを作りながら、APIの特徴や使い方について紹介していきます。

AIを使った個人開発は初めてなので間違ってる情報などもあるかもしれないです。その際はコメントくださると嬉しいです。

Gemini APIとは

Gemini APIは、Googleが提供する生成AIのAPIです。

  • 無料枠が比較的大きい(月$10相当)
  • レスポンスが高速
  • マルチモーダル(テキスト、画像、音声など)に対応
  • PaLM 2から大幅に性能向上

GeminiAPIを使ってアプリを作ってみた

Post PolishというXの投稿を改善するアプリを作ってみました。

https://github.com/884js/x-post-reviewer

https://x-post-reviewer.pages.dev/

バズってる投稿を試しに投げてみましたが、「意見表明」が圧倒的に多く、意外と気付きがありました。

主な機能

  • 投稿文の投稿推奨度を判定「絶対に投稿すべき」「投稿してもよい」「どちらとも言えない」「投稿は控えたほうがよい」「投稿すべきでない」
  • 投稿文の投稿タイプを判定「独り言」「問いかけ」「意見表明」「情報共有」
  • 投稿文の改善提案
  • 文体の調整

構成

  • npm
  • Next.js
  • tailwindcss
  • Cloudflare Pages
    • デプロイ先
  • Gemini
    • gemini-2.0-flash

セットアップ

  1. APIキーの取得
    こちらにアクセスしてAPIキーを取得します。
    https://aistudio.google.com/apikey

  2. @google/generative-aiをインストール

$ npm install @google/generative-ai

GeminiAPIをつかってみる

Next.jsでGeminiAPIを実行します。

今回は文章の評価、校正するアプリなので、画像入力、ストリーミング出力、マルチターンの会話は取り扱いません。

api routes or Server Actionsを作成

apiキーはクライアント側に公開されてはダメな情報なので、サーバーサイドで実行します。
また、Server Actionsはストリーミング出力に対応していないため、api routes上に構築したほうが良よさそうです。
https://ai.google.dev/gemini-api/docs/text-generation?hl=ja#streaming-output

エンドポイントを実装します。
src/app/api/generateContent/route.ts

import { GenerationConfig, GoogleGenerativeAI, Schema, SchemaType } from '@google/generative-ai';
import { POST_TYPE, POST_RECOMMENDATION } from '@/constants/postNuance';

export const runtime = 'edge';

export async function POST(request: Request) {
  const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || '');
  const { prompt } = await request.json()

  const generationConfig = {
    responseMimeType: "application/json",
    responseSchema: {
      type: SchemaType.OBJECT,
      properties: {
        post_recommendation: {
          type: SchemaType.STRING,
          format: "enum",
          enum: [
            POST_RECOMMENDATION.HIGHLY_RECOMMENDED,
            POST_RECOMMENDATION.RECOMMENDED,
            POST_RECOMMENDATION.NEUTRAL,
            POST_RECOMMENDATION.NOT_RECOMMENDED,
            POST_RECOMMENDATION.STRONGLY_DISCOURAGED
          ],
        },
        reason: {
          type: SchemaType.STRING,
        },
        usefulness_score: {
          type: SchemaType.NUMBER,
        },
        improvement_suggestions: {
          type: SchemaType.ARRAY,
          items: { 
            type: SchemaType.OBJECT,
            properties: {
              text: { type: SchemaType.STRING },
              improvements: { type: SchemaType.STRING }
            },
            required: ["text", "improvements"]
          },
        },
        post_type: {
          type: SchemaType.STRING,
          format: "enum",
          enum: [POST_TYPE.TALK_TO_ONESELF, POST_TYPE.QUESTION, POST_TYPE.OPINION, POST_TYPE.INFORMATION],
        }
      },
      required: ["post_recommendation", "reason", "usefulness_score", "improvement_suggestions", "post_type"]
    } satisfies Schema
  } satisfies GenerationConfig;

  const model = genAI.getGenerativeModel({ model: 'gemini-2.0-flash', generationConfig })

  const result = await model.generateContent({
    contents: [
      {
        role: 'user',
        parts: [
          {
            text: prompt
          }
        ]
      }
    ],
    systemInstruction: {
      role: 'model',
      parts: [
        {
          text: 'あなたはX(旧Twitter)投稿価値や有益性を判断し、改善案を提供するAIです。すべての応答は日本語でのみ行ってください。',
        },
    }
  })

  const responseText = result.response.text();
  return new Response(responseText, {
    headers: {
      'Content-Type': 'application/json',
    },
  });
}

ポイント

generationConfigでAIが返すレスポンスにルールを設ける

今回はAIの回答をそのままレスポンスとして扱いたいので、responseSchemaを定義することで、返り値のフォーマットを強制しています。

OpenAPIに似た形式で定義できるし、enumなども使用できて、とても便利だと思いました。

systemInstructionを使って、コンテキストを与える

以下のような設定を渡して、AIの役割を明示にしています。これを行うことでユーザーのプロンプトに書かずにすみます。

あなたはX(旧Twitter)投稿価値や有益性を判断し、改善案を提供するAIです。すべての応答は日本語でのみ行ってください。

本当はもっと指示を与えてますが、割愛します。

最後に

Gemini APIは無料枠も多く、SDKもスムーズに使えて、体験が良かったです。

これからもAI周りは少しずつキャッチアップしていきたいと思います。

ここまで読んでいただきありがとうございました!

参考リンク

1

Discussion

ログインするとコメントできます