🔥 はじめに
今回は音声データをリアルタイムで文字起こしし、その内容を要約するシステムを構築してみました!
今回は AWS Transcribe Streaming を使ってリアルタイム文字起こしを行い、その後 AWS Bedrock の LLM を活用して要約を実施します。
📌 実装の流れは以下の通りです:
- Node.js でマイク入力を取得し、AWS Transcribe で文字起こし
- 取得したテキストを AWS Bedrock に渡して要約
- 結果をコンソールに表示
それでは、さっそく実装していきましょう!
✅ 必要なもの
- AWS アカウント(AWS Transcribe & AWS Bedrock の利用を有効化)
- Node.js 環境(v18 以上推奨)
- AWS SDK for JavaScript (v3)
🚀 実装手順
1️⃣ 必要なパッケージをインストール
まず、プロジェクトをセットアップし、必要なパッケージをインストールします。
npm init -y
npm install express node-record-lpcm16 @aws-sdk/client-transcribe-streaming @aws-sdk/client-bedrock-runtime @aws-sdk/credential-provider-ini
2️⃣ Node.js サーバーのセットアップ
以下のコードを server.mjs
として作成します。
const express = require("express");
const recorder = require("node-record-lpcm16");
const { TranscribeStreamingClient, StartStreamTranscriptionCommand } = require("@aws-sdk/client-transcribe-streaming");
const { BedrockRuntimeClient, InvokeModelCommand } = require('@aws-sdk/client-bedrock-runtime');
const { fromIni } = require('@aws-sdk/credential-provider-ini');
const { PassThrough } = require('stream'); // 追加
// =============== 設定項目を適宜書き換えてください ===============
const REGION = "ap-northeast-1"; // Bedrock対応リージョン
const MODEL_ID = "anthropic.claude-3-5-sonnet-20240620-v1:0"; // 使用するBedrockモデルIDの例
// ============================================================
// AWSクライアント
const credentials = fromIni();
const transcribeClient = new TranscribeStreamingClient({ region: REGION, credentials });
const bedrockClient = new BedrockRuntimeClient({ region: REGION, credentials });
// Expressサーバー
const app = express();
const port = 3000;
// 録音ボタン・結果表示
app.get("/", (req, res) => {
// Node.jsで直接マイク録音→ストリーミング
const html = `
<html>
<body>
<h1>リアルタイム文字起こし & 要約デモ</h1>
<p>このページを表示しても録音は開始されません。<br/>
サーバー側で起動しているNode.jsプロセスがマイクに直接アクセスするデモです。</p>
<p>コンソールを確認し、「Ctrl+C」等で録音終了後、Bedrockで要約を実行し結果が表示されます。</p>
</body>
</html>`;
res.send(html);
});
let recording;
let transcriptTextAll = "";
// 録音開始用のエンドポイント
app.get("/start", async (req, res) => {
try {
console.log("録音を開始します。Ctrl+Cで停止してください...");
// 1. 録音を開始
recording = recorder.record({
sampleRate: 16000,
channels: 1,
audioType: 'raw',
verbose: true,
recordProgram: 'rec',
silence: 0
});
// 2. 録音ストリームの取得を確認
const recordStream = recording.stream();
if (!recordStream) {
throw new Error("録音ストリームの初期化に失敗しました");
}
// 3. AWS Transcribe用のコマンドを作成
const command = new StartStreamTranscriptionCommand({
LanguageCode: "ja-JP",
MediaEncoding: "pcm",
MediaSampleRateHertz: 16000,
AudioStream: async function* () {
try {
for await (const chunk of recordStream) {
yield { AudioEvent: { AudioChunk: chunk } };
}
} catch (error) {
console.error("音声ストリーム処理エラー:", error);
throw error;
}
}(),
EnablePartialResultsStabilization: true,
PartialResultsStability: "medium",
});
// 4. AWS Transcribeでのストリーミングの開始
const transcribeStream = await transcribeClient.send(command);
// 5. 文字起こし結果の処理
if (transcribeStream.TranscriptResultStream) {
(async () => {
try {
for await (const event of transcribeStream.TranscriptResultStream) {
const results = event.TranscriptEvent?.Transcript?.Results || [];
for (const result of results) {
if (result.IsPartial === false && result.Alternatives?.[0]) {
const text = result.Alternatives[0].Transcript;
transcriptTextAll += text + "\n";
console.log("【確定】", text);
} else if (result.IsPartial === true && result.Alternatives?.[0]) {
const partial = result.Alternatives[0].Transcript;
console.log("(途中)", partial);
}
}
}
} catch (error) {
console.error("文字起こしストリーム処理エラー:", error);
}
})();
}
res.send("録音開始しました。コンソールを確認して下さい。\n停止はCtrl+Cで。");
} catch (error) {
console.error("録音開始エラー:", error);
if (recording) {
recording.stop();
}
res.status(500).send("録音の開始に失敗しました");
}
});
// 録音終了 (Ctrl+Cなどでサーバーごと終了→後処理で要約)
process.on("SIGINT", async () => {
if (!recording) {
console.log("録音が開始されていません。");
process.exit(0);
}
if (recording) {
recording.stop();
}
await new Promise(r => setTimeout(r, 1000));
console.log("最終的なトランスクリプトをBedrockで要約します...");
// 文字起こしテキストの確認
if (!transcriptTextAll || transcriptTextAll.trim() === '') {
console.log("文字起こしテキストが空のため、要約をスキップします。");
process.exit(0);
}
console.log("=== 文字起こし結果 ===");
console.log(transcriptTextAll);
console.log("\n=== 要約処理開始 ===");
try {
const bedrockParams = {
modelId: MODEL_ID,
contentType: "application/json",
accept: "application/json",
body: JSON.stringify({
anthropic_version: "bedrock-2023-05-31",
max_tokens: 300,
messages: [
{
role: "user",
content: `以下の文章を簡潔に要約してください:\n\n${transcriptTextAll}`
}
]
})
};
const bedrockClient = new BedrockRuntimeClient({ region: REGION, credentials });
const response = await bedrockClient.send(new InvokeModelCommand(bedrockParams));
const responseBody = JSON.parse(new TextDecoder().decode(response.body));
const summaryText = responseBody.content[0].text || "(要約取得失敗)";
console.log("=== 要約結果 ===");
console.log(summaryText);
} catch (e) {
console.error("Bedrock要約時にエラー:", e);
if (e.response) {
console.error("エラーレスポンス:", JSON.stringify(e.response, null, 2));
}
}
process.exit(0);
});
// サーバー起動
app.listen(port, () => {
console.log(`Server is running at http://localhost:${port}`);
});
3️⃣ 実行方法
node server.mjs
録音を開始するには、ブラウザで http://localhost:3000/start
にアクセスします。
録音を終了するには Ctrl+C
を押せば、自動的に AWS Bedrock による要約が行われ、結果がコンソールに表示されます。
-
ブラウザ画面
-
コンソールログ
🎯 まとめ
このプロジェクトでは、AWS Transcribe Streaming を活用して 音声のリアルタイム文字起こし を行い、その後 AWS Bedrock による自動要約 を実現しました。
📌 今後追加したい機能:
- ✅ WebSocket を利用したフロントエンドとの連携
- ✅ UI を作成し、録音の制御を可能に
- ✅ 文字起こしの結果を保存し、後から確認できるようにする
これらを実装すれば、さらに 実用的なシステム へ進化できます!
🚀 ぜひ試してみてください!
💡 質問やフィードバックがあれば、ぜひコメントで教えてください!
参考資料・リンク集
(※投稿内容は個人の意見であり、所属組織の公式見解ではありません。)