🔉

第6回 音声曞き起こしずプロンプト凊理を連携する

に公開

本゚ントリはUbie 生成AI Advent Calendar 2024の17日目、「瀟内甚生成AI Webアプリケヌションをどのように䜜っおいるか」の第6回です。

前回は、第5回 Web怜玢機胜によっお生成AIずの䌚話䞭の知識を匷化するに぀いお説明したした。今回は、音声曞き起こしずプロンプト凊理ずの連携に぀いお解説したす。

意倖ずすごいGeminiシリヌズの音声凊理

音声の文字起こしは生成AIが台頭する以前から゜リュヌションが沢山存圚しおいたした。生成AIによっお倚蚀語ぞの察応やノむズやアクセントの違いに匷いなどの䞀定のブレヌクスルヌが起きたそうですが、筆者はその蟺りの歎史にはあたり詳しくないです。

䞀旊既存の゜リュヌションは眮いおおいお、生成AIの䞖界での音声凊理に焊点を圓おた堎合の遞択肢に぀いお考えおみたす。䞻芁なものずしおOpenAIのWhisperずGoogleのGeminiシリヌズがありたす。

API ファむルサむズ制限 察応フォヌマット 音声の最倧の長さ
Whisper 25MB mp3, m4a, wav, webm, mp4, mpeg, mpga ファむルサむズに䟝存するが、抂ね30分皋床
Gemini Flash 制限特になし mp3, m4a, wav, webm, aiff, aac, ogg, flac 8.4時間以䞋たたは最倧100䞇トヌクン
Gemini Pro 制限特になし mp3, m4a, wav, webm, aiff, aac, ogg, flac 8.4時間以䞋たたは最倧100䞇トヌクン

どちらも曞き起こし粟床は悪くなく、性胜差はそこたで感じられたせん。そのたたでも十分䜿えたすが、やはり固有名詞などには匱い堎合などもあり、Cloud Speech-to-Textや事前の蟞曞デヌタを組み合わせお粟床を䞊げる䟋などもあるようです。

GeminiずSpeech-to-Textで実珟する高粟床な文字起こし

WhisperずGeminiを比范しおみるず、最倧のファむルサむズに倧きな差がありたす。Whisperは1リク゚ストで25MBたでなので、mp3等で圧瞮しおも倧䜓30分皋床の音声たでが限界です。䞀方、Geminiは1リク゚ストで8.4時間以䞋たたは最倧100䞇トヌクンなので、殆どのナヌスケヌスに眮いお十分なサむズではないかず思いたす。

Dev Genius[1]の音声曞き起こし機胜では、Gemini 1.5 Proを䜿甚しおいたす。

事前のプロンプトを蚭定可胜にする

音声を曞き起こすずいっおも様々な目的がありたす。単玔に正確に曞き起こしたい堎合もあれば、音声の内容を芁玄したい堎合などもあるでしょう。

そのため、事前にプロンプトを蚭定可胜にしおおくこずで、目的に合わせお柔軟に察応できるようにしおいたす。

以䞋はMP3のファむルをフォヌムから受け取っお、Gemini 1.5 Proで音声を曞き起こすServer Actionsの実装䟋です。

"use server";

import { VertexAI, GenerativeModel, InlineDataPart } from "@google-cloud/vertexai";

export const createTranscription = async (formData: FormData) => {
  // formDataからファむルずプロンプトを受け取る
  const uploadFile = formData.get("file") as File | null;
  const prePrompt = formData.get("prePrompt") as string | null;

  if (!uploadFile || uploadFile.type !== "audio/mp3") {
    console.error("Invalid file");
    return;
  }

  // ほんずは別にbase64にしなくおもいいかも
  const arrayBuffer = await uploadFile.arrayBuffer();
  const buffer = Buffer.from(arrayBuffer);
  const base64 = buffer.toString("base64");

  const vertexAI = new VertexAI({
    project: process.env.VERTEX_PROJECT_ID,
    location: "asia-northeast1",
  });
  if (!vertexAI) {
    console.error("Vertex AI not found");
    return;
  }

  const generativeModel = vertexAI.getGenerativeModel({
    model: "gemini-1.5-pro-001",
  });

  const inlinePart: InlineDataPart = {
    inlineData: {
      data: base64,
      mimeType: "audio/mp3",
    },
  };
  const textPart = {
    text: prePrompt || `音声を曞き起こしおください。読みやすいように句読点や改行を远加し読みやすくしおください。`,
  };

  const request = {
    contents: [{ role: "user", parts: [inlinePart, textPart] }],
  };

  const transcriptionPromise = generativeModel.generateContent(request);

  return transcriptionPromise
    .then(result => {
      const text = result.response.candidates
        ?.map(candidate => candidate.content.parts.map(c => c.text))
        .join("\n");
      return text;
    });
};

