ローカルLLMとWhisperで作る、完全オフライン議事録作成ツール

ローカルLLM(gemma-3-12b-it)とWhisperで作る、完全オフライン議事録作成ツール

ローカルLLMとWhisperで作る、完全オフライン議事録作成ツール

最近のAIの進化、特に大規模言語モデル(LLM)の発展には目を見張るものがあります。クラウドベースの強力なモデルが次々と登場する一方で、「ローカル環境で動くLLM」も驚くべきスピードで進化しています。 今回は、そんなローカルLLMの「今」とそれを使ってプライバシーも安全な完全ローカル環境で動作する「音声からの議事録作成プログラム」を紹介します。
なおこの記事は、Pythonのプログラムを元に生成AIがベースを作成したものです。最終的には人が手直しをしていますがご認識おきください。


ローカルLLMでセキュリティとカスタマイズ性を手元に

「LLMって便利だけど、会社の機密情報を含むデータを外部APIに送信するのはちょっと、、」と感じる方も多いのではないでしょうか。 ローカルLLMなら、その心配は無用です!

  • セキュリティ・プライバシー
  • データが外部に出ないため、機密情報を扱う業務にも安心して利用できます。

  • オフライン動作
  • インターネット接続がない環境でも利用可能です。

  • コスト
  • API利用料を気にせず、好きなだけ試行錯誤ができます。(初期のハードウェア投資は考慮が必要)

  • カスタマイズ性
  • モデルによっては、特定のタスクに合わせてファインチューニングすることも可能です。

最近では、Llama4Gemma3といった高性能なモデルが比較的軽量で登場し、個人のPCでも十分に活用できるようになってきました。 今回はその中でもGoogleが開発した比較的新しいモデルファミリーであるGemmaから、指示応答性能が高いgoogle/gemma-3-12b-itを使って、その実力を見ていきましょう。


LM StudioでローカルLLM環境をサクッと構築

「ローカルLLMって環境構築が大変そう。。」と思われるかもしれませんが、LM Studioというツールを使えば、驚くほど簡単にバックエンド環境を構築できます。 モデルのダウンロードからAPIサーバーの起動まで、GUIでポチポチ進められるので、プログラミング初心者の方でも安心です。


完全ローカル!音声から議事録を自動作成するプログラム

さて、本題です。今回は、高精度な音声認識を実現するOpenAIのWhisperモデル(large-v3)と、先程紹介したローカルLLM(gemma-3-12b-it on LM Studio)を組み合わせて、音声ファイルから議事録を自動作成するPythonプログラムを作ってみました。

システム構成

  • 音声認識:Whisper(large-v3モデル)
  • ローカルで動作し、日本語も高精度に認識。

  • 議事録生成:ローカルLLM(gemma-3-12b-it)
  • LM Studio経由でAPIとして利用

  • 処理フロー
    1. 音声ファイルをWhisperで文字起こし。
    2. 文字起こし結果の簡単な整形(「えーと」などのフィラー除去)。
    3. 整形済みテキストをLM Studioで起動したローカルLLMに送信し、議事録形式に要約・整形してもらう。
    4. 結果をテキストファイルとして保存。


必要なもの・準備

ハードウェア
  • LLMを快適に動かすためには、ある程度の性能を持つPCが必要です。特にVRAMが大きいGPUがあると高速に動作します。
    ※WhisperとGemmaの両方でVRAMを消費します。
    ※Whisperで約10GBのVRAMを消費し、Gemmaでは約12GB消費します。私の場合はGemmaのAPIサーバーを別端末で動作させています。
ソフトウェア
  • Python(3.8以上推奨)
  • LM Studio
  • 公式サイトからダウンロードし、お好みのLLMをダウンロードしておきます。
  • 必要なPythonライブラリ
  • pip install openai-whisper requests


Pythonプログラム紹介

以下が、議事録作成プログラムの全コードです。設定項目を自分の環境に合わせて変更すれば、すぐに試せます。
import whisper
import os
import datetime
import requests # HTTPリクエスト用に追加
import json     # JSONレスポンス処理用に追加

# --- 設定項目 ---

