Build a 2026‑Ready LangGraph AI Agent: A Step‑by‑Step Guide

This tutorial walks you through constructing a LangGraph‑based AI agent for automated Strava training plans, covering core concepts like state, nodes, and edges, detailed workflow steps, Python code examples, conditional graph routing, testing, and deployment via GitHub Actions.

AI Waka
AI Waka
AI Waka
Build a 2026‑Ready LangGraph AI Agent: A Step‑by‑Step Guide

What is LangGraph?

LangGraph is a library built on LangChain that focuses on explicit control flow for LLM applications. Instead of a linear pipeline, it models an application as a graph that can branch, loop, and revisit earlier steps.

Why LangGraph over traditional LangChain DAGs?

Traditional LangChain workflows are directed acyclic graphs (DAGs) that move forward and terminate. This works for many use cases, but it limits agents that need iteration, re‑evaluation, or dynamic decision‑making. LangGraph solves this by allowing cycles, enabling nodes to be revisited and decisions to be made repeatedly.

Core abstractions: State, Nodes, and Edges

Understanding state , nodes , and edges is enough to grasp how LangGraph works.

State – Agent memory

State is a shared data structure that flows through the graph. Each node receives the current state and can return updates, acting as the agent’s cumulative context (messages, decisions, tool outputs, etc.).

Node – Focused behavior unit

A node is an executable unit representing a single, well‑defined operation, such as:

Calling an LLM

Calling a tool or API

Transforming or validating data

Making routing decisions

Small, focused nodes are easier to test, reason about, and modify.

Edge – Control flow

Edges define how execution moves from one node to the next. They can be:

Sequential (always move to the next node)

Conditional (choose the next node based on state)

Cyclic (return to a previous node)

Explicit edges make the control flow visible in the graph rather than hidden in prompt logic.

Example: Strava‑based training agent

The tutorial builds an AI agent that connects to a Strava account, summarizes recent training, evaluates progress against goals, generates a weekly plan, adds warnings if needed, composes an email, and optionally sends it.

Step 1 – Map the workflow to discrete steps

Break the automation into clear steps, each becoming a node (a small function with a single responsibility). The diagram below shows how the nodes combine in the Strava training workflow.

Step 2 – Define each node’s purpose

For each step decide its type, required context, and expected output. Example nodes include:

Data step : Sync Strava activities (fetch past 90 days of data).

LLM step : Summarize recent training into high‑level metrics.

LLM step : Evaluate progress vs. goal, producing a status, confidence score, risk flags, and recommendation (keep/adjust/deload).

LLM step : Generate next‑week plan based on goal, summary, and recommendation.

Operation step : Send the weekly email.

State definition (TypedDict)

from typing import TypedDict, Literal, List, Dict

class TrainingEvaluation(TypedDict):
    status: Literal["on_track", "behind", "overloaded"]
    confidence: float
    risk_flags: List[str]
    recommendation: Literal["keep", "adjust", "deload"]

class TrainingSession(TypedDict):
    day: str
    description: str
    duration_min: int
    intensity: Literal["easy", "moderate", "hard"]

class StravaTrainingAgentState(TypedDict):
    activities: List[Dict] | None
    training_summary: Dict | None
    goal: Dict
    sessions_per_week: int
    evaluation: TrainingEvaluation | None
    next_week_plan: List[TrainingSession] | None
    weekly_email: str | None
    run_id: str
    last_sync_timestamp: str

Step 4 – Implement nodes (Python functions)

Nodes are simple Python functions that take the shared state and return updates. The key node generate_next_week_plan uses a minimal ChatOpenAI wrapper to produce a JSON plan.

def generate_next_week_plan(state: StravaTrainingAgentState) -> dict:
    """LangGraph node: generate next week's training plan."""
    llm: LLMService = _get_service("llm")
    goal = state.get("goal", {})
    summary = state.get("training_summary", {})
    sessions = state.get("sessions_per_week", 3)
    recommendation = (state.get("evaluation") or {}).get("recommendation", "adjust")

    system_prompt = (
        "You are a running coach. Return ONLY valid JSON: "
        "a list of sessions with fields: day (Mon-Sun), description, "
        "duration_min, intensity (easy|moderate|hard). "
        "Respect sessions_per_week exactly. Cap progression at +10% unless deloading (20–30% down)."
    )
    user_prompt = (
        f"Goal: {json.dumps(goal)}
"
        f"Recent summary: {json.dumps(summary)}
"
        f"Recommendation: {recommendation}
"
        f"Sessions per week: {sessions}"
    )
    plan = llm.structured_completion(system_prompt, user_prompt)
    return {"next_week_plan": plan}

Step 5 – Connect the graph

After implementing all nodes, create a StateGraph, add nodes, and define edges. Most edges are sequential; the edge after generate_next_week_plan is conditional based on the recommendation.

# Initiate graph
graph = StateGraph(StravaTrainingAgentState)
# Add nodes
graph.add_node("sync_strava_activities", sync_strava_activities)
graph.add_node("summarize_recent_training", summarize_recent_training)
graph.add_node("evaluate_progress_vs_goal", evaluate_progress_vs_goal)
graph.add_node("generate_next_week_plan", generate_next_week_plan)
graph.add_node("adjust_plan_add_warnings", adjust_plan_add_warnings)
graph.add_node("compose_weekly_email", compose_weekly_email)
graph.add_node("send_email", send_email)
# Add edges
graph.add_edge(START, "sync_strava_activities")
graph.add_edge("sync_strava_activities", "summarize_recent_training")
graph.add_edge("summarize_recent_training", "evaluate_progress_vs_goal")
graph.add_edge("evaluate_progress_vs_goal", "generate_next_week_plan")
graph.add_conditional_edges(
    "generate_next_week_plan",
    lambda state: (state.get("evaluation") or {}).get("recommendation", "adjust"),
    {
        "keep": "compose_weekly_email",
        "adjust": "adjust_plan_add_warnings",
        "deload": "adjust_plan_add_warnings",
    },
)
graph.add_edge("adjust_plan_add_warnings", "compose_weekly_email")
graph.add_edge("compose_weekly_email", "send_email")
graph.add_edge("send_email", END)
app = graph.compile()

Step 6 – Test the agent

Clone the GitHub repository, set up a virtual environment, install dependencies, and configure a .env file with Strava and OpenAI credentials. Run the agent locally with: python strava_training_agent.py If SMTP credentials are missing, the agent prints the email preview instead of sending.

Automation

For weekly execution, a GitHub Actions workflow ( weekly-agent.yml) uses a cron trigger. Secrets store Strava, OpenAI, and optional SMTP credentials, and the same script runs in CI.

Final thoughts

The tutorial’s goal is to demonstrate how LangGraph enables explicit, testable, and reasoning‑friendly agent workflows. By modeling problems as graphs with shared state, small nodes, and clear edges, you can build robust agents for training plans, customer support, data analysis, or any scenario requiring reasoning, action, and loops.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

PythonAutomationLLMworkflowai-agentLangGraph
AI Waka
Written by

AI Waka

AI changes everything

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.