業務用スクリプトの依頼文に入力・出力・エラー処理のルールを入れる
「これを自動化して」を、実行可能な task description に変えます。sensitive data を含まない合成 sample と、手作業で確認した expected result を添えて、team ごとの work logs を集計する script request を完成させます。
AIは整理されていない文章を構造化JSONに変換できますが、結果は別途検証する必要があります。このガイドでは、小さな合成例とPythonの検証処理を使って、必須フィールド、型、許可値、基本的な整合性を確認します。
この翻訳はAIで作成しました。コード、単位、数値は原文と併せて確認してください。各言語のネイティブ話者による校閲は、まだ完了していません。 English
対象読者メモ、メール、フォーム、その他の乱雑な文章をAIチャットアシスタントで構造化JSONに変換し、再現可能な検証手順を加えたい人向けです。
AIは不規則な文章を構造化されたフィールドに変換するのに便利ですが、見た目が正しいJSONでも内容が間違っていることがあります。必須キーが欠ける、数値が文字列として返る、許可されていない値が入る、元テキストにない情報をAIが推測して追加する、といった問題が起こります。
安全な方法は、抽出と検証を分けることです。まずAIに元テキストを固定された構造へ変換させます。次に、返されたJSONを明示的なルールに照らして確認します。検証だけで抽出されたすべての事実が正しいとは証明できませんが、表計算、データベース、スクリプト、業務フローへ渡す前に多くの構造的な誤りを検出できます。
この例は合成例です。短いサポートメモに、顧客名、チケット番号、優先度、影響を受ける製品、任意の折り返し時間が含まれているとします。
元テキスト: "Ticket 1842. Customer: Mira Lee. Login fails on the desktop app and web portal. Priority is high. Please call after 15:30. Products affected: Desktop, Web."
| フィールド | ルール |
|---|---|
| ticket_id | 必須の整数 |
| customer | 必須の空でない文字列 |
| priority | 必須: low、medium、highのいずれか |
| products | 必須の空でない文字列リスト |
| callback_time | HH:MM形式の文字列またはnull |
このスキーマは意図的に単純にしてあり、Python標準ライブラリだけで確認できます。単にJSONとして解析できるかどうかを確認するより厳しいルールです。
使用可能なフィールドと、情報が欠けている場合の扱いを正確に指定します。たとえば「次の文章を、ticket_id、customer、priority、products、callback_timeというキーだけを持つ1つのJSONオブジェクトに抽出してください。ticket_idは整数、priorityはlow、medium、highのいずれか、productsは文字列リストにしてください。callback_timeが書かれていない場合はnullを使ってください。欠けている事実を推測しないでください。JSONだけを返してください」と依頼できます。
この合成メモに対する正しい結果は、ticket_idが1842、customerがMira Lee、priorityがhigh、productsがDesktopとWeb、callback_timeが15:30です。
もっともらしいが不正なAI結果では、ticket_idを文字列"1842"として返したり、priorityを"urgent"に変えたり、issue_summaryのような許可されていないフィールドを追加したりすることがあります。見た目が整理されていても、合意したスキーマを満たしていません。
次のスクリプトはPython標準ライブラリだけを使用します。合成AI応答を読み込み、予期しないキーがないか、必須キーと型が正しいか、priorityが許可された値か、productsリストが正しいか、callback_timeの形式が正しいかを確認します。結果はoutputsに保存し、同じレポートがすでに存在する場合は上書きせず停止します。
import json
import re
from pathlib import Path
AI_RESPONSE = '''{
"ticket_id": "1842",
"customer": "Mira Lee",
"priority": "urgent",
"products": ["Desktop", "Web"],
"callback_time": "15:30",
"issue_summary": "Login problem"
}'''
REQUIRED_KEYS = {
"ticket_id",
"customer",
"priority",
"products",
"callback_time",
}
ALLOWED_PRIORITIES = {"low", "medium", "high"}
TIME_PATTERN = re.compile(r"^(?:[01]\d|2[0-3]):[0-5]\d$")
errors = []
try:
data = json.loads(AI_RESPONSE)
except json.JSONDecodeError as exc:
raise SystemExit(f"Invalid JSON: {exc}")
if not isinstance(data, dict):
errors.append("Top-level value must be a JSON object.")
else:
actual_keys = set(data)
missing = REQUIRED_KEYS - actual_keys
unexpected = actual_keys - REQUIRED_KEYS
if missing:
errors.append("Missing keys: " + ", ".join(sorted(missing)))
if unexpected:
errors.append("Unexpected keys: " + ", ".join(sorted(unexpected)))
if "ticket_id" in data and not isinstance(data["ticket_id"], int):
errors.append("ticket_id must be an integer.")
if "customer" in data:
customer = data["customer"]
if not isinstance(customer, str) or not customer.strip():
errors.append("customer must be a non-empty string.")
if "priority" in data:
priority = data["priority"]
if not isinstance(priority, str) or priority not in ALLOWED_PRIORITIES:
errors.append("priority must be low, medium, or high.")
if "products" in data:
products = data["products"]
if not isinstance(products, list) or not products:
errors.append("products must be a non-empty list.")
elif not all(isinstance(item, str) and item.strip() for item in products):
errors.append("Every product must be a non-empty string.")
if "callback_time" in data:
callback = data["callback_time"]
if callback is not None:
if not isinstance(callback, str) or not TIME_PATTERN.fullmatch(callback):
errors.append("callback_time must be HH:MM or null.")
status = "PASS" if not errors else "FAIL"
lines = [f"Validation: {status}"]
lines.extend(f"- {error}" for error in errors)
output_dir = Path("outputs")
output_dir.mkdir(exist_ok=True)
output_file = output_dir / "json_validation.txt"
if output_file.exists():
raise SystemExit(f"Stop: {output_file} already exists.")
output_file.write_text("\n".join(lines) + "\n", encoding="utf-8")
print(f"Wrote {output_file}")
不正な合成応答は3つの理由で検証に失敗するはずです。ticket_idが整数ではなく文字列、priorityが許可集合にないurgent、issue_summaryが予期しないキーです。productsリストとcallback_timeの形式は構造ルールを満たしています。
| 確認項目 | AIの値 | 期待値 | 結果 |
|---|---|---|---|
| ticket_id | "1842" | 整数 | FAIL |
| customer | "Mira Lee" | 空でない文字列 | PASS |
| priority | "urgent" | low、medium、high | FAIL |
| products | ["Desktop", "Web"] | 空でない文字列リスト | PASS |
| callback_time | "15:30" | HH:MMまたはnull | PASS |
| 追加キー | issue_summary | なし | FAIL |
スキーマ検証に失敗した場合、通常は値を黙って型変換するより、結果を修正対象として扱う方が安全です。"1842"を自動的に1842へ変換すると、抽出指示に従わなかった事実が隠れることがあります。自動変換を許可するかは後続処理の要件によって決めます。
構造検証は1つの層にすぎません。重要なフィールドは元テキストと直接比較する必要があります。この例では、ticket 1842、Mira Lee、high、Desktop、Web、15:30はすべて元テキストに明示されています。一方、元テキストにはurgentという語はないため、highをurgentに変えることは単なる書式変更ではなく、元の値を変更することになります。
よくあるミスは、json.loadsが応答を読み込めるかだけを確認することです。解析に成功しても、それは文法的に正しいJSONであることしか示しません。必須キー、型、値が正しいことまでは保証しません。もう1つのミスは、任意の追加フィールドを許可することです。そうすると後続コードが、元の仕様に含まれていなかった情報へ依存する可能性があります。
スキーマが大きくなると、手書きの検証コードは保守しにくくなります。その場合は専用のスキーマ方式や検証ライブラリが適していますが、基本的な問いは変わりません。どのフィールドが必須か、どの型を許可するか、どの値を認めるか、不足情報をnullで表現してよいかを決める必要があります。
検証は、元テキスト自体が完全で信頼できることも保証しません。元テキストに誤ったチケット番号がある場合や文章が曖昧な場合、構造的に正しく抽出してもその問題は残ります。検証や業務ルールで問題が起きたときに重要なフィールドを追跡できるよう、元テキストも保持しておくと便利です。
2026-09-21 · hand-checked example · Python 3.12
説明と例は独自に作成しました。関連する動作や概念は、以下の公式資料で確認できます。