Amazon Bedrock Amazon Titan(AIモデル)を使用して、シンプルなチャットプログラムの作成、応用(テキスト校閲)

ローカル環境 Node.jsアプリ – 質問入力 – 回答表示 AWS Bedrock Amazon Titan – テキスト生成 – 自然言語処理 質問を送信 回答を受信

シンプルなチャットプログラムの作成

Node.jsのプロジェクトを初期化

npm init -y

@aws-sdk/client-bedrock-runtime

Bedrock Runtime API のリクエスト作成と送信するために@aws-sdk/client-bedrock-runtimeをインストール

npm install @aws-sdk/client-bedrock-runtime
  • Node.js環境でAWS Bedrockサービスと通信するための公式ライブラリ(AWSが提供する公式SDK)
  • 【開発環境の構築が容易】Node.jsのインストールだけで開発開始可能
  • フロントエンド開発との親和性が高い、共通の言語(JavaScript/TypeScript)使用

1)Node.jsライブラリ(データの前処理や基本的な処理)

  • Tesseract.js(OCR)
  • Express(APIサーバー)
  • Multer(ファイルアップロード)
  • Sharp(画像処理)

2)プロンプト:AIに適切な指示を出す

App.jsを作成

// AWS SDKから必要な機能をインポート
// BedrockRuntimeClient: AWSのBedrockサービスに接続するためのクライアント
// InvokeModelCommand: AIモデルを呼び出すためのコマンド
const { BedrockRuntimeClient, InvokeModelCommand } = require('@aws-sdk/client-bedrock-runtime');

// AWS Bedrockクライアントの設定
// region: AWSのサービスを使用するリージョン(地域)
// credentials: AWS認証情報
const client = new BedrockRuntimeClient({
    region: "us-east-1",  // 米国東部(バージニア)リージョン
    credentials: {
        accessKeyId: "あなたのアクセスキー",      // AWSアクセスキー
        secretAccessKey: "あなたのシークレットキー" // AWSシークレットキー
    }
});

// AIに質問するための関数
// async/await: 非同期処理を扱うための構文
async function askAI(question) {
    try {
        // AIモデルへのリクエスト内容を設定
        const input = {
            // 使用するAIモデルを指定(Amazon Titan)
            modelId: "amazon.titan-text-express-v1",
            
            // データの形式をJSON形式に指定
            contentType: "application/json",
            accept: "application/json",
            
            // AIモデルへの具体的な指示内容
            body: JSON.stringify({
                // 質問文を設定
                inputText: question,
                
                // AIの応答設定
                textGenerationConfig: {
                    maxTokenCount: 1000,    // 最大応答長
                    temperature: 0.7        // 応答の創造性(0-1: 高いほど創造的)
                }
            })
        };

        // AIモデルを呼び出すコマンドを作成
        const command = new InvokeModelCommand(input);
        
        // コマンドを実行してAIからの応答を取得
        const response = await client.send(command);
        
        // AIからの応答(バイナリ)をテキストに変換
        const responseBody = JSON.parse(new TextDecoder().decode(response.body));
        
        // 応答を表示
        console.log("回答:", responseBody.results[0].outputText);
        
    } catch (error) {
        // エラーが発生した場合の処理
        console.error("エラーが発生しました:", error);
    }
}

// プログラムのテスト実行
// ここの質問を変更することで、違う質問ができます
askAI("こんにちは!簡単な自己紹介をしてください。");
時間の流れ async function askAI() await client.send(command) AIからの応答待ち 応答を表示 1. 非同期関数の開始 2. AIモデルの呼び出しを待機 3. 応答を受け取って処理

AWSが提供しているclient.send()というメソッドが 内部でPromiseを使用していて 私たちはそれをawaitで待っている

AWSのアクセスキーとシークレットキーを取得する

IAMユーザーの作成

  1. 「ポリシーを直接アタッチする」を選択
  2. 検索ボックスに「Bedrock」と入力
  3. 「AmazonBedrockFullAccess」にチェック
  4. 「次へ」をクリック
  1. 作成したユーザー名をクリック
  2. 「セキュリティ認証情報」タブを選択
  3. 「アクセスキーを作成」ボタンをクリック
  4. 「ユースケース」で「サードパーティーのサービス」を選択
  5. 警告の確認チェックボックスにチェック
  6. 「次へ」をクリック

ファイルを実行

先ほど作成したapp.jsに取得したキーに更新してファイルを下記の通り実行すると、、

node app.js
回答: 
私はAIです。人工知能の一種で、あなたの質問に答えたり、あなたの指示に従ったりすることができます。
私はあなたとチャットできることをうれしく思います。

テキスト校閲

使っているもの:

  1. AWS Bedrockのサービス
  2. Amazon TitanのAIモデル
  3. AWS SDKのクライアント
