7
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

議事録生成ツール

7
Posted at

実務向けにするなら、**「議事録生成ツール」ではなく「音声資産からレビュー可能な業務成果物を生成するシステム」**として設計するのがよいです。

採用前提として、Ollama は JSON Schema による structured outputs をサポートしており、議事録JSONの安定化に使えます。faster-whisper は CTranslate2 ベースで、OpenAI Whisper 実装より高速・省メモリを掲げています。python-pptx は PowerPoint の作成・読み込み・更新、python-docx は Word 文書の作成・更新に使えるライブラリです。話者分離には pyannote.audio が実務候補になります。

以下をそのまま「実務向け設計書」として使える形で作成します。

ローカルLLM議事録生成システム 実務向け設計書

  1. システム目的

本システムは、会議の音声ファイルまたは動画ファイルを入力として、文字起こし、要約、議事録JSON化、Word議事録、PowerPoint報告資料を自動生成するローカル環境向け議事録生成システムである。

クラウドAIに機密情報を送信せず、社内PCまたは社内サーバー上で処理することを前提とする。

  1. 基本方針

重要なのは、LLMに音声や動画を直接渡さないこと。

実務では以下の流れにする。

audio / video

音声抽出

音声前処理

ASR文字起こし

話者分離・整形

ローカルLLM

議事録JSON

Pydantic検証

人間レビュー

docx / pptx 出力

LLMの役割は、音声認識ではなく、文字起こし済みテキストを構造化することに限定する。

領域 採用技術 目的
UI Streamlit 初期MVP、社内ツール向け
API FastAPI 将来的なAPI化、バッチ処理
音声抽出 ffmpeg mp4 / mov から wav を生成
文字起こし faster-whisper ローカルASR
話者分離 pyannote.audio 誰が話したかの推定
LLM Ollama ローカルLLM実行
JSON制御 Ollama structured outputs + Pydantic JSON破綻防止
Word生成 python-docx 詳細議事録
PowerPoint生成 python-pptx 報告用資料
設定管理 YAML モデル、出力形式、テンプレート制御
ログ logging / SQLite 処理履歴、エラー追跡
ジョブ管理 SQLite / Redis Queue 長時間処理の管理

4. システム構成

local-minutes-system/
├── app.py
├── api.py
├── worker.py
├── requirements.txt
├── config.yaml
├── input/
├── work/
│   ├── audio/
│   ├── transcript/
│   ├── diarized/
│   └── logs/
├── output/
│   ├── json/
│   ├── docx/
│   └── pptx/
├── templates/
│   ├── minutes_template.docx
│   └── minutes_template.pptx
├── prompts/
│   ├── minutes_prompt.txt
│   ├── review_prompt.txt
│   └── correction_prompt.txt
├── dictionaries/
│   ├── company_terms.yaml
│   ├── member_names.yaml
│   └── project_terms.yaml
├── modules/
│   ├── audio_extract.py
│   ├── audio_preprocess.py
│   ├── transcribe.py
│   ├── diarize.py
│   ├── transcript_cleaner.py
│   ├── llm_client.py
│   ├── schema.py
│   ├── validator.py
│   ├── export_docx.py
│   ├── export_pptx.py
│   ├── review.py
│   └── logger.py
└── database/
    └── jobs.db

5. 実務フロー

5.1 ファイル投入

対応形式。

音声:

  • wav
  • mp3
  • m4a
  • flac
    動画:
  • mp4
  • mov
  • mkv

動画の場合は ffmpeg で音声を抽出する。

ffmpeg -i input.mp4 -vn -acodec pcm_s16le -ar 16000 -ac 1 output.wav

5.2 音声前処理

実務では、文字起こし前に以下を行う。

  • モノラル化
  • 16kHz変換
  • 無音区間の処理
  • 音量正規化
  • ノイズが強いファイルの警告

初期MVPでは最低限、以下だけでよい。

  • wav化
  • 16kHz
  • mono

5.3 文字起こし

最初は faster-whisper を使う。

