CoolFace
Apppublic

LPXian/Graph-News.AI

sourceHugging Faceupdated 6mo agoView on Hugging Face
0likes
critic.py98 linesDownload Raw Back to agents
1from langchain_core.messages import HumanMessage, SystemMessage2from app.core.llm import get_llm3from app.graph.state import GraphState4import re5 6def critic_node(state: GraphState) -> GraphState:7    """8    Evaluates the quality of the drafted article.9    """10    print("--- CRITIC NODE ---")11    draft = state.get("final_draft", "")12    iteration = state.get("iteration_count", 0)13    14    # Basic word count validation15    word_count = len(re.findall(r'\w+', draft))16    print(f"Current Draft Word Count: {word_count}")17    18    # LLM-based evaluation19    llm = get_llm()20    eval_prompt = f"""21    Evaluate the following AI news draft based on these criteria:22    1. Length: Is it between 150 and 800 words? (Current: {word_count})23    2. Structure: Does it have a title, exec summary, 4-5 topics (each with Brief, Detail, Sources), and conclusion?24    3. Clarity: Is it written in a natural, human-like tone for AI engineers/students?25    26    Draft:27    ---28    {draft}29    ---30    31    Respond in JSON format with two fields:32    "is_approved": boolean,33    "feedback": string (specific improvements if not approved, or "Excellent" if approved)34    """35    36    # Use a structured output approach or just parse JSON from content37    # For simplicity in MVP, we'll parse JSON from response38    try:39        response = llm.invoke([SystemMessage(content="You are a strict editor evaluating AI news quality. Respond ONLY with valid JSON."), HumanMessage(content=eval_prompt)])40        # Basic JSON extraction (Gemini often wraps in ```json)41        json_str = re.search(r'\{.*\}', response.content, re.DOTALL).group(0)42        import json43        evaluation = json.loads(json_str)44        45        is_approved = evaluation.get("is_approved", False)46        feedback = evaluation.get("feedback", "Needs improvement")47    except Exception as e:48        print(f"Critical Error in evaluation parsing: {e}")49        # Fallback if LLM fails50        is_approved = 150 <= word_count <= 50051        feedback = "Check word count and structure."52 53    # Logic: Approve if LLM says yes, or if we have already retried once.54    # We enforce "max 1 retry" here.55    final_approval = is_approved or (iteration >= 1)56    57    if final_approval:58        # Prepare the final payload for the database59        lines = draft.strip().split('\n')60        title = lines[0].replace('#', '').strip() if lines else "Daily AI News"61        62        # Collect source links63        source_links = [{"title": a["title"], "link": a["link"]} for a in state.get("clean_articles", [])[:10]]64        65        # New: Tracking metrics66        tokens = response.usage_metadata.get("total_tokens", 0) if hasattr(response, "usage_metadata") else 067        total_tokens = state.get("total_tokens", 0) + tokens68        69        # Calculate a mock processing load based on cleaning ratio70        raw_count = len(state.get("raw_articles", []))71        clean_count = len(state.get("clean_articles", []))72        load = round((clean_count / raw_count * 100), 1) if raw_count > 0 else 073        74        final_payload = {75            "title": title,76            "summary": draft[:200] + "...",77            "content": draft,78            "source_links": source_links,79            "metadata": {80                "total_tokens": total_tokens,81                "processing_load": load82            }83        }84        85        return {86            "is_approved": True,87            "final_payload": final_payload,88            "total_tokens": total_tokens,89            "iteration_count": iteration + 190        }91    else:92        print(f"Draft rejected. Feedback: {feedback}. Moving to retry 1.")93        return {94            "is_approved": False,95            "critic_feedback": feedback,96            "iteration_count": iteration + 197        }98