# 1. 音声ファイルのパス
AUDIO_FILE_NAME = "525f1844-3226-4490-9247-386c8fa3a72b"  # 拡張子は除く ★ご自身のファイル名に変更してください
AUDIO_FILE_PATH = "audio/" + AUDIO_FILE_NAME + ".wav"    # 例: "audio/meeting_audio.wav"
RESULT_DIR = "result" # 出力ディレクトリ

# 2. Whisperモデル名
MODEL_NAME = "large-v3"

# 3. (プレフィックスはファイル名に直接AUDIO_FILE_NAMEを使用するため、ここでは不要)

# 4. 誤字訂正用のリスト (想定誤字, 正しい文字列)
TYPO_CORRECTIONS = [
    #
    ("えーと、", ""),
    ("えーと。", ""),
    ("はい。", ""), 
    ("あの、", ""),
    ("あのー", ""),
]

# 5. LM Studio API設定
LM_STUDIO_API_URL = "http://192.168.11.45:1234/v1/chat/completions" # ★ご自身のLM StudioサーバーのURLに変更してください
LM_STUDIO_MODEL_NAME = "gemma-3-12b-it" # ★LM Studioでロードしているモデル名に合わせてください

# 6. LM Studioへの指示プロンプト (システムメッセージ)
LM_STUDIO_SYSTEM_PROMPT = """あなたは優秀なAIアシスタントです。
提供された音声認識結果と会議の日時情報に基づいて、プロフェッショナルな会議の議事録を作成してください。
以下の項目を可能な限り含め、具体的かつ簡潔にまとめてください。

- 会議名: (音声認識結果から推定、不明な場合は「[会議名未定]」と記載)
- 日時: (提供された日時情報を記載)
- 場所: (音声認識結果から推定、不明な場合は「[場所不明]」と記載)
- 出席者: (音声認識結果から推定、不明な場合は「[出席者不明]」と記載)
- 議題:
- 主要な議論内容:
- 決定事項: (担当者、期限も可能な限り含める)
- 懸案事項・TODOリスト: (担当者も可能な限り含める)
- 次回会議予定: (もし言及があれば)
- 備考: (その他特記事項)

フォーマットは読みやすく、構造化されていることが望ましいです。マークダウン形式などを活用してください。
"""

# --- 設定項目ここまで ---

def format_timestamp(seconds: float) -> str:
    """
    秒数を MM:SS.mmm または HH:MM:SS.mmm 形式の文字列に変換する。
    1時間未満ならMM:SS.mmm、1時間以上ならHH:MM:SS.mmm。
    """
    assert seconds >= 0, "non-negative timestamp expected"
    milliseconds = round(seconds * 1000)

    hours = milliseconds // (3600 * 1000)
    milliseconds %= (3600 * 1000)
    minutes = milliseconds // (60 * 1000)
    milliseconds %= (60 * 1000)
    secs = milliseconds // 1000
    milliseconds %= 1000

    if hours > 0:
        return f"{hours:02d}:{minutes:02d}:{secs:02d}.{milliseconds:03d}"
    else:
        return f"{minutes:02d}:{secs:02d}.{milliseconds:03d}"

def correct_text(text, corrections):
    """
    指定されたテキスト内の誤字を修正リストに基づいて置換する関数。
    """
    corrected_text = text
    for wrong, correct in corrections:
        corrected_text = corrected_text.replace(wrong, correct)
    return corrected_text

