The resilient graph

LLM calls fail. The provider rate-limits, the JSON parsing blows up, or the model returns a field that Pydantic rejects. A production graph treats each node as fallible and keeps moving when it can. That is what turns a demo into a workflow you can ship.

Errors contained at the node level

A failing agent writes an error flag into state. The graph keeps running and the downstream consumer handles the missing data.

cv_agentic_analyzer.py
python
async def analyze_jd(self, state: CVAnalysisState) -> CVAnalysisState:
    jd = state.get("job_description", "") or ""
    if len(jd.strip()) < 10:
        state["job_analysis"] = JobAnalysis().model_dump()
        return state

    try:
        raw = await self.llm.generate_text(build_prompt(jd))
        cleaned = clean_json_response(raw)
        parsed = JobAnalysis.model_validate_json(cleaned)
        state["job_analysis"] = parsed.model_dump()
    except ValidationError as e:
        state["job_analysis"] = JobAnalysis().model_dump()
        state["error"] = f"JD analysis failed: {e}"
    return state

A node that fails never raises out of the graph. It writes a fallback into state and records the failure. Downstream agents read the safe default and keep working.

Yes, for transient errors like rate limits or timeouts. Wrap the LLM call in a small retry helper with exponential backoff. Reserve fallbacks for the cases where retries still fail or where the input itself is bad. The graph treats both cases uniformly by writing a safe default into state.

Quiz: Quiz

Loading practice…