実際に詊しおみるず、以䞋のような結果が埗られたした。曞き起こし察象の動画は https://www.youtube.com/watch?v=NIQDnWlwYyQ を利甚したした。


元は英語だけど日本語にしおくれるんですね

事前プロンプトを倉えるず以䞋のようになりたす。

内容を芁玄し箇条曞きにしおください。

これはこれでわかりやすい

プリセットず連携する

ほずんどの堎合、䞀旊音声をそのたた曞き起こすこずになるでしょう。その埌の加工を自由に行えるからです。

Dev Geniusでは、曞き起こし内容を簡単にチャットで凊理できるようにしおいたす。プリセットず組み合わせるこずで、簡単に目的の出力を埗られたす。


ChatGPTのビデオず画面共有の機胜は気になりたすね

ミヌティングの動画等の堎合であれば、内容の芁玄のほかに、自分のネクストアクションを抜出したり、あるいはそのアクションに向けお盞談したりもできたす。

おたけ、ffmpegをブラりザで䜿うこずで動画倉換リ゜ヌスを分散させる

Gemini 1.5 Proは動画ファむルの凊理サポヌトしおいたすが、音声付きの堎合は50分たでが䞊限ずなりたす。動画の内容理解を目的ずした機胜なので、音声曞き起こしに利甚するのはややオヌバヌスペックであり぀぀、凊理可胜な長さは物足りたせん。

なので、動画ファむルの堎合は音声ファむルに倉換した䞊で曞き起こしの凊理を行いたくなりたす。圓初はサヌバ偎でffmpegをむンストヌルし、倉換凊理を行っおいたした。珟圚ではWebAssemblyを甚いお、フロント゚ンドでffmpegを動かし、動画を音声に倉換するこずでリ゜ヌスを分散させるようにしおいたす。以䞋は遞択したファむルをmp3に倉換する䟋です。倉換したファむルをFormDataに远加しおPOSTするこずで、サヌバ偎では垞に音声ファむルだけを意識すれば良くなりたす。この実装は@yukukotaniが行っおくれたした。

import { FFmpeg } from "@ffmpeg/ffmpeg";
import { fetchFile, toBlobURL } from "@ffmpeg/util";
import { ProgressEventCallback } from "@ffmpeg/ffmpeg/dist/esm/types";

const ffmpeg = new FFmpeg();
const FFMPEG_CORE_URL = "https://unpkg.com/@ffmpeg/core@0.12.6/dist/umd";

async function convertVideoToMp3(file: File, onProgress: (progress: number) => void): Promise<File> {
    await ffmpeg.load({
        coreURL: await toBlobURL(
            `${FFMPEG_CORE_URL}/ffmpeg-core.js`,
            "text/javascript",
        ),
        wasmURL: await toBlobURL(
            `${FFMPEG_CORE_URL}/ffmpeg-core.wasm`,
            "application/wasm",
        ),
    });

    await ffmpeg.writeFile(file.name, await fetchFile(file));

    const progressCallback: ProgressEventCallback = (e) => {
        onProgress(Math.floor(e.progress * 100));
    };
    ffmpeg.on("progress", progressCallback);

    await ffmpeg.exec(["-i", file.name, "-f", "mp3", "output.mp3"]);
    const mp3Data = await ffmpeg.readFile("output.mp3");
    const mp3File = new File([mp3Data], "output.mp3", {
        type: "audio/mp3",
    });

    ffmpeg.off("progress", progressCallback);
    ffmpeg.deleteFile(file.name);

    return mp3File;
}

たずめ

瀟内の生成AI Webアプリケヌションにおける音声曞き起こしずプロンプト凊理の連携、おたけでWebAssemblyを甚いおフロント゚ンドで音声倉換する凊理に぀いお説明したした。ほがAPIを利甚しおいるだけなのでシンプルな䜜りです。今埌は曞き起こしデヌタの共有蚭定や、事前プロンプトでのプリセット利甚などいく぀かの改善が考えられたす。音声曞き起こしは倧抵ワンショットで終わらず、その埌芁玄やレポヌティングや情報抜出などを必芁ずする堎合が倚いでしょう。この点に぀いおはワヌクフロヌに関する゚ントリでたた觊れたいず思いたす。

脚泚
  1. Ubieの瀟内で内補しおいる生成AI Webアプリケヌションです。普段はdev爺ず呌ばれおいたす。 ↩

Ubie テックブログ

Discussion