def generate_minutes_with_lm_studio(transcription, date_time_str):
    """
    LM StudioのAPIにリクエストを送信し、議事録を生成する関数。
    """
    print("LM Studioに議事録作成をリクエスト中...")

    user_prompt = f"""以下の音声認識結果に基づいて、議事録を作成してください。
会議の日時は {date_time_str} です。

--- 音声認識結果 ---
{transcription}
--- ここまで ---
"""

    payload = {
        "model": LM_STUDIO_MODEL_NAME,
        "messages": [
            {"role": "system", "content": LM_STUDIO_SYSTEM_PROMPT},
            {"role": "user", "content": user_prompt}
        ],
        "temperature": 0.7, # 生成されるテキストの多様性。低いと決定的、高いとランダム性が増す。
        "stream": False # ストリーミング応答は今回は不要
    }

    headers = {
        "Content-Type": "application/json"
    }

    try:
        response = requests.post(LM_STUDIO_API_URL, headers=headers, json=payload, timeout=300) # タイムアウトを300秒(5分)に設定
        response.raise_for_status()  # ステータスコードが200番台でなければ例外を発生
        response_data = response.json()

        if "choices" in response_data and len(response_data["choices"]) > 0:
            generated_text = response_data["choices"][0]["message"]["content"]
            print("LM Studioによる議事録生成完了。")
            return generated_text.strip()
        else:
            print("エラー: LM Studioからのレスポンスに期待する形式のデータが含まれていません。")
            print("レスポンス:", response_data)
            return None
    except requests.exceptions.ConnectionError:
        print(f"エラー: LM Studioサーバー ({LM_STUDIO_API_URL}) に接続できませんでした。")
        print("LM Studioが起動しており、APIサーバーが有効になっているか確認してください。")
        return None
    except requests.exceptions.Timeout:
        print("エラー: LM Studioサーバーへのリクエストがタイムアウトしました。")
        return None
    except requests.exceptions.RequestException as e:
        print(f"LM Studioへのリクエスト中にエラーが発生しました: {e}")
        if hasattr(e, 'response') and e.response is not None:
            try:
                print("エラーレスポンス:", e.response.json())
            except json.JSONDecodeError:
                print("エラーレスポンス (非JSON):", e.response.text)
        return None
    except json.JSONDecodeError:
        print("エラー: LM StudioからのレスポンスがJSON形式ではありません。")
        print("レスポンス本文:", response.text)
        return None


