4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Gemini APIでビジネスメールの敬語を添削するWebアプリをAWSで作った

4
Posted at

はじめに

「させていただく」の多用、文末表現の混在、二重敬語……ビジネスメールの敬語は意外と難しいです。

送信前に AI に確認してもらえれば安心できると思い、Gemini API を使ったビジネスメール添削 Web アプリを作りました。

デモ: https://mail-checker.eggsystems.jp
GitHub: https://github.com/kojiman55/mail-checker

メール敬語添削アプリ スクリーンショット

主な機能

  • 指摘の 3 分類表示(誤り・改善提案・参考情報を色分け)
  • 詳細な修正案(元の表現・修正案・理由を記載)
  • AI 総評機能
  • 添削後の全文生成とコピー機能
  • 日本語・英語対応

技術スタック

レイヤー 技術
フロントエンド React + TypeScript + Vite
ホスティング S3 + CloudFront (OAC)
バックエンド AWS Lambda (TypeScript) / API Gateway
AI Gemini API
認証 Amazon Cognito
DB Aurora MySQL Serverless v2

システム構成

メール本文入力
    ↓
Amazon Cognito(認証)
    ↓
API Gateway
    ↓
Lambda(プロンプト組立 → Gemini API 呼出 → レスポンス構造化)
    ↓
Aurora MySQL Serverless v2(添削履歴保存)
    ↓
フロントエンド(色分け表示)

Cognito 認証で添削履歴を Aurora Serverless に保存し、マイページで過去の結果を確認できます。Aurora Serverless はアイドル時に自動スケールダウンするため、デモ運用では実質コストゼロです。

実装で詰まったポイントと解決策

1. Gemini の出力形式が安定しない

「JSON で返してください」とプロンプトに書いても、期待と異なる形式が返ってくることが頻発しました。

解決策: JSON スキーマ + 具体例をセットで記述する

型定義・フィールド名・具体例・制限事項をセットでプロンプトに渡します。

interface ReviewIssue {
  type: "error" | "warning" | "info";
  original: string;   // 元の表現
  suggestion: string; // 修正案
  reason: string;     // 理由(1〜2文)
}

interface ReviewResult {
  issues: ReviewIssue[];
  overall: string;      // AI 総評
  corrected: string;    // 添削後の全文
}

プロンプトに含めるスキーマ記述例:

以下の JSON スキーマで返してください。それ以外のテキストは出力しないこと。

{
  "issues": [
    {
      "type": "error" | "warning" | "info",
      "original": "元の表現",
      "suggestion": "修正案",
      "reason": "理由"
    }
  ],
  "overall": "全体の総評",
  "corrected": "添削後の全文"
}

「JSON で返して」だけでは不十分で、スキーマ定義と具体例の組み合わせが必須でした。

2. 分類基準の曖昧さ

3 分類(error / warning / info)の基準が曖昧だと、出力が毎回変わります。「部長様」のような慣例的な二重敬語をどう扱うか、などです。

解決策: 評価基準をプロンプトに明記する

各指摘の type は以下の基準で分類してください:

error  : 文法的に正しくない、敬語として成立しない
         例)「ご覧になられる」(二重敬語)
warning: より自然な言い回しがある、使いすぎ
         例)「〜させていただく」が 3 回以上
info   : 慣例上よく使われるが厳密には問題がある表現
         例)「部長様」(様は役職に付けない)

UI にも「AI による提案です」と注意書きを加え、利用者への誤解を防ぎました。

3. 日英で評価基準が大きく異なる

日本語は敬語・クッション言葉が評価軸、英語はフォーマリティ・トーンが評価軸です。同じプロンプトでは対応できません。

解決策: language フィールドでシステムプロンプトを動的に切り替え

const prompt = language === "ja"
  ? buildJapanesePrompt(text)
  : buildEnglishPrompt(text);

function buildJapanesePrompt(text: string): string {
  return `
あなたはビジネスメールの敬語専門家です。
以下のメール本文を添削し、敬語・クッション言葉・文末表現の観点から指摘してください。
...
本文:
${text}
  `;
}

function buildEnglishPrompt(text: string): string {
  return `
You are a business email writing expert.
Review the following email for formality, tone, and professionalism.
...
Email:
${text}
  `;
}

Lambda の実装詳細

export const handler = async (event: APIGatewayProxyEvent) => {
  const { text, language } = JSON.parse(event.body ?? '{}');

  const prompt = language === 'ja'
    ? buildJapanesePrompt(text)
    : buildEnglishPrompt(text);

  const result = await callGemini(prompt);

  // Gemini のレスポンスを JSON として安全にパース
  const parsed = safeParseJSON<ReviewResult>(result);
  if (!parsed) {
    return { statusCode: 500, body: JSON.stringify({ error: 'parse_failed' }) };
  }

  // 履歴を Aurora に保存
  await saveHistory(event.requestContext.authorizer?.claims?.sub, text, parsed);

  return {
    statusCode: 200,
    headers: corsHeaders,
    body: JSON.stringify(parsed),
  };
};

月額コスト

サービス 費用
Lambda + API Gateway $0(無料枠)
S3 + CloudFront $0(無料枠)
Cognito $0(月 50,000 MAU まで無料)
Aurora MySQL Serverless v2 数円(ACU 使用量)
Gemini API $0(無料枠)
Route 53 $0.50
合計 数円〜/月

まとめ

生成 AI から構造化 JSON を安定して取得するには、「JSON 形式で返して」という指示だけでは不十分です。スキーマ定義・フィールド説明・具体例・制限事項をセットで渡すことが必須でした。

分類基準の曖昧さも同様で、プロンプトに明確な定義を書かないと毎回異なる基準で出力されます。AI への指示は「読んで分かる仕様書」として書くのが安定化のコツです。

関連記事

この記事の API を Flutter でそのまま叩いてモバイルアプリを作った記事も書いています。

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?