from faster_whisper import WhisperModel
model = WhisperModel(
    "large-v3",
    device="cuda",
    compute_type="float16"
)
segments, info = model.transcribe(
    "work/audio/meeting.wav",
    language="ja",
    vad_filter=True
)
transcript = []
for segment in segments:
    transcript.append({
        "start": segment.start,
        "end": segment.end,
        "text": segment.text.strip()
    })

GPUが弱い場合。

環境 推奨モデル
CPUのみ small / medium
RTX 3060クラス medium / large-v3
RTX 4070 Ti SUPER以上 large-v3
精度優先 large-v3
速度優先 small / medium

6. 話者分離の扱い

実務では、最初から完璧な話者名を出そうとしない。

話者分離は次の2段階に分ける。

Phase 1

SPEAKER_00
SPEAKER_01
SPEAKER_02

のような仮ラベルでよい。

Phase 2

レビュー画面で人間が名前を割り当てる。

SPEAKER_00 → 田中さん
SPEAKER_01 → 佐藤さん
SPEAKER_02 → 鈴木さん

実務ではこの方が安全。

LLMに勝手に参加者名を推測させない。

7. 中間JSON設計

このシステムの中心は minutes.json。

{
  "metadata": {
    "meeting_title": "string",
    "date": "YYYY-MM-DD",
    "start_time": "HH:mm or null",
    "end_time": "HH:mm or null",
    "source_file": "string",
    "language": "ja",
    "created_at": "YYYY-MM-DDTHH:mm:ss"
  },
  "participants": [
    {
      "name": "string",
      "role": "string or null",
      "speaker_label": "SPEAKER_00 or null"
    }
  ],
  "executive_summary": {
    "summary": "string",
    "key_points": [
      "string"
    ]
  },
  "agenda": [
    {
      "title": "string",
      "discussion": "string",
      "decisions": [
        "string"
      ],
      "issues": [
        "string"
      ],
      "related_speakers": [
        "string"
      ]
    }
  ],
  "decisions": [
    {
      "decision": "string",
      "reason": "string or null",
      "owner": "string or null"
    }
  ],
  "action_items": [
    {
      "task": "string",
      "owner": "string or null",
      "due_date": "YYYY-MM-DD or null",
      "priority": "high | medium | low",
      "status": "not_started | in_progress | done | pending",
      "source": "string or null"
    }
  ],
  "risks": [
    {
      "risk": "string",
      "impact": "high | medium | low | unknown",
      "countermeasure": "string or null"
    }
  ],
  "open_questions": [
    "string"
  ],
  "next_meeting_topics": [
    "string"
  ],
  "review": {
    "requires_human_review": true,
    "warnings": [
      "string"
    ]
  }
}

8. Pydanticスキーマ

from pydantic import BaseModel, Field
from typing import List, Optional, Literal
class Metadata(BaseModel):
    meeting_title: str
    date: str
    start_time: Optional[str] = None
    end_time: Optional[str] = None
    source_file: str
    language: str = "ja"
    created_at: str
class Participant(BaseModel):
    name: str
    role: Optional[str] = None
    speaker_label: Optional[str] = None
class ExecutiveSummary(BaseModel):
    summary: str
    key_points: List[str] = Field(default_factory=list)
class AgendaItem(BaseModel):
    title: str
    discussion: str
    decisions: List[str] = Field(default_factory=list)
    issues: List[str] = Field(default_factory=list)
    related_speakers: List[str] = Field(default_factory=list)
class Decision(BaseModel):
    decision: str
    reason: Optional[str] = None
    owner: Optional[str] = None
class ActionItem(BaseModel):
    task: str
    owner: Optional[str] = None
    due_date: Optional[str] = None
    priority: Literal["high", "medium", "low"] = "medium"
    status: Literal["not_started", "in_progress", "done", "pending"] = "not_started"
    source: Optional[str] = None
class Risk(BaseModel):
    risk: str
    impact: Literal["high", "medium", "low", "unknown"] = "unknown"
    countermeasure: Optional[str] = None
