Agent Skills by ALSEL
汎用LLM・AI開発⭐ リポ 0品質スコア 60/100

regex-vs-llm-structured-text

構造化テキストの解析時に正規表現とLLMのどちらを使うかを判断するためのフレームワークです。まず正規表現で対応し、信頼度の低いエッジケースに限ってLLMを追加することをお勧めします。このアプローチにより、シンプルなケースは高速かつ低コストで処理しながら、複雑なパターンには言語モデルの柔軟性を活用できます。

description の原文を見る

Decision framework for choosing between regex and LLM when parsing structured text — start with regex, add LLM only for low-confidence edge cases.

SKILL.md 本文

構造化テキスト解析における正規表現 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("few_choices")
        score -= 0.3

    if not item.answer:
        reasons.append("missing_answer")
        score -= 0.5

    if len(item.text) < 10:
        reasons.append("short_text")
        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"必要に応じて修正した JSON を返すか、正確な場合は 'CORRECT' を返してください。"
            ),
        }],
    )
    # 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 がここではより良い)
  • 信頼度スコアリングをスキップして正規表現が「単に機能する」ことを願う
  • クリーニング/バリデーションステップ中にパースされたオブジェクトを変更する
  • エッジケース (形式が不正な入力、欠落フィールド、エンコーディング問題) をテストしない

使用するタイミング

  • クイズ/試験問題の解析
  • フォームデータの抽出
  • 請求書/レシートの処理
  • ドキュメント構造の解析 (ヘッダー、セクション、テーブル)
  • コストが重要な、繰り返しパターンを持つ任意の構造化テキスト

ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ

詳細情報

作者
flatrick
リポジトリ
flatrick/mdt
ライセンス
MIT
最終更新
2026/3/21

Source: https://github.com/flatrick/mdt / ライセンス: MIT

本サイトは GitHub 上で公開されているオープンソースの SKILL.md ファイルをクロール・インデックス化したものです。 各スキルの著作権は原作者に帰属します。掲載に問題がある場合は info@alsel.co.jp または /takedown フォームよりご連絡ください。
原作者: flatrick · flatrick/mdt · ライセンス: MIT