structuring-training-data.py (5782B)
1 import glob 2 import json 3 import os 4 from typing import Optional 5 6 from google import genai 7 from google.genai import types 8 9 10 def generate_structured_json( 11 typst_file_path: str, 12 feedback_file_path: str, 13 output_dir: str, 14 sequence_num: int, # 新しい引数: 入力ファイルから抽出した連番 15 base_output_name: str, # 出力ファイル名のベース部分を保持 16 ): 17 client: Optional[genai.Client] = None 18 try: 19 # クライアントの初期化はtryブロック内に移動してAPIキーのチェックをスムーズに 20 client = genai.Client() 21 except Exception as e: 22 print( 23 f"エラー: Geminiクライアントの初期化に失敗しました。APIキーを確認してください: {e}" 24 ) 25 return 26 27 # 連番を使用して直接出力ファイル名を生成 (例: structured_data_001.json) 28 output_file_path = os.path.join( 29 output_dir, f"{base_output_name}_{sequence_num:03d}.json" 30 ) 31 32 if os.path.exists(output_file_path): 33 print(f"警告: 出力ファイル {output_file_path} は既に存在します。上書きします。") 34 35 with open(typst_file_path, "r", encoding="utf-8") as f: 36 typst_content = f.read() 37 38 with open(feedback_file_path, "r", encoding="utf-8") as f: 39 feedback_content = f.read() 40 41 system_instruction = "あなたは、提供されたTypstレポートとそのフィードバックを構造化するための専門家です。入力テキストから、指摘事項を分析し、指定されたJSONスキーマに厳密に従って、指摘事項のリストを生成してください。応答はJSON形式のみとし、Markdownの装飾や説明文は一切含めないでください。" 42 43 json_schema = { 44 "type": "array", 45 "items": { 46 "type": "object", 47 "properties": { 48 "type": { 49 "type": "string", 50 "description": "指摘のカテゴリ(例:format, logic, grammar, citationなど)。", 51 }, 52 "location": { 53 "type": "string", 54 "description": "Typstファイル内の指摘箇所(例:line 25-30, 全体)。", 55 }, 56 "text": { 57 "type": "string", 58 "description": "フィードバックの具体的な内容。", 59 }, 60 }, 61 "required": ["type", "location", "text"], 62 }, 63 } 64 65 prompt = ( 66 f"Typstファイルの内容:\n---\n{typst_content}\n---\n\n" 67 f"対応するフィードバックの生テキスト:\n---\n{feedback_content}\n---" 68 ) 69 70 try: 71 response = client.models.generate_content( 72 model="gemini-2.5-flash", 73 contents=[prompt], 74 config=types.GenerateContentConfig( 75 system_instruction=system_instruction, 76 response_mime_type="application/json", 77 response_schema=json_schema, 78 temperature=0.1, 79 ), 80 ) 81 82 structured_json_data = json.loads(response.text) 83 84 with open(output_file_path, "w", encoding="utf-8") as f: 85 json.dump(structured_json_data, f, ensure_ascii=False, indent=2) 86 87 print(f"構造化されたJSONを {output_file_path} に保存しました。") 88 89 except Exception as e: 90 print(f"Gemini API呼び出しまたはJSON保存中にエラーが発生しました: {e}") 91 92 93 if __name__ == "__main__": 94 REPORTS_DIR = "reports" 95 OUTPUT_DIR = "structured-feedback" 96 OUTPUT_BASE_NAME = "structured_data" 97 98 os.makedirs(OUTPUT_DIR, exist_ok=True) 99 100 if not os.getenv("GEMINI_API_KEY"): 101 print("エラー: 環境変数 GEMINI_API_KEY が設定されていません。") 102 else: 103 feedback_files = glob.glob(os.path.join(REPORTS_DIR, "feedback_*.txt")) 104 for feedback_file_path in feedback_files: 105 base_filename = os.path.basename(feedback_file_path) 106 107 try: 108 # 連番を文字列として抽出 (例: "001") 109 sequence_num_str = base_filename.split("_")[-1].split(".")[0] 110 111 # Typstファイル名の構築 112 typst_file_name = f"report{sequence_num_str}.typ" 113 typst_file_path = os.path.join(REPORTS_DIR, typst_file_name) 114 115 # 連番を数値に変換 116 sequence_num = int(sequence_num_str) 117 118 if os.path.exists(typst_file_path): 119 print( 120 f"Processing {typst_file_path} and {feedback_file_path}. Sequence number: {sequence_num:03d}" 121 ) 122 generate_structured_json( 123 typst_file_path=typst_file_path, 124 feedback_file_path=feedback_file_path, 125 output_dir=OUTPUT_DIR, 126 sequence_num=sequence_num, # 抽出した連番を渡す 127 base_output_name=OUTPUT_BASE_NAME, 128 ) 129 else: 130 print( 131 f"警告: 対応するTypstファイル {typst_file_path} が見つかりませんでした。スキップします。" 132 ) 133 except ValueError: 134 print( 135 f"警告: ファイル名 {base_filename} から有効な数値の連番を抽出できませんでした。スキップします。" 136 ) 137 except Exception as e: 138 print(f"エラー処理ファイル {feedback_file_path}: {e}")