class Review(BaseModel):
    requires_human_review: bool = True
    warnings: List[str] = Field(default_factory=list)
class MeetingMinutes(BaseModel):
    metadata: Metadata
    participants: List[Participant] = Field(default_factory=list)
    executive_summary: ExecutiveSummary
    agenda: List[AgendaItem] = Field(default_factory=list)
    decisions: List[Decision] = Field(default_factory=list)
    action_items: List[ActionItem] = Field(default_factory=list)
    risks: List[Risk] = Field(default_factory=list)
    open_questions: List[str] = Field(default_factory=list)
    next_meeting_topics: List[str] = Field(default_factory=list)
    review: Review

9. LLMプロンプト設計

9.1 基本プロンプト

あなたは企業向け議事録作成アシスタントです。
以下の文字起こしを読み、指定されたJSONスキーマに厳密に従って議事録データを作成してください。
重要ルール:

  • JSON以外の文章を出力しない
  • 担当者名を推測で作らない
  • 明確に発言されていない期限は null にする
  • 決定事項、議論内容、課題、ToDoを分ける
  • 雑談、相槌、重複表現は要約する
  • 不明点は open_questions に入れる
  • 確認が必要な内容は review.warnings に入れる
  • 医療、法務、契約、金額、納期に関する内容は必ず review.warnings に入れる
  • action_items には、実行すべき作業だけを入れる
  • 「検討する」「確認する」などもタスクとして扱う
  • 優先度が不明な場合は medium にする
    文字起こし:
    {transcript}

10. 実務向けの安全設計

Whisper系ASRは便利ですが、音声にない内容を文字起こしとして生成する、いわゆる hallucination リスクが研究されています。特に無音、雑音、低品質音声では注意が必要です。OpenAI Whisper 自体は多言語ASR・翻訳・言語識別に対応するモデルとして公開されていますが、実務利用ではレビュー前提にするべきです。

そのため、以下を必須にする。

  • 原文文字起こしを保存する
  • LLM要約後のJSONを保存する
  • docx / pptx はレビュー後に確定出力する
  • 金額、日付、契約、納期、担当者は警告対象にする
  • 音声品質が悪い場合は警告を出す
  • LLM出力をPydanticで必ず検証する

  1. レビュー画面で必要な機能

Streamlitで作る場合、最低限これを用意する。

  1. 音声/動画アップロード
  2. 文字起こし結果表示
  3. 話者ラベル編集
  4. 会議タイトル編集
  5. 参加者編集
  6. 要約編集
  7. 決定事項編集
  8. アクションアイテム編集
  9. 警告一覧表示
  10. docx / pptx ダウンロード

重要なのは、自動生成して終わりにしないこと。

実務では、以下の流れにする。

AI生成

人間レビュー

修正

確定

docx / pptx 出力

  1. docx出力仕様

Wordは詳細議事録として使う。

構成

議事録

  1. 会議概要
  • 会議名
  • 日付
  • 開始時刻
  • 終了時刻
  • 参加者
  1. エグゼクティブサマリー
  • 全体要約
  • 重要ポイント
  1. 議題別内容
  • 議題名
  • 議論内容
  • 決定事項
  • 課題
  1. 決定事項一覧
  2. アクションアイテム
  • タスク
  • 担当者
  • 期限
  • 優先度
  • ステータス
  1. リスク・懸念事項
  2. 未確認事項
  3. 次回確認事項
  4. AI生成に関する注意
  • 本議事録はAIにより生成され、人間レビューを前提とする

  1. pptx出力仕様

PowerPointは報告・共有用に使う。

Slide 1: 会議タイトル
Slide 2: エグゼクティブサマリー
Slide 3: 主要決定事項
Slide 4: 主要論点
Slide 5: 課題・リスク
Slide 6: アクションアイテム
Slide 7: 未確認事項
Slide 8: 次回確認事項

pptxは詳細を書きすぎない。

目安。

