opencode-groq-api
用 Groq 的免費 API 做「語音轉字幕」與「逐字稿清洗」,並把整條流程包成可重複使用的技能。說「語音轉字幕」「影片上字幕」「做逐字稿」「Whisper」「Groq」「把錄音整理成講義」「把 API 變成工具」時載入。
适合你,如果经常需要将录音或视频转成可编辑的文字稿
npx oh-my-skill add mathruffian-dot/opencode-lazy-packs/opencode-groq-apicurl -fsSL https://oh-my-skill.com/install.sh | bash -s -- mathruffian-dot/opencode-lazy-packs/opencode-groq-apinpx oh-my-skill verify mathruffian-dot/opencode-lazy-packs/opencode-groq-api怎么用
商店整理自技能原文 · 版本 1d61b46 · 表述以原文为准装上此技能后,当你提供录音或影片文件,Claude会先调用Groq的Whisper模型转成字幕(.srt),再用文字模型清洗成讲义(.md)。
当你说出“语音转字幕”、“做逐字稿”、“把录音整理成讲义”等关键词,或提到“Whisper”、“Groq”时触发。
技能原文 SKILL.md
把 API 變成自己的工具(Groq)
官方文件查證日期:2026-07-27 依據:<https://console.groq.com/docs/speech-to-text>、<https://console.groq.com/docs/api-reference>、<https://console.groq.com/docs/rate-limits>、<https://console.groq.com/docs/models>、<https://console.groq.com/docs/your-data>、<https://console.groq.com/docs/billing-faqs>、<https://console.groq.com/docs/quickstart>
🔴 先看這裡:隱私紅線(這一段是本包最重要的內容)
這一包做的每一件事,都會把你的聲音檔送到別人的伺服器上。
不是「處理完就消失」那種送,是整個音檔上傳、在美國的機器上跑完、再把文字傳回來。你電腦上的麥克風錄到什麼,就送出去什麼。
這三類錄音,一律不要上傳
| ❌ 不要上傳 | 為什麼 | |------------|--------| | 含學生聲音的課堂錄音 | 學生是未成年人。要不要讓孩子的聲音上傳到境外雲端,這件事你不能代替家長同意。 | | 家長會談、親師溝通、電話錄音 | 對方通常不知道有錄音,更不知道會被上傳 | | 輔導紀錄、個案會議、學生私下談話 | 這類內容一旦外流,受傷的是孩子,而且你會有法律責任 |
順便三條也不要:唸到學生姓名的點名錄音、學生口說測驗的作答錄音、任何你不確定裡面有誰在講話的檔案。
這些可以上傳
- 你自己一個人講課、備課、口述的錄音(只有你的聲音)
- 你自己的公開演講、研習分享
- 你自己錄的旁白、口播稿
- 公開影片(新聞、公播教材、你自己頻道的影片)
一句話的判斷標準
## 🔴 「這個音檔如果明天出現在網路上,會不會有人受傷?會不會需要通報?」 只要答案是「會」或「不確定」——不上傳。沒有例外,不看時間趕不趕。
⚠️ 最容易出事的地方:檔案結尾
課堂錄音最常見的意外不是內容,是「忘記按停止」。
你以為錄的是自己講課的 40 分鐘,實際上錄音一直開到下課、開到學生跑上來問問題、開到你跟隔壁老師聊完。
🔴 規矩:上傳前先跳到檔案的最後 30 秒聽一遍。 這一步花 30 秒,可以擋掉這一包 90% 的真實風險。
Groq 官方的資料政策(查證 2026-07-27)
講完紅線再講政策,順序不能反——政策再好,也不會讓紅線消失。
| 項目 | 官方說法 | 出處 | |------|---------|------| | 推論請求的資料會保留嗎 | 預設不保留("By default, Groq does not retain customer data for inference requests.") | Your Data in GroqCloud | | 會拿去訓練模型嗎 | 不會。服務協議明訂:未經客戶明示同意,不得將輸入/輸出用於訓練或微調模型 | Groq Services Agreement | | 有例外嗎 | 有。為了排除故障、調查濫用,可能暫時記錄輸入與輸出,最長保留 30 天(法律要求時可能更久) | Your Data in GroqCloud | | 可以完全關掉嗎 | 可以。所有客戶都能在 Console 的 Data Controls 開啟 Zero Data Retention(ZDR) | Your Data in GroqCloud | | 免費方案有比較差嗎 | 官方文件沒有為免費方案另訂較寬鬆的規定,寫的是同一套 | 同上 |
建議研習前先做一件事:登入 Console → Settings → Data Controls → 開啟 Zero Data Retention。花 10 秒,之後所有請求都適用。
🔴 但是——開了 ZDR,紅線一條都不會消失。 政策是廠商的承諾,不是「你的檔案沒有離開過你的電腦」的技術保證。 中間還有網路、還有你的電腦、還有你等一下會不會把 srt 檔傳到 LINE 群組。 真正安全的做法只有一個:那個檔案根本不要上傳。
真的非處理不可怎麼辦
有些內容就是不能上雲端,但你又真的需要逐字稿。三條路:
- 請當事人自己來:讓學生/家長自己用手機的語音輸入打字,你只收文字
- 本機跑:在自己電腦上跑離線的語音辨識模型(不連網、不上傳)。這條不在本研習範圍,也不在本包
- 不做:這是完全合理的選項。省下的 20 分鐘,遠遠比不上一次個資事件
這一包在研習裡的位置:作品⑤「把 API 變成自己的工具」
第三天上午。這一包做的是「內部工具」——只有你自己在電腦上用,還不碰網頁。
前面幾包是「把別人做好的東西接進來」(NotebookLM、Obsidian、Supabase)。這一包不一樣:
你會拿一個原始的 API,做出一個以後可以一直用的東西。
這是整套研習裡第一次「呼叫外部服務、拿回結果、包成工具」。學會這個模式,之後接任何服務都是同一套邏輯。
🔑 這一包成不成功,判斷標準只有一個
| | 跑一次 | 變成工具 | |---|-------|-------------| | 你做的事 | 貼一串指令,看到 srt 跑出來 | 對 OpenCode 說一句人話,它自己做完 | | 換一份新素材 | 要回來翻講義、重貼指令 | 同一句話再說一次就好 | | 下個月還會用嗎 | 不會,因為忘了怎麼跑 | 會,因為只要記得那句話 | | 這一包的驗收 | ❌ 不算完成 | ✅ 這才算完成 |
所以步驟六(包成 Skill)不是加分題,是這一包的正題。 前面五個步驟都只是為了搞清楚「那句人話背後要做什麼」。
📌 下午的 10-netlify 會把這個工具再往外推一層:變成一個網頁,讓別人也能用。 今天上午先把「自己能用」做穩,下午才有東西可以搬上網。
這一包會幫你做什麼
兩條主線,都用同一把金鑰:
| 線 | 做什麼 | 輸入 → 輸出 | 用到的服務 | |----|--------|-------------|-----------| | A 轉字幕 | 把聲音變成有時間碼的字幕檔 | .mp4 / .mp3 / .m4a → .srt | Groq 的 Whisper | | B 文字清洗 | 把逐字稿整理成能發給學生看的講義 | .srt / .txt → .md | Groq 的文字模型 |
A 線的產物剛好是 B 線的原料。合起來就是:一段錄音 → 一份講義。
🚨 這一包的四個陷阱(先看完再動手)
| # | 陷阱 | 症狀 | 一句話解法 | |---|------|------|-----------| | ① | Groq 沒有 response_format=srt | 照 OpenAI 的教學打,回 400 | 拿 verbose_json,自己組 srt(步驟三附小程式) | | ② | 免費方案單檔上限 25 MB | 一支 40 分鐘影片直接被打回 | 先用 ffmpeg 壓成 16k 單聲道,超過 25 MB 就切段 | | ③ | Whisper 的中文不分繁簡 | 辛苦轉完,字幕是簡體 | language=zh 只能到「中文」;繁體要另外處理(步驟三 A5) | | ④ | 切段後時間碼要自己加偏移 | 第二段字幕全部從 00:00 開始,整份對不上畫面 | 用每一段的實際長度累加,不要用名目的「10 分鐘」 |
陷阱 ① 是最花時間的一個——網路上 95% 的 Whisper 教學都是寫給 OpenAI 的,那邊有 srt,Groq 沒有。 你會以為自己打錯字。
免費方案(不用綁信用卡)
⚠️ 以下為 2026-07-27 查到的數字。服務商隨時會調整,以官方頁面為準(本節最後教你怎麼自己查)。
要不要綁卡
| 問題 | 答案(查證 2026-07-27) | |------|------------------------| | 註冊要不要信用卡 | 不用。 用 Google/GitHub/Email 註冊,直接就能產金鑰 | | 免費用量要不要信用卡 | 不用。 免費方案是「限速」不是「限時」,沒有試用到期這回事 | | 什麼時候才需要卡 | 只有升級 Developer 方案時。官方文件寫得很清楚:升級需要「a valid payment method(credit card, US bank account, or SEPA debit account)」 |
🔴 研習現場的規矩:整個過程不會有任何畫面要你輸入卡號。 只要看到要求付款方式的畫面,代表你走進了升級流程——按上一頁退出來,不要輸入。 這一包主線完全不需要 Developer 方案。
免費額度(Whisper 語音)
| 項目 | 免費方案 | 換算成老師聽得懂的話 | |------|---------|---------------------| | 單檔大小上限 | 25 MB | 壓成 16k 單聲道 FLAC 後,約 25~30 分鐘的音訊 | | 每分鐘請求數 | 20 次 | 一次跑 20 個檔沒問題 | | 每日請求數 | 2,000 次 | 用不完 | | 每小時音訊秒數 | 7,200 秒 | 每小時可以轉 2 小時的音訊 | | 每日音訊秒數 | 28,800 秒 | 每天可以轉 8 小時的音訊 | | 最短音訊 | 0.01 秒 | — | | 最低計費長度 | 10 秒 | ⚠️ 見下方 |
⚠️ 「最低計費 10 秒」這條會咬人。 意思是:再短的檔,也至少吃掉 10 秒額度。 所以「把一堂課切成 200 個小片段各自送一次」是最浪費的做法——200 個 3 秒的檔會被算成 2,000 秒。 正確做法:切大段(10 分鐘一段),不要切小段。
(付費的 Developer 方案單檔上限是 100 MB。本包不走那條,寫在這裡只是讓你看到別人的教學寫 100 MB 時不會困惑。)
免費額度(文字模型,B 線用)
| 模型 | 每分鐘請求 | 每日請求 | 每分鐘 token | 每日 token | |------|-----------|---------|-----------------|-----------| | llama-3.3-70b-versatile | 30 | 1,000 | 12,000 | 100,000 | | openai/gpt-oss-120b | 30 | 1,000 | 8,000 | 200,000 | | llama-3.1-8b-instant | 30 | 14,400 | 6,000 | 500,000 |
🚨 這裡有一個沒人講、但一定會撞到的坑: 「每分鐘 token」小得驚人。 一小時的中文演講逐字稿大約 9,000~12,000 字,換算下來遠遠超過 12,000 token 的每分鐘上限。 也就是說:你不可能把一整份逐字稿一次丟給它清洗。 一定會拿到 429。 解法:分段送。 步驟五的小程式已經內建分段(預設一段 1,200 字)和自動重試,照著跑就好。
怎麼自己查最新的(模型會改名、額度會變)
| 想查什麼 | 去哪查 | |---------|--------| | 現在有哪些模型(最可靠) | 在終端機跑:curl.exe https://api.groq.com/openai/v1/models -H "Authorization: Bearer $env:GROQ_API_KEY" | | 模型說明頁 | <https://console.groq.com/docs/models> | | 我這個帳號的實際限額 | Console → Settings → Limits(這裡看到的才是你的,文件是通則) | | 語音的檔案大小上限 | <https://console.groq.com/docs/speech-to-text> | | 資料政策 | <https://console.groq.com/docs/your-data> |
📌 本包刻意不寫任何價格。 免費方案用不到,而寫死的價格只會在半年後變成錯誤資訊。 模型名稱也一樣:如果下面哪個模型名在你跑的時候不存在了,先跑上面那行 /v1/models 看它改叫什麼,不要硬套本文件。
選哪個 Whisper 模型:研習主線用 whisper-large-v3
Groq 上目前有兩個(查證 2026-07-27):
| 模型 | 官方標示的準確度 | 速度 | 語言 | |------|-----------------|------|------| | whisper-large-v3 | 錯字率 10.3% | 189 倍即時 | 多語(含中文) | | whisper-large-v3-turbo | 錯字率 12% | 216 倍即時 | 多語(含中文) |
🔴 推薦:whisper-large-v3。理由是一個很多人沒注意到的事實——
在免費方案下,這兩個模型的額度完全一樣(都是 20 次/分、7,200 音訊秒/小時、28,800 音訊秒/日)。 turbo 的賣點是「比較便宜」。但免費方案本來就不用錢,所以那個賣點在這裡等於零。 而速度差多少?189 倍 vs 216 倍。一支 30 分鐘的錄音,一個要 9.5 秒、一個要 8.3 秒。你根本感覺不到。 結論:免費用戶沒有任何理由選 turbo。用準的那個。
什麼時候才換 turbo:你要一次跑幾十個檔、而且真的坐在旁邊等;或者以後升級成付費、開始在意費用。
📌 關於繁體中文的表現:Groq 官方沒有公布任何中文專屬的準確度數字,Whisper 官方也只有整體多語數字。 本包推薦 whisper-large-v3 的依據是「整體錯字率較低」+「免費方案額度相同」,不是任何中文的實測比較。 實際上兩個模型對台灣國語的表現都算堪用,主要問題不是聽錯字,是專有名詞(人名、地名、課本術語)和繁簡——這兩件事都靠步驟五的清洗解決,換模型幫助不大。
選哪個文字模型:主線用 llama-3.3-70b-versatile
| 模型 | 為什麼選/不選 | |------|---------------| | llama-3.3-70b-versatile | ✅ 主線。中文品質在免費模型裡較穩,而且每分鐘 token 上限 12,000 是免費方案裡最大的——清洗逐字稿吃的就是這個 | | openai/gpt-oss-120b | 🟡 備援。主線那個掛掉或改名時用這個 | | llama-3.1-8b-instant | 🟡 量大時用。每日 500,000 token 最寬鬆,但中文品質明顯較弱,適合「整批粗清、之後自己再看過」 |
⚠️ 模型名稱會改。跑之前先用 /v1/models 確認一次(見上方「怎麼自己查最新的」)。
🤔 等一下——文字清洗為什麼不直接叫 OpenCode 做就好?
這是研習現場一定會有人問的問題,而且問得很對。
你已經在 01-connect-model 幫 OpenCode 接了模型。要清洗逐字稿,把檔案丟給它說「幫我整理成講義」,它就做完了。幹嘛還要多申請一把 Groq 金鑰、多寫一支程式?
答案是三件事:
| 理由 | 說明 | |------|------| | ① 這一包在教「模式」,不是在教「清洗」 | 呼叫 API 拿結果,這個模式之後接任何服務都通用。清洗只是拿來練手的題目 | | ② 語音那條沒有替代方案 | OpenCode 的模型聽不懂聲音檔。A 線非走 API 不可,而 B 線用同一把金鑰、同一套寫法,順著學最省力 | | ③ 下午要變成網頁 | 10-netlify 會把這個工具放到網路上給別人用。網頁不能呼叫你電腦裡的 OpenCode,只能呼叫 API。今天寫的這支程式,下午直接搬上去 |
💡 所以:日常自己用,直接叫 OpenCode 清洗完全 OK,不必每次都走 API。 但這一包要你走一次 API,是為了讓你手上有一個「離開 OpenCode 也能跑」的東西。
先備條件
- [ ] 已完成
00-env-setup與01-connect-model - [ ] 已完成
02-file-toolkit的 B 段(要用ffmpeg和ffprobe壓縮、切檔、量長度) - [ ] 建議完成
07-github(金鑰不外洩這件事在那一包有完整說明) - [ ] 有網路
- [ ] 手上有一份符合上面隱私紅線的音訊或影片檔
驗證:
ffmpeg -version; ffprobe -version
兩個都印出版本 = 過。
⚠️ffprobe常常缺席。 如果只裝了 yt-dlp 自帶的那套 FFmpeg,ffprobe可能不在 PATH 上。 補裝系統版:winget install --id Gyan.FFmpeg -e(沒有管理員權限就加--scope user)。 mac:brew install ffmpeg/Linux:sudo apt install -y ffmpeg。 真的裝不起來也還有救——步驟四的小程式有備援做法,看那一節。
📁 先建一個工作資料夾,名字用英數字
New-Item -ItemType Directory -Force "$HOME\Documents\groq-subtitle"
🚨 資料夾和檔名先不要用中文。 Windows 的命令列(PowerShell 5.1)傳中文檔名給ffmpeg、curl時偶爾會變亂碼,你會看到「找不到檔案」但檔案明明就在。 做法:轉檔前先把來源改成lesson01.mp4這種名字,全部跑完再改回中文。 這一條看起來很瑣碎,但研習現場每一梯都有人卡在這裡半小時。
步驟一:申請 Groq 金鑰
🖐️ 全程在瀏覽器手動做。AI agent 不要代為註冊、不要代填任何欄位。
1-1 註冊
- 開 <https://console.groq.com>
- 選 Continue with Google 或 Continue with GitHub(建議用
07-github那個 GitHub 帳號,之後好管理),也可以用 Email - 沒有等待清單、不用審核、不會問你要信用卡
1-2 建立金鑰
左側選單 API Keys → Create API Key → 取一個看得懂的名字(例如 研習-字幕工具)→ 建立。
🔴 金鑰只會完整顯示這一次。 關掉視窗之後看不到第二次,只能刪掉重產一把。 現在就複製起來,貼到你的密碼管理器(不是貼到記事本、不是貼到 LINE 給自己)。
金鑰長得像 gsk_ 開頭的一長串。
1-3 順手開啟 Zero Data Retention
Settings → Data Controls → 開啟 Zero Data Retention。
10 秒的事,之後所有請求都適用。(再說一次:開了它,前面那些紅線一條都不會消失。)
1-4 驗證:金鑰是活的嗎
先做完步驟二設定好環境變數,再回來跑這一行(步驟二的驗證裡有)。
步驟二:金鑰要放哪裡(🚨 這一步做錯,金鑰會上 GitHub)
🔴 三條絕對規則
① 金鑰不寫進任何.md檔案裡 —— 包含你等一下要做的那個 Skill 檔。 ② 金鑰不寫進任何會被git add的檔案裡。 ③ 金鑰不貼進聊天視窗(包含貼給 AI agent 看)。
理由:研習的設定檔會投影在大螢幕上、會被拍照;而 GitHub 上有機器人專門在掃新推上去的金鑰,掃到的速度比你發現的速度快。
主線做法:設成環境變數(推薦,整台電腦設一次)
環境變數的好處是:金鑰不存在於任何一個檔案裡,所以永遠不可能被 commit。
Windows(PowerShell)
[System.Environment]::SetEnvironmentVariable("GROQ_API_KEY", "貼上你的金鑰", "User")
設完一定要重開終端機(連 OpenCode 也要完全關掉重開),新的環境變數才會生效。
macOS / Linux(寫進 ~/.zshrc 或 ~/.bashrc,然後 source 一次)
echo 'export GROQ_API_KEY="貼上你的金鑰"' >> ~/.zshrc source ~/.zshrc
驗證(🔴 這行刻意不印出金鑰本身,可以安心投影)
Windows
if ($env:GROQ_API_KEY) { "KEY_SET len=" + $env:GROQ_API_KEY.Length } else { "KEY_MISSING" }
macOS / Linux
if [ -n "$GROQ_API_KEY" ]; then echo "KEY_SET len=${#GROQ_API_KEY}"; else echo "KEY_MISSING"; fi
看到 KEY_SET len=<某個數字> = 過。看到 KEY_MISSING → 沒重開終端機,或變數名打錯。
驗證金鑰是活的
curl.exe https://api.groq.com/openai/v1/models -H "Authorization: Bearer $env:GROQ_API_KEY"
- 回傳一大段 JSON(裡面看得到
whisper-large-v3)→ ✅ 金鑰可用,順便看到現在有哪些模型 - 回
invalid_api_key→ 金鑰複製時少了一截,或前後有多餘的空白,重複製一次
Windows 一定要打curl.exe(有.exe),否則會叫到 PowerShell 內建的別名,行為完全不同。
專案做法:.env + .gitignore(下午的 10-netlify 會用到)
如果你的工具要放進一個「會推上 GitHub 的專案資料夾」,用 .env:
先建 .gitignore(🔴 先建這個,再建 .env,順序不能反)
.env *.key credentials.* desktop.ini *.tmp
再建 .env
GROQ_API_KEY=貼上你的金鑰
驗證 .env 真的被擋掉了:
git status --short
🔴 輸出裡出現.env就是失敗——代表 git 還看得到它,下一次git add .就會把金鑰送上 GitHub。 回頭檢查.gitignore是不是拼錯、是不是放在 repo 根目錄。
更嚴格的檢查(agent 請跑這一行):
git check-ignore -v .env
有輸出(會告訴你被 .gitignore 第幾行擋掉)= ✅ 過。沒有輸出 = ❌ 沒擋到。
📌 關於 Google 雲端硬碟:.env放在雲端硬碟資料夾裡會被同步到你的雲端帳號——這通常可以接受(那是你自己的帳號)。 但它跟.gitignore是兩回事,雲端同步不會擋 git。兩件事都要做。
如果你要在 opencode.json 裡引用金鑰
本包用不到這件事(我們是自己呼叫 API,不是把 Groq 接成 OpenCode 的模型供應商)。但萬一你之後要:
"environment": { "GROQ_API_KEY": "{env:GROQ_API_KEY}" }
🔴 寫{env:GROQ_API_KEY},不要寫實際那串字。 這是00-env-setup附錄「MCP 通用守則」第 5 條的規矩,本包完全沿用。
🚨 萬一金鑰不小心外流了
刪掉檔案、刪掉訊息、清掉 git 歷史——全部沒有用。 金鑰已經在別人手上了。
## 唯一有效的處置:回 Console → API Keys → 把那把金鑰刪掉,重新產一把。 這件事做完之前,其他都不用做。做完之後,記得把環境變數也換成新的。
步驟三:A 線 — 把聲音變成字幕(.srt)
A1. 先把檔案壓小(🚨 這裡處理陷阱 ②)
免費方案單檔上限 25 MB。一支手機錄的 30 分鐘影片動輒好幾百 MB,直接送一定被打回。
官方建議的做法:降到 16kHz 單聲道、轉成 FLAC。 這樣做不會影響辨識準確度——Whisper 本來就只吃 16kHz,多出來的取樣率和第二個聲道,送上去也是被丟掉。
Groq 官方文件給的指令(原封不動):
ffmpeg -i "lesson01.mp4" -ar 16000 -ac 1 -map 0:a -c:a flac "clean.flac"
| 參數 | 意思 | |------|------| | -ar 16000 | 取樣率降到 16kHz(Whisper 的原生規格) | | -ac 1 | 併成單聲道 | | -map 0:a | 只要聲音,把影像整個丟掉(影片檔的體積 95% 是影像) | | -c:a flac | 轉成 FLAC(無損壓縮) |
壓完看大小:
"{0:N1} MB" -f ((Get-Item "clean.flac").Length / 1MB)
# macOS / Linux du -h clean.flac
經驗值:16k 單聲道 FLAC 大約每分鐘 0.8~1 MB。 所以 25 MB ≈ 25~30 分鐘。超過就要切段 → 跳到步驟四。
🟡 真的不想切檔的備案:改用 mp3 低位元率,一小時大約壓到 14 MB。 ``powershell ffmpeg -i "lesson01.mp4" -ar 16000 -ac 1 -map 0:a -c:a libmp3lame -b:a 32k "clean.mp3" `` 代價是有損壓縮,中文的準確度會略微下降。本包主線仍走 FLAC + 切段,這個備案只在時間真的不夠時用。
A2. 呼叫 API
Windows(PowerShell,整段是一行,長是正常的)
curl.exe https://api.groq.com/openai/v1/audio/transcriptions -H "Authorization: Bearer $env:GROQ_API_KEY" -F "file=@clean.flac" -F "model=whisper-large-v3" -F "language=zh" -F "temperature=0" -F "response_format=verbose_json" -o "clean.json"
macOS / Linux
curl https://api.groq.com/openai/v1/audio/transcriptions \ -H "Authorization: Bearer $GROQ_API_KEY" \ -F "file=@clean.flac" \ -F "model=whisper-large-v3" \ -F "language=zh" \ -F "temperature=0" \ -F "response_format=verbose_json" \ -o "clean.json"
| 參數 | 為什麼要 | |------|---------| | model=whisper-large-v3 | 見前面「選哪個模型」 | | language=zh | 一定要加。官方明講:指定語言會同時提升準確度和速度。不加的話它要先花時間猜語言,中英夾雜的課堂錄音很容易猜錯 | | temperature=0 | 要它照聽到的寫,不要自由發揮 | | response_format=verbose_json | 時間碼在這裡面。用 json 或 text 拿不到時間碼 | | -o clean.json | 存成檔案。不存的話一大堆 JSON 會直接噴在螢幕上 |
🚨 A3. 陷阱 ①:不要寫 response_format=srt
OpenAI 的 Whisper API 可以直接回 .srt。Groq 不行。
官方 API 文件寫得很明確——response_format 的 Allowed values 只有 json、text、verbose_json。
打 srt 或 vtt 會拿到 400 錯誤。而網路上 95% 的 Whisper 教學都是寫給 OpenAI 的,照抄過來就會撞這一關。
🔴 請直接記這句話: Groq 只給你「時間碼 + 文字」,.srt 這個檔案要你自己組。 聽起來很麻煩,其實只是把 JSON 換個排版而已——下面那支小程式一次解決,而且以後長檔切段也是同一支。
A4. 把 verbose_json 組成 .srt
在工作資料夾建立 make_srt.py(這支程式不需要安裝任何套件,全部用 Python 內建的):
# make_srt.py — 把 Groq 的 verbose_json 組成 .srt
# 用法:uv run python make_srt.py 輸出.srt 第1段.json 第2段.json ...
# 只有一段就只傳一個 json;多段會自動接上時間碼偏移(見步驟四)
import json, os, subprocess, sys
def fmt(t):
"""秒數 → SRT 的 00:00:00,000 格式"""
ms = int(round(t * 1000))
h, ms = divmod(ms, 3600000)
m, ms = divmod(ms, 60000)
s, ms = divmod(ms, 1000)
return f"{h:02d}:{m:02d}:{s:02d},{ms:03d}"
def real_seconds(path):
"""用 ffprobe 量音訊檔的實際長度(秒)。量不到就回 None"""
try:
out = subprocess.run(
["ffprobe", "-v", "error", "-show_entries", "format=duration",
"-of", "csv=p=0", path],
capture_output=True, text=True, check=True).stdout.strip()
return float(out)
except Exception:
return None
out_path, json_files = sys.argv[1], sys.argv[2:]
if not json_files:
sys.exit("SRT_FAIL 沒有指定任何 json 檔")
offset, idx, blocks = 0.0, 1, []
for jf in json_files:
with open(jf, encoding="utf-8") as f:
data = json.load(f)
for seg in data.get("segments", []):
text = (seg.get("text") or "").strip()
if not text:
continue
blocks.append(
f"{idx}\n{fmt(seg['start'] + offset)} --> {fmt(seg['end'] + offset)}\n{text}\n")
idx += 1
# 🚨 陷阱 ④:偏移量用「這一段的實際長度」累加,不要用名目的 600 秒
audio = os.path.splitext(jf)[0] + ".flac"
dur = real_seconds(audio) if os.path.exists(audio) else None
if dur is None: # ffprobe 不在?改用 API 回傳的長度
dur = float(data.get("duration") or 0.0)
offset += dur
with open(out_path, "w", encoding="utf-8") as f:
f.write("\n".join(blocks))
print(f"SRT_OK segments={idx - 1} files={len(json_files)} out={out_path}")
跑它:
uv run python make_srt.py "lesson01.srt" "clean.json"
(uv 來自 00-env-setup。沒有 uv 就用 python make_srt.py ...,這支程式只用內建模組,任何 Python 3 都跑得動。)
成功的樣子:印出 SRT_OK segments=<某個數字> files=1 out=lesson01.srt,而且 segments 不是 0。
A5. 🚨 陷阱 ③:字幕變成簡體怎麼辦
Whisper 的語言代碼只有「中文(zh)」,沒有「繁體中文」。 它自己決定要吐繁體還是簡體,同一份檔案跑兩次結果還可能不一樣。
兩條路,建議兩條都做:
路線 A(一次搞定,但 Windows 有編碼坑):用 prompt 參數暗示它
Whisper 的 prompt 參數是用來「示範風格」的(上限 224 token)。你用繁體字寫這個提示,它輸出繁體的機率會大幅提高。
先用記事本建立 prompt.txt(存檔時編碼選 UTF-8),內容一行:
以下是繁體中文的課堂錄音逐字稿。
然後在 A2 的指令尾巴加一個參數:
-F "prompt=<prompt.txt"
🚨 為什麼要用檔案,不直接把中文寫在指令裡? Windows PowerShell 5.1 把中文參數傳給curl.exe時會用系統的 cp950 編碼,送出去常常變亂碼——而且不會報錯,你只會覺得「這個 prompt 好像沒效」。-F "欄位名=<檔名"是 curl 的語法,意思是「這個欄位的值從檔案讀」,直接讀原始位元組,繞過整個編碼問題。 macOS / Linux 沒有這個問題,直接寫中文也可以。
📌 這是社群通用做法,不是官方保證。 有效但不是 100%。所以還要有路線 B。
路線 B(零風險保底):讓 OpenCode 只改字、不准動時間碼
把產出的 .srt 丟給 OpenCode,說:
幫我把這個 .srt 檔的字幕文字全部改成繁體中文(台灣用語), 順便修掉明顯的錯字。 🔴 硬規定: - 時間碼那幾行(00:00:00,000 --> 00:00:03,120)一個字都不准改 - 字幕的行數與編號不准增減 - 不要合併或拆開任何一段
🔴 最後那三條一定要講,而且要講得像規定。 不講的話,AI 很常「順手」幫你合併太短的句子、重新分段——結果字幕跟畫面整份對不上,而且看起來完全正常,你要放完整支影片才會發現。
建議做法:路線 A 先做(省事),路線 B 當保底(順便修錯字)。
A6. ✅ A 線驗收:換一份新素材,同一句話能不能再跑一次
這才是這一包的驗收,不是「跑出一個 srt」。
| # | 做什麼 | 通過的樣子 | |---|--------|-----------| | 1 | 用播放器開啟原始影片,把 .srt 拖進去 | 字幕跟嘴巴對得上(不是「有字幕就好」) | | 2 | 跳到影片最後面看 | 最後一句字幕的時間接近影片總長(差很多 = 時間碼歪了,看步驟四) | | 3 | 換一支完全不同的錄音,從 A1 重跑一次 | 一樣跑得完 | | 4 | 🔴 步驟六做完之後:對 OpenCode 說「幫我把 <新檔名> 轉成字幕」 | 它自己做完全部,你沒有再貼任何指令 ← 這一項才是真的通過 |
第 4 項現在還做不到(Skill 還沒包)。先記著它,這是你等一下要達成的目標。
步驟四:長音檔怎麼切、時間碼怎麼接(🚨 這裡處理陷阱 ④)
只有超過 25 MB(約 25~30 分鐘)才需要這一步。 短檔請直接跳到步驟五。
官方建議 vs 本包做法
Groq 官方建議的是「切成有重疊的片段,各自轉,合併時處理重疊的部分」,並附了一份 Cookbook 範例。
本包不走重疊那條,理由很實際:重疊之後要判斷「這兩段重複的文字哪一份才對、接縫在哪」,這是整條流程裡最容易寫錯、也最難驗證的部分。對老師來說划不來。
本包做法:不重疊、切固定長度、用「每一段的實際長度」累加時間碼。 代價是接縫處可能斷在句子中間(少一兩個字或斷句怪怪的)。 但這件事步驟五的清洗會順手修掉,而且比起「整份時間碼歪掉」,這個代價便宜太多。
4-1 切檔(先壓再切,順序不能反)
# ① 先壓成 16k 單聲道 FLAC(跟 A1 一模一樣) ffmpeg -i "lesson01.mp4" -ar 16000 -ac 1 -map 0:a -c:a flac "clean.flac" # ② 再切成每段 10 分鐘(600 秒) ffmpeg -i "clean.flac" -f segment -segment_time 600 -c:a flac "chunk_%03d.flac"
會產生 chunk_000.flac、chunk_001.flac、chunk_002.flac…
為什麼是 10 分鐘? 10 分鐘的 16k 單聲道 FLAC 約 8~10 MB,離 25 MB 上限有很大的安全邊際。 切太小反而浪費(記得「最低計費 10 秒」那條),切太大會踩到上限。10 分鐘是甜蜜點。
為什麼要先壓再切? 這樣每一段的規格完全一致,量長度、接時間碼都不會出意外。 先切再壓的話,每段會各自被 ffmpeg 處理一次,長度可能出現零點幾秒的差異。
4-2 逐段呼叫 API
Windows(PowerShell)
Get-ChildItem "chunk_*.flac" | Sort-Object Name | ForEach-Object {
$out = $_.BaseName + ".json"
Write-Host "轉換中:$($_.Name)"
curl.exe https://api.groq.com/openai/v1/audio/transcriptions -H "Authorization: Bearer $env:GROQ_API_KEY" -F "file=@$($_.Name)" -F "model=whisper-large-v3" -F "language=zh" -F "temperature=0" -F "response_format=verbose_json" -o $out
Start-Sleep -Seconds 3
}
macOS / Linux
for f in chunk_*.flac; do
echo "轉換中:$f"
curl https://api.groq.com/openai/v1/audio/transcriptions \
-H "Authorization: Bearer $GROQ_API_KEY" \
-F "file=@$f" -F "model=whisper-large-v3" -F "language=zh" \
-F "temperature=0" -F "response_format=verbose_json" \
-o "${f%.flac}.json"
sleep 3
done
💡 中間的Start-Sleep 3/sleep 3是刻意的:免費方案每分鐘 20 次,這樣跑絕對不會撞到上限。 一支 1 小時的錄音會切成 6 段,全部跑完大約 1 分鐘。
檢查每一段都有回來:
Get-ChildItem "chunk_*.json" | Select-Object Name, Length
🔴 有哪個 .json 大小只有幾百 bytes → 那一段失敗了,裡面裝的是錯誤訊息不是字幕。 用記事本打開看它寫什麼,對照本文件最後的「常見卡關」。不要就這樣往下做,那一段的字幕會整段消失。
4-3 合併(🚨 這裡就是陷阱 ④)
為什麼不能直接接起來
每一段轉回來的時間碼,都是從 00:00:00 開始的。
也就是說第二段的第一句話,API 說它在 0 分 3 秒——但它實際上在整支影片的 10 分 3 秒。 直接把 6 段接起來,你會得到一份「前 10 分鐘有 6 份字幕、後面 50 分鐘全空」的檔案。
正確做法:加偏移量。但不能用「10 分鐘」硬算
🔴 這是本包最細、也最多人做錯的一條:ffmpeg的-segment_time 600是「大約 600 秒」,不是「剛好 600 秒」——實際切點會落在音訊的封包邊界上。 每段差個零點幾秒,六段累積起來就是好幾秒。 字幕一開始對得很好,越到後面越飄——而這種錯誤最難發現,因為前 10 分鐘完全正常。 正解:用ffprobe量每一段的實際長度,一段一段累加。make_srt.py已經內建這個做法(real_seconds()那個函式),你不用自己算。
直接跑(把所有 json 按順序一次餵進去):
Windows
$files = (Get-ChildItem "chunk_*.json" | Sort-Object Name).Name uv run python make_srt.py "lesson01.srt" $files
macOS / Linux
uv run python make_srt.py "lesson01.srt" $(ls chunk_*.json | sort)
🔴 順序絕對不能錯。chunk_010.json一定要排在chunk_002.json後面。 上面用了Sort-Object Name/sort,而檔名是三位數補零的(%03d),所以字母排序就是正確順序。 如果你自己手動列檔名,請務必檢查順序——順序一錯,整份字幕會亂到看不出規律。
📌ffprobe真的裝不起來的備援:make_srt.py量不到長度時,會自動改用 API 回傳的duration欄位。 那個數字是 Groq 算的,通常也很準,只是不像ffprobe那樣是直接量檔案。能裝ffprobe還是裝。
4-4 驗證時間碼有沒有歪(🔴 這一步不要跳過)
這是唯一能抓到時間碼漂移的方法:
| # | 做什麼 | 通過的樣子 | |---|--------|-----------| | 1 | 看原始影片的總長度 | 記下來,例如 58:20 | | 2 | 用記事本打開 .srt,捲到最後一行 | 最後一個時間碼很接近 58:20(差 5 秒內算正常) | | 3 | 把 .srt 拖進播放器,跳到影片最後 2 分鐘 | 字幕還對得上嘴巴 | | 4 | 跳到每一個接縫處(10 分、20 分、30 分…) | 那附近的字幕沒有明顯偏移 |
🔴 只驗開頭是抓不到這個 bug 的。 時間碼漂移的特徵就是「前面完全正常」。 一定要看最後面。
歪掉了怎麼辦:
| 症狀 | 原因 | |------|------| | 最後一句只到 10:00 左右 | 只餵了一個 json 進去,或檔案順序被打亂 | | 越後面偏差越大 | ffprobe 沒裝,而且 API 的 duration 也不準 → 補裝系統版 FFmpeg 再跑一次 make_srt.py(不用重轉,json 都還在) | | 中間整段字幕消失 | 某一段的 .json 其實是錯誤訊息(回 4-2 檢查每個 json 的大小) |
步驟五:B 線 — 把逐字稿清洗成講義
5-1 先把 srt 變成純文字
字幕檔裡有編號和時間碼,那些不需要送去清洗(還會白白吃掉寶貴的 token 額度)。
uv run python -c "import re,sys;t=open('lesson01.srt',encoding='utf-8').read();lines=[l for l in t.splitlines() if l.strip() and not l.strip().isdigit() and '-->' not in l];open('lesson01.txt','w',encoding='utf-8').write('\n'.join(lines))"
或者更簡單——直接把 .srt 丟給 OpenCode 說「把時間碼和編號拿掉,只留文字,存成 lesson01.txt」。
5-2 建立清洗程式
在同一個資料夾建立 clean.py(一樣不需要安裝任何套件):
# clean.py — 把逐字稿清洗成可讀講義(Groq Chat Completions)
# 用法:uv run python clean.py 逐字稿.txt 講義.md
import json, os, sys, time, urllib.error, urllib.request
API = "https://api.groq.com/openai/v1/chat/completions"
MODEL = "llama-3.3-70b-versatile" # 會改名!先跑 /v1/models 確認
LIMIT = 1200 # 每段送幾個字。免費方案 TPM 很小,不要調大
KEY = os.environ.get("GROQ_API_KEY")
if not KEY:
sys.exit("CLEAN_FAIL 環境變數 GROQ_API_KEY 沒設,回步驟二")
SYSTEM = (
"你是台灣國中小老師的文字編輯助理。把語音辨識出來的逐字稿,"
"整理成可以直接發給學生看的講義段落。規則:"
"① 一律輸出繁體中文,用台灣的慣用詞;"
"② 刪掉贅詞、口頭禪、結巴、重複、以及講者自己更正掉的前半句;"
"③ 補上標點符號,依語意分段;"
"④ 不要新增原文沒有的內容,不要加自己的解釋;"
"⑤ 聽不出來的地方原樣保留,並在後面加上 [?];"
"⑥ 不要寫開場白或結語,直接輸出整理後的內文。"
)
def split_text(raw, limit):
"""依行切段,盡量不切在句子中間"""
parts, buf = [], ""
def flush():
nonlocal buf
if buf.strip():
parts.append(buf)
buf = ""
for line in raw.splitlines(keepends=True):
while len(line) > limit: # 整篇沒換行的長文,只好硬切
flush()
parts.append(line[:limit])
line = line[limit:]
if len(buf) + len(line) > limit:
flush()
buf += line
flush()
return parts
def call(text):
body = json.dumps({
"model": MODEL,
"temperature": 0.2,
"messages": [{"role": "system", "content": SYSTEM},
{"role": "user", "content": text}],
}).encode("utf-8")
req = urllib.request.Request(API, data=body, headers={
"Authorization": "Bearer " + KEY,
"Content-Type": "application/json",
})
with urllib.request.urlopen(req, timeout=180) as r:
return json.load(r)["choices"][0]["message"]["content"].strip()
raw = open(sys.argv[1], encoding="utf-8").read()
parts = split_text(raw, LIMIT)
out = []
for n, p in enumerate(parts, 1):
print(f"清洗第 {n}/{len(parts)} 段…")
for attempt in range(3):
try:
out.append(call(p))
break
except urllib.error.HTTPError as e:
detail = e.read().decode("utf-8", "ignore")[:300]
if e.code == 429 and attempt < 2: # 額度滿了,等一下再試
print(" 額度滿了,等 35 秒再試")
time.sleep(35)
continue
sys.exit(f"CLEAN_FAIL http={e.code} {detail}")
time.sleep(2) # 段與段之間刻意留間隔
open(sys.argv[2], "w", encoding="utf-8").write("\n\n".join(out))
print(f"CLEAN_OK pieces={len(parts)} out={sys.argv[2]}")
跑它:
uv run python clean.py "lesson01.txt" "lesson01-講義.md"
成功的樣子:一段一段印出進度,最後印 CLEAN_OK pieces=<數字> out=lesson01-講義.md。
5-3 為什麼要分段、為什麼中間要等
| 設計 | 理由 | |------|------| | LIMIT = 1200(一段 1,200 字) | 免費方案 llama-3.3-70b-versatile 每分鐘只有 12,000 token。一小時的逐字稿一次送必定 429 | | 段與段之間 sleep 2 | 讓每分鐘的 token 用量攤平,不會集中在前 10 秒燒完 | | 遇到 429 等 35 秒重試 | token 額度是每分鐘重置的,等一下就會恢復 |
🔴 不要把LIMIT調大想跑快一點。 調大只會讓你更快撞到429,然後每次都要等 35 秒,整體反而更慢。
5-4 想要別的產出,改 SYSTEM 那段就好
clean.py 裡的 SYSTEM 字串就是「你要它做什麼」。改那一段,這支程式就變成另一個工具:
| 你想要 | SYSTEM 改成 | |--------|--------------| | 講義(預設) | 上面那段 | | 課堂重點摘要 | 「把逐字稿整理成 5~8 條重點,每條一行,用繁體中文…」 | | 學生自學版(口語轉書面) | 「把逐字稿改寫成適合國中生自己閱讀的說明文…」 | | 抽出考題素材 | 「從逐字稿裡找出可以出題的概念,列成清單…」 |
💡 這就是「把 API 變成工具」的意思——同一支程式、同一把金鑰,換一段話就換一個工具。
5-5 ✅ B 線驗收:換一份新素材,同一句話能不能再跑一次
| # | 做什麼 | 通過的樣子 | |---|--------|-----------| | 1 | 打開 lesson01-講義.md | 是通順的繁體中文段落,不是一長串沒標點的字 | | 2 | 跟原逐字稿對照 | 意思沒被改掉、沒有多出原文沒講過的內容 | | 3 | 找一段你記得自己講錯又更正的地方 | 更正前那半句被刪掉了 | | 4 | 換一份完全不同的逐字稿,重跑一次 | 一樣跑得完 | | 5 | 🔴 步驟六做完之後:對 OpenCode 說「幫我把 <新檔名> 整理成講義」 | 它自己做完,你沒有再貼指令 ← 這一項才是真的通過 |
⚠️ 第 2 項要真的看。語言模型偶爾會「幫你補充」——補出來的東西看起來很專業,但你根本沒講過。 發講義前自己讀一遍,這件事不能外包。
步驟六:把整條流程包成 Skill(🔴 這是作品⑤ 的正題)
前面五步都是「跑一次」。跑一次的東西下個月一定忘記。
現在把它變成一個技能:以後在任何資料夾、對 OpenCode 說一句人話,它就會自己做完。
6-1 建立技能資料夾
Windows
New-Item -ItemType Directory -Force "$HOME\.config\opencode\skills\audio-notes"
macOS / Linux
mkdir -p ~/.config/opencode/skills/audio-notes
⚠️ 資料夾名稱要和技能的name一模一樣,檔名要是全大寫的SKILL.md,OpenCode 才掃得到。(規矩同08-workflow-skills。)
6-2 把兩支小程式搬進去
Copy-Item "$HOME\Documents\groq-subtitle\make_srt.py" "$HOME\.config\opencode\skills\audio-notes\" Copy-Item "$HOME\Documents\groq-subtitle\clean.py" "$HOME\.config\opencode\skills\audio-notes\"
🔴 搬之前先確認這兩支檔案裡沒有金鑰。 它們讀的是 os.environ.get("GROQ_API_KEY"),本來就沒有——但如果你剛剛「為了測試方便」把金鑰貼死在裡面,現在就是把它拿掉的時候。
6-3 建立 SKILL.md
把下面整段內容原封不動寫入 ~/.config/opencode/skills/audio-notes/SKILL.md (不要把最外層的 ``` ` `` 圍籬符號寫進檔案,檔案第一行必須是 ---`):
--- name: audio-notes description: 把音訊或影片轉成字幕檔(.srt),以及把逐字稿清洗成可讀講義(.md)。當使用者說「轉字幕」、「上字幕」、「做逐字稿」、「這個錄音幫我轉文字」、「把逐字稿整理成講義」、「把這段錄音變成講義」時,請一定要使用此技能。走 Groq 的 Whisper 與文字模型,金鑰讀自環境變數 GROQ_API_KEY。 --- # 音訊 → 字幕 → 講義 ## 🔴 執行前必問(不可跳過) 動手之前,先問使用者這一句,**等到回答再繼續**: > 「這個音檔裡面**有沒有學生、家長的聲音**,或是輔導、個案相關的內容? > 有的話這條流程不能用——語音會上傳到雲端。」 - 回答「有」或「不確定」 → **停止,不要呼叫任何 API**,把原因說清楚 - 回答「沒有,只有我自己的聲音」 → 繼續 順便提醒一次:**上傳前先聽檔案的最後 30 秒**,課堂錄音常常錄到下課後的對話。 ## 金鑰 金鑰一律從環境變數 `GROQ_API_KEY` 讀取。 - **不准**把金鑰寫進本檔案、寫進任何 `.py`、寫進任何會被 commit 的檔案 - **不准**把金鑰印在終端機上、或複述給使用者看 - 環境變數不存在 → 停下來,請使用者依懶人包 `09-groq-api` 步驟二設定,**不要自己想辦法繞過** ## 模式 A:音訊 → 字幕(.srt) 1. **檢查工具**:`ffmpeg -version`、`ffprobe -version`。缺了就停下來告訴使用者(懶人包 `02-file-toolkit` B 段) 2. **檔名先換成英數字**(中文檔名在 Windows 命令列可能亂碼),全部跑完再改回來 3. **壓縮**: `ffmpeg -i <來源> -ar 16000 -ac 1 -map 0:a -c:a flac clean.flac` 4. **看大小**。25 MB 以內 → 跳到第 6 步;超過 → 第 5 步 5. **切段**(每段 600 秒): `ffmpeg -i clean.flac -f segment -segment_time 600 -c:a flac chunk_%03d.flac` 6. **逐段呼叫 API**,每次之間停 3 秒: 端點 `https://api.groq.com/openai/v1/audio/transcriptions` 參數 `model=whisper-large-v3`、`language=zh`、`temperature=0`、`response_format=verbose_json` 輸出存成與音訊同名的 `.json` - 🚨 **`response_format` 不可以填 `srt` 或 `vtt`**,Groq 不支援,會回 400 7. **檢查每個 `.json`**:檔案太小(幾百 bytes)代表那一段失敗了,是錯誤訊息不是字幕。**先處理再往下** 8. **組 srt**(json 一定要照檔名排序傳入): `uv run python <本技能資料夾>/make_srt.py <輸出>.srt chunk_000.json chunk_001.json ...` 9. **繁體化**:把產出的 srt 逐句改成繁體中文並修錯字。 🔴 **時間碼那幾行一個字都不准改,行數與編號不准增減,不准合併或拆開任何一段。** 10. **回報**:段數、輸出路徑,並提醒使用者「請拖進播放器確認**最後 2 分鐘**的字幕還對得上」 ## 模式 B:逐字稿 → 講義(.md) 1. 來源是 `.srt` → 先去掉編號與時間碼,只留文字,存成 `.txt` 2. 執行:`uv run python <本技能資料夾>/clean.py <逐字稿>.txt <輸出>.md` 3. 拿到 `CLEAN_OK` 才算成功 4. 回報時**一定要加這句**:「語言模型偶爾會補充原文沒有的內容,**發給學生前請自己讀一遍**」 ## 常見狀況 | 狀況 | 處理 | |------|------| | `400` 且訊息提到 `response_format` | 用了 `srt`/`vtt`,改成 `verbose_json` | | `413` 或檔案太大 | 沒壓縮,或壓完仍超過 25 MB → 回去切段 | | `429` | 額度滿了,等 35 秒再試,**不要換模型硬闖** | | `401` / `invalid_api_key` | `GROQ_API_KEY` 沒設或設錯 → 停下來請使用者處理 | | 模型名稱不存在 | 跑 `https://api.groq.com/openai/v1/models` 看現在有哪些,不要亂猜 | | 字幕越後面越對不上 | `ffprobe` 沒裝,`make_srt.py` 量不到每段實際長度。補裝 FFmpeg 後**重跑 make_srt.py 就好,不用重轉** | ## 不該做的事 - ❌ 使用者沒回答隱私那一題就開始上傳 - ❌ 把金鑰寫進任何檔案或印出來 - ❌ 修改 srt 的時間碼 - ❌ 為了「快一點」把清洗的分段字數調大(只會更快撞 429) - ❌ 一次送幾十個很短的小檔(最低計費 10 秒,額度會白白燒光) ## 注意事項 - 所有訊息使用**繁體中文** - Windows 用 PowerShell,串指令用 `;` 不要用 `&&` - Windows 呼叫 curl 要打 `curl.exe`(有 `.exe`)
6-4 允許這個技能
打開(或建立)~/.config/opencode/opencode.json,確認 permission.skill 裡有這一項:
{
"permission": {
"skill": {
"audio-notes": "allow"
}
}
}
⚠️ 如果opencode.json已經有內容(前面幾包加的 MCP 設定、08-workflow-skills加的三個技能),不要整份覆蓋,只要把audio-notes這一行併進既有的permission.skill區塊就好。
6-5 重啟 OpenCode
OpenCode 只有啟動時才會掃描技能目錄。 剛剛新增的資料夾,要完全關掉再重開才會被認得(關視窗不算,要真的結束程序)。
6-6 ✅ 最終驗收:這一包成不成功,看這裡
| # | 做什麼 | 通過的樣子 | |---|--------|-----------| | 1 | 對 OpenCode 說「列出你現在可用的技能」 | 清單裡有 audio-notes | | 2 | 拿一份全新的、沒跑過的錄音,說「幫我把 <檔名> 轉成字幕」 | 它自己壓縮、呼叫 API、組 srt、繁體化,你沒有貼任何指令 | | 3 | 檢查它有沒有先問隱私那一題 | 有問 ← 沒問的話回去看 SKILL.md 是不是貼漏了 | | 4 | 拿一份全新的逐字稿,說「幫我把 <檔名> 整理成講義」 | 它自己做完 | | 5 | 打開 SKILL.md、make_srt.py、clean.py 搜尋 gsk_ | 🔴 三個檔案裡都搜不到 ← 搜到就是失敗,回步驟二 |
## 🔴 第 2 項和第 4 項就是這一包的分水嶺。 做得到 = 你有了一個工具。做不到 = 你只是跑過一次。 差別在下個月:有工具的人會再用,跑過一次的人早就忘了。
常見卡關
金鑰與帳號
| 症狀 | 原因 / 解法 | |------|------------| | KEY_MISSING | 環境變數沒生效。重開終端機(連 OpenCode 也要完全關掉重開) | | 401 / invalid_api_key | 金鑰複製時少一截,或前後多了空白。回 Console 重新產一把 | | 金鑰畫面關掉了才發現沒複製 | 看不回來,只能刪掉重產。 這是設計,不是壞掉 | | 註冊時要求信用卡 | 你走進升級流程了。 按上一頁退出來,免費方案不需要卡 | | 金鑰不小心 push 上 GitHub | 刪檔案沒用。立刻回 Console 刪掉那把金鑰、重產一把。 然後更新環境變數 |
A 線(轉字幕)
| 症狀 | 原因 / 解法 | |------|------------| | 400 提到 response_format | 🚨 陷阱 ①。填了 srt/vtt,Groq 不支援。改 verbose_json,用 make_srt.py 組 | | 413 / 檔案太大 | 🚨 陷阱 ②。免費方案單檔 25 MB。回 A1 壓縮,還是太大就切段 | | 轉出來是簡體 | 🚨 陷阱 ③。看 A5 兩條路線 | | .srt 拖進播放器沒反應 | 檔名要和影片同名(lesson01.mp4 ↔ lesson01.srt),或播放器裡手動載入 | | 字幕全部集中在前 10 分鐘 | 🚨 陷阱 ④。只餵了一個 json,或檔案順序亂了。回 4-3 | | 越後面偏差越大 | ffprobe 沒裝。補裝 FFmpeg,重跑 make_srt.py 就好,不用重轉(json 都還在) | | 中間有一整段沒字幕 | 那一段的 .json 是錯誤訊息。回 4-2 檢查每個 json 的大小 | | SRT_OK segments=0 | API 回的 json 裡沒有 segments——多半是 response_format 沒填 verbose_json | | 字幕內容整段重複 | 音檔裡有長時間靜音,Whisper 的已知行為。剪掉靜音再轉 | | ffmpeg 說找不到檔案,但檔案就在 | 中文檔名 / 路徑有空白。先改成英數字檔名,或整個路徑用雙引號包起來 | | 一句一個檔跑,額度突然沒了 | 最低計費 10 秒。改成切 10 分鐘大段 |
B 線(文字清洗)
| 症狀 | 原因 / 解法 | |------|------------| | 429 | 每分鐘 token 額度滿了。程式會自己等 35 秒重試;一直發生就把 LIMIT 調小(例如 800) | | CLEAN_FAIL http=404 | 模型改名了。跑 /v1/models 看現在叫什麼,改 clean.py 的 MODEL | | 講義裡出現我沒講過的內容 | 語言模型「幫你補充」了。把 SYSTEM 的第 ④ 條講更重,或改用比較大的模型 | | 輸出還是簡體 | SYSTEM 第 ① 條被忽略了。在使用者訊息開頭再寫一次「請用繁體中文(台灣)輸出」 | | 跑很慢 | 正常。免費方案要分段+等待。一小時的逐字稿約 10 段,大約 2~3 分鐘 | | 專有名詞一直錯 | 語音辨識階段就聽錯了。把正確寫法列在 SYSTEM 裡,例如「本文的專有名詞:正三角形、外心、內心」 |
Skill 與環境
| 症狀 | 原因 / 解法 | |------|------------| | 技能沒出現在清單 | ①檔名要全大寫 SKILL.md ②資料夾名要等於 name ③檔案第一行必須是 ---(圍籬符號不要寫進去)④沒重啟 OpenCode | | 技能叫得動但找不到 .py | SKILL.md 裡要用技能資料夾的完整路徑叫程式,不是相對路徑 | | uv 說找不到 | 回 00-env-setup。或直接用 python make_srt.py ...,這兩支程式只用內建模組 | | ffprobe 找不到 | 只裝了 yt-dlp 自帶的 FFmpeg。winget install --id Gyan.FFmpeg -e(沒管理員權限加 --scope user) | | PowerShell 串指令語法錯誤 | PowerShell 5.1 不支援 &&,改用 ; | | curl 行為很奇怪 | Windows 要打 curl.exe(有 .exe),否則叫到 PowerShell 的別名 | | 中文參數傳給 curl 變亂碼 | PowerShell 5.1 的編碼問題。改用 -F "欄位=<檔名" 從檔案讀(見 A5) | | opencode.json 存檔後 OpenCode 起不來 | JSON 格式:大括號要成對、引號要用英文半形 "、Windows 路徑反斜線寫兩條 \\ |
✅ 成功訊號(機器可判讀)
執行完請完整輸出下面這個區塊,讓後續的 agent 可以直接判讀。
=== OPENCODE_GROQ_API === result=PASS # PASS | PARTIAL | FAIL os=windows # windows | macos | linux privacy_confirmed=yes # yes | no(使用者已確認素材不含學生/家長聲音) key_set=yes # yes | no(環境變數 GROQ_API_KEY 存在) key_in_files=no # no | yes(SKILL.md 與 .py 內是否搜得到 gsk_) api_ok=yes # yes | no(/v1/models 回傳正常) ffmpeg=ok # ok | missing ffprobe=ok # ok | missing(缺了會退用 API 的 duration) model_stt=whisper-large-v3 # 實際使用的語音模型 model_llm=llama-3.3-70b-versatile # 實際使用的文字模型 srt_single=OK # OK | FAIL | SKIP(單檔轉字幕) srt_chunked=OK # OK | FAIL | SKIP(長檔切段合併) srt_tail_aligned=yes # yes | no(影片最後 2 分鐘字幕仍對得上) clean_ok=OK # OK | FAIL | SKIP skill_installed=yes # yes | no(~/.config/opencode/skills/audio-notes/) skill_listed=yes # yes | no(重啟後 OpenCode 掃得到) skill_rerun_srt=OK # OK | FAIL(換新素材,一句話跑完 A 線) skill_rerun_clean=OK # OK | FAIL(換新素材,一句話跑完 B 線) === END ===
判定規則(agent 請照這個判斷,不要自行認定):
| 結果 | 條件 | |------|------| | PASS | skill_rerun_srt=OK 且 skill_rerun_clean=OK 且 key_in_files=no | | PARTIAL | A、B 兩線手動跑得起來,但技能沒包成/換素材沒跑通 | | FAIL | api_ok=no,或 A、B 兩線都跑不起來 |
## 🔴 兩條會直接讓整體變成FAIL的:key_in_files=yes—— 金鑰出現在任何檔案裡,不管其他項目多漂亮,一律FAIL。privacy_confirmed=no—— 沒跟使用者確認過素材內容就上傳,一律FAIL,而且要在回報裡明確寫出來。 這兩條不准「先繼續、之後再補」。
完成回報格式
## Groq API 工具化結果 ### 隱私確認 - 已向使用者確認素材不含學生/家長/輔導內容:是 / 否 - 已提醒「上傳前聽最後 30 秒」:是 / 否 - Zero Data Retention 已開啟:是 / 否 / 未確認 ### 金鑰 - 存放方式:環境變數 / 專案 .env - `.gitignore` 已擋住 `.env`(`git check-ignore -v .env` 有輸出):是 / 否 / 不適用 - SKILL.md 與 .py 內搜尋 `gsk_`:查無 / ⚠️ 有(立刻撤銷重產) - 金鑰驗證(`/v1/models`):PASS / FAIL ### A 線:轉字幕 - 使用模型:<實際用的> - 來源檔長度:<分鐘> / 壓縮後大小:<MB> - 是否切段:否 / 是(<N> 段,每段 600 秒) - 產出:`<路徑>.srt`(<N> 句) - 時間碼驗證(看**最後 2 分鐘**):PASS / FAIL - 繁體化方式:prompt 參數 / OpenCode 改字 / 兩者都做 ### B 線:文字清洗 - 使用模型:<實際用的> - 分段數:<N> - 產出:`<路徑>.md` - 已提醒使用者「發給學生前自己讀一遍」:是 / 否 ### 工具化(本包正題) - 技能位置:`~/.config/opencode/skills/audio-notes/` - 隨附程式:`make_srt.py`、`clean.py` - `opencode.json` 權限已加:是 / 否 - 已重啟 OpenCode:是 / 否 - 技能出現在清單:是 / 否 - 🔴 **換新素材、一句話跑完 A 線**:PASS / FAIL - 🔴 **換新素材、一句話跑完 B 線**:PASS / FAIL ### 待處理 - <把 FAIL 的項目與原因列在這裡;全部 PASS 就寫「無」>
如果做壞了,怎麼重來
對 OpenCode 說:「Groq 這一包做壞了,幫我清乾淨重來。」
| 想重來的東西 | 做法 | |-------------|------| | 只是某一段轉壞了 | 刪掉那一段的 .json,重跑那一段就好。其他段不用重轉 | | 時間碼歪了 | 補裝 ffprobe,重跑 make_srt.py。API 不用重打,json 都還在 | | 字幕整份要重來 | 刪掉 chunk_*.json 和 .srt,從 4-2 重跑 | | 技能沒被掃到 | 檢查檔名是不是 SKILL.md(全大寫)、資料夾名是不是 audio-notes、第一行是不是 ---。改完完全關閉 OpenCode 再開 | | 技能整個不要了 | 刪掉 ~/.config/opencode/skills/audio-notes/ 資料夾,並從 opencode.json 的 permission.skill 移除那一行 | | 金鑰外洩 | 🔴 Console → API Keys → 刪掉那把、重產一把 → 更新環境變數。這件事優先於其他所有事 |
⚠️ 重跑 API 之前先想一下額度:免費方案每小時 7,200 音訊秒。 一支 1 小時的錄音重轉三次就用掉一半。先確認是哪一步壞了,不要整條重來。
下一步
## 🚀 下午:把這個工具變成網頁 →10-netlify現在你有一個「只有你的電腦能用」的工具。 下午那一包會把它放上網路——別的老師打開網址就能用,不用裝 OpenCode、不用申請金鑰。 今天早上寫的clean.py、學到的「金鑰放環境變數不放檔案」,下午會原封不動用上。 (尤其是金鑰那條:網頁上線之後,金鑰放錯地方是全世界都看得到。這件事今天先練,下午就不會慌。)
其他方向:
- 素材從哪裡來(下載影片、抽音軌、抓現成字幕)→
02-file-toolkit的 B 段 - 產出的講義要收進第二大腦 →
04-obsidian - 工具要備份、換電腦也帶著走 →
07-github - 想讓「轉字幕」也能被開工/收工流程接住 →
08-workflow-skills
📌 提醒一件事:~/.config/opencode/skills/底下的技能不在任何專案的 repo 裡。 換一台電腦,這個技能不會跟著過去。要帶著走,就把audio-notes這個資料夾也放進你的 GitHub(見07-github)——但記得,裡面永遠不要有金鑰。