業務用スクリプトの依頼文に入力・出力・エラー処理のルールを入れる
「これを自動化して」を、実行可能な task description に変えます。sensitive data を含まない合成 sample と、手作業で確認した expected result を添えて、team ごとの work logs を集計する script request を完成させます。
AIが生成した正規表現は完成したルールではなく、まず下書きとして扱います。小さな合成テスト表を作り、期待結果と実際の一致結果を比較し、パターンを修正してから実データで使う前にレビュー結果を保存します。
この翻訳はAIで作成しました。コード、単位、数値は原文と併せて確認してください。各言語のネイティブ話者による校閲は、まだ完了していません。 English
対象読者AIを使って正規表現の下書きを作成し、既知の一致例と不一致例で簡単に検証したい人向けです。
社内の作業IDがWK-YYYY-NNNNという形式でなければならないとします。接頭辞は大文字のWK、年は2000から2099、区切り文字はハイフン、最後の部分は正確に4桁のASCII数字である必要があります。
AIアシスタントがWK-\d{4}-\d{4}というパターンを提案したとします。一見もっともらしく見えますが、年については4桁の数字であることしか要求していません。指定された2000-2099の範囲は強制されません。最も安全な次の手順は、すぐに使うのではなく、正解が分かっている例でテストすることです。
以下のデータセットは合成データで、このページ専用に作成したものです。regex_cases.csvとして保存してください。一致すべき文字列が3件、一致すべきでない文字列が7件含まれています。
case_id,value,should_match
C001,WK-2026-0001,yes
C002,WK-2000-0042,yes
C003,WK-2099-9999,yes
C004,WK-1999-0001,no
C005,wk-2026-0001,no
C006,WK-2026-001,no
C007,WK-2026-00001,no
C008,WK-2026_0001,no
C009," WK-2026-0001",no
C010,WK-20A6-0001,no
テストケースには、許可される年の境界、範囲外の年、小文字の接頭辞、短すぎる・長すぎる連番、誤った区切り文字、先頭の空白、年の中の数字以外の文字が含まれています。正規表現を実行する前に、人手で分類できるほど小さな例です。
AIのパターンWK-\d{4}-\d{4}はC001、C002、C003を受け入れるはずです。C005からC010までは構造上の理由で拒否されるはずです。ただし1999も4桁の数字なので、C004も誤って受け入れてしまいます。
| ケース | 期待結果 | AIパターン | 結果 |
|---|---|---|---|
| C001 | MATCH | MATCH | PASS |
| C002 | MATCH | MATCH | PASS |
| C003 | MATCH | MATCH | PASS |
| C004 | NO MATCH | MATCH | FAIL |
| C005 | NO MATCH | NO MATCH | PASS |
| C006 | NO MATCH | NO MATCH | PASS |
| C007 | NO MATCH | NO MATCH | PASS |
| C008 | NO MATCH | NO MATCH | PASS |
| C009 | NO MATCH | NO MATCH | PASS |
| C010 | NO MATCH | NO MATCH | PASS |
修正版のパターンはWK-20[0-9]{2}-[0-9]{4}です。先頭の20によって年を2000から2099に制限し、[0-9]によって意図した数字集合を明示します。スクリプトはfullmatchを使うため、入力文字列全体がパターンを満たす必要があります。
以下のスクリプトをai_regex_check.pyとして保存してください。AIの提案パターンと修正版パターンの両方をテストし、すべてのケースをCSVレポートに書き出し、既存の出力フォルダは再利用しません。
import csv
import re
from pathlib import Path
SOURCE = Path("regex_cases.csv")
OUTPUT_DIR = Path("outputs") / "regex_check_result"
REPORT = OUTPUT_DIR / "regex_check.csv"
AI_PATTERN = re.compile(r"WK-\d{4}-\d{4}")
REVISED_PATTERN = re.compile(r"WK-20[0-9]{2}-[0-9]{4}")
def parse_expected(value: str) -> bool:
normalized = value.strip().lower()
if normalized == "yes":
return True
if normalized == "no":
return False
raise ValueError(f"Expected yes or no, got: {value!r}")
def label(value: bool) -> str:
return "MATCH" if value else "NO MATCH"
def main() -> None:
if not SOURCE.is_file():
raise FileNotFoundError(f"Source CSV not found: {SOURCE}")
if OUTPUT_DIR.exists():
raise FileExistsError(f"Output folder already exists: {OUTPUT_DIR}")
results = []
ai_failures = 0
revised_failures = 0
with SOURCE.open("r", encoding="utf-8-sig", newline="") as stream:
reader = csv.DictReader(stream)
required = {"case_id", "value", "should_match"}
if reader.fieldnames is None or not required.issubset(reader.fieldnames):
raise ValueError("CSV is missing a required column.")
for row_number, row in enumerate(reader, start=2):
expected = parse_expected(row["should_match"])
text = row["value"]
ai_actual = AI_PATTERN.fullmatch(text) is not None
revised_actual = REVISED_PATTERN.fullmatch(text) is not None
ai_pass = ai_actual == expected
revised_pass = revised_actual == expected
if not ai_pass:
ai_failures += 1
if not revised_pass:
revised_failures += 1
results.append({
"case_id": row["case_id"],
"value": text,
"expected": label(expected),
"ai_actual": label(ai_actual),
"ai_test": "PASS" if ai_pass else "FAIL",
"revised_actual": label(revised_actual),
"revised_test": "PASS" if revised_pass else "FAIL",
})
OUTPUT_DIR.parent.mkdir(parents=True, exist_ok=True)
OUTPUT_DIR.mkdir()
fields = [
"case_id", "value", "expected", "ai_actual", "ai_test",
"revised_actual", "revised_test"
]
with REPORT.open("x", encoding="utf-8", newline="") as stream:
writer = csv.DictWriter(stream, fieldnames=fields)
writer.writeheader()
writer.writerows(results)
print(f"Cases: {len(results)}.")
print(f"AI pattern failures: {ai_failures}.")
print(f"Revised pattern failures: {revised_failures}.")
print(f"Report: {REPORT.as_posix()}")
if revised_failures:
raise RuntimeError("Revised pattern still fails one or more tests.")
if __name__ == "__main__":
main()
python ai_regex_check.pyテストケースは10件です。AIのパターンはC004の1件だけで失敗し、修正版パターンは10件すべてに合格するはずです。
| 項目 | 期待値 |
|---|---|
| ケース数 | 10 |
| AIパターンの失敗数 | 1 |
| 修正版パターンの失敗数 | 0 |
| AIパターンの失敗ケース | C004 |
| 修正版パターンの合格ケース数 | 10 |
以下の期待コンソール出力は、テスト表とスクリプトから手作業で導いたものです。実際の実行ログではありません。
Cases: 10.
AI pattern failures: 1.
Revised pattern failures: 0.
Report: outputs/regex_check_result/regex_check.csv| ミス | なぜ重要か |
|---|---|
| 一致すべき文字列だけをテストする | 緩すぎる正規表現でも、不一致例を試すまでは正しく見えることがあります。 |
| 要件を言い直さずにAIのパターンを使う | 意図した問題とは少し異なる問題を解くパターンになっている可能性があります。 |
| 通常の例を1つだけ確認する | 境界、長さ、大文字・小文字、区切り文字の誤りが未検証のまま残ります。 |
| フィールド全体のルールなのにfullmatchではなくsearchを使う | 全体としては無効な値の中に、有効に見える部分文字列が見つかることがあります。 |
| 正規表現の出力を見た後で期待結果を変更する | テストが必要な動作を独立して定義する役割を失います。 |
この10件のテストに合格しても、修正版パターンがこの10件の合成例で正しく動くことしか示しません。考えられるすべての入力を正しく処理できることを数学的に証明するものではありません。新しい境界条件や失敗パターンが見つかったら、テストケースを追加してください。
正規表現が検証するのは構文であり、業務上の事実ではありません。WK-2026-1234が完全に一致しても、そのIDが実際にデータベースに存在するとは限りません。存在確認、一意性、権限、他フィールドとの関係は別途確認が必要です。
この小さな例では、非常に長い入力や攻撃的に作られた入力に対する性能は評価していません。より複雑なAI生成パターンを信頼できないテキストを処理するサービスに組み込む前に、正確性と実行時の挙動の両方を確認してください。
2026-09-20 · 手作業で確認した例 · 対象: Python 3.12 · 標準ライブラリ: csv, pathlib, re · 未実行
説明と例は独自に作成しました。関連する動作や概念は、以下の公式資料で確認できます。