def main():
    print(f"音声ファイル: {AUDIO_FILE_PATH}")

    if not os.path.exists(AUDIO_FILE_PATH):
        print(f"エラー: 音声ファイルが見つかりません。指定されたパスを確認してください: {AUDIO_FILE_PATH}")
        return

    # 出力ディレクトリを作成 (存在しない場合)
    os.makedirs(RESULT_DIR, exist_ok=True)

    # 出力ファイルパスの定義
    base_output_filename = os.path.join(RESULT_DIR, AUDIO_FILE_NAME)
    raw_transcription_output_path = f"{base_output_filename}_transcription_raw.txt"
    corrected_transcription_output_path = f"{base_output_filename}_transcription_corrected.txt"
    minutes_output_path = f"{base_output_filename}_minutes_lm.txt"

    print(f"Whisperモデル ({MODEL_NAME}) をロード中... (初回は時間がかかります)")
    try:
        model = whisper.load_model(MODEL_NAME)
        print("モデルのロード完了。")
    except Exception as e:
        print(f"モデルのロード中にエラーが発生しました: {e}")
        return

    print("音声認識を開始します...")
    try:
        result = model.transcribe(AUDIO_FILE_PATH, language="ja", verbose=False) # verbose=False で認識中のログを抑制
        print("音声認識完了。")
    except Exception as e:
        print(f"音声認識中にエラーが発生しました: {e}")
        return

    # --- 生の音声認識結果 (タイムスタンプ付き) の準備と保存 ---
    print("生の音声認識結果 (タイムスタンプ付き) を準備中...")
    timestamped_transcription_lines_raw = []
    for segment in result["segments"]:
        start_time_str = format_timestamp(segment["start"])
        end_time_str = format_timestamp(segment["end"])
        text = segment["text"].strip()
        timestamped_transcription_lines_raw.append(f"[{start_time_str} --> {end_time_str}] {text}")
    
    raw_transcription_with_timestamps = "\n".join(timestamped_transcription_lines_raw)
    print("準備完了。")

    print(f"生の音声認識結果 (タイムスタンプ付き) を '{raw_transcription_output_path}' に保存中...")
    with open(raw_transcription_output_path, "w", encoding="utf-8") as f:
        f.write(raw_transcription_with_timestamps)
    print("保存完了。")
    print("\n--- 生の音声認識結果 (タイムスタンプ付き、先頭5行) ---")
    for line in timestamped_transcription_lines_raw[:5]:
        print(line)
    if len(timestamped_transcription_lines_raw) > 5:
        print("...")
    print("----------------------------------------------\n")

    # --- 誤字訂正後の音声認識結果 (タイムスタンプ付き) の準備と保存 ---
    print("誤字訂正後の音声認識結果 (タイムスタンプ付き) を準備中...")
    timestamped_transcription_lines_corrected = []
    full_text_segments_corrected_for_lm = [] 

    for segment in result["segments"]:
        start_time_str = format_timestamp(segment["start"])
        end_time_str = format_timestamp(segment["end"])
        original_text = segment["text"].strip()
        corrected_segment_text = correct_text(original_text, TYPO_CORRECTIONS)
        
        timestamped_transcription_lines_corrected.append(f"[{start_time_str} --> {end_time_str}] {corrected_segment_text}")
        full_text_segments_corrected_for_lm.append(corrected_segment_text)
    
    corrected_transcription_with_timestamps = "\n".join(timestamped_transcription_lines_corrected)
    corrected_text_for_lm_studio = " ".join(full_text_segments_corrected_for_lm).strip()
    print("準備完了。")

    print(f"誤字訂正後の認識結果 (タイムスタンプ付き) を '{corrected_transcription_output_path}' に保存中...")
    with open(corrected_transcription_output_path, "w", encoding="utf-8") as f:
        f.write(corrected_transcription_with_timestamps)
    print("保存完了。")
    print("\n--- 誤字訂正後の音声認識結果 (タイムスタンプ付き、先頭5行) ---")
    for line in timestamped_transcription_lines_corrected[:5]:
        print(line)
    if len(timestamped_transcription_lines_corrected) > 5:
        print("...")
    print("------------------------------------------------------\n")

    current_datetime_str = datetime.datetime.now().strftime("%Y年%m月%d日 %H:%M")

    # --- LM Studio APIを呼び出して議事録を生成 ---
    generated_minutes = generate_minutes_with_lm_studio(corrected_text_for_lm_studio, current_datetime_str)
    # --- ここまで ---

    if generated_minutes:
        print("\n--- LM Studioによって生成された議事録 (一部) ---")
        print(generated_minutes[:1000] + "..." if len(generated_minutes) > 1000 else generated_minutes)
        print("--------------------------------------\n")

        print(f"LM Studioによって生成された議事録を '{minutes_output_path}' に保存中...")
        with open(minutes_output_path, "w", encoding="utf-8") as f:
            f.write(f"生成日時: {current_datetime_str}\n")
            f.write("--- 生成された議事録 ---\n\n")
            f.write(generated_minutes)
        print("保存完了。")
    else:
        print("LM Studioによる議事録の生成に失敗したため、ファイルへの保存は行いません。")

    print(f"\n処理が完了しました。出力は '{RESULT_DIR}' フォルダに保存されています。")
    print(f" - 生の音声認識結果 (タイムスタンプ付き): {raw_transcription_output_path}")
    print(f" - 誤字訂正後の音声認識結果 (タイムスタンプ付き): {corrected_transcription_output_path}")
    if generated_minutes:
        print(f" - LM Studio生成議事録: {minutes_output_path}")

if __name__ == "__main__":
    if AUDIO_FILE_NAME == "your_audio_file_name_without_extension": # 初期値のままの場合の警告 (このサンプルでは別の初期値ですが、同様の趣旨)
        print("警告: スクリプト内の `AUDIO_FILE_NAME` がデフォルトのままの可能性があります。")
        print("実際の音声ファイル名(拡張子なし)に書き換えてから実行してください。")
        print(f"現在の設定: AUDIO_FILE_PATH = {AUDIO_FILE_PATH}")
    
    main()


