regex-vs-llm-structured-text

regex-vs-llm-structured-text

熱門

在解析結構化文字時,於正規表達式與 LLM 之間做選擇的決策框架——先從正規表達式開始,只在低信心的邊緣案例才加入 LLM。

23萬星標
3.5萬分支
更新於 2026/7/14
SKILL.md
readonlyread-only
name
regex-vs-llm-structured-text
description

在解析結構化文字時,於正規表達式與 LLM 之間做選擇的決策框架——先從正規表達式開始,只在低信心的邊緣案例才加入 LLM。

正規表達式 vs LLM 用於結構化文字解析

一個實用的決策框架,用於解析結構化文字(測驗、表單、發票、文件)。關鍵洞察:正規表達式能以低成本且確定性地處理 95-98% 的案例。將昂貴的 LLM 呼叫保留給其餘的邊緣案例。

何時啟用

  • 解析具有重複模式的結構化文字(題目、表單、表格)
  • 在正規表達式與 LLM 之間做選擇以進行文字萃取
  • 建立結合兩種方法的混合管道
  • 在文字處理中最佳化成本/準確度取捨

決策框架

文字格式是否一致且重複?
├── 是(>90% 遵循某種模式)→ 從正規表達式開始
│   ├── 正規表達式處理 95% 以上 → 完成,不需要 LLM
│   └── 正規表達式處理低於 95% → 只在邊緣案例加入 LLM
└── 否(自由格式、高度變異)→ 直接使用 LLM

架構模式

原始文字
    │
    ▼
[正規表達式解析器] ─── 萃取結構(95-98% 準確度)
    │
    ▼
[文字清理器] ─── 移除雜訊(標記、頁碼、人工產物)
    │
    ▼
[信心評分器] ─── 標記低信心的萃取結果
    │
    ├── 高信心(≥0.95)→ 直接輸出
    │
    └── 低信心(<0.95)→ [LLM 驗證器] → 輸出

實作

1. 正規表達式解析器(處理多數情況)

import re
from dataclasses import dataclass

@dataclass(frozen=True)
class ParsedItem:
    id: str
    text: str
    choices: tuple[str, ...]
    answer: str
    confidence: float = 1.0

def parse_structured_text(content: str) -> list[ParsedItem]:
    """使用正規表達式模式解析結構化文字。"""
    pattern = re.compile(
        r"(?P<id>\d+)\.\s*(?P<text>.+?)\n"
        r"(?P<choices>(?:[A-D]\..+?\n)+)"
        r"Answer:\s*(?P<answer>[A-D])",
        re.MULTILINE | re.DOTALL,
    )
    items = []
    for match in pattern.finditer(content):
        choices = tuple(
            c.strip() for c in re.findall(r"[A-D]\.\s*(.+)", match.group("choices"))
        )
        items.append(ParsedItem(
            id=match.group("id"),
            text=match.group("text").strip(),
            choices=choices,
            answer=match.group("answer"),
        ))
    return items

2. 信心評分

標記可能需要 LLM 審查的項目:

@dataclass(frozen=True)
class ConfidenceFlag:
    item_id: str
    score: float
    reasons: tuple[str, ...]

def score_confidence(item: ParsedItem) -> ConfidenceFlag:
    """評分萃取信心並標記問題。"""
    reasons = []
    score = 1.0

    if len(item.choices) < 3:
        reasons.append("選項太少")
        score -= 0.3

    if not item.answer:
        reasons.append("缺少答案")
        score -= 0.5

    if len(item.text) < 10:
        reasons.append("文字過短")
        score -= 0.2

    return ConfidenceFlag(
        item_id=item.id,
        score=max(0.0, score),
        reasons=tuple(reasons),
    )

def identify_low_confidence(
    items: list[ParsedItem],
    threshold: float = 0.95,
) -> list[ConfidenceFlag]:
    """回傳低於信心門檻的項目。"""
    flags = [score_confidence(item) for item in items]
    return [f for f in flags if f.score < threshold]

3. LLM 驗證器(僅邊緣案例)

def validate_with_llm(
    item: ParsedItem,
    original_text: str,
    client,
) -> ParsedItem:
    """使用 LLM 修正低信心的萃取結果。"""
    response = client.messages.create(
        model="claude-haiku-4-5-20251001",  # 用於驗證的最便宜模型
        max_tokens=500,
        messages=[{
            "role": "user",
            "content": (
                f"從這段文字中萃取出題目、選項和答案。\n\n"
                f"文字:{original_text}\n\n"
                f"目前的萃取結果:{item}\n\n"
                f"如果正確請回傳 'CORRECT',否則回傳修正後的 JSON。"
            ),
        }],
    )
    # 解析 LLM 回應並回傳修正後的項目...
    return corrected_item

4. 混合管道

def process_document(
    content: str,
    *,
    llm_client=None,
    confidence_threshold: float = 0.95,
) -> list[ParsedItem]:
    """完整管道:正規表達式 -> 信心檢查 -> LLM 處理邊緣案例。"""
    # 步驟 1:正規表達式萃取(處理 95-98%)
    items = parse_structured_text(content)

    # 步驟 2:信心評分
    low_confidence = identify_low_confidence(items, confidence_threshold)

    if not low_confidence or llm_client is None:
        return items

    # 步驟 3:LLM 驗證(僅針對被標記的項目)
    low_conf_ids = {f.item_id for f in low_confidence}
    result = []
    for item in items:
        if item.id in low_conf_ids:
            result.append(validate_with_llm(item, content, llm_client))
        else:
            result.append(item)

    return result

實際指標

來自一個生產環境的測驗解析管道(410 個項目):

指標 數值
正規表達式成功率 98.0%
低信心項目 8(2.0%)
需要的 LLM 呼叫次數 ~5
相較於全部使用 LLM 的成本節省 ~95%
測試覆蓋率 93%

最佳實務

  • 從正規表達式開始——即使不完美的正規表達式也能給你一個改善的基準
  • 使用信心評分來程式化地識別哪些需要 LLM 協助
  • 使用最便宜的 LLM進行驗證(Haiku 等級的模型就足夠了)
  • 絕不修改已解析的項目——從清理/驗證步驟回傳新的實例
  • TDD 對解析器很有效——先為已知模式撰寫測試,再處理邊緣案例
  • 記錄指標(正規表達式成功率、LLM 呼叫次數)以追蹤管道健康狀況

應避免的反模式

  • 當正規表達式能處理 95% 以上的案例時,仍將所有文字送給 LLM(昂貴且緩慢)
  • 對自由格式、高度變異的文字使用正規表達式(LLM 在這裡更適合)
  • 跳過信心評分,指望正規表達式「剛好能運作」
  • 在清理/驗證步驟中修改已解析的物件
  • 不測試邊緣案例(格式錯誤的輸入、缺少欄位、編碼問題)

何時使用

  • 測驗/考試題目解析
  • 表單資料萃取
  • 發票/收據處理
  • 文件結構解析(標題、章節、表格)
  • 任何具有重複模式且成本重要的結構化文字