async function checkText(text) {
    try {
        // 1. AIへの指示(プロンプト)を作成
        const input = {
            modelId: "amazon.titan-text-express-v1",  // Amazon TitanのAIモデル
            contentType: "application/json",
            accept: "application/json",
            body: JSON.stringify({
                // ここが重要:AIへの指示内容
                inputText: `
                以下の文章を校閲してください。
                - 誤字脱字
                - 文法の間違い
                - より良い表現の提案
                を箇条書きで指摘してください。

                文章:${text}`,
                textGenerationConfig: {
                    maxTokenCount: 1000,
                    temperature: 0.7
                }
            })
        };

        // 2. AIに送信して結果を待つ
        const command = new InvokeModelCommand(input);
        const response = await client.send(command);
        
        // 3. 結果を表示
        const responseBody = JSON.parse(new TextDecoder().decode(response.body));
        console.log("校閲結果:", responseBody.results[0].outputText);
    } catch (error) {
        console.error("エラーが発生しました:", error);
    }
}

上記のの校閲処理は特別な校閲ロジックは実装していないので、全てAIモデル(プロンプトの指示)に依存しています

より高度なプログラムにするために下記のようにします

  • 独自の校閲ルール
  • 形態素解析
  • 文法チェックライブラリ などを組み合わせる
ComprehensiveTextChecker メインコントローラー CustomChecker MorphAnalyzer GrammarChecker
入力テキスト チェック処理 独自ルールチェック 形態素解析 文法チェック AIチェック チェック結果 – 表記の修正 – 文法エラー – 形態素情報 – AI提案

// 1. 独自の校閲ルールを追加
const customRules = {
    "です/ます": "敬語の統一をチェック",
    "こと/もの": "表現の統一をチェック",
    // ... その他のルール
};

// 2. 形態素解析ライブラリの使用
// npm install kuromoji
const kuromoji = require('kuromoji');

// 3. 独自の校閲ロジックを実装
async function advancedCheck(text) {
    // AIによる校閲
    const aiResult = await checkText(text);
    
    // 独自ルールによるチェック
    const customResult = applyCustomRules(text);
    
    // 形態素解析による確認
    const morphResult = await morphologicalAnalysis(text);
    
    return {
        ai: aiResult,
        custom: customResult,
        morph: morphResult
    };
}

独自の校閲ルール

// 独自の校閲ルールの実装例
class TextChecker {
    constructor() {
        // 基本的なルール定義
        this.rules = {
            // 表記ゆれチェック
            styleRules: {
                'とっても': 'とても',
                'みたい': 'ような',
                'すごく': '大変'
            },
            
            // 文末表現チェック
            endingRules: {
                'です・ます': /([てで]います|てます|です)(?![かが])/,
                'である': /である(?![かが])/
            },

            // ビジネス文書チェック
            businessRules: {
                '不適切': ['思います', 'そうです', 'だと思います'],
                '推奨': ['考えられます', '示唆されます', '推察されます']
            }
        };
    }

    // テキストをチェックするメソッド
    checkText(text) {
        const results = [];
        
        // 表記ゆれチェック
        for (const [incorrect, correct] of Object.entries(this.rules.styleRules)) {
            if (text.includes(incorrect)) {
                results.push(`「${incorrect}」は「${correct}」に修正することを推奨します`);
            }
        }

        // その他のルールも同様にチェック
        return results;
    }
}

形態素解析

// npm install kuromoji
const kuromoji = require('kuromoji');

class MorphologicalAnalyzer {
    async initialize() {
        return new Promise((resolve, reject) => {
            kuromoji.builder({ dicPath: 'node_modules/kuromoji/dict' })
                .build((err, tokenizer) => {
                    if (err) {
                        reject(err);
                    } else {
                        this.tokenizer = tokenizer;
                        resolve();
                    }
                });
        });
    }

    analyze(text) {
        const tokens = this.tokenizer.tokenize(text);
        return this.processTokens(tokens);
    }

    processTokens(tokens) {
        const analysis = {
            wordTypes: {},      // 品詞の分布
            conjugations: [],   // 活用の問題
            suggestions: []     // 改善提案
        };

        tokens.forEach(token => {
            // 品詞の集計
            const pos = token.pos;
            analysis.wordTypes[pos] = (analysis.wordTypes[pos] || 0) + 1;

            // 活用のチェック
            if (token.pos === '動詞' && token.conjugated_form !== '基本形') {
                analysis.conjugations.push({
                    word: token.surface_form,
                    basic: token.basic_form,
                    type: token.conjugated_form
                });
            }
        });

        return analysis;
    }
}

文法チェックライブラリ

// npm install textlint textlint-rule-preset-japanese
const { TextLintEngine } = require('textlint');

class GrammarChecker {
    constructor() {
        this.engine = new TextLintEngine({
            rules: {
                // プリセットルールの使用
                'preset-japanese': true,
                // カスタムルールの追加
                'max-ten': {
                    // 一文での「、」の数を制限
                    max: 3
                },
                'sentence-length': {
                    // 一文の長さを制限
                    max: 100
                }
            }
        });
    }