1スライド = 3〜5項目
1項目 = 1〜2行

詳細はdocxに寄せる。

  1. 実装優先順位

Phase 1: MVP

まずここまで作る。

  • Streamlit UI
  • ファイルアップロード
  • ffmpeg音声抽出
  • faster-whisper文字起こし
  • OllamaでJSON生成
  • Pydantic検証
  • docx生成
  • pptx生成

この段階では、話者分離なしでよい。

Phase 2: 実務最低ライン

  • 話者分離
  • レビュー画面
  • 用語辞書
  • 参加者辞書
  • 再生成ボタン
  • JSON修正機能
  • エラー時の再試行
  • ログ保存

Phase 3: 社内運用レベル

  • ユーザー認証
  • 権限制御
  • 処理履歴
  • テンプレート選択
  • プロジェクト別辞書
  • 社内固有名詞補正
  • 議事録承認フロー
  • RAG連携
  • バッチ処理

  1. config.yaml案

system:
  app_name: "Local Minutes System"
  language: "ja"
  timezone: "Asia/Tokyo"
asr:
  engine: "faster-whisper"
  model: "large-v3"
  device: "cuda"
  compute_type: "float16"
  vad_filter: true
audio:
  sample_rate: 16000
  channels: 1
  format: "wav"
llm:
  provider: "ollama"
  model: "qwen3:14b"
  temperature: 0.1
  structured_output: true
  max_retries: 2
output:
  generate_docx: true
  generate_pptx: true
  require_review_before_export: true
templates:
  docx: "templates/minutes_template.docx"
  pptx: "templates/minutes_template.pptx"
review:
  warn_keywords:
    - "契約"
    - "金額"
    - "納期"
    - "法律"
    - "医療"
    - "個人情報"
  require_human_review: true

16. ローカルLLM選定

日本語議事録では、最初は以下が現実的。

用途 モデル候補
軽量 Qwen系 7B / 8B
バランス Qwen 14B
精度重視 Qwen 32B
CPU運用 7B量子化
GPU運用 14B以上

OllamaのQwen3ライブラリでは、Qwen3系モデルが提供されています。ローカルLLMはモデル更新が速いため、実装では config.yaml でモデル名を差し替えられるようにしておくのが安全です。

17. 実務で重要な例外処理

問題 対応
音声が長すぎる チャンク分割
LLMのコンテキストに入らない 議題単位で分割要約
JSONが壊れる Pydantic検証後に再生成
担当者が不明 owner を null
期限が不明 due_date を null
話者分離が不正確 レビュー画面で修正
専門用語が誤認識 用語辞書で補正
無音や雑音が多い 警告表示
出力文書が長すぎる docx詳細、pptx要約に分離

  1. 実務向けの最終構成

最終的にはこの構成がよいです。

[User]

[Streamlit UI]

[Job Manager]

[Audio Processor]

[ASR: faster-whisper]

[Diarization: pyannote]

[Transcript Cleaner]

[Local LLM: Ollama]

[JSON Validator: Pydantic]

[Review UI]

[Exporter]
├── docx
└── pptx

  1. MVPで作るべき最小機能

最初に作るなら、これで十分です。

  1. 動画/音声ファイルをアップロード
  2. ffmpegでwav化
  3. faster-whisperで文字起こし
  4. Ollamaで議事録JSON生成
  5. Pydanticで検証
  6. JSONを画面表示
  7. docx生成
  8. pptx生成
  9. ダウンロード

話者分離、RAG、承認フローは後回しでよいです。

  1. 結論

実務向けの正解構成は以下です。

Input audio/video

Audio extraction

Audio preprocessing

ASR transcription

Transcript cleanup

Local LLM structured output

Validated minutes JSON

Human review

docx / pptx generation

Final output

この設計の肝は3つです。

  1. 音声処理とLLM処理を分ける
  2. JSONを中間データの中心にする
  3. 人間レビューを正式フローに入れる

これで、単なる自動要約ツールではなく、社内で運用できる議事録生成システムになります。

7
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?