コードのポイント解説

  • 設定項目
    • AUDIO_FILE_NAME
    • 処理したい音声ファイルの名前(拡張子なし)を指定します。
      auidoフォルダ内に.wavファイルがある想定です。

    • TYPO_CORRECTIONS
    • Whisperの認識結果によくあるフィラー(「えーと、」など)や、特定の単語の誤認識を事前に置換するためのリストです。ここで制度をカスタマイズできます。

    • LM_STUDIO_API_URL
    • LM Studioを起動し、"Server"タブでAPIサーバーを開始した際に表示されるURLを指定します。
      例:http://localhost:1234/v1/chat/completions

    • LM_STUDIO_MODEL_NAME
    • LM Studioで使用するモデルの名前を指定します。これはLM Studio上でロードしたモデルと一致させる必要があります。

    • LM_STUDIO_SYSTEM_PROMPT
    • これがLLMへ指示書(システムプロンプト)です。どのような情報を抽出し、どのような形式で出力してほしいかを詳細に記述することで、期待する議事録が得られやすくなります。プロンプトの工夫が、議事録の質を大きく左右します。

  • format_timestamp関数
  • Whisperが出力する秒数をHH:MM:ss形式のタイムスタンプ文字列に変換します。

  • correct_text関数
  • TYPO_CORRECTIONSに基づいて文字起こしテキストを整形します。

  • generate_minutes_with_lm_studio関数
  • 整形後の文字起こしテキストと現在日時を、システムプロンプトと共にLM StudioのAPIに送信し、議事録を生成させます。payload内のmessagesの構造はOpenAIのAPI形式に準拠しており、LM Studioもこれをサポートしています。
    temperatureで生成結果のランダム性を調整できます。

  • main関数
  • 全体の処理フローを制御します。
    1. Whisperモデルをロード。
    2. 音声ファイルを文字起こし。
    3. 生の文字起こし結果(タイムスタンプ付き)を保存。
    4. テキストを整形し、整形後の文字起こし結果(タイムスタンプ付き)を保存。
    5. ローカルLLMに議事録生成を依頼。
    6. 生成された議事録を保存。


実行方法

  1. LM Studioの準備
    • LM Studioを起動します。
    • 使いたいLLMを検索し、ダウンロード&ロードします。
    • 左側のメニューから「Developer」タブを選択し、Statusのトグルを押してサーバーをスタートさせます。表示されたAPIエンドポイントURLを控えておきます。

  2. 音声ファイルの準備
    • スクリプトと同じ階層にaudioというフォルダを作成し、その中に変換したい音声ファイルを置きます。
    • スクリプト内のAUDIO_FILE_NAMEを、拡張子を除いたファイル名に書き換えます。

  3. スクリプトの実行
    • ターミナル(コマンドプロンプト)を開き、スクリプトがあるディレクトリに移動します。
    • python your_script_name.pyを実行します。

  4. 結果の確認
    • 処理が完了すると、resultフォルダ内に以下のファイルが生成されます。
      • [音声ファイル名]_transcription_raw.txt
      • Whisperによる生の文字起こし結果(タイムスタンプ付き)。

      • [音声ファイル名]_transcription_corrected.txt
      • フィラーなどを除去した後の文字起こし結果(タイムスタンプ付き)。

      • [音声ファイル名]_minutes_lm.txt
      • LLMによって生成された議事録。


実行結果のサンプル

実際にどのような入力からどのような出力が得られるのか、イメージのしやすいようにサンプルとして私のYoutube動画での結果を3つ用意しました。

RakutenのLLM RakutenAI-2.0を試してみる【SLM】【LLM】

出力される生の文字起こし(一部)

[00:00.000 --> 00:19.560] はいどうもドビーです。最近では色々な企業がローカル環境で動作するLLMを公開していますが、先日楽天もローカルLLMを公開したとネットニュースになっていました。
[00:19.560 --> 00:33.800] 私も早速試してみたのですが、それなりの精度で動いており、かつminiのモデルについてはCPU環境でもそれなりの速度で推論できることがわかったので、今回動画で紹介していこうと思います。
[00:34.160 --> 00:40.860] 7Bのモデルについても少し触れるかとは思いますが、メインとしてはminiを扱っていこうと思います。
[00:42.340 --> 00:48.700] 早速試していきたいのですが、環境が整っていない人は環境構築から必要になります。
[00:48.700 --> 00:55.280] Pythonや必要ライブラリのインストールなどはブログでまとめているので、そちらを参考にしてください。

出力される議事録(一部)

生成日時: 2025年06月01日 14:08
--- 生成された議事録 ---

## 会議議事録