    async check(text) {
        try {
            const results = await this.engine.executeOnText(text);
            return this.formatResults(results);
        } catch (error) {
            console.error('文法チェックエラー:', error);
            return [];
        }
    }

    formatResults(results) {
        return results[0].messages.map(message => ({
            line: message.line,
            column: message.column,
            message: message.message,
            ruleId: message.ruleId
        }));
    }
}

組み合わせた場合

class ComprehensiveTextChecker {
    constructor() {
        this.customChecker = new TextChecker();
        this.morphAnalyzer = new MorphologicalAnalyzer();
        this.grammarChecker = new GrammarChecker();
    }

    async initialize() {
        await this.morphAnalyzer.initialize();
    }

    async checkText(text) {
        const results = {
            customRules: this.customChecker.checkText(text),
            morphology: await this.morphAnalyzer.analyze(text),
            grammar: await this.grammarChecker.check(text)
        };

        // AIの結果と組み合わせる
        const aiResults = await checkText(text);  // 先ほどのAI機能
        results.ai = aiResults;

        return this.summarizeResults(results);
    }

    summarizeResults(results) {
        // 結果を整理して返す
        return {
            suggestions: [
                ...results.customRules,
                ...results.grammar.map(g => g.message)
            ],
            analysis: {
                wordTypes: results.morphology.wordTypes,
                grammarIssues: results.grammar.length,
                aiSuggestions: results.ai
            }
        };
    }
}

使用例

async function main() {
    const checker = new ComprehensiveTextChecker();
    await checker.initialize();

    const text = "私はとっても疲れたので、今日は早く帰りたいと思います。";
    const results = await checker.checkText(text);
    console.log(results);
}

補足)フォルダ構成

text-checker-project/
│
├── src/                      # ソースコードのメインディレクトリ
│   ├── checkers/            # 各チェッカーの実装
│   │   ├── CustomChecker.js    # 独自ルールチェッカー
│   │   ├── MorphAnalyzer.js    # 形態素解析
│   │   ├── GrammarChecker.js   # 文法チェッカー
│   │   └── AIChecker.js        # AI利用のチェッカー
│   │
│   ├── rules/               # ルール定義
│   │   ├── styleRules.js       # 表記ゆれルール
│   │   ├── grammarRules.js     # 文法ルール
│   │   └── businessRules.js    # ビジネス文書ルール
│   │
│   ├── utils/               # ユーティリティ関数
│   │   ├── formatter.js        # 結果フォーマット
│   │   └── logger.js           # ログ処理
│   │
│   └── index.js             # メインのエントリーポイント
│
├── config/                  # 設定ファイル
│   ├── default.json           # デフォルト設定
│   └── textlint-rules.json    # textlintルール設定
│
├── tests/                   # テストファイル
│   ├── customChecker.test.js
│   ├── morphAnalyzer.test.js
│   └── grammarChecker.test.js
│
├── examples/                # 使用例
│   └── basic-usage.js
│
├── docs/                    # ドキュメント
│   ├── API.md
│   └── RULES.md
│
├── node_modules/            # 依存パッケージ
├── package.json
├── package-lock.json
└── README.md

src/index.js

const ComprehensiveTextChecker = require('./checkers/ComprehensiveChecker');
const config = require('../config/default.json');

module.exports = ComprehensiveTextChecker;

src/checkers/CustomChecker.js

const styleRules = require('../rules/styleRules');
const businessRules = require('../rules/businessRules');

class CustomChecker {
    constructor(config = {}) {
        this.rules = {
            style: styleRules,
            business: businessRules,
            ...config.rules
        };
    }

    checkText(text) {
        // 実装
    }
}

module.exports = CustomChecker;

src/rules/styleRules.js

module.exports = {
    // 表記ゆれルール
    corrections: {
        'とっても': 'とても',
        'みたい': 'ような'
    },
    // その他のルール
};

package.json

{
  "name": "text-checker",
  "version": "1.0.0",
  "main": "src/index.js",
  "scripts": {
    "start": "node src/index.js",
    "test": "jest",
    "lint": "eslint src/"
  },
  "dependencies": {
    "kuromoji": "^1.0.0",
    "textlint": "^12.0.0",
    "textlint-rule-preset-japanese": "^7.0.0",
    "@aws-sdk/client-bedrock-runtime": "^3.0.0"
  },
  "devDependencies": {
    "jest": "^27.0.0",
    "eslint": "^8.0.0"
  }
}

examples/basic-usage.js

const TextChecker = require('../src/index');

async function example() {
    const checker = new TextChecker();
    await checker.initialize();

    const text = "私はとっても疲れたので、今日は早く帰りたいと思います。";
    const results = await checker.checkText(text);
    console.log(results);
}

example().catch(console.error);