Stop Overtraining: Build an AI Agent to Auto-Sync Your Fitness Plan with Your Heart Rate (LangGraph + Notion)
Weβve all been there. You have a "Leg Day" scheduled in your Notion database, but you woke up feeling like a truck hit you. Your Apple Watch says your Heart Rate Variability (HRV) is in the gutter, but your rigid calendar doesn't care. Usually, youβd either push through and risk injury or manually move cards around in Notion-which is a friction-filled nightmare.
In this tutorial, we are building a Self-Optimizing Health Agent using LangGraph, Notion API, and HealthKit. This agent acts as a closed-loop system: it analyzes your physiological recovery data, reasons about your physical state using an LLM, and automatically rewrites your training schedule. By mastering AI agents, LLM orchestration, and fitness automation, youβll turn your static "To-Do" list into a dynamic "Should-Do" list.
The Architecture: The Bio-Feedback Loop
Using LangGraph, we can treat our fitness logic as a state machine. Unlike a linear script, a graph allows our agent to decide whether it needs to fetch more context (like yesterday's sleep) before making a final decision on your workout.
graph TD
Start((Start)) --> FetchHRV[Fetch HRV Data via HealthKit]
FetchHRV --> CheckRecovery{LLM: Analyze Recovery}
CheckRecovery -- "Low Recovery (Fatigued)" --> ModifyNotion[Action: Downgrade Workout Intensity]
CheckRecovery -- "High Recovery (Fresh)" --> KeepNotion[Action: Maintain/Boost Intensity]
ModifyNotion --> UpdateNotion[Update Notion Page]
KeepNotion --> UpdateNotion
UpdateNotion --> End((Done))
style CheckRecovery fill:#f96,stroke:#333,stroke-width:2px
style FetchHRV fill:#bbf,stroke:#333
Prerequisites
Before we dive into the code, ensure you have:
- Python 3.10+
- LangChain & LangGraph installed (
pip install langgraph langchain_openai) - Notion Integration Token (with access to your workout database)
- HealthKit SDK (Note: Since we are in a Python environment, we'll simulate the HealthKit fetcher, though in a real-world scenario, this would be bridged via a FastAPI endpoint from an iOS app).
Step 1: Defining the Agent State
In LangGraph, everything revolves around the State. Our state will track the current HRV, the recovery score, and the workout plan we need to adjust.
from typing import TypedDict, List
class AgentState(TypedDict):
hrv_value: float
recovery_score: str # e.g., "Optimal", "Strained", "Warning"
current_workout: str
adjustment_made: bool
log: List[str]
Step 2: The Logic Nodes
We need to build the functions that will act as "nodes" in our graph. The most critical part is the Recovery Analyzer, where the LLM decides what to do.
Node: Fetch HRV Data
def fetch_health_data(state: AgentState):
print("π Fetching latest HRV data from HealthKit...")
# Simulation: In production, use a bridge to access HealthKit SDK
mock_hrv = 45.0 # Measured in ms
return {"hrv_value": mock_hrv, "log": state["log"] + ["Fetched HRV: 45ms"]}
Node: LLM Reasoning (The Brain)
from langchain_openai import ChatOpenAI
def analyze_recovery(state: AgentState):
llm = ChatOpenAI(model="gpt-4o")
hrv = state["hrv_value"]
prompt = f"""
The user's Heart Rate Variability (HRV) is {hrv}ms.
Typically, 40-60ms is moderate, below 30ms is highly fatigued.
Current workout: 'Heavy Squat Day'.
Should we adjust the intensity? Respond with 'REDUCE' or 'KEEP'.
"""
response = llm.invoke(prompt)
decision = "Strained" if "REDUCE" in response.content else "Optimal"
return {"recovery_score": decision, "log": state["log"] + [f"LLM Decision: {decision}"]}
Step 3: Updating Notion via API
This is where the magic happens. If the LLM says "Strained," we find today's entry in Notion and swap "Heavy Squats" for "Active Recovery Flow."
import requests
NOTION_TOKEN = "your_token"
DATABASE_ID = "your_db_id"
def update_notion_plan(state: AgentState):
if state["recovery_score"] == "Strained":
print("β οΈ Recovery low! Adjusting Notion schedule...")
# Notion API Patch Request Logic here
# headers = {"Authorization": f"Bearer {NOTION_TOKEN}", "Notion-Version": "2022-06-28"}
# payload = {"properties": {"Name": {"title": [{"text": {"content": "Active Recovery Flow"}}]}}}
return {"adjustment_made": True, "log": state["log"] + ["Notion updated to Active Recovery"]}
return {"adjustment_made": False, "log": state["log"] + ["No changes needed to Notion"]}
The "Official" Way (Advanced Patterns)
While this script works for a single user, scaling AI agents to handle complex multi-step reasoning requires more robust design patterns. For example, how do you handle state persistence or human-in-the-loop approvals before updating Notion?
For more production-ready examples and advanced LangGraph patterns (like adding a "Human Review" node to this workflow), check out the deep-dive guides at WellAlly Tech Blog. They cover everything from LLM security to building high-performance agentic workflows that go far beyond basic tutorials.
Step 4: Compiling the Graph
Now, we wire everything together into a StateGraph.
from langgraph.graph import StateGraph, END
workflow = StateGraph(AgentState)
# Add Nodes
workflow.add_node("fetcher", fetch_health_data)
workflow.add_node("analyzer", analyze_recovery)
workflow.add_node("notion_manager", update_notion_plan)
# Define Edges
workflow.set_entry_point("fetcher")
workflow.add_edge("fetcher", "analyzer")
workflow.add_edge("analyzer", "notion_manager")
workflow.add_edge("notion_manager", END)
# Compile
app = workflow.compile()
# Run the Agent
final_state = app.invoke({"log": [], "current_workout": "Heavy Squat Day"})
print(f"Final Log: {final_state['log']}")
Conclusion: Let Your Data Drive Your Schedule
By building this agent, you've moved past simple automation into Agentic Reasoning. Your Notion page is no longer a static document; itβs a living reflection of your bodyβs actual needs.
What's next?
- Add Sleep Data: Incorporate "Hours of Deep Sleep" from HealthKit into the
analyze_recoveryprompt. - Multimodal: Use GPT-4o to analyze a photo of your fridge and suggest a post-workout meal based on the adjusted plan.
If you enjoyed this build, drop a comment below! Are you team LangGraph or CrewAI for building your agents? Letβs discuss! π
Comments
No comments yet. Start the discussion.