**会議名:** 楽天ローカルLLM動作検証に関する技術紹介 (推定)
**日時:** 2025年06月01日 14:08
**場所:** [場所不明]
**出席者:** ドビー (発言者) ([出席者不明])
**議題:** 楽天ローカルLLMの動作検証と紹介

### 主要な議論内容

*   **楽天ローカルLLM公開状況:** 近年、多くの企業がローカル環境で動作するLLMを公開しており、楽天もその一つとして公開された。
*   **ミニモデルの検証:** ミニモデルはCPU環境でも比較的良好な速度で推論可能であることが確認された。7Bモデルについても触れる予定だったが、今回はメインでミニモデルを紹介することになった。
*   **環境構築:** 動作にはPython3Kと必要なライブラリが必要。ブログ記事に環境構築手順をまとめているので、そちらを参照(概要欄へリンク)。
*   **サンプルコードの実行:** Hugging Faceで公開されている楽天LLMのサンプルコードを利用して検証を実施。モデルサイズ(7B, Mini)によって処理時間と知識量が異なる。Instruct/Chatモデルの説明。
*   **動作確認とパラメータ調整:**
    *   初期設定では英語での出力となるため、システムロールを日本語に修正し、日本語出力を試した。
    *   リアルタイム出力を実現するために、ストリーム対応させた。
    *   `max_length`パラメータの調整が必要である可能性について言及。
    *   要約機能とキーワード抽出機能をテストし、指示に対する精度を確認。細かい指示は難しい場合がある。
*   **7Bモデルとの比較:** 7Bチャットモデルを実行した結果、ミニモデルと比較して出力内容が理解しづらかった。

### 決定事項

*   **環境構築手順のブログ記事参照:** 環境構築に不慣れな参加者は、ブログ記事を参照する。(担当:ドビー、期限:随時)
*   **プロンプト調整の検討:** ミニモデルの精度をさらに向上させるため、プロンプトの調整を行うことを検討する。(担当:ドビー、期限:未定)

### 懸案事項・TODOリスト

*   **7B Instructモデルの検証:** チャット版ではなく、インストラクト版で7Bモデルを実行し、動作確認を行う。(担当:ドビー、期限:未定)
*   **コードの見直し:**  7Bモデルに関する問題点を解消するため、コードを見直す。(担当:ドビー、期限:未定)
*   **プロンプト調整の検証:** プロンプトを調整し、より詳細な指示に対応できるか検証する。(担当: ドビー, 期限: 未定)

### 次回会議予定

*   言及なし

### 備考

*   動画形式での情報共有。
*   CPU環境での動作確認。
*   Hugging Faceのモデルを利用。
*   動画への高評価とチャンネル登録のお願いがあった。

SB IntuitionsのSarashina2.2を試してみる【SLM】【LLM】

出力される生の文字起こし(一部)

[00:00.000 --> 00:19.420] はいどうもとびーです。先日楽天がローカルLLM楽天AI 2.0のモデルを公開しましたが、今度はSB Intuitionsが3つのSLMモデルを公開しました。
[00:19.420 --> 00:31.460] SLMモデルはLLMと似ているのですが、SLMはパラメーター数が小さく特定タスクの処理を得意とする軽量型の言語モデルと言われています。
[00:31.960 --> 00:37.740] 今回はSB Intuitionsが公開したSarasina 2.2を試してみます。
[00:39.060 --> 00:48.400] Sarasina 2.2の動作環境ですが、前回楽天AI 2.0を動作させた環境と同じもので動作可能でした。
[00:48.400 --> 00:57.240] 環境構築については私が公開した楽天AIを紹介するブログに記載してあるので、そちらを参考にしてください。
[00:57.660 --> 00:59.700] リンクは概要欄に貼っておきます。
[01:00.160 --> 01:11.660] 動画でも簡単に紹介すると、Python3K、ライブラリ管理のためPIPを準備し、ライブラリとして表示のものをインストールしておけば大丈夫だと思います。

出力される議事録(一部)

生成日時: 2025年06月01日 14:18
--- 生成された議事録 ---

## 会議議事録

