Build a Self‑Thinking AI Agent with LangGraph: A Step‑by‑Step Guide

This tutorial explains how LangGraph adds explicit control‑flow, cycles, and shared state to LLM applications, and walks through building a Strava‑based intelligent training coach with Python code, node definitions, state design, graph assembly, and GitHub Actions deployment.

Data STUDIO
Data STUDIO
Data STUDIO
Build a Self‑Thinking AI Agent with LangGraph: A Step‑by‑Step Guide

1. Core of LangGraph: Making AI Workflows Alive

LangGraph is a library that provides explicit control‑flow for LLM applications. Traditional pipelines are directed acyclic graphs (DAGs) that cannot loop or re‑evaluate. LangGraph allows cycles, enabling agents that can iterate, reassess, and decide the next step.

LangGraph concept diagram
LangGraph concept diagram

Understanding the three key concepts

State: the agent’s memory A shared data structure that persists across nodes, holding conversation history, decisions, tool outputs, and any information that must survive step‑to‑step.

Node: focused behavior unit Each node does one thing – call an LLM, run a tool, transform data, or perform routing.

Edge: explicit control flow Edges define how execution moves between nodes. Types include sequential, conditional, and loop edges.

Explicit edges make the control flow visible, simplifying debugging of loops and branches.

2. Hands‑on: Build a Strava Training Coach

Step 1 – Decompose the goal into nodes

Break the “automatic training coach” into clear nodes such as sync Strava, understand goals, generate weekly plan, compare plan vs actual, adjust plan, and send email.

Connect to a Strava account and fetch recent activities (runs, trail runs, etc.).

Understand user goals (target distance/date, desired pace, elevation changes).

Generate a concise weekly training plan while respecting weekly session limits and deload rules.

Compare the generated plan with completed work to detect missed, extra, or risky sessions.

If risk signals appear, adjust the next week’s plan and explain the changes.

When an SMTP server is configured, email the plan; otherwise preview it in logs or the terminal.

Strava workflow diagram
Strava workflow diagram
graph LR
    A[开始] --> B[同步 Strava 活动]
    B --> C[汇总近期训练]
    C --> D[评估进度 vs 目标]
    D --> E[生成下周计划]
    E --> F{推荐策略?}
    F -- keep/保持 --> G[撰写周报邮件]
    F -- adjust or deload/调整或减载 --> H[调整计划 & 添加警告]
    H --> G
    G --> I[发送邮件]
    I --> J[结束]

Step 2 – Define node responsibilities

Data nodes : fetch external information, e.g., sync_strava_activities, which needs an API key and outputs a list of recent activities.

LLM nodes : perform analysis, reasoning, and generation, e.g., evaluate_progress_vs_goal, which consumes activity summaries and goals and outputs risk assessments.

Action nodes : interact with the outside world, e.g., send_email, which needs email content and addresses and performs the send operation.

Step 3 – Design shared State

State is a TypedDict that holds all information passed between nodes.

from typing import TypedDict, List, Dict

class StravaTrainingAgentState(TypedDict):
    activities: List[Dict] | None  # raw Strava activity data
    training_summary: Dict | None   # aggregated training data
    goal: Dict                     # user‑defined goals
    sessions_per_week: int         # weekly session constraint
    evaluation: Dict | None       # risk assessment and recommendation
    next_week_plan: List[Dict] | None  # generated plan
    weekly_email: str | None       # email content to be sent

Step 4 – Implement nodes

Each node is a plain Python function that receives the state and returns the part of the state it updates. Example for generating the next‑week plan:

def generate_next_week_plan(state: StravaTrainingAgentState) -> dict:
    """LangGraph node: generate next‑week training plan"""
    llm = LLMService()
    goal = state["goal"]
    summary = state["training_summary"]
    sessions = state["sessions_per_week"]
    recommendation = state["evaluation"]["recommendation"]

    system_prompt = """
    You are a running coach. Return valid JSON with fields:
    day, description, duration (min), intensity.
    Respect the weekly session limit; unless it is a deload week, increase no more than 10%.
    """
    user_prompt = f"""Goal: {goal}
Recent training: {summary}
Recommendation: {recommendation}
Weekly sessions: {sessions}
"""
    plan_json = llm.structured_completion(system_prompt, user_prompt)
    return {"next_week_plan": json.loads(plan_json)}

Step 5 – Assemble the graph

Create a StateGraph, add all nodes, and define edges, including conditional edges that route based on the evaluation recommendation.

from langgraph.graph import StateGraph, START, END

graph = StateGraph(StravaTrainingAgentState)

graph.add_node("sync_strava", sync_strava_activities)
graph.add_node("summarize", summarize_recent_training)
graph.add_node("evaluate", evaluate_progress_vs_goal)
graph.add_node("generate_plan", generate_next_week_plan)
graph.add_node("adjust_plan", adjust_plan_add_warnings)
graph.add_node("compose_email", compose_weekly_email)
graph.add_node("send_email", send_email)

# Define sequential flow
graph.add_edge(START, "sync_strava")
graph.add_edge("sync_strava", "summarize")
graph.add_edge("summarize", "evaluate")
graph.add_edge("evaluate", "generate_plan")

# Conditional routing based on recommendation
graph.add_conditional_edges(
    "generate_plan",
    lambda state: state["evaluation"]["recommendation"],
    {
        "keep": "compose_email",   # keep the plan, go straight to email
        "adjust": "adjust_plan",   # adjust the plan before emailing
        "deload": "adjust_plan",   # treat deload as an adjustment
    },
)

graph.add_edge("adjust_plan", "compose_email")
graph.add_edge("compose_email", "send_email")
graph.add_edge("send_email", END)

app = graph.compile()

Step 6 – Run and deploy

After successful local testing, schedule the agent with a GitHub Actions cron job. Store API keys and SMTP credentials in GitHub Secrets for a fully automated weekly plan generation and delivery.

Conclusion

LangGraph makes complex AI agents transparent and maintainable by separating state, small focused nodes, and explicit edges. This pattern applies to customer‑support assistants, data‑analysis agents, internal approval tools, or any scenario that requires a “reason‑act‑loop” architecture.

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.

PythonAI agentsLLMworkflowState MachineLangGraphStrava
Data STUDIO
Written by

Data STUDIO

Click to receive the "Python Study Handbook"; reply "benefit" in the chat to get it. Data STUDIO focuses on original data science articles, centered on Python, covering machine learning, data analysis, visualization, MySQL and other practical knowledge and project case studies.

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.