Datavision / backend /agents /critic.py
DataVision CI/CD Bot
release: clean production build for HuggingFace Space
09801ca
Raw
History Blame Contribute Delete
2.61 kB
import json
import logging
from typing import Dict, Any
from core.llm import chat
logger = logging.getLogger(__name__)
def evaluate_with_critic(query: str, context: str, draft_answer: str) -> Dict[str, Any]:
"""
Agentic Critic: Analyzes a drafted answer for hallucinations, math errors,
or incorrect Python/SQL code. Returns a JSON indicating pass/fail and feedback.
"""
prompt = f"""You are a senior Data Science Critic in Silicon Valley.
Your job is to relentlessly review a Junior Analyst's drafted answer to ensure it is 100% accurate,
does not hallucinate, and properly addresses the user's query using ONLY the provided context.
USER QUERY: "{query}"
AVAILABLE DATA CONTEXT (SCHEMA/INFO):
{context[:2000]}
JUNIOR ANALYST DRAFT ANSWER:
---
{draft_answer}
---
CRITIC INSTRUCTIONS:
1. Does the answer directly address the User Query?
2. Does the answer invent any numbers, tables, or metrics not supported by the Data Context? (Hallucination)
3. If there is code (Python/SQL) in the answer, does it look syntactically correct and logical?
4. Is the tone appropriate and helpful?
If the draft is good, pass it. If there is ANY major hallucination or error, FAIL it and provide specific feedback on how to fix it.
Output ONLY a valid JSON object in this exact format:
{{
"pass": true,
"feedback": "Detailed feedback on what to fix, or 'Looks good' if passed."
}}
"""
try:
# Use a strong model for criticism (e.g., Llama 3 70B or 8B)
response = chat(prompt, temperature=0.1, max_tokens=250)
# Parse JSON
response = response.strip()
if '```json' in response:
response = response.split('```json')[1].split('```')[0]
elif '```' in response:
response = response.split('```')[1].split('```')[0]
start = response.find('{')
end = response.rfind('}') + 1
if start >= 0 and end > start:
response = response[start:end]
decision = json.loads(response)
# Ensure keys exist
if "pass" not in decision:
decision["pass"] = True
if "feedback" not in decision:
decision["feedback"] = "Looks good"
logger.info(f"Critic Agent evaluation: Pass={decision['pass']}")
return decision
except Exception as e:
logger.error(f"Critic Agent failed to parse response: {e}")
# Default to pass if the critic fails, to avoid breaking the pipeline
return {"pass": True, "feedback": "Critic failed to evaluate, defaulting to pass."}