メインコンテンツへスキップ
見出し画像
Photo bylight_mimosa502

LINEで動く日本語校正AIボットを作ってみた!AWS Lambda + OpenAI APIで実現する自動校正サービス

    はじめに

    「〜する為」「〜の様な」「して欲しい」...日本語の表記ゆれ、気になりませんか?

    私は司法試験予備試験の講師として、日々多くの文章を書いています。その中で、表記の統一は常に悩みの種でした。そこで今回、LINEにメッセージを送るだけで自動的に文章を校正してくれるボットを作ってみました!

    完成したボットはこんな感じで動きます:

    【送信】
    3才の子供の為に、して欲しいことがあります。
    
    【返信】
    【校正前】
    3才の子供の為に、して欲しいことがあります。
    
    【校正後】
    3歳の子供のために、してほしいことがあります。
    
    【校正箇所】
    ・3才 → 3歳
    ・為に → ために
    ・して欲しい → してほしい
    

    今回は、このLINE校正ボットの作り方を、躓きポイントも含めて詳しく解説します!

    必要なもの

    • AWSアカウント(Lambda、S3、API Gatewayを使用)

    • LINE Developersアカウント

    • OpenAI APIキー(GPT-4o-miniを使用)

    • 校正ルールのCSVファイル

    料金の目安:

    • Lambda: ほぼ無料(月100万リクエストまで無料枠)

    • OpenAI API: GPT-4o-miniなら1000回の校正で約$0.5程度

    • LINE Messaging API: 無料(月1000通まで)

    アーキテクチャ

    ユーザー → LINE公式アカウント → LINE Platform 
                                        ↓
                                  Webhook送信
                                        ↓
                                  API Gateway
                                        ↓
                                  Lambda関数
                                   ↙    ↘
                                S3        OpenAI API
                          (校正ルール)   (GPT-4o-mini)
                                   ↘    ↙
                                  Lambda関数
                                        ↓
                                  LINE Platform → ユーザー
    

    STEP 1: LINE公式アカウントの作成

    1. LINE Developersでチャネル作成

    LINE Developersにアクセスして:

    1. 新規プロバイダーを作成

    2. 「Messaging API」チャネルを作成

    3. チャネル基本設定で以下を取得:

      • Channel Secret

      • Channel Access Token(長期)

    2. 重要な設定

    • Webhook: オン

    • 応答メッセージ: オフ(自動応答を無効化)

    • あいさつメッセージ: お好みで設定

    STEP 2: 校正ルールの準備

    校正ルールをCSVで管理します。以下のような形式で作成:

    original,replacement,note,regex_pattern
    ◯才,◯歳,,
    して欲しい,してほしい,,
    〜する為,〜するため,,
    〜の様な,〜のような,,
    有難う,ありがとう,,
    

    このCSVをS3バケットにアップロードします:

    # S3バケット作成
    aws s3 mb s3://your-proofreading-rules
    
    # CSVアップロード
    aws s3 cp proofreading_rules.csv s3://your-proofreading-rules/
    

    STEP 3: Lambda関数の作成

    1. 基本設定

    • 関数名: line-proofreading-bot

    • ランタイム: Python 3.9

    • メモリ: 512MB

    • タイムアウト: 30秒

    2. 環境変数の設定

    LINE_CHANNEL_ACCESS_TOKEN = (取得したトークン)
    LINE_CHANNEL_SECRET = (取得したシークレット)
    OPENAI_API_KEY = (OpenAI APIキー)
    S3_BUCKET_NAME = your-proofreading-rules
    PROOFREADING_TABLE_KEY = proofreading_rules.csv
    

    3. Lambda関数のコード

    import json
    import os
    import hashlib
    import hmac
    import base64
    import boto3
    import csv
    from io import StringIO
    import urllib.request
    import urllib.parse
    
    # 環境変数
    LINE_CHANNEL_ACCESS_TOKEN = os.environ['LINE_CHANNEL_ACCESS_TOKEN']
    LINE_CHANNEL_SECRET = os.environ['LINE_CHANNEL_SECRET']
    OPENAI_API_KEY = os.environ['OPENAI_API_KEY']
    S3_BUCKET_NAME = os.environ['S3_BUCKET_NAME']
    PROOFREADING_TABLE_KEY = os.environ['PROOFREADING_TABLE_KEY']
    
    s3_client = boto3.client('s3')
    
    # 校正ルールのキャッシュ
    proofreading_cache = None
    
    def load_proofreading_table():
        """S3から校正ルールを読み込む"""
        global proofreading_cache
        
        if proofreading_cache is not None:
            return proofreading_cache
        
        try:
            obj = s3_client.get_object(Bucket=S3_BUCKET_NAME, Key=PROOFREADING_TABLE_KEY)
            csv_content = obj['Body'].read().decode('utf-8')
            
            csv_reader = csv.DictReader(StringIO(csv_content))
            rules = []
            for row in csv_reader:
                rules.append({
                    'original': row.get('original', ''),
                    'replacement': row.get('replacement', ''),
                    'note': row.get('note', '')
                })
            
            proofreading_cache = rules
            return rules
        except Exception as e:
            print(f"Error loading rules: {e}")
            return None
    
    def proofread_with_openai(text, rules):
        """OpenAI APIを使って校正を実行"""
        # ルールをプロンプトに変換
        rules_text = []
        for rule in rules:
            rule_str = f"- '{rule['original']}' → '{rule['replacement']}'"
            if rule['note']:
                rule_str += f" ({rule['note']})"
            rules_text.append(rule_str)
        
        prompt = f"""以下の校正ルールに基づいて、与えられた文章を校正してください。
    
    校正ルール:
    {chr(10).join(rules_text)}
    
    校正する文章:
    {text}
    
    以下の形式で出力してください(これ以外の内容は一切含めないでください):
    
    【校正前】
    (元の文章をそのまま記載)
    
    【校正後】
    (校正した文章を記載)
    
    【校正箇所】
    (変更した表現を「○○ → ××」の形式で箇条書き)"""
    
        # OpenAI APIリクエスト
        url = "https://api.openai.com/v1/chat/completions"
        headers = {
            "Authorization": f"Bearer {OPENAI_API_KEY}",
            "Content-Type": "application/json"
        }
        
        data = {
            "model": "gpt-4o-mini",
            "messages": [
                {"role": "system", "content": "あなたは日本語の校正専門家です。"},
                {"role": "user", "content": prompt}
            ],
            "temperature": 0.1,
            "max_tokens": 2000
        }
        
        req = urllib.request.Request(
            url,
            data=json.dumps(data).encode('utf-8'),
            headers=headers
        )
        
        try:
            with urllib.request.urlopen(req) as response:
                result = json.loads(response.read().decode('utf-8'))
                return result['choices'][0]['message']['content'].strip()
        except Exception as e:
            print(f"OpenAI API error: {e}")
            return "校正処理中にエラーが発生しました。"
    
    def send_line_reply(reply_token, text):
        """LINE APIで返信を送る"""
        url = 'https://api.line.me/v2/bot/message/reply'
        
        headers = {
            'Content-Type': 'application/json',
            'Authorization': f'Bearer {LINE_CHANNEL_ACCESS_TOKEN}'
        }
        
        data = {
            'replyToken': reply_token,
            'messages': [{
                'type': 'text',
                'text': text
            }]
        }
        
        req = urllib.request.Request(
            url, 
            data=json.dumps(data).encode('utf-8'),
            headers=headers
        )
        
        try:
            with urllib.request.urlopen(req) as response:
                print(f"Reply sent: {response.status}")
        except Exception as e:
            print(f"Error sending reply: {e}")
    
    def lambda_handler(event, context):
        """メインハンドラー"""
        # 署名検証
        signature = event['headers'].get('x-line-signature', '')
        body = event['body']
        
        hash = hmac.new(
            LINE_CHANNEL_SECRET.encode('utf-8'),
            body.encode('utf-8'), 
            hashlib.sha256
        ).digest()
        
        if not hmac.compare_digest(
            signature.encode('utf-8'),
            base64.b64encode(hash)
        ):
            return {'statusCode': 400, 'body': json.dumps('Invalid signature')}
        
        # イベント処理
        try:
            events = json.loads(body)['events']
            
            for event in events:
                if event['type'] == 'message' and event['message']['type'] == 'text':
                    reply_token = event['replyToken']
                    user_text = event['message']['text']
                    
                    # 校正ルールを読み込み
                    rules = load_proofreading_table()
                    if rules is None:
                        send_line_reply(reply_token, "校正ルールの読み込みに失敗しました。")
                        continue
                    
                    # 校正実行
                    result = proofread_with_openai(user_text, rules)
                    
                    # 返信
                    send_line_reply(reply_token, result)
            
            return {'statusCode': 200, 'body': json.dumps('OK')}
            
        except Exception as e:
            print(f"Error: {e}")
            return {'statusCode': 500, 'body': json.dumps('Internal Server Error')}
    

    STEP 4: API Gatewayの設定

    1. REST APIを作成

    1. API Gateway コンソールで「REST API」を作成

    2. リソース名: /webhook

    3. メソッド: POST

    2. Lambda統合設定(超重要!)

    ここが最大のハマりポイントでした:

    • 統合タイプ: Lambda関数

    • Lambda プロキシ統合の使用: ✅ 必ずチェック!

    • Lambda関数: 作成したLambda関数を選択

    Lambda プロキシ統合を有効にしないと、LINEからのリクエストが正しくLambda関数に渡されません。

    3. デプロイ

    1. 「アクション」→「APIのデプロイ」

    2. ステージ名: prod

    3. デプロイ実行

    エンドポイントURL(Webhook URL)が生成されます:

    https://xxxxx.execute-api.region.amazonaws.com/prod/webhook
    

    STEP 5: LINE DevelopersでWebhook設定

    1. LINE Developersコンソールに戻る

    2. Messaging API設定で「Webhook URL」に上記URLを設定

    3. 「検証」ボタンで接続確認

    4. Webhookを「オン」に

    実際に使ってみる

    1. 作成したLINE公式アカウントを友だち追加

    2. 校正したい文章を送信

    3. 数秒で校正結果が返ってくる!

    ハマったポイントと解決策

    1. Lambda Layerのエラー

    Runtime.ImportModuleError: No module named 'line-bot-sdk'
    

    解決策: 外部ライブラリは標準ライブラリのみで実装するか、Lambda Layerを作成する必要があります。今回はurllibを使って実装しました。

    2. API GatewayでLambdaが呼ばれない

    CloudWatchログに何も出力されない場合は、Lambda プロキシ統合が有効になっていない可能性大です。

    3. 署名検証エラー

    LINE Webhookの署名検証は必須です。環境変数の設定ミスに注意。

    カスタマイズのアイデア

    1. 校正ルールの拡張: 正規表現パターンを追加して、より高度な校正を実現

    2. ユーザー別設定: DynamoDBを使って、ユーザーごとに校正レベルを変更

    3. 校正履歴の保存: S3に校正履歴を保存して、よくある間違いを分析

    4. 画像内テキストの校正: Amazon Textractと連携して、画像内の文字も校正

    コスト削減のポイント

    1. Lambda関数のキャッシュ活用: 校正ルールをグローバル変数にキャッシュ

    2. 適切なメモリサイズ: 512MBで十分(それ以上は過剰)

    3. OpenAI APIの選択: GPT-4o-miniは高速で安価

    まとめ

    LINE + AWS Lambda + OpenAI APIを組み合わせることで、実用的な日本語校正ボットを作ることができました。特に以下の点がポイントです:

    • サーバーレスで運用コストが最小限

    • LINEという身近なツールで使える

    • 校正ルールをCSVで簡単に管理できる

    ぜひ皆さんも、自分だけの校正ルールでカスタマイズしてみてください!

    参考リンク

    質問や改善案があれば、ぜひコメントで教えてください!


    この記事が参考になった方は、ぜひ「スキ」をお願いします!

    #LINE開発 #AWS #Lambda #OpenAI #ChatGPT #生成AI #サーバーレス #Pythonプログラミング #個人開発 #技術ブログ #プログラミング初心者 #APIGateway #LINEBot #自動化 #日本語校正 #Webhook #クラウド開発 #AWSLambda #LINEMessagingAPI #GPT4 #エンジニアリング

    あなたへのおすすめ