Files
pp-qa-km/tools/convert/from_vision.py
LittleYellow cfd1876359 轉換工具強化:失敗診斷訊息 + 舊版格式升版 + 掃描 PDF vision fallback
- convert.py:轉換失敗改印例外型別、批次尾端彙總、--traceback 旗標;
  空白轉換結果示警(避免純圖片 docx 等假成功靜默入庫)
- from_legacy.py(B):.doc/.xls/.ppt 經本地 LibreOffice headless 升版為
  現代格式後再走既有轉換器;raw/originals 仍保存真正的舊格式原始檔
- to_image.py + from_vision.py(A):needs_ocr 的掃描/圖片型 PDF 逐頁
  render → 本地 vision 模型轉錄(--vision);:cloud/未設定防護,
  模型設定走 config/models.yaml 的 tasks.vision(預設不啟用)
- kb.ollama_chat 加 images 參數(向後相容);models.yaml 加 vision 範例
- selfcheck 補 legacy 路由、to_image 真測、from_vision 注入測、
  --vision 端到端,全數 ALL PASS

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 07:00:32 +08:00

62 lines
2.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""影像 → Markdown 轉錄(本地 vision 模型AGENTS.md §1.1 資料落地)。
用於 vision fallback文字抽取失敗的頁面 render 成影像後,逐頁交本地 vision
模型忠實轉錄為 Markdown。模型設定讀 config/models.yaml 的 tasks.vision§1.2
未設定即報錯、不使用預設值;:cloud 模型一律拒絕(影像不得離開本機)。
"""
import base64
import pathlib
import sys
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1])) # tools/ 供 import kb
import kb
VISION_PROMPT = (
"你是文件轉錄引擎。忠實轉錄此頁影像中的所有文字與表格內容為 Markdown"
"表格用 Markdown table保留標題階層。只輸出轉錄內容本身不要加任何說明或臆測。"
"影像中沒有可辨識內容時,只回覆「(本頁無可辨識文字)」。")
def _require_vision(cfg):
"""取得並驗證 vision 模型設定。缺設定 / :cloud 皆報錯信任邊界§1.1/§1.2)。"""
spec = (cfg.get("tasks") or {}).get("vision")
if not isinstance(spec, dict) or not spec.get("model") or spec["model"] == "CHANGE_ME":
raise SystemExit(
"models.yaml 未設定 tasks.vision.model--vision 需要合規本地 vision 模型"
"(請自行確認非中國關聯、非 :cloudAGENTS.md §1.1/§1.3)。")
if str(spec["model"]).endswith(":cloud"):
raise SystemExit("tasks.vision.model 為 :cloud 模型禁止文件影像不得離開本機§1.1)。")
return spec
def _b64(path):
return base64.b64encode(pathlib.Path(path).read_bytes()).decode("ascii")
def transcribe(cfg, image_paths):
"""逐頁轉錄影像,回傳 (每頁 markdown 列表, 實際使用的 model)。
單頁重試 max_retries 次仍失敗即拋 RuntimeErrorvision 無文字 fallback 可用——
fallback 模型非 vision不切換
"""
spec = _require_vision(cfg)
model = spec["model"]
retries = int(spec.get("max_retries", 2))
temperature = float(spec.get("temperature", 0.1))
pages = []
for idx, img in enumerate(image_paths, 1):
b64 = _b64(img)
last = None
for _ in range(max(1, retries)):
try:
text = kb.ollama_chat(cfg, model, VISION_PROMPT, temperature, images=[b64])
if text and text.strip():
pages.append(text.strip())
break
last = "空回應"
except Exception as e:
last = f"{type(e).__name__}: {e}"
else:
raise RuntimeError(f"vision 轉錄失敗(第 {idx}model={model}{last}")
return pages, model