會議逐字稿

本使用情境以 examples/meeting_transcriber.py 為主軸,這是一支約 280 行完整腳本,可加入 Discord 伺服器語音頻道,逐字辨識並即時標示發言者名稱,寫入本機 Markdown 檔,離開時再透過 Claude 產生「摘要 / 決議事項 / 行動項目 / 未解決問題」。

這個功能存在的核心原因是,最常見的語音需求不是「讓機器人發聲」,而是「要可搜尋的逐字紀錄」。本文件示範的就是這一點。

功能說明

  • 以伺服器中的語音頻道 ID 連線。
  • 串流音訊到 Deepgram(或 OpenAI Whisper),將每位發言者的最終結果即時輸出到主控台。
  • 每一列都會附加到 ~/.discli/transcripts/meeting-<YYYYMMDD-HHMMSS>.md
  • 按下 Ctrl+C 時,將完整逐字稿送入 Claude,並把結構化摘要附加到同一個檔案。

不包含的內容

  • 無法在 DM 或群組 DM 語音使用:機器人權杖無法加入這類語音。
  • 會議中不會即時總結:只在離開時一次輸出。
  • 不支援跨會議辨識同一位說話者:顯示名稱取自當前伺服器成員資料。

安裝設定

Terminal window
pip install 'discord-cli-agent[voice,deepgram]' claude-agent-sdk
discli config set token YOUR_BOT_TOKEN
export DEEPGRAM_API_KEY=...

先檢查:

Terminal window
discli doctor

你應該看到 CORE、VOICE 都為綠色,且 DEEPGRAM_API_KEY 在 STT 欄位有設定。

Note

Claude 總結使用你既有的 Claude Code 驗證,不需再額外配置 Anthropic API Key。

執行方式

先取得語音頻道 ID(Discord 開啟開發者模式,對目標語音頻道按右鍵複製 ID,或用 discli channel list --type voice):

Terminal window
python examples/meeting_transcriber.py 1016638171854938152

若要改用 OpenAI Whisper:

Terminal window
export OPENAI_API_KEY=...
python examples/meeting_transcriber.py 1016638171854938152 --stt openai

會議過程中,主控台會出現逐字紀錄:

Connected as MyBot#1234 (12345)
Listening to #standup。逐字稿:/home/me/.discli/transcripts/meeting-20260514-103000.md
按 Ctrl+C 停止後會自動產生摘要。
- **[10:30:14] Roy:** 好,大家先依序報告——昨天大家做了什麼?
- **[10:30:22] Sara:** 完成了 auth 轉移,並開始進行速率限制器
- **[10:30:35] Roy:** 很好,現在有誰卡住了嗎?

按 Ctrl+C 結束:

停止監聽…
共處理 47 行文字…
摘要成本:$0.0084
=== 會議摘要 ===
## 摘要
團隊回顧昨日工作並規劃今日任務。Sara 已完成 auth
移轉,接著進行 rate limiter。
## 關鍵決策
- 本次移轉先採用 read-path-first 流程。
## 行動項目
- Sara:今天完成 rate limiter。
- Roy:在週五前完成移轉 runbook 文件。
## 未解決問題
- 舊的會議會話要補回填,還是直接過期?
完整逐字稿與摘要已儲存到: /home/me/.discli/transcripts/meeting-20260514-103000.md

輸出檔案

輸出的逐字稿是一般 Markdown,方便閱讀、可用 grep,也可提交到內部 repo 或轉貼到文件:

# 會議逐字稿 — 2026-05-14 10:30:00
- **頻道:** #standup(Engineering)
- **機器人:** MyBot#1234
## 逐字稿
- **[10:30:14] Roy:** 好,大家先依序報告——昨天大家做了什麼?
- **[10:30:22] Sara:** 完成了 auth 轉移,並開始進行速率限制器
- **[10:30:35] Roy:** 很好,現在有誰卡住了嗎?
---
## 摘要
...

運作原理(約 280 行)

腳本刻意維持精簡,直接重用 discli 的 VoiceEngine,而非重建整套語音堆疊:

from discli.voice_engine import VoiceEngine
engine = VoiceEngine(config={"stt_provider": stt_provider})
engine.set_event_handler(on_event)
await engine.connect(channel)
await engine.listen_start(channel.guild.id)

on_event 會接收每一筆 voice_speech_detected 事件,根據伺服器成員查出顯示名稱,組成 markdown 行寫入檔案,也同步輸出到主控台:

def on_event(event: dict) -> None:
if event.get("event") != "voice_speech_detected" or not event.get("is_final"):
return
uid = int(event.get("user_id") or 0)
if uid == client.user.id:
return
name = resolve_name(uid)
ts = datetime.now().strftime("%H:%M:%S")
line = f"- **[{ts}] {name}:** {event['text']}"
transcript_lines.append(line)
print(line, flush=True)
with path.open("a", encoding="utf-8") as f:
f.write(line + "\n")

按下 Ctrl+C 後,finally 會停止監聽、斷線,並將完整逐字稿透過 Claude 彙總:

async with sdk.ClaudeSDKClient(options) as claude:
await claude.query(prompt)
async for msg in claude.receive_response():
...

SUMMARY_SYSTEM_PROMPT 會要求 Claude 只輸出四個欄位:Summary(摘要)、Key decisions(關鍵決策)、Action items(行動項目)、Open questions(未解決問題),並使用逐字稿中的顯示名稱。

客製化

常見調整:

調整項目說明
切換 STT 提供者使用 --stt openai,或修改預設 stt_provider 設定
修改總結提示詞編輯檔案上方的 SUMMARY_SYSTEM_PROMPT
完全略過總結finally 區塊註解 _write_summary(...) 呼叫
回傳總結到 Discord在寫入檔案後呼叫 client.get_channel(channel_id).send(summary)
自訂逐字稿檔名修改 path = transcript_dir / f"meeting-{stamp}.md"

隱私

逐字稿會以顯示名稱紀錄每位參與者,因此應視為錄音紀錄來處理:

  • 提前告知參與者正在轉錄;許多司法管轄區對錄音有同意義務
  • 將檔案保存在私有目錄(預設 ~/.discli/transcripts/ 位在使用者家目錄)
  • 若要將摘要張貼到公開頻道,先清除可能敏感內容

下一步