| import os |
| import json |
| from openai import OpenAI |
| import tqdm |
| import re |
| from FH_es import fernandez_huerta |
| |
| client = OpenAI(api_key=json.load(open('/home/mshahidul/api.json', 'r'))['openai_api_key']) |
|
|
| PROMPTS_ES = { |
| "B1": """Eres un asistente que reescribe resúmenes de casos clínicos para niñas y niños de primaria (aprox. 6–11 años). |
| Escribe SIEMPRE en español claro. |
| |
| Objetivo de legibilidad (aprox. Fernández–Huerta): 70–100. |
| Restricciones de forma (cumple todas): |
| - Longitud total: 45–90 palabras. |
| - Oraciones: 4–6 oraciones. |
| - Promedio de palabras por oración: 8–12. |
| - Palabras: prefiere palabras cortas (1–2 sílabas). Evita tecnicismos. Si un término médico es inevitable, explícalo con 3–8 palabras sencillas. |
| - Conectores simples: “y”, “pero”, “porque”. Evita oraciones subordinadas largas. |
| - No inventes información. Sé fiel al artículo y al resumen experto. |
| - Prohibido: viñetas, listas, emojis, abreviaturas técnicas, explicaciones de pronunciación, títulos/cabeceras. |
| |
| Tono y contenido: |
| - Amable, tranquilizador, sin alarmar. |
| - Destaca 1–3 ideas principales. Explica hallazgos normales con calma; anormalidades con lenguaje sencillo y breve. |
| Responde solo con el resumen (sin prefacios, sin notas).""", |
|
|
| "B2": """Eres un asistente que reescribe resúmenes de casos clínicos para estudiantes de secundaria (aprox. 11–17 años). |
| Escribe SIEMPRE en español claro. |
| |
| Objetivo de legibilidad (aprox. Fernández–Huerta): 55–65. |
| Restricciones de forma (cumple todas): |
| - Longitud total: 90–140 palabras. |
| - Oraciones: 5–8 oraciones. |
| - Promedio de palabras por oración: 12–18. |
| - Palabras: evita jerga innecesaria. Puedes usar términos médicos comunes con una breve explicación (3–10 palabras) la primera vez. |
| - Conectores permitidos: “porque”, “aunque”, “sin embargo”, “por eso”. Oraciones compuestas moderadas. |
| - No inventes información. Sé fiel al artículo y al resumen experto. |
| - Prohibido: viñetas, listas, emojis, explicaciones de pronunciación, títulos/cabeceras. |
| |
| Tono y contenido: |
| - Claro y empático. |
| - Distingue hallazgos normales y anormales, e incluye posibles pasos siguientes cuando sea útil. |
| Responde solo con el resumen (sin prefacios, sin notas).""", |
|
|
| "B3": """Eres un asistente que reescribe resúmenes de casos clínicos para lectores con nivel universitario (17+), sin especialización médica. |
| Escribe SIEMPRE en español claro. |
| |
| Objetivo de legibilidad (aprox. Fernández–Huerta): 40–55. |
| Restricciones de forma (cumple todas): |
| - Longitud total: 140–220 palabras. |
| - Oraciones: 6–10 oraciones. |
| - Promedio de palabras por oración: 18–25. |
| - Palabras: se permiten términos técnicos de uso común; define brevemente solo los poco conocidos. Se aceptan oraciones subordinadas si mantienen claridad. |
| - Conectores: “sin embargo”, “por lo tanto”, “además”, “no obstante”, “en consecuencia”. |
| - No inventes información. Sé fiel al artículo y al resumen experto. |
| - Prohibido: viñetas, listas, emojis, explicaciones de pronunciación, títulos/cabeceras. |
| |
| Tono y contenido: |
| - Preciso y empático. |
| - Estructura más detallada: contexto breve, hallazgos clave, implicaciones y posibles próximos pasos. |
| Responde solo con el resumen (sin prefacios, sin notas).""" |
| } |
|
|
|
|
| FH_TARGETS = { |
| "B1": (70, 100), |
| "B2": (55, 65), |
| "B3": (40, 55), |
| } |
|
|
| def count_syllables(word): |
| |
| word = word.lower() |
| word = re.sub(r'[^a-záéíóúüñ]', '', word) |
| return len(re.findall(r'[aeiouáéíóúü]+', word)) |
|
|
|
|
|
|
| def generate_synthetic_summary(article, gold_summary, band, lang='es'): |
| prompt_user = f"""Artículo: |
| {article} |
| |
| Resumen experto: |
| {gold_summary} |
| |
| Tarea: |
| Genera un resumen en la banda {band} indicada por el sistema. Responde solo con el resumen.""" |
| response = client.chat.completions.create( |
| model="gpt-4.1-mini", |
| messages=[ |
| {"role": "system", "content": PROMPTS_ES[band]}, |
| {"role": "user", "content": prompt_user} |
| ], |
| temperature=0.4, |
| ) |
| return response.choices[0].message.content.strip() |
|
|
| def revise_to_band(text, band): |
| adjustments = { |
| "B1": "Acorta oraciones a 8–12 palabras, usa palabras más comunes y evita tecnicismos.", |
| "B2": "Ajusta oraciones a 12–18 palabras y limita tecnicismos con breve explicación.", |
| "B3": "Usa 18–25 palabras por oración, permite frases subordinadas y vocabulario más técnico.", |
| } |
| msg = f"""Reescribe el texto para que cumpla la banda {band}: |
| - {adjustments[band]} |
| - Mantén fidelidad al contenido. |
| Devuelve solo el texto revisado, sin comentarios.""" |
| r = client.chat.completions.create( |
| model="gpt-4.1-mini", |
| messages=[ |
| {"role": "system", "content": PROMPTS_ES[band]}, |
| {"role": "user", "content": text}, |
| {"role": "user", "content": msg} |
| ], |
| temperature=0.3, |
| ) |
| return r.choices[0].message.content.strip() |
|
|
| def build_synthetic_dataset(input_path, output_path, max_samples=None): |
| """Generate synthetic dataset from a JSON file with {fulltext, summary}""" |
| results = [] |
| seen_articles = set() |
| if os.path.exists(output_path): |
| with open(output_path, 'r') as f: |
| results = json.load(f) |
| seen_articles = set(r['article'] for r in results) |
| with open(input_path, "r") as f: |
| data = json.load(f) |
| for item in tqdm.tqdm(data): |
| if max_samples and len(results) >= max_samples: |
| break |
| article, gold = item["fulltext"], item["summary"] |
| if article in seen_articles: |
| continue |
| temp = {} |
| for band in ["B1", "B2", "B3"]: |
| synthetic = generate_synthetic_summary(article, gold, band) |
| fh = fernandez_huerta(synthetic) |
| lo, hi = FH_TARGETS[band] |
| if fh is None or not (lo <= fh <= hi): |
| synthetic = revise_to_band(synthetic, band) |
| temp[band] = synthetic |
| results.append({ |
| "article": article, |
| "gold_summary": gold, |
| "synthetic_summary": temp |
| }) |
| seen_articles.add(article) |
| if len(results) % 5 == 0: |
| print(f"Processed {len(results)} samples, saving progress...") |
| with open(output_path, "w") as f: |
| json.dump(results, f, ensure_ascii=False, indent=4) |
| with open(output_path, "w") as f: |
| json.dump(results, f, ensure_ascii=False, indent=4) |
|
|
| |
| lang = "es" |
| path = f"/home/mshahidul/readctrl/data/testing_data_gs/multiclinsum_gs_train_{lang}.json" |
| build_synthetic_dataset(path, f"/home/mshahidul/readctrl/generating_data/{lang}_synthetic.json", max_samples=100) |