One-shot 本機會議轉錄與會議記錄 Instruction
這份 instruction 是給 agent 用的。目標是:使用者提供一個本機錄音或影片檔後,agent 一次完成媒體導入、Modal + VibeVoice 轉錄、品質檢查、會議記錄整理,最後在需要時請使用者確認 speaker 身份。
整個流程只根據使用者指定的本機檔案工作。不要假設有任何特定外部系統。
目標輸出
每次處理一個 media file,產出:
workdir/
├── input.meta.json
├── audio.ogg
├── transcript.vibevoice.json
├── transcript.txt
├── speaker-review.md
└── meeting-note.md
input.meta.json:原始檔資訊與處理參數。audio.ogg:標準化後的音訊。transcript.vibevoice.json:VibeVoice 原始轉錄 JSON。transcript.txt:有 timestamp 與 speaker label 的逐字稿。speaker-review.md:speaker 確認表,必要時交給使用者回覆。meeting-note.md:整理後的會議記錄。
Phase 1: 導入本機錄音或影片
使用者可能提供:
- 絕對路徑:
/Users/example/Downloads/meeting.mp4 - 相對路徑:
./recordings/sync.m4a - shell 展開路徑:
~/Desktop/interview.wav - 一個資料夾中的單一 media file
處理規則:
- 展開路徑,確認檔案存在且是 regular file。
- 只處理一個檔案。若資料夾中有多個 media file,請使用者指定其中一個。
- 使用
ffprobe讀取 duration、stream、codec 等基本資訊。 - 建立獨立 workdir,避免覆蓋先前輸出。
- 將原始檔案資訊寫入
input.meta.json。
建議命令:
INPUT="/absolute/path/to/input.mp4"
WORKDIR="./local-meeting-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$WORKDIR"
ffprobe -v quiet -print_format json -show_format -show_streams "$INPUT" \
> "$WORKDIR/input.ffprobe.json"
shasum -a 256 "$INPUT" > "$WORKDIR/input.sha256"
input.meta.json 至少記錄:
{
"source_file": "/absolute/path/to/input.mp4",
"source_sha256": "<sha256>",
"duration_seconds": 0,
"created_at": "YYYY-MM-DDTHH:MM:SSZ",
"engine": "VibeVoice via Modal",
"audio_file": "audio.ogg"
}
Phase 2: 標準化音訊
先把任何影片或錄音轉成單聲道音訊,再交給 VibeVoice。這能降低影片容器、codec、聲道數造成的不穩定。
建議使用 Opus 64kbps mono:
ffmpeg -y -i "$INPUT" \
-vn \
-c:a libopus \
-b:a 64k \
-ac 1 \
"$WORKDIR/audio.ogg" \
-loglevel warning
如果 VibeVoice runner 對 WAV 更穩定,也可以產出 24kHz mono WAV:
ffmpeg -y -i "$INPUT" \
-vn \
-ac 1 \
-ar 24000 \
"$WORKDIR/audio.wav" \
-loglevel warning
轉檔後再跑一次 ffprobe,確認 duration 沒有明顯變短。若標準化音訊 duration 比原始 media 少很多,先停止並回報使用者,不要直接進轉錄。
Phase 3: 配合 Modal 與 VibeVoice 運作
前提:
- 本機已安裝並登入 Modal CLI。
- 有一個可執行的 VibeVoice Modal app,例如
modal_vibevoice.py。 - VibeVoice runner 接受本機 audio path,並輸出 JSON。
典型命令:
modal run modal_vibevoice.py \
--audio-path "$WORKDIR/audio.ogg" \
--hotwords "$HOTWORDS"
預期輸出是一個 JSON transcript,例如:
{
"segments": [
{
"Start": 0.0,
"End": 4.2,
"Speaker": 1,
"Content": "..."
}
],
"text": "...",
"model": "microsoft/VibeVoice-ASR-HF",
"truncated": false,
"coverage_end": 1234.5
}
將輸出固定整理成:
$WORKDIR/transcript.vibevoice.json
長音檔處理
長會議不要一次塞完整音訊給模型。建議策略:
- 25 分鐘左右為一個 chunk。
- chunk 之間保留約 20 秒 overlap。
- 優先使用 silence-aware split;沒有工具時再用固定時間切段。
- 每個 chunk 分別跑 VibeVoice。
- 合併時把每段 timestamp 加回全域 offset。
- overlap 區間要做去重,避免同一句話出現兩次。
粗略 ffmpeg 切段範例:
mkdir -p "$WORKDIR/chunks"
ffmpeg -y -i "$WORKDIR/audio.ogg" \
-f segment \
-segment_time 1500 \
-c copy \
"$WORKDIR/chunks/chunk_%03d.ogg"
若使用固定切段,請在合併時特別檢查邊界是否漏字或重複。silence-aware split 通常更好。
Phase 4: 轉錄 Best Practices
使用 hotwords
轉錄前建立 hotwords,放入:
- 參與者姓名與常見拼法。
- 組織、產品、專案、客戶、地名。
- 專有名詞、縮寫、技術名詞。
- 會議中可能反覆出現但容易誤聽的詞。
範例:
Participants: Alice Chen, Bob Lee, Charlie Wang.
Products: Atlas, Billing API, Mobile SDK.
Terms: activation, retention, onboarding, API gateway, Q3 roadmap.
hotwords 只用來提升辨識,不可用來補寫逐字稿沒有出現的內容。
Coverage gate
每次轉錄後都要檢查 transcript 是否覆蓋完整音訊。
檢查點:
truncated不應為true。- 最後一個 segment 的 end time 應接近音訊 duration。
- 建議 coverage ratio 至少 97%。
- segments 不應為空。
概念檢查:
coverage_ratio = transcript_last_end / audio_duration
assert not transcript.get("truncated")
assert coverage_ratio >= 0.97
assert len(transcript["segments"]) > 0
若失敗:
- 不要使用 partial transcript 產出正式會議記錄。
- 把失敗 JSON 改名保存,例如
transcript.vibevoice.truncated.json。 - 使用更小 chunk 重新轉錄。
- 重新合併後再跑 coverage gate。
保留 timestamp
所有中間產物都要保留 timestamp。整理會議記錄時可以摘要,但遇到 action item、決議、關鍵數字、爭議點,最好能追溯到原始時間。
Speaker label 不等於人名
VibeVoice 的 Speaker 1、Speaker 2 只是聲音分群,不是身份確認。除非使用者提供對照或 agent 取得明確確認,不要把 speaker label 直接改成人名。
不要用二手摘要當 ground truth
會議記錄只能根據逐字稿整理。檔名、行事曆標題、使用者口頭背景、模型猜測都只能作為輔助脈絡,不能拿來補出逐字稿沒有的決議或 action item。
Phase 5: 產生可讀逐字稿
將 VibeVoice JSON 轉成文字格式,方便使用者檢查,也方便後續 LLM 摘要。
格式:
[00:00:02-00:00:08] Speaker 1: 今天先討論下週上線前還缺什麼。
[00:00:09-00:00:15] Speaker 2: 我這邊主要卡在付款流程的測試資料。
轉換規則:
- 使用
Start/End轉成HH:MM:SS或MM:SS。 - 使用
Speaker產生Speaker N。 - 使用
Content作為文字。 - 跳過空白、純音樂、純噪音 segment。
輸出:
$WORKDIR/transcript.txt
Phase 6: 整理成會議記錄
讀取 transcript.txt,整理成 meeting-note.md。不要逐字稿流水帳,請依議題歸納。
會議記錄模板:
# <會議標題>
## Overview
**來源檔案:** `<file name>`
**時長:** `<duration>`
**轉錄引擎:** VibeVoice via Modal
**Speaker 狀態:** 未確認 / 已確認
### Summary
用 80-150 字說明這場會議主要討論什麼、最後形成哪些方向。
## Topics
### 1. <議題標題>
- **狀態:** 已決議 / 未決議 / 討論中
- **結論:** <一句話描述目前結論>
- **討論重點:**
- <Speaker 1> 提到...
- <Speaker 2> 補充...
- **關鍵數字 / 日期 / 條件:**
- <數字或日期>:<上下文>
- **相關時間:** 00:12:30-00:18:45
## Decisions
- <決議內容>(evidence: 00:23:10)
## Action Items
- [ ] <Speaker 或姓名>:<任務內容>(deadline: YYYY-MM-DD / 未指定;evidence: 00:31:22)
## Open Questions
- <問題>:<為什麼尚未解決 / 誰要跟進>
## Risks / Follow-up
- <需要後續確認的風險、缺口或低信心段落>
可直接使用的會議記錄整理 Prompt
將 transcript.txt 交給 LLM 時,可以使用以下 prompt。若 transcript 太長,先依時間分段使用同一個 prompt 抽出 topics / decisions / action items,再用第二輪合併成完整 meeting-note.md。
你是一位專業的會議記錄整理助手。請只根據我提供的逐字稿整理會議記錄,不要使用外部資訊,不要根據檔名或背景描述補出逐字稿沒有的內容。
輸入:
- 逐字稿包含 timestamp 與 speaker label,例如 `[00:12:30-00:12:45] Speaker 1: ...`
- speaker label 可能尚未確認身分。除非我提供 speaker mapping,否則請保留 `Speaker 1`、`Speaker 2` 這種匿名標籤。
- 若我提供 speaker mapping,請使用確認後姓名,但不要猜測未確認的 speaker。
輸出語言:
- 使用使用者指定的語言。
- 若使用者沒有指定,使用會議主要語言。
任務:
1. 先判斷這場會議的主要目的與主軸。
2. 依照真實討論內容切分 topics,不要按照逐字稿時間流水帳重寫。
3. 對每個 topic,整理目前狀態、結論、討論重點、關鍵數字/日期/條件、相關 timestamp。
4. 列出所有明確 decisions。沒有明確共識的內容不要寫成決議。
5. 列出所有 action items。每個 action item 必須有 owner;若逐字稿沒有 owner,標為 `未指定`。deadline 不明時標為 `未指定`。
6. 列出會議結束時仍未解決的 open questions。
7. 標記低信心或需要使用者確認的段落,例如音訊不清、多人重疊、speaker 身分不確定。
品質規則:
- 不要虛構參與者、決議、owner、deadline、數字或理由。
- 任何數字、日期、金額、比例、版本號、承諾時程都要保留在相關 topic 中。
- action item 必須是會議中明確承諾或明確指派的事。
- 若 speaker 未確認,使用 `Speaker N`,不要自行替換成人名。
- 摘要可以改寫,但決議、任務、數字與風險必須能追溯到 timestamp。
- 如果逐字稿沒有足夠證據,請寫「逐字稿未明確說明」。
請輸出以下 Markdown 結構:
# <會議標題>
## Overview
**來源檔案:** <若有提供則填,否則寫未指定>
**時長:** <若有提供則填,否則寫未指定>
**Speaker 狀態:** 未確認 / 已確認 / 部分確認
### Summary
80-150 字說明這場會議主要討論什麼、形成哪些方向,以及還有哪些未決事項。
## Topics
### 1. <議題標題>
- **狀態:** 已決議 / 未決議 / 討論中
- **結論:** <一句話描述目前結論;沒有結論就寫未形成明確結論>
- **討論重點:**
- <Speaker 或姓名>:<重點>
- <Speaker 或姓名>:<重點>
- **關鍵數字 / 日期 / 條件:**
- <數字或日期>:<上下文>
- **相關時間:** <timestamp range>
## Decisions
- <決議內容>(evidence: <timestamp>)
## Action Items
- [ ] <owner>:<任務內容>(deadline: <YYYY-MM-DD / 未指定>;evidence: <timestamp>)
## Open Questions
- <問題>:<為什麼尚未解決 / 誰可能需要跟進>
## Risks / Follow-up
- <低信心內容、需要使用者確認的 speaker、音訊品質問題或後續建議>
以下是逐字稿:
<TRANSCRIPT>
貼上 transcript.txt 全文
</TRANSCRIPT>
整理原則:
- 使用使用者指定的語言;若未指定,使用會議主要語言。
- 不虛構參與者、決議、deadline、owner。
- 明確區分「已決議」與「仍在討論」。
- action item 必須有明確動詞與 owner;沒有 owner 就標
未指定。 - 會議中出現的數字、日期、金額、比例、版本號都要收進相關議題。
- 對低音質或重疊說話造成的不確定內容,標記
低信心。
Phase 7: Speaker 確認
轉錄完成後,請產出 speaker-review.md,讓使用者可以確認 speaker 是誰。這一步可以在會議記錄前或後執行;若確認後再更新 note,品質會更好。
產生 speaker 摘要
針對每個 speaker,整理:
- speaker label。
- 總發言時間。
- 主要出現時段。
- 2-3 段最清楚的代表性 timestamp。
- 若使用者提供參與者名單,可列出「可能候選」,但不能直接當作事實。
範例:
# Speaker Review
請回覆 mapping,例如:
```text
Speaker 1 = Alice
Speaker 2 = Bob
Speaker 3 = Unknown
```
## Speakers
### Speaker 1
- **總發言時間:** 約 18 分鐘
- **主要出現時段:** 00:00-12:30、28:10-42:05
- **代表片段:**
- 00:02:14-00:02:32:「我們今天先把 launch blocker 收斂。」
- 00:31:08-00:31:24:「我會在週五前補完測試資料。」
- **可能候選:** Alice / Bob / 不確定
### Speaker 2
- **總發言時間:** 約 9 分鐘
- **代表片段:**
- 00:05:40-00:05:58:「付款流程還有一個 edge case。」
- **可能候選:** 不確定
套用使用者確認
使用者回覆 mapping 後:
- 建立
speaker-map.json。 - 將
transcript.txt中的 speaker label 替換為確認後姓名。 - 重新產出
meeting-note.md。 - 在 note 中標記
Speaker 狀態:已確認。
如果使用者只確認部分 speaker,保留其他匿名 label,例如 Speaker 3。不要自行猜完。
Final Response
完成後回覆使用者:
- 轉錄是否完整通過 coverage gate。
- 逐字稿路徑。
- 會議記錄路徑。
- speaker 是否需要確認。
- 若需要,附上
speaker-review.md的摘要與請使用者回覆的 mapping 格式。
範例:
已完成轉錄與會議記錄。
- Coverage: 98.6%,未偵測到 truncated。
- Transcript: workdir/transcript.txt
- Meeting note: workdir/meeting-note.md
- Speaker 尚未確認:請用 `Speaker 1 = ...` 格式回覆,我會再更新逐字稿與會議記錄。