**会議名:** SB Intuitions SLMモデル Sarasina 2.2 検証会 (推測)
**日時:** 2025年06月01日 14:18
**場所:** [場所不明]
**出席者:** [出席者不明] (音声認識結果から「とびー」氏のプレゼンテーションとして推定)
**議題:** SB Intuitionsが公開したSLMモデル Sarasina 2.2 の動作検証と性能評価

### 主要な議論内容

*   SB Intuitions が公開した SLM モデル Sarasina 2.2 を、楽天 AI 2.0 と同じ環境で試用。
*   Sarasina 2.2 は LLM と似ているが、パラメータ数が小さく特定タスクの処理に特化した軽量型言語モデルである。
*   0.5B, 1B, 3B の3つのモデルを検証し、サンプルコードを実行して動作確認を実施。
*   CPU環境での動作速度と精度について評価。
*   LLMとは何か、Hugging Faceとは何か、といった質問応答のテスト。
*   過去の Yahoo!ニュース記事の要約テスト (出力が若干長い傾向)。
*   キーワード抽出テスト(指示通りに抽出できていない場合も存在)。

### 決定事項

*   Sarasina 2.2 の環境構築は、楽天AIを紹介するブログを参照 (リンク概要欄に掲載)。
*   検証結果に基づき、用途に応じて最適なモデルを選択する。

### 懸案事項・TODOリスト

*   Sarasina 2.2 の要約機能の改善 (プロンプトチューニングで調整が必要な可能性)
*   キーワード抽出機能の改善(指示通りに抽出されるように調整が必要)
*   他のタスクや記事を用いた検証を実施し、更なる性能評価を行う。

### 次回会議予定

*   [次回会議予定なし] (言及なし)

### 備考

*   Sarasina 2.2 は CPU 環境でも高速に動作するが、要約やキーワード抽出などのタスク系は苦手な傾向が見られる。
*   3BモデルではCPUでの処理に時間がかかるものの、タスク実行の精度は向上している。
*   動画の内容はブログで詳細をまとめている (リンク概要欄に掲載)。



---

**注記:** 本議事録は音声認識結果に基づいて作成されたため、内容に誤りや不正確な点が含まれている可能性があります。

プロンプトチューニング勉強会【LLM】【SLM】

出力される生の文字起こし(一部)

[00:00.000 --> 00:10.880] LLMとSLMおよびプロンプトチューニングの勉強会
[00:10.880 --> 00:12.500] はいどうもトビーです
[00:12.500 --> 00:18.260] 私は普段ローカル環境で動作するLLMをいくつか紹介しています
[00:18.260 --> 00:24.540] なので今回はLLMやSLMについての勉強会を実施したいと思います
[00:24.540 --> 00:28.400] その中でプロンプトチューニングなども扱いたいと思います
[00:28.940 --> 00:29.920] アジェンダです
[00:29.920 --> 00:33.480] まずはこの勉強会で学ぶことを説明し
[00:33.480 --> 00:36.200] LLMとSLMについて説明
[00:36.200 --> 00:40.320] 使いこなす鍵としてプロンプトチューニングを紹介します

出力される議事録(一部)

生成日時: 2025年06月01日 14:24
--- 生成された議事録 ---

## 会議議事録

**会議名:** LLMとSLMおよびプロンプトチューニングの勉強会
**日時:** 2025年06月01日 14:24
**場所:** [場所不明] (オンライン会議を想定)
**出席者:** [出席者不明] (トビー氏が講師)

**議題:**

1.  LLMとSLMの基礎知識の解説
2.  プロンプトチューニングの重要性とテクニック紹介
3.  ローカル環境でのLLM/SLM実行のメリットと代表モデル紹介

**主要な議論内容:**

*   **LLM (大規模言語モデル):**
    *   特徴: 高い汎用性、文脈理解能力、豊富な知識。
    *   課題: 計算コスト、メモリ使用量、消費電力大。API経由での利用が一般的。
    *   代表例: GPT-4, Cloud3, Lama3
*   **SLM (小規模言語モデル):**
    *   特徴: 軽量、高速性、特定のタスクに絞ったファインチューニングによる高性能化。リソースの限られた環境での動作が可能。
    *   課題: LLMに比べて汎用性や複雑タスクへの対応能力が劣る。
    *   代表例: GEMA3, PHI3, 楽天AI
