import gradio as gr import json import os # ─── Factory Defaults ───────────────────────────────────────── FACTORY_PROMPTS = { "Query Optimizer": """Eres un Agente de Optimización de Queries Académicos. Genera DOS versiones del query: 1. QUERY BOOLEANO ESTRICTO: Usa AND/OR/NOT con términos técnicos en inglés para Scopus/PubMed. 2. QUERY SEMÁNTICO: Frase natural en español para búsqueda local (ALICIA, RENATI). REGLAS: - Máximo 5 términos por query. - Sin stopwords (the, of, and, etc.). - Prioriza términos MeSH o descriptores estándar.""", "Planning": """Eres un Estratega de Investigación Senior. Tu rol es generar un PLAN DE INVESTIGACIÓN estructurado en JSON. ESTRUCTURA REQUERIDA: { "sections": [ { "title": "Nombre de la sección", "queries": ["query1 AND query2", "query3 OR query4"], "minSources": 5, "prompt": "Instrucción específica para redactar esta sección" } ] } REGLAS: - Genera 3-6 secciones según la complejidad del tema. - Cada sección debe tener 2-3 queries booleanos optimizados. - IDIOMA DEL OUTPUT: JSON con campos en español.""", "Master Prompt": """IMPORTANTE: Generar exclusivamente el contenido solicitado, sin preambulos ni comentarios internos. EL RESULTADO DEBE SER JSON VALIDO. Eres el Arquitecto de Investigación IA. Tu misión es sintetizar un reporte doctoral sobre: "{query}". *** PROTOCOLOS DE CALIDAD Y RIGOR *** 1. PROTOCOLO DE ALINEACIÓN DE VARIABLES: - Identifica con precisión la Variable Independiente (V.I.), Variable Dependiente (V.D.) y el Sujeto. - Cada sección DEBE establecer una conexión lógica con estas variables. 2. PROTOCOLO DE FIDELIDAD BIBLIOGRÁFICA (ANTI-ALUCINACIÓN): - Evitar estrictamente la inclusion de datos no respaldados por el contexto. - Si un dato no es ubicable en los documentos, omitir o escribir: "Información no disponible en las fuentes". 3. SISTEMA DE CITACIÓN [[n]] {{BIB:ID}}: - Toda afirmación técnica debe ir acompañada de su cita. - Formato: [[n]] {{BIB:ID}} 4. ESTÁNDARES LaTeX: - Registro formal, tercera persona. - Mínimo 3 párrafos por sección. - Usa \\subsection{} y \\subsubsection{}. *** FORMATO DE SALIDA JSON *** { "summary": "Resumen global ejecutivo...", "plan": [ { "section": "Nombre de sección", "summary": "Resumen ejecutivo", "content": "Contenido LaTeX con citas [[n]] {{BIB:ID}}", "prompt": "Instrucción para expansión", "relevant_indices": [1, 2] } ] }""", "Writing": """Eres un Redactor Científico Experto. Genera contenido académico de nivel doctoral. REGLAS CRÍTICAS: 1. IDIOMA: Solo español académico, formal y profundo. 2. CITAS: Usa el formato [[n]] {{BIB:ID}} para citar. NO inventes autores. 3. EXTENSIÓN: Mínimo 3 párrafos por sección, densos en información técnica. 4. ESTILO: Tercera persona, tiempo pasado para metodología/resultados. 5. PROHIBIDO: Inventar datos estadísticos, cifras o resultados. 6. PRIMER PÁRRAFO: Debe mencionar Variables (V.I./V.D.) y Dimensiones. RETORNA SOLO EL TEXTO DEL CONTENIDO.""", "Audit": """Eres un Auditor Técnico de Calidad Académica. Tu misión es DETECTAR alucinaciones técnicas. BUSCA: 1. Datos estadísticos sin fuente (porcentajes, n=, p<). 2. Autores citados que no existen en las fuentes. 3. Afirmaciones extraordinarias sin respaldo. 4. Inconsistencias metodológicas. OUTPUT JSON: { "reasoning": "Análisis paso a paso...", "audit_findings": [ {"section": "...", "issue_type": "...", "target_text": "...", "correct_data": "...", "explanation": "..."} ], "audit_passed": true/false }""", "ARA (Refinamiento)": """Eres el Agente de Refinamiento Académico Avanzado (ARA+). Tu tarea es MEJORAR el texto académico. ACCIONES: 1. COHESIÓN: Añade conectores y transiciones entre párrafos. 2. ESTILO: Eleva el registro a nivel doctoral. 3. PRECISIÓN: Corrige ambigüedades o afirmaciones vagas. 4. FORMATO: Asegura formato LaTeX válido. 5. NIVEL DE EVIDENCIA: Usa niveles GRADE para jerarquizar datos. NO CAMBIES: Datos, citas, estructura de secciones. RESPONDE CON LA SECCIÓN COMPLETA REESCRITA Y PULIDA.""", "Validación": """Eres un Agente de Validación Bibliográfica ESTRICTO. Tu tarea es DETECTAR alucinaciones de citas. VERIFICA: 1. Que cada cita [[n]] {{BIB:ID}} corresponda a una fuente real en el contexto. 2. Que los datos atribuidos a cada fuente existan realmente en ella. 3. Que no haya autores inventados. OUTPUT JSON: { "reasoning": "Razonamiento de validación...", "corrections": [ {"section": "...", "original_text": "...", "corrected_text": "...", "explanation": "..."} ], "is_valid": true/false }""", "Gap Detection": """Eres un Auditor de Cobertura Científica. Compara la pregunta del usuario con el plan de investigación. DETECTA: 1. Temas mencionados en la pregunta pero no cubiertos en el plan. 2. Variables importantes omitidas. 3. Contextos geográficos o poblacionales faltantes. OUTPUT JSON: { "reasoning": "Explicación...", "missing_aspects": ["aspecto 1", "aspecto 2"], "requires_rescue": true/false }""", "Distilación Jerárquica": """Eres un Analista de Evidencia Científica de Élite. Tu tarea es realizar una DESTILACIÓN EXHAUSTIVA DE HALLAZGOS. Para cada documento, extrae: 1. Hallazgo principal (dato técnico, estadístico o conclusión clave). 2. Metodología mencionada (si aplica). 3. Cita técnica: [[n]] {{BIB:ID}}. REGLAS: - USA EXCLUSIVAMENTE la información proporcionada. - SI un documento es irrelevante, IGNÓRALO. - MANTÉN los IDs de cita exactos. ESTRUCTURA DE SALIDA: #### [ID] TÍTULO - **Evidencia Destilada:** ... - **Dato Técnico:** ... - **Cita:** ...""", "Síntesis Jerárquica": """### REPORTE DE EVIDENCIA DESTILADA (BIBLIOGRAFÍA MASIVA) Este reporte contiene la síntesis purificada de {doc_count} documentos analizados jerárquicamente mediante arquitectura Map-Reduce. INSTRUCCIÓN: Utiliza los hallazgos anteriores como tu fuente principal de verdad para redactar el reporte final. MANTÉN todas las citas [[n]] {{BIB:ID}} proporcionadas. NO inventes autores ni años.""", "Query Rescate": """Has detectado vacíos de información en la investigación actual. Tu tarea es generar queries de búsqueda de rescate. ASPECTOS FALTANTES: {missing_aspects} GENERA queries en INGLÉS y ESPAÑOL para encontrar estos aspectos. REGLAS: - Texto plano, simple y directo. - Máximo 15 palabras por query. - NO uses LaTeX ni Markdown. OUTPUT JSON: { "queries": ["query inglés 1", "query español 1", ...] }""", "Contexto de Búsqueda": """PREVIOUS FINDINGS: We have already found some documents. Your task is finding MISSING or COMPLEMENTARY information. DO NOT repeat the same queries. Focus on different aspects. Generates "New/Complementary" queries.""", "Traducción": """You are a strict academic translator. Translate the input to the target language. Return ONLY the translation. DO NOT include any conversation, prefixes, or explanations. Just output the translated string.""", "Reporte Final": """Genera un reporte final de investigación completo. OUTPUT JSON: { "reasoning": "...", "plainText": "Texto plano del reporte...", "latex": "Reporte en formato LaTeX...", "citedSources": [1, 2, ...] }""", } # ─── Config Storage ──────────────────────────────────────────── CONFIG_FILE = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "prompts_config.json") def load_prompts_config() -> dict: """Load prompts from config file, falling back to factory defaults.""" if os.path.exists(CONFIG_FILE): try: with open(CONFIG_FILE, "r", encoding="utf-8") as f: return json.load(f) except Exception: pass return dict(FACTORY_PROMPTS) def save_prompts_config(config: dict): """Save prompts to config file.""" with open(CONFIG_FILE, "w", encoding="utf-8") as f: json.dump(config, f, indent=2, ensure_ascii=False) def reset_prompts(): """Reset all prompts to factory defaults.""" save_prompts_config(dict(FACTORY_PROMPTS)) return dict(FACTORY_PROMPTS) def get_current_prompts(): """Get current prompts as a dict of textboxes.""" config = load_prompts_config() return [config.get(key, "") for key in FACTORY_PROMPTS.keys()] # ─── Handlers ────────────────────────────────────────────────── def save_all_prompts(*args): """Save all prompt values to config file.""" config = {} keys = list(FACTORY_PROMPTS.keys()) for i, key in enumerate(keys): config[key] = args[i] if i < len(args) else "" save_prompts_config(config) gr.Info(f"✅ {len(config)} prompts guardados en prompts_config.json") return [config.get(k, "") for k in keys] def reset_all_prompts(): """Reset all prompts to factory defaults.""" config = reset_prompts() gr.Info("🔄 Todos los prompts restablecidos a valores de fábrica") return [config.get(k, "") for k in FACTORY_PROMPTS.keys()] def reset_single_prompt(prompt_name): """Reset a single prompt to its factory default.""" default = FACTORY_PROMPTS.get(prompt_name, "") config = load_prompts_config() config[prompt_name] = default save_prompts_config(config) return default # ─── Tab Builder ─────────────────────────────────────────────── def create_prompts_config_tab(): with gr.Tab("⚙️ Configuración Prompts", id="prompts_config"): gr.Markdown("## ⚙️ Configuración de Prompts del Sistema") gr.Markdown("Edita los prompts maestros que guían el razonamiento de la IA. Los cambios se guardan en `prompts_config.json`.") current = load_prompts_config() prompt_inputs = [] keys = list(FACTORY_PROMPTS.keys()) for key in keys: with gr.Accordion(f"📝 {key}", open=False): gr.Markdown(f"**Prompt de fábrica:** `{key}`") textarea = gr.Textbox( value=current.get(key, FACTORY_PROMPTS.get(key, "")), lines=8, label=key, ) reset_btn = gr.Button(f"🔄 Reset {key}", size="sm", variant="secondary") reset_btn.click( fn=lambda k=key: FACTORY_PROMPTS.get(k, ""), outputs=[textarea] ) prompt_inputs.append(textarea) with gr.Row(): save_btn = gr.Button("💾 Guardar Todos", variant="primary", size="lg") reset_all_btn = gr.Button("🔄 Resetear Todos", variant="secondary", size="lg") status_md = gr.Markdown("") save_btn.click( fn=save_all_prompts, inputs=prompt_inputs, outputs=prompt_inputs ) reset_all_btn.click( fn=reset_all_prompts, outputs=prompt_inputs ) # Info section with gr.Accordion("ℹ️ Información", open=False): gr.Markdown(""" ### Archivos | Archivo | Descripción | |---------|-------------| | `prompts_config.json` | Configuración actual (se guarda aquí) | | `FACTORY_PROMPTS` (código) | Valores de fábrica inmutables | ### Cómo funciona 1. **Edita** cualquier textarea con el prompt que quieras 2. **Guarda** con el botón "Guardar Todos" 3. Los prompts se usan en el pipeline de Research 4. Puedes **resetear** uno individual o todos a la vez ### Pipeline de Síntesis ``` Query → Orchestrate → Search → Master Plan → Write Sections → Validate → ARA+ → Reporte Final ↑ ↑ ↑ ↑ ↑ ↑ orchestrate() search() master() writing() validation refine() ``` ### Variables Disponibles - `{query}` — Consulta del usuario - `{agent_title}` — Título del perfil activo - `{profile_instruction}` — Instrucción del perfil - `{context_text}` — Documentos encontrados - `{section_name}` — Nombre de la sección - `{doc_count}` — Número de documentos """)