はじめに
こんにちは。ホネグミ代表、エンジニア×マーケターのなおきちです。
今回は、AIを使って「小泉構文」を生成するWebアプリを作ったら、予想以上にバズって2ヶ月で13,000回も使われた話をします。
本記事について
今回の診断テスト開発およびこの記事執筆は、Claude(Anthropic社のAI)との協業で進めました。AI時代の新しい開発スタイルの実践例として参考になれば幸いです。
この記事で分かること
- Gemini APIを使ったテキスト生成の実装方法
- 無料枠を最大限活用するフォールバック機能の作り方
- WordPressでのAjax実装とカウンター機能
- 個人開発サービスを1万回使ってもらうための工夫
この記事の対象者
- API連携に興味があるエンジニア
- 個人でWebサービスを作りたい人
- Gemini APIの実装例を見たい人
動作環境・使用技術
- フロントエンド: HTML5, CSS3, JavaScript (ES6)
- API: Google Gemini API (gemini-2.0-flash-lite, gemini-2.0-flash, gemini-1.5-flash)
- バックエンド: WordPress (PHP 8.2)
- Ajax通信: WordPressの標準Ajax機能
自己紹介
ホネグミ代表、応用情報技術者の資格を持つエンジニア×マーケターです。これまでIT系の会社役員を4年、独立して4年目になります。クライアントワークでは「こうしたい」を技術で形にすることを専門としていますが、最近は個人開発でAIを「無駄に」使った社会風刺的なサービスも作っています。
完成品・デモ
「お腹が空いた」と入力すると...
→「お腹が空いているということは、つまり空腹だということなんです。これは意外に知られていない事実ですが、空腹の時というのは、お腹が空いている状態なんですね」
といった具合に、小泉進次郎氏特有のトートロジー(同語反復)を生成します。
技術構成
使用したAPI・ライブラリ
- Google Gemini API: メインのテキスト生成
- WordPress Ajax: サーバーサイド処理
- Font Awesome: アイコン表示
アーキテクチャ概要
フロントエンド (JavaScript)
↓ Ajax
WordPressバックエンド (PHP)
↓ HTTP Request
Google Gemini API
↓ Response
WordPressバックエンド
↓ JSON Response
フロントエンド (結果表示)
実装手順
1. Gemini API の準備
まず、Google AI Studioでプロジェクトを作成し、APIキーを取得します。
// wp-config.php での API設定
define('GEMINI_API_KEY', 'your_api_key_here');
2. フォールバック機能付きAPI呼び出し
無料枠の制限に対応するため、複数のモデルでフォールバック機能を実装しました。
function call_gemini_api_with_fallback($prompt) {
$models = [
'gemini-2.0-flash-lite',
'gemini-2.0-flash',
'gemini-1.5-flash'
];
foreach ($models as $model) {
$result = call_gemini_api($prompt, $model);
if ($result['success']) {
return $result;
}
// ログ出力(デバッグ用)
error_log("Model {$model} failed, trying next...");
}
return ['success' => false, 'error' => 'All models failed'];
}
function call_gemini_api($prompt, $model) {
$api_key = GEMINI_API_KEY;
$url = "https://generativelanguage.googleapis.com/v1beta/models/{$model}:generateContent?key={$api_key}";
$data = [
'contents' => [
[
'parts' => [
['text' => $prompt]
]
]
]
];
$options = [
'http' => [
'header' => "Content-type: application/json\r\n",
'method' => 'POST',
'content' => json_encode($data)
]
];
$context = stream_context_create($options);
$response = file_get_contents($url, false, $context);
if ($response === FALSE) {
return ['success' => false, 'error' => 'HTTP request failed'];
}
$json_response = json_decode($response, true);
if (isset($json_response['candidates'][0]['content']['parts'][0]['text'])) {
return [
'success' => true,
'text' => $json_response['candidates'][0]['content']['parts'][0]['text']
];
}
return ['success' => false, 'error' => 'Invalid response format'];
}
3. WordPressでのAjax実装
// Ajax ハンドラーの登録
add_action('wp_ajax_koizumi_generate', 'handle_koizumi_generate');
add_action('wp_ajax_nopriv_koizumi_generate', 'handle_koizumi_generate');
function handle_koizumi_generate() {
// セキュリティチェック(必要に応じてnonceを追加)
if (!isset($_POST['text']) || empty(trim($_POST['text']))) {
wp_send_json_error(['message' => '入力テキストが空です']);
return;
}
$input_text = sanitize_text_field($_POST['text']);
// プロンプト作成(詳細は企業秘密!)
$prompt = create_koizumi_prompt($input_text);
// API呼び出し
$result = call_gemini_api_with_fallback($prompt);
if ($result['success']) {
wp_send_json_success(['koizumi_text' => $result['text']]);
} else {
wp_send_json_error(['message' => 'API呼び出しに失敗しました']);
}
}
function create_koizumi_prompt($input_text) {
// プロンプトの詳細は企業秘密ですが、
// 基本的には小泉構文の特徴を分析して
// 適切な変換ルールを指示しています
return generate_prompt_for_koizumi_style($input_text);
}
4. フロントエンド実装
document.addEventListener('DOMContentLoaded', function() {
const generateBtn = document.getElementById('generate-koizumi-btn');
const inputText = document.getElementById('input-text');
const resultSection = document.querySelector('.result-section');
const loadingSection = document.querySelector('.loading');
const koizumiText = document.getElementById('koizumi-text');
generateBtn.addEventListener('click', generateKoizumi);
async function generateKoizumi() {
const text = inputText.value.trim();
if (!text) {
alert('変換したい文章を入力してください。');
return;
}
// ローディング状態に切り替え
toggleLoadingState(true);
try {
const response = await fetch('/wp-admin/admin-ajax.php', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `action=koizumi_generate&text=${encodeURIComponent(text)}`
});
const result = await response.json();
if (!result.success) {
throw new Error(result.data?.message || 'APIエラーが発生しました');
}
// 結果表示
displayResult(result.data.koizumi_text);
updateUsageCounter();
} catch (error) {
console.error('生成エラー:', error);
showErrorMessage(error.message);
} finally {
toggleLoadingState(false);
}
}
function toggleLoadingState(isLoading) {
if (isLoading) {
resultSection.style.display = 'none';
loadingSection.style.display = 'block';
} else {
loadingSection.style.display = 'none';
}
}
function displayResult(koizumiResult) {
koizumiText.textContent = koizumiResult;
resultSection.style.display = 'block';
setupShareButtons(koizumiResult);
}
});
5. 利用回数カウンター実装
// カウンター用テーブル作成
function create_koizumi_counter_table() {
global $wpdb;
$table_name = $wpdb->prefix . 'koizumi_counter';
$charset_collate = $wpdb->get_charset_collate();
$sql = "CREATE TABLE $table_name (
id mediumint(9) NOT NULL AUTO_INCREMENT,
count_value bigint(20) DEFAULT 0,
last_updated datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id)
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql);
// 初期値設定
$wpdb->insert($table_name, ['count_value' => 0]);
}
// カウンター更新
add_action('wp_ajax_koizumi_counter', 'update_koizumi_counter');
add_action('wp_ajax_nopriv_koizumi_counter', 'update_koizumi_counter');
function update_koizumi_counter() {
global $wpdb;
$table_name = $wpdb->prefix . 'koizumi_counter';
// カウンターを1増加
$wpdb->query("UPDATE $table_name SET count_value = count_value + 1 WHERE id = 1");
// 現在のカウント数を取得
$count = $wpdb->get_var("SELECT count_value FROM $table_name WHERE id = 1");
echo $count;
wp_die();
}
// カウント数取得
add_action('wp_ajax_koizumi_get_count', 'get_koizumi_count');
add_action('wp_ajax_nopriv_koizumi_get_count', 'get_koizumi_count');
function get_koizumi_count() {
global $wpdb;
$table_name = $wpdb->prefix . 'koizumi_counter';
$count = $wpdb->get_var("SELECT count_value FROM $table_name WHERE id = 1");
echo $count ?: 0;
wp_die();
}
工夫したポイント・苦労した点
1. API制限への対応
Gemini APIの無料枠は以下の通りでした(2025年7月31日14時30分時点):
- gemini-2.0-flash-lite: 200リクエスト/日
- gemini-2.0-flash: 200リクエスト/日
- gemini-1.5-flash: 50リクエスト/日
合計450リクエスト/日で、1日100〜200人の利用に対応できています。
2. ユーザビリティの改善
当初は「政治家言い訳メーカー」のように複数の選択肢を用意していましたが、「小泉構文」という明確なコンセプトがあるため、選択肢をなくしてシンプルな仕様に変更しました。
// 例文ボタンの実装
const exampleBtns = document.querySelectorAll('.example-btn');
exampleBtns.forEach(btn => {
btn.addEventListener('click', function() {
inputText.value = this.getAttribute('data-example');
inputText.focus();
// 視覚的フィードバック
this.style.background = '#fd7e14';
setTimeout(() => {
this.style.background = '#2c5aa0';
}, 500);
});
});
3. エラーハンドリング
API制限に達した場合の分かりやすいメッセージ表示:
function showApiLimitMessage() {
const message = `
🙏 予想以上のご利用をいただき、ありがとうございます!
無料のAIサービスには1日の利用制限があるため、
制限に達した場合は数時間後に再度お試しください。
`;
alert(message);
}
パフォーマンス最適化
1. レスポンス時間の改善
- 複数APIキーによる負荷分散
- タイムアウト処理の実装
- キャッシュ機能(今後実装予定)
2. フロントエンドの最適化
.spinner {
display: inline-block;
width: 50px;
height: 50px;
border: 5px solid rgba(44, 90, 160, 0.3);
border-radius: 50%;
border-top-color: #2c5aa0;
animation: spin 1s ease-in-out infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
結果・反響
数値的成果
- 利用回数: 13,000回(2ヶ月)
- 1日平均利用者: 100〜200人
- SEO効果: 「小泉構文メーカー」でGoogle検索1位獲得
ユーザーからの反応
- SNSでの共有多数
- 「思ったよりも精度が高い」という評価
- リピーター率の高さ
今後の改善予定
1. 技術的改善
- レスポンス速度の向上
- キャッシュ機能の実装
- エラーログの詳細化
2. 機能追加
- 生成履歴の保存機能
- お気に入り機能
- APIの有料プラン検討
まとめ
個人開発したWebサービスが2ヶ月で1万回使われるのは、正直予想外でした。技術的には特別新しいことはしていませんが、以下のポイントが成功要因だったと思います:
- シンプルな UI/UX: 余計な機能を削ぎ落として直感的に使える設計
- 安定したAPI運用: 複数キーでのフォールバック機能
- 適切なエラーハンドリング: ユーザーにストレスを与えない情報提供
- 話題性: 時事ネタ + AI という組み合わせ
同じ課題で困っている人の参考になれば幸いです。何か質問があればコメントください!
お仕事や取材等のご依頼は下記HPまでお願いします。