Validación de Coherencia IA Implementada

Cambios en el Backend (v1.0.96)
Nuevo campo expected_answer en el análisis de IA:

La IA ahora retorna cuál debería ser la respuesta correcta según lo que observa en la imagen
Se incluyen las opciones de respuesta disponibles en el prompt para que la IA elija la correcta
Extracción de opciones de pregunta:

El sistema extrae las opciones disponibles (Buen Estado, Mal Estado, etc.)
Las envía a la IA para que determine cuál es la respuesta esperada
Cambios en el Frontend
Validación antes de continuar:

Cuando el mecánico intenta avanzar a la siguiente pregunta o firmar
El sistema compara su respuesta con expected_answer del análisis de IA
Si NO coinciden, aparece un popup con:
This commit is contained in:
2025-12-03 10:40:33 -03:00
parent 58bf1bfc69
commit 44cd81956f
2 changed files with 67 additions and 7 deletions

View File

@@ -276,7 +276,7 @@ def extract_pdf_text_smart(pdf_content: bytes, max_chars: int = None) -> dict:
}
BACKEND_VERSION = "1.0.95"
BACKEND_VERSION = "1.0.96"
app = FastAPI(title="Checklist Inteligente API", version=BACKEND_VERSION)
# S3/MinIO configuration
@@ -1807,8 +1807,8 @@ def generate_inspection_pdf(inspection_id: int, db: Session) -> str:
elements.append(Spacer(1, 3*mm))
# Cuadro de métricas con diseño moderno
metric_label = ParagraphStyle('metric_label', parent=small_style, fontSize=9, textColor=colors.HexColor('#64748b'), alignment=TA_CENTER)
metric_value = ParagraphStyle('metric_value', parent=info_style, fontSize=16, fontName='Helvetica-Bold', alignment=TA_CENTER)
metric_label = ParagraphStyle('metric_label', parent=small_style, fontSize=10, textColor=colors.HexColor('#64748b'), alignment=TA_CENTER)
metric_value = ParagraphStyle('metric_value', parent=info_style, fontSize=18, fontName='Helvetica-Bold', alignment=TA_CENTER)
metrics_data = [
[Paragraph("Puntuación", metric_label), Paragraph("Ítems Críticos", metric_label)],
@@ -1818,13 +1818,13 @@ def generate_inspection_pdf(inspection_id: int, db: Session) -> str:
]
]
score_table = Table(metrics_data, colWidths=[90*mm, 90*mm])
score_table = Table(metrics_data, colWidths=[90*mm, 90*mm], rowHeights=[12*mm, 18*mm])
score_table.setStyle(TableStyle([
('BACKGROUND', (0, 0), (-1, 0), colors.HexColor('#f8fafc')),
('BACKGROUND', (0, 1), (-1, -1), colors.white),
('ALIGN', (0, 0), (-1, -1), 'CENTER'),
('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
('PADDING', (0, 0), (-1, -1), 12),
('PADDING', (0, 0), (-1, -1), 16),
('BOX', (0, 0), (-1, -1), 2, score_color),
('LINEABOVE', (0, 1), (-1, 1), 1.5, score_color),
('GRID', (0, 0), (-1, -1), 0.5, colors.HexColor('#e2e8f0')),
@@ -2733,12 +2733,19 @@ async def analyze_image(
# Obtener contexto de la pregunta si se proporciona
question_obj = None
question_options = []
if question_id:
question_obj = db.query(models.Question).filter(models.Question.id == question_id).first()
print(f"📋 Pregunta encontrada:")
print(f" - ID: {question_obj.id}")
print(f" - Texto: {question_obj.text}")
print(f" - Tipo: {question_obj.options.get('type') if question_obj.options else 'N/A'}")
print(f" - ai_prompt en DB: {question_obj.ai_prompt[:100] if question_obj.ai_prompt else 'NO TIENE'}")
# Extraer opciones de respuesta si existen
if question_obj.options and 'options' in question_obj.options:
question_options = question_obj.options['options']
print(f" - Opciones disponibles: {question_options}")
# Si no se proporciona custom_prompt en el Form, usar el de la pregunta
if not custom_prompt and question_obj and question_obj.ai_prompt:
@@ -2774,6 +2781,11 @@ INFORMACIÓN DEL VEHÍCULO INSPECCIONADO:
try:
# Construir prompt dinámico basado en la pregunta específica
if question_obj:
# Agregar información de opciones de respuesta al prompt
options_context = ""
if question_options:
options_context = f"\n\nOPCIONES DE RESPUESTA DISPONIBLES:\n{', '.join(question_options)}\n\nEn el campo 'expected_answer', indica cuál de estas opciones es la más apropiada según lo que observas en la imagen."
# Usar prompt personalizado si está disponible
if custom_prompt:
# Prompt personalizado - DIRECTO Y SIMPLE
@@ -2782,13 +2794,14 @@ INFORMACIÓN DEL VEHÍCULO INSPECCIONADO:
{vehicle_context}
TAREA ESPECÍFICA:
{custom_prompt}
{custom_prompt}{options_context}
Responde SOLO en formato JSON válido (sin markdown, sin ```json):
{{
"status": "ok",
"observations": "Describe lo que observas en la imagen en relación a la tarea solicitada",
"recommendation": "Acción sugerida basada en lo observado",
"expected_answer": "La respuesta que debería seleccionar el mecánico según lo observado (si hay opciones disponibles)",
"confidence": 0.85,
"context_match": true
}}
@@ -2818,7 +2831,7 @@ IMPORTANTE:
{vehicle_context}
PREGUNTA ESPECÍFICA A RESPONDER: "{question_text}"
Sección: {section}
Sección: {section}{options_context}
Analiza la imagen ÚNICAMENTE para responder esta pregunta específica.
Sé directo y enfócate solo en lo que la pregunta solicita.
@@ -2833,6 +2846,7 @@ Responde SOLO en formato JSON válido (sin markdown, sin ```json):
"status": "ok",
"observations": "Respuesta técnica específica a: {question_text}",
"recommendation": "Acción técnica recomendada o mensaje si la foto no es apropiada",
"expected_answer": "La respuesta correcta que debería seleccionar según lo observado",
"confidence": 0.85,
"context_match": true
}}