From 1e1e9b247f2a93745991a1094e241a72c5a04143 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 15 Jun 2026 09:52:26 +0000 Subject: [PATCH] =?UTF-8?q?feat(C-3):=20=E6=97=81=E7=99=BD/=E7=AB=A0?= =?UTF-8?q?=E7=AF=80=E5=88=87=E5=88=86=E6=A8=A1=E5=9E=8B=E9=81=B7=20gemini?= =?UTF-8?q?-3.5-flash=EF=BC=88A/B=20=E9=A9=97=E9=81=8E=E5=BE=8C=E5=88=87?= =?UTF-8?q?=E6=8F=9B=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 劉老師本機 A/B(PID 控制器三頁)驗過品質後拍板遷移:正確性兩邊乾淨、3.5 口吻更自然且長度更貼 75s/頁、成本約 64%;唯一弱點是偶爾漏列點,已補 prompt 糾正。 - slide_ingest.py:寫死 MODEL=gemini-2.5-flash → narration_model()=resolve_id(TEXT_FAST) (呼叫時解析,設定頁 text.fast 覆寫即時生效)。旁白 + 章節切分一起遷(共用模型常數)。 - NARRATION_PROMPT_DETAILED 補第 7 條:條列項目每項至少帶一句、可精簡不可整項遺漏。 - rollback 免改 code:設定頁把 text.fast 覆寫回 2.5 即可。 - solve.py(解題)不在此遷移範圍(正確性更敏感、未 A/B)。 本機全套 2702 passed(剩 1 QR 像素為容器缺 Noto CJK 字型假象,CI 權威)。 --- docs/PRODUCT_READINESS.md | 18 ++++++++++++++---- slide_ingest.py | 25 ++++++++++++++++++------- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/docs/PRODUCT_READINESS.md b/docs/PRODUCT_READINESS.md index bb8f367..2b1faec 100644 --- a/docs/PRODUCT_READINESS.md +++ b/docs/PRODUCT_READINESS.md @@ -268,8 +268,8 @@ char-based 近似模型。 - [ ] 🟡 **C-2 單價對齊真實**(GATE,需查官方定價)— 現況單價是估算。對齊 Gemini 3 系列 + GCP TTS + (未來)image 真實單價。定價會變動 → 抽成設定常數 + 文件註明「以官方為準」。 -- [~] 🟡 **C-3 旁白模型遷 3.x**(GATE,需開額度驗證品質)— `slide_ingest.py:43` - `MODEL = "gemini-2.5-flash"`(將淘汰)。**M 軸完成後這只是改角色表 `text.fast` 一個值**。 +- [x] 🟡 **C-3 旁白模型遷 3.x**(GATE→已驗已切)— ✅ 2026-06-15 完成。`slide_ingest.py` + 原寫死 `MODEL = "gemini-2.5-flash"`(將淘汰)。**M 軸完成後切換只是改角色表 `text.fast` 一個值**。 3.5-flash 實測接受 `thinking_budget=0`,但**旁白品質要先驗**再換。寫成 A/B proposal,劉老師 開額度跑過再切。(劉老師 2026-06-07:需額度會給權限;2026-06-15:開額度。) - ✅ 2026-06-15 **A/B 工具 + 提案完成(offline 前置;實跑=你本機開額度)**。劉老師 2026-06-15 @@ -283,8 +283,18 @@ (chokepoint 改走 `resolve("text.fast")`)+ rollback(設定頁覆寫回 2.5、免改 code)/ 3 個待拍板 開放問題(範圍 / `text.fast` vs 新增 `narration` 角色 / 候選模型)。補 `tests/test_ab_narration.py` 11 測(頁碼解析 / run_ab 每頁每模型透傳 / 報告並排+用量 / 缺 key SystemExit,**全 fake client - 不打 API**)。本機相關子集 142 passed。**下一步=你本機跑 A/B → 看品質 → 回報要不要切**,要切就 - 開後續一刀換 chokepoint。 + 不打 API**)。本機相關子集 142 passed。 + - ✅ 2026-06-15 **切換完成(劉老師本機 A/B 驗過品質後拍板遷移)**。劉老師跑 A/B(材力/自控 + `Chap08-PID控制器設計` 三頁)逐項比對:**正確性兩邊都乾淨無誤**(review gate 真正守的底線)、 + 3.5-flash **口吻更自然 + 長度更貼 ~75s/頁時間預算**(2.5 偏長易被 `_truncate_at_sentence` 截)、 + **成本約 64%**(省 ~36% 輸出字);唯一退步是 3.5 偶爾壓縮會漏列點(學習目標頁少帶一項)。 + 拍板**切**。落地:`slide_ingest.py` 把寫死 `MODEL` 換成 `narration_model()=resolve_id(TEXT_FAST)` + (**呼叫時解析**=設定頁 `text.fast` 覆寫即時生效),**旁白 + 章節切分**一起遷(共用同一模型常數, + 章節切分也得離開 2.5);並在 `NARRATION_PROMPT_DETAILED` 補第 7 條「條列項目每項至少帶一句、可 + 精簡不可整項遺漏」糾正 3.5 唯一弱點。**rollback 免改 code**:設定頁把 `text.fast` 覆寫回 + `gemini-2.5-flash` 即時退。**`solve.py`(解題)模型不在本遷移範圍**(正確性更敏感、未 A/B,另議)。 + `model` 計帳如實落 resolved id。本機全套 2702 passed(剩 1 QR 像素為容器缺 Noto CJK 字型假象, + CI 權威)。至此「換旁白模型 = 改登錄表/設定頁 `text.fast` 一個值」閉環。 - [ ] 🟢 **C-4 `gemini-3.1-pro-image` 等開放再換**(GATE)— 劉老師想用但 API 未開放。等開放 從 `gemini-3-pro-image` 換(`core/infocards/models.py`)。掛追蹤。 - [x] 🟢 **C-5 模型 id 自我健檢**(offline)— ✅ 2026-06-09 完成。新增 `tools/check_models.py`:蒐集 diff --git a/slide_ingest.py b/slide_ingest.py index 540f101..ef2dd47 100644 --- a/slide_ingest.py +++ b/slide_ingest.py @@ -40,7 +40,16 @@ SLIDES_ROOT = BASE_DIR / "slides" EXAMS_ROOT = BASE_DIR / "exams" -MODEL = "gemini-2.5-flash" +# C-3(2026-06-15 劉老師拍板,A/B 驗過品質後遷移):旁白 + 章節切分模型走 M 軸角色登錄表 +# text.fast(預設 gemini-3.5-flash),不再寫死 gemini-2.5-flash(將淘汰)。換模型=改登錄表/ +# 設定頁 text.fast 一個值;rollback 把 text.fast 覆寫回 2.5 即可、免改 code。呼叫時解析 → +# 設定頁覆寫即時生效。注意:solve.py(解題)模型不在此遷移範圍(正確性更敏感、未 A/B)。 +def narration_model() -> str: + """旁白 / 章節切分用的模型 id(M 軸 text.fast 角色,呼叫時解析)。""" + from core.models import TEXT_FAST, resolve_id + return resolve_id(TEXT_FAST) + + # Gemini HTTP 逾時 (毫秒): 不設的話連線 stall 會無限掛住 (140 頁論文出過此狀況)。 GEMINI_TIMEOUT_MS = 90_000 SLIDE_DPI = 200 # 1920px 寬左右 (16:9 投影片) @@ -100,6 +109,7 @@ 4. 開頭銜接多樣化, 不要每張都「好, 我們來看」 5. 不要 LaTeX、不要 Markdown、不要符號標記; 程式碼用「等於」「冒號」念 6. 純中文 + 必要英文術語 / 數值 +7. 若投影片以條列列出多個項目/目標/步驟, 每一項都要至少帶到一句, 可精簡但不要整項遺漏 ==== 輸出格式 ==== 直接輸出純文字, 不要前言、引號、分段。 @@ -176,11 +186,12 @@ def detect_chapters_with_gemini(thumbs: list[bytes], total_pages: int) -> list[d client = genai.Client(api_key=api_key, http_options=types.HttpOptions(timeout=GEMINI_TIMEOUT_MS)) + model = narration_model() parts = [types.Part.from_bytes(data=t, mime_type="image/png") for t in thumbs] resp = client.models.generate_content( - model=MODEL, + model=model, contents=parts + [CHAPTER_PROMPT], - # thinking_budget=0: 2.5-flash 預設開 thinking, 會吃掉 max_output_tokens 導致回空/截斷, + # thinking_budget=0: flash 系列預設開 thinking, 會吃掉 max_output_tokens 導致回空/截斷, # 且每次呼叫慢 5 倍 (11s→2s)。章節切分/旁白都不需要 thinking。 config=types.GenerateContentConfig( temperature=0.1, max_output_tokens=4096, @@ -188,7 +199,7 @@ def detect_chapters_with_gemini(thumbs: list[bytes], total_pages: int) -> list[d ) raw = (resp.text or "").strip() from core import usage - usage.record_text_now("video", MODEL, CHAPTER_PROMPT, raw, label="chapters") + usage.record_text_now("video", model, CHAPTER_PROMPT, raw, label="chapters") if "```" in raw: raw = raw.split("```")[1] if raw.startswith("json"): @@ -272,11 +283,11 @@ def narrate_page_with_gemini(client, page_png: bytes, chapter_title: str, """單頁 → narration 草稿。Gemini 偶爾會在中文句中提早 STOP 導致句子腰斬, 結尾若不是句號類符號就 retry 一次, temperature 提高 + prompt 加強完整性要求。 - model: 覆寫旁白模型 id(預設沿用模組 MODEL)。供 C-3 旁白模型 A/B 比對用 - (tools/ab_narration.py 對同一頁跑 2.5 vs 3.x 比品質),不影響正式 pipeline 預設。""" + model: 覆寫旁白模型 id(預設走角色登錄表 text.fast=narration_model())。供 C-3 旁白模型 + A/B 比對用(tools/ab_narration.py 對同一頁跑 2.5 vs 3.x 比品質)。""" from google.genai import types - model = model or MODEL + model = model or narration_model() template = NARRATION_PROMPT_BRIEF if brief else NARRATION_PROMPT_DETAILED base_prompt = template.format(