Skip to main content
Back to Blog

Building Practical AI Agents

November 15, 2025·2 min read

labagentslanggraphllm

“AI agents” is overloaded. A lot of demos are just a prompt plus a loop. I care about agents that close a real loop: produce an artifact, verify it against explicit criteria, and revise until it’s good enough to ship.

Real-World Implementation: News Summarizer

Instead of talking in the abstract, here’s the core pattern from my News Analyzer agent. It uses a “draft → verify → revise” workflow: a fast model produces a draft, and a stronger model acts as a critic.

The Agent Architecture

I use LangGraph to define the agent's behavior as a state machine. This makes the "loops" (draft -> verify -> revise) explicit and easy to debug.

Here is the core graph definition from agent.py:

class SpecState(TypedDict):
    task: str
    draft: str
    verified: str
    needs_revision: bool
    revision_count: int
    max_revisions: int

def _build_graph(self):
    workflow = StateGraph(SpecState)

    # Define the nodes
    workflow.add_node("draft_step", self.draft_node)
    workflow.add_node("verify_step", self.verify_node)
    workflow.add_node("revise_step", self.revise_node)

    # Define the flow
    workflow.set_entry_point("draft_step")
    workflow.add_edge("draft_step", "verify_step")

    # Conditional logic: If verification fails, revise. Otherwise, end.
    workflow.add_conditional_edges(
        "verify_step",
        self.should_revise,
        {"revise": "revise_step", "end": END}
    )
    workflow.add_edge("revise_step", "verify_step")

    return workflow.compile()

Self-Correction in Action

The reliability gain comes from the verification step. The verification model (usually larger, or tuned for critique) acts as a critic.

def verify_node(self, state: SpecState) -> SpecState:
    """Quality verification using orchestrator model."""
    response = self.verify_model.invoke([
        SystemMessage(content="Review this draft. If acceptable, output APPROVED. If issues, output REVISE: <feedback>"),
        HumanMessage(content=f"Task: {state['task']}\n\nDraft:\n{state['draft']}")
    ])

    if "APPROVED" in str(response.content):
        return {"verified": state['draft'], "needs_revision": False}
    return {"needs_revision": True, "verified": str(response.content)}

This lets the agent catch hallucinations and missed details without a human in the loop, which is the main difference between “a generator” and “a system you can lean on.”

Comparison to Standard RAG

Many “agents” are closer to RAG (Retrieval Augmented Generation) pipelines. This system differs because it has agency: it can decide to loop back and fix its own mistakes based on the should_revise condition.

  1. Start simple - Don't over-architect; LangGraph + simple functions is often enough.
  2. Log everything - Debugging agents is hard.
  3. Test with real tasks - Demos lie.
  4. Local models are good enough - 7B models (like Dolphin-Llama3) handle most agent tasks fine when given a specific role.

Check out the Agent Orchestrator project for more details.

Related Articles

Comments

Join the discussion. Be respectful.