Correction QA: Validation arborescence + qualité/sécurité, ajout logique de fin complète, suppression repo non conforme
This commit is contained in:
@@ -3,8 +3,8 @@ from langchain_openai import ChatOpenAI
|
||||
from langchain_core.messages import SystemMessage, HumanMessage
|
||||
import httpx
|
||||
from app.core.config import settings
|
||||
from app.schemas.qa_report import QAEvaluation
|
||||
from app.llm.prompts import QA_AGENT_PROMPT
|
||||
from app.schemas.qa_report import QAEvaluation, StructureEvaluation
|
||||
from app.llm.prompts import QA_AGENT_PROMPT, QA_STRUCTURE_AGENT_PROMPT
|
||||
from app.sandbox.qa_client import run_project_qa
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -15,8 +15,9 @@ async_client = httpx.AsyncClient(verify=False)
|
||||
async def run_qa_agent(project_title: str, dev_output: dict) -> QAEvaluation:
|
||||
"""
|
||||
Agent QA :
|
||||
Analyse le code généré à l'aide de l'API externe de la Sandbox (Semgrep uniquement)
|
||||
et produit le rapport d'évaluation structuré final.
|
||||
Analyse le code généré à l'aide de l'API externe de la Sandbox (Semgrep uniquement),
|
||||
valide la structure minimale du projet à l'aide d'un second appel LLM,
|
||||
puis produit le rapport d'évaluation fusionné.
|
||||
"""
|
||||
logger.info(f"[QA Agent] Début du processus d'audit statique pour le projet : {project_title}")
|
||||
|
||||
@@ -39,12 +40,12 @@ async def run_qa_agent(project_title: str, dev_output: dict) -> QAEvaluation:
|
||||
http_async_client=async_client,
|
||||
)
|
||||
|
||||
# 2. Exécution de l'analyse statique via le service Sandbox indépendant
|
||||
# 1. Exécution de l'analyse statique via le service Sandbox indépendant
|
||||
try:
|
||||
repo_name = repo_url.rstrip("/").split("/")[-1]
|
||||
logger.info(f"[QA Agent] Envoi du dépôt '{repo_name}' au conteneur d'analyse statique...")
|
||||
|
||||
raw_results = await run_project_qa(repo_name=repo_name)
|
||||
raw_results = await run_project_qa(repo_url=repo_url)
|
||||
|
||||
logger.info(f"[QA Agent] 📊 Retour Sandbox Statis - Status : {'Succès' if raw_results.is_executable else 'Échec'}")
|
||||
logger.info(f"[QA Agent] 📊 Retour Sandbox - Nombre d'alertes de sécurité/qualité (Semgrep): {len(raw_results.issues)}")
|
||||
@@ -57,46 +58,84 @@ async def run_qa_agent(project_title: str, dev_output: dict) -> QAEvaluation:
|
||||
technical_feedback=[f"L'API de la sandbox a échoué : {type(e).__name__}: {str(e)}"]
|
||||
)
|
||||
|
||||
# 3. Génération de l'évaluation finale via LLM
|
||||
structured_llm = llm.with_structured_output(QAEvaluation, strict=True)
|
||||
|
||||
messages = [
|
||||
SystemMessage(content=QA_AGENT_PROMPT),
|
||||
]
|
||||
|
||||
# Le prompt est nettoyé des mentions relatives à l'exécution de tests dynamiques
|
||||
user_content = f"PROJET À AUDITER : {project_title}\n"
|
||||
user_content += f"LIEN DU DÉPÔT : {repo_url}\n"
|
||||
user_content += f"RÉSULTATS DE L'ANALYSE STATIQUE DU CODE (Semgrep) :\n"
|
||||
user_content += f"{raw_results.model_dump_json(indent=2)}\n\n"
|
||||
user_content += "Analyse ces failles, vulnérabilités et défauts de qualité de code, puis génère ton évaluation de sécurité au format structuré demandé."
|
||||
|
||||
messages.append(HumanMessage(content=user_content))
|
||||
|
||||
# 2. Premier appel LLM : Audit de sécurité (Semgrep)
|
||||
qa_evaluation = None
|
||||
try:
|
||||
qa_evaluation = await structured_llm.ainvoke(messages)
|
||||
logger.info(f"[QA Agent] 📝 Justification du LLM : {qa_evaluation.global_summary}")
|
||||
logger.info("[QA Agent] Lancement de l'audit de sécurité et de qualité (Semgrep)...")
|
||||
structured_security_llm = llm.with_structured_output(QAEvaluation, strict=True)
|
||||
|
||||
security_messages = [
|
||||
SystemMessage(content=QA_AGENT_PROMPT),
|
||||
HumanMessage(content=(
|
||||
f"PROJET À AUDITER : {project_title}\n"
|
||||
f"LIEN DU DÉPÔT : {repo_url}\n"
|
||||
f"RÉSULTATS DE L'ANALYSE STATIQUE DU CODE (Semgrep) :\n"
|
||||
f"{raw_results.model_dump_json(indent=2)}\n\n"
|
||||
f"Analyse ces failles, vulnérabilités et défauts de qualité de code, puis génère ton évaluation de sécurité au format structuré demandé."
|
||||
))
|
||||
]
|
||||
|
||||
qa_evaluation = await structured_security_llm.ainvoke(security_messages)
|
||||
|
||||
if not qa_evaluation:
|
||||
logger.error("[QA Agent] Le LLM a renvoyé une réponse vide (None).")
|
||||
return QAEvaluation(
|
||||
is_complete_and_safe=False,
|
||||
global_summary="Erreur de formatage de l'évaluation par l'IA.",
|
||||
technical_feedback=["L'évaluation automatique n'a pas pu être structurée correctement."]
|
||||
)
|
||||
|
||||
if qa_evaluation.is_complete_and_safe:
|
||||
logger.info(f"[QA Agent] ✅ Projet '{project_title}' VALIDÉ (Analyse statique propre).")
|
||||
else:
|
||||
reason = "Erreur d'exécution ou non-conformité" if "exit_code" in str(qa_evaluation) else "Failles de sécurité"
|
||||
logger.warning(f"[QA Agent] ❌ Projet '{project_title}' REJETÉ ({reason}).")
|
||||
|
||||
return qa_evaluation
|
||||
except Exception as e:
|
||||
logger.error(f"[QA Agent] ❌ Échec lors de l'audit de sécurité LLM : {type(e).__name__}: {str(e)}")
|
||||
qa_evaluation = QAEvaluation(
|
||||
is_complete_and_safe=False,
|
||||
global_summary="Erreur lors de la génération du rapport de sécurité par le LLM.",
|
||||
technical_feedback=[f"Le module de sécurité a échoué : {type(e).__name__}"]
|
||||
)
|
||||
|
||||
# 3. Second appel LLM : Validation de la structure minimale (Arborescence)
|
||||
structure_evaluation = None
|
||||
try:
|
||||
logger.info("[QA Agent] Lancement de l'audit de conformité de l'arborescence...")
|
||||
structured_structure_llm = llm.with_structured_output(StructureEvaluation, strict=True)
|
||||
|
||||
repo_structure = raw_results.runtime.stdout or "Arborescence non détectée ou vide."
|
||||
|
||||
structure_messages = [
|
||||
SystemMessage(content=QA_STRUCTURE_AGENT_PROMPT),
|
||||
HumanMessage(content=(
|
||||
f"PROJET À ANALYSER : {project_title}\n"
|
||||
f"ARBORESCENCE DU RÉPERTOIRE CLONÉ :\n"
|
||||
f"{repo_structure}\n\n"
|
||||
f"Vérifie si la structure respecte les exigences minimales et fournis ton évaluation structurée."
|
||||
))
|
||||
]
|
||||
|
||||
structure_evaluation = await structured_structure_llm.ainvoke(structure_messages)
|
||||
logger.info(f"[QA Agent] 📁 Structure détectée : {structure_evaluation.detected_language} | Valide : {structure_evaluation.is_valid}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[QA Agent] ❌ Échec critique lors de l'analyse LLM : {type(e).__name__}: {str(e)}")
|
||||
return QAEvaluation(
|
||||
is_complete_and_safe=False,
|
||||
global_summary="Erreur interne de l'Agent QA lors de la génération du rapport LLM.",
|
||||
technical_feedback=[f"Le LLM a crashé avec l'erreur : {type(e).__name__}."]
|
||||
)
|
||||
logger.error(f"[QA Agent] ❌ Échec lors de la validation de structure LLM : {type(e).__name__}: {str(e)}")
|
||||
structure_evaluation = StructureEvaluation(
|
||||
is_valid=False,
|
||||
detected_language="Inconnu",
|
||||
missing_elements=["Erreur d'analyse structurelle"],
|
||||
feedback="Le module d'analyse d'arborescence a rencontré une erreur de traitement."
|
||||
)
|
||||
|
||||
# 4. Fusion des résultats des deux audits
|
||||
final_is_complete_and_safe = qa_evaluation.is_complete_and_safe and structure_evaluation.is_valid
|
||||
final_feedback = list(qa_evaluation.technical_feedback)
|
||||
if not structure_evaluation.is_valid:
|
||||
for missing in structure_evaluation.missing_elements:
|
||||
final_feedback.append(f"[Structure] Élément obligatoire manquant : {missing}")
|
||||
|
||||
final_summary = (
|
||||
f"{qa_evaluation.global_summary}\n\n"
|
||||
f"--- 📁 Validation de la Structure ({structure_evaluation.detected_language}) ---\n"
|
||||
f"Statut : {'Conforme' if structure_evaluation.is_valid else 'Non conforme'}\n"
|
||||
f"Feedback : {structure_evaluation.feedback}"
|
||||
)
|
||||
|
||||
if final_is_complete_and_safe:
|
||||
logger.info(f"[QA Agent] ✅ Projet '{project_title}' VALIDÉ (Sécurité & Structure OK).")
|
||||
else:
|
||||
logger.warning(f"[QA Agent] ❌ Projet '{project_title}' REJETÉ (Non-conformité détectée).")
|
||||
|
||||
return QAEvaluation(
|
||||
is_complete_and_safe=final_is_complete_and_safe,
|
||||
global_summary=final_summary,
|
||||
technical_feedback=final_feedback
|
||||
)
|
||||
Reference in New Issue
Block a user