*   **プロンプトチューニング:**
    *   重要性: プロンプトの質がアウトプットの質に直結する。
    *   原則: 明確性、具体性、文脈、役割付与、例示、思考プロセス指示。
    *   手順: 目的設定 → プロンプト設計 → 実行・評価 → 改善を繰り返す。
*   **ローカル環境でのLLM/SLM実行:**
    *   メリット: データプライバシーとセキュリティの確保、コスト効率、カスタマイズ性。
    *   代表モデル: イライザ (LAMA3ベース)、JEMA (Google)、楽天AI。
    *   利用ツール例:LM Studio

**決定事項:**

*   参加者は、今回の勉強会で紹介された内容を元に、自身の業務や学習におけるLLM/SLMの活用を検討すること。(担当者:全出席者、期限:随時)

**懸案事項・TODOリスト:**

*   ローカル環境でのモデル実行を検討する。(担当者: 全出席者、期限:随時)
*   プロンプトチューニングの手法を実践的に試行錯誤し、効果的なプロンプトを作成する。(担当者: 全出席者、期限:随時)

**次回会議予定:** 未定

**備考:**

*   今回の勉強会は、LLM/SLMの基礎知識とプロンプトチューニングの重要性を理解するための入門編であった。
*   ローカル環境でのLLM/SLM実行は、セキュリティやコストを重視する際に有効な選択肢である。
*   具体的なタスクで試行錯誤し、アウトプットの質を高めることが重要である。

注意: 上記の出力例はあくまで理想的なものです。実際の出力は、音声の品質、会話の内容、LLMの性能、そして何よりもプロンプトの質に大きく左右されます。色々と試行錯誤して、最適な設定を見つけてみてください!


使ってみた感想・考察

実際にこのプログラムを試してみて、まず驚いたのはローカルLLMの応答速度の品質です。gemma-3-12b-itをLM Studioで動かすと、数分程度の会議音声から生成される文字起こしテキストであれば、数十秒から数分程度で議事録のドラフトを生成してくれました。 もちろん、クラウドの最先端モデルには及ばない部分もありますが、オフラインで、かつ無料でここまでできるのは本当に素晴らしいと感じました。

Whisper large-v3の文字起こし精度も非常に高く、専門用語が少ない一般的な会話であれば、実用的なレベルでテキスト化してくれます。ただ、早口や複数人の声が重なる部分は認識が難しくなる傾向がありました。このあたりは、マイクの品質や録音環境も影響しそうです。

改善点・今後の展望

  • 話者分離
  • 現状のスクリプトでは話者を区別していません。他の話者分離技術と組み合わせることで、「誰が何を言ったか」が明確な議事録が可能になります。
    しかし、今現在検証していますが、話者分離できても音声認識の精度が落ちる状況になっています。。

  • プロンプトのさらなる最適化
  • LM_STUDIO_SYSTEM_PROMPTをより具体的に、あるいは出力形式をさらに細かく指定することで、議事録の質をさらに向上させられるはずです。Few-shotプロンプティング(いくつかの良い例をプロンプトに含める方法)も有効かもしれません。

  • GUI化
  • 現在はコマンドラインで実行するスクリプトですが、GUIを付けて誰でも簡単に使えるツールに発展させるのも面白そうです。


まとめ

今回は、進化したローカルLLMの可能性と、Whisperと組み合わせた音声議事録作成プログラムを紹介しました。
LM Studioのようなツールの登場により、ローカルLLMの導入ハードルは格段に下がっています。プライバシーを保護しつつ、AIの恩恵を手元に享受できるローカルLLMは、今後ますます活躍の場を広げていくことでしょう。

もちろん、今回紹介したプログラムはあくまで一例です。これをベースに、皆さんのアイデアでさらに便利なツールへと進化させてみてください。 プログラミング初心者の方も、ぜひこの機会にローカルLLMの世界に足を踏み入れてみてはいかがでしょうか?

今回はこのへんで、ではまた!

コメント

このブログの人気の投稿

PowerAppsで座席表を作成する

Power Automateでファイル名から拡張子を取得

Power AutomateでTeamsのキーワードをトリガーにする