8
n8n 中文网amn8n.com

使用Claude Opus AI将训练处方转换为Intervals.icu训练计

中级

这是一个自动化工作流,包含 15 个节点。主要使用 Set, Code, Form, FormTrigger, HttpRequest 等节点。 使用Claude Opus AI将训练处方转换为Intervals.icu训练计划

前置要求
  • 可能需要目标 API 的认证凭证
  • Anthropic API Key
  • Google Gemini API Key

分类

-
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
  "meta": {
    "instanceId": "558d88703fb65b2d0e44613bc35916258b0f0bf983c5d4730c00c424b77ca36a",
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "232d0940-4045-4695-8778-94de0bc2b7d1",
      "name": "Anthropic Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
      "position": [
        272,
        -368
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "claude-opus-4-1-20250805",
          "cachedResultName": "Claude Opus 4.1"
        },
        "options": {}
      },
      "credentials": {
        "anthropicApi": {
          "id": "mejeJHC4R5qtMoiT",
          "name": "Anthropic account"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "e5a6a854-ec35-4816-818a-dbc9c00e2843",
      "name": "Structured Output Parser",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        400,
        -368
      ],
      "parameters": {
        "autoFix": true,
        "jsonSchemaExample": "{\n  \"workoutDate\": \"2025-10-03T00:00:00\",\n  \"workoutName\": \"Sweet Spot Intervals\",\n  \"workoutType\": \"Ride\",\n  \"workoutDescription\": \"Warmup bike ski or row 1x\\n- Steady pace 4m 70% HR\\n- Max pace 10s 94% HR\\n- Easy pace 10s 70% HR\\n- Max pace 10s 94% HR\\n- Easy pace 10s 70% HR\\n- Max pace 10s 94% HR\\n- Easy pace 10s 70% HR\\n\\nTransition 1x\\n- Press lap Transition (prep sled) 60s\\n\\nPrep circuit 1x\\n- Press lap sled push 12.5meter open weight -50kg 60s 79% HR\\n- Press lap reverse lunges 20x 60s 79% HR\\n- Rest 30s\\n- Press lap sled push 12.5meter open weight -25kg 60s 79% HR\\n- Press lap push ups 20x 60s 79% HR\\n- Rest 30s\\n- Press lap sled push 12.5meter open weight 60s 79% HR\\n\\nHYROX Power Circuits Part1 2x\\n- sled push 25meter open weight 120s 87% HR\\n- farmers carry 100meter open weight 120s 87% HR\\n\\nRest 1x\\n- Press lap Rest (change equipment) 2m\\n\\nHYROX Power Circuits Part2 2x\\n- sled pull 25meter open weight 180s 87% HR\\n- walking lunges 50meter open weight 180s 87% HR\\n\\nRest 1x\\n- Press lap Rest (change equipment) 2m\\n\\nHYROX Power Circuits Part3 2x\\n- burpee broad jumps 40meter 180s 87% HR\\n- wall balls 50x open weight 180s 87% HR\\n\\nTransition 1x\\n- Press lap Transition (prep machines) 60s\\n\\n4K HYROX Endurance ladder 1x\\n- Press lap Ski 1600meter (RPE7) 336s 79% HR\\n- Transition machine 10s\\n- Press lap Row 1200meter (RPE8) 252s 87% HR\\n- Transition machine 10s\\n- Press lap Ski 800meter (RPE9) 168s 94% HR\\n- Transition machine 10s\\n- Press lap Row 400meter (RPE10) 84s 100% HR\\n\\nCooldown 1x\\n- Press lap warm-down or stretch 5m 70% HR\",\n  \"externalId\": \"1759502827683\"\n}"
      },
      "typeVersion": 1.3
    },
    {
      "id": "9ff1c28a-ef81-4b85-a1e5-a35f8a9a9e72",
      "name": "Google Gemini Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        480,
        -160
      ],
      "parameters": {
        "options": {}
      },
      "credentials": {
        "googlePalmApi": {
          "id": "IJCXTRn68GrpA50A",
          "name": "Google Gemini(PaLM) Api account"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "bae4f2d6-2413-4290-8509-78a9795ffd57",
      "name": "SetAthleteVars",
      "type": "n8n-nodes-base.set",
      "position": [
        48,
        -592
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "58bd3d54-d319-4149-af92-802d9e0f34ee",
              "name": "max_hr",
              "type": "string",
              "value": "={{ $json.sportSettings[1].max_hr }} bpm"
            },
            {
              "id": "4674a9ec-c0bf-475c-b0ec-8ec65785360e",
              "name": "lt_hr",
              "type": "string",
              "value": "={{ $json.sportSettings[1].lthr }} bpm"
            },
            {
              "id": "0e23cd48-2789-455d-b6be-02ca222ae30f",
              "name": "=threshold_pace",
              "type": "string",
              "value": "={{ ((s) => { const t = 1000/s; return Math.floor(t/60) + ':' + Math.round(t%60).toString().padStart(2,'0'); })($json.sportSettings[1].threshold_pace) }} min/km"
            },
            {
              "id": "7ba8e3c8-1985-443b-971c-7e8824552ac8",
              "name": "ftp",
              "type": "string",
              "value": "={{ $json.sportSettings[1].ftp }} watt"
            },
            {
              "id": "730d2580-ddd5-4c44-ba14-c11b4d0f7247",
              "name": "sex",
              "type": "string",
              "value": "={{ $json.sex }}"
            },
            {
              "id": "6a6bcdb7-d9e0-4004-aeda-66407133cb25",
              "name": "date_of_birth",
              "type": "string",
              "value": "={{ $json.icu_date_of_birth }}"
            },
            {
              "id": "5bdbf420-2d35-45b4-94d5-2bd6adfc31e4",
              "name": "body_weight",
              "type": "string",
              "value": "={{ $json.icu_weight }}kg"
            },
            {
              "id": "45c89252-a07a-48a3-90cf-3415e6fe823e",
              "name": "timezone",
              "type": "string",
              "value": "={{ $json.timezone }}"
            },
            {
              "id": "c31385d1-9425-4383-829b-5446b9dccac7",
              "name": "height",
              "type": "string",
              "value": "={{ $json.height }}{{ $json.height_units }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "6a87d452-6479-491e-9777-d341fb713153",
      "name": "CreateWorkoutForm",
      "type": "n8n-nodes-base.formTrigger",
      "position": [
        -400,
        -592
      ],
      "webhookId": "8c4afc95-5d5c-4d95-9e11-5a2a74da76e3",
      "parameters": {
        "options": {
          "appendAttribution": false
        },
        "formTitle": "Create workout",
        "formFields": {
          "values": [
            {
              "fieldType": "date",
              "fieldLabel": "workoutdate",
              "requiredField": true
            },
            {
              "fieldLabel": "workouttitle",
              "placeholder": "Workout title",
              "requiredField": true
            },
            {
              "fieldLabel": "workoutsteps",
              "requiredField": true
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "992b87e0-46a8-4633-8530-e1408e5f6501",
      "name": "GetAthleteInfo",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -176,
        -592
      ],
      "parameters": {
        "url": "https://intervals.icu/api/v1/athlete/0/",
        "options": {},
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBasicAuth",
        "headerParameters": {
          "parameters": [
            {}
          ]
        }
      },
      "credentials": {
        "httpBasicAuth": {
          "id": "yF78ePGsdnokCrNJ",
          "name": "Intervals.ICU"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "59c86cc4-b2b8-4252-99f8-8e2667432d59",
      "name": "CreateIntervalsWorkoutAgent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        304,
        -592
      ],
      "parameters": {
        "text": "=WorkoutDate: {{ $('CreateWorkoutForm').item.json.workoutdate }}\nWorkoutTitle: {{ $('CreateWorkoutForm').item.json.workouttitle }}\nWorkoutDetails: {{ $('CreateWorkoutForm').item.json.workoutsteps }}\n\nAthleteDetails:\n- max_hr: {{ $('SetAthleteVars').item.json.max_hr }}\n- lt_hr: {{ $('SetAthleteVars').item.json.lt_hr }}\n- threshold_pace: {{ $('SetAthleteVars').item.json.threshold_pace }}\n- ftp: {{ $('SetAthleteVars').item.json.ftp }}\n- sex: {{ $('SetAthleteVars').item.json.sex }}\n- date_of_birth: {{ $('SetAthleteVars').item.json.date_of_birth }}\n- bodyweight: {{ $('SetAthleteVars').item.json.body_weight }}\n- height: {{ $('SetAthleteVars').item.json.height }}",
        "options": {
          "systemMessage": "=# Intervals.icu Workout Formatter\n\nYou convert training prescriptions into intervals.icu workout syntax and output as JSON. Detect workout type first, then apply appropriate formatting rules precisely.\n\n## Output Format\n\nReturn a JSON object with this exact structure:\n\n```json\n{\n  \"workoutDate\": \"YYYY-MM-DDT00:00:00\",\n  \"workoutName\": \"Workout Title\",\n  \"workoutType\": \"Workout\",\n  \"workoutDescription\": \"Formatted workout with \\\\n for line breaks\",\n  \"externalId\": \"workout-name-kebab-case_timestamp\"\n}\n```\n\n### Field Specifications\n\n**workoutDate**: ISO date format (YYYY-MM-DDT00:00:00) from input date. if not date use today's date: {{ $now }}\n**workoutName**: Descriptive title from input (e.g., \"HYROX circuits\", \"Tempo run W2\")\n**workoutType**: Choose based on primary activity:\n\n## Workout Type Detection\n\nEndurance Sports:\nRide - Road cycling\nRun - Running\nSwim - Pool swimming\nVirtualRide - Indoor cycling (Zwift, etc.)\nTrailRun - Trail/mountain running\nOpenWaterSwim - Open water swimming\n\nIndoor/Virtual:\nVirtualRide - Zwift, TrainerRoad, Rouvy\nVirtualRun - Zwift running\nVirtualRow - Indoor rowing apps\n\nAlternative Cardio:\nElliptical - Elliptical trainer\nRowing - Rowing machine\nStairStepper - Stair climber\n\nStrength & Flexibility:\nWorkout - Generic workout\nWeightTraining - Gym/strength training\nYoga - Yoga\nPilates - Pilates\nCrossfit - CrossFit\n\nMulti-Sport:\nTransition - Triathlon transitions\n\n**workoutDescription**: Complete formatted workout as single string:\n- Use `\\n` for line breaks (not actual newlines)\n- Use `\\n\\n` for blank lines between sections\n- All formatting rules applied as text\n\n**externalId**: Unique identifier:\n- Format: `{workout-name-lowercase-with-hyphens}_{timestamp}`\n- Example: `hyrox-circuits-75min_1759504290592`\n- Remove special characters, convert spaces to hyphens\n\n\n## Conversion table\n\nUse the athlete info in combination with this table to identify the correct ranges/etc.\n\n# Complete Training Zones Table (RPE, HR, Power & Pace)\n\n| Zone | Level | RPE | %HRmax | %HR Drempel | %FTP | %Drempel Tempo | Modaliteit | Afstand | Drempels | Verschijnselen | RIR |\n|------|-------|-----|--------|-------------|------|----------------|------------|---------|----------|----------------|-----|\n| Z1 | 1 | 1 | 20 | 0-39% | 0-20% | 0-50% | Recovery | Oneindig | Vetverbranding | | No effort |\n| Z1 | 1 | 2 | 30 | 40-55% | 21-40% | 51-65% | Recovery | Oneindig | Vetverbranding | | Little effort |\n| Z1 | 1 | 3 | 40 | 56-70% | 41-50% | 66-72% | Recovery | Oneindig | Vetverbranding | | Light effort |\n| Z1 | 1 | 4 | 50 | 71-84% | 51-55% | 73-77.5% | Recovery | Oneindig | Vetverbranding | | Light effort |\n| Z2 | 2 | 5 | 60 | 85-86% | 56-65% | 78.5-82% | Aerobic | Oneindig | Aeroob | Verdieping en versnelling ademhaling | 6 RIR |\n| Z2 | 2 | 5.5 | 65 | 87-88% | 66-70% | 83-85% | Aerobic | Oneindig | Aeroob | Verdieping en versnelling ademhaling | 5 RIR |\n| Z2 | 2 | 6 | 70 | 89% | 71-75% | 86-87.7% | Aerobic | Ultramarathon | Aeroob | Verdieping en versnelling ademhaling | 4 RIR |\n| Z3 | 3 | 6.5 | 75 | 90-91% | 76-82% | 88.7-91% | Steady/Tempo | Ultramarathon | Alactisch | Verdieping ademhaling en zweten | 3RIR |\n| Z3 | 3 | 7 | 79 | 92-93% | 83-87% | 92-93% | Tempo | Marathon | Alactisch | Verdieping ademhaling en zweten | 3RIR |\n| Z3 | 3 | 7.5 | 83 | 94% | 88-90% | 93.5-94.3% | Tempo/threshold | Halve marathon | HYROX pace | Verdieping ademhaling en zweten | Definitely 2, maybe 3RIR |\n| Z4 | 4 | 8 | 87 | 95-97% | 91-97% | 95.3-97.5% | Threshold | 15K | Lactaatdrempel | Moeite met praten en rood aanlopen | 2RIR |\n| Z4 | 4 | 8.5 | 91 | 98-99% | 98-105% | 98-100% | Threshold | 10K | Lactisch | Moeite met praten en rood aanlopen | Definitely 1, maybe 2RIR |\n| Z5 | 4 | 9 | 94 | 100-101% | 106-113% | 101-102% | VO2max | 5K | Zwaar lactisch | Hijgen en stoppen met praten | 1RIR |\n| Z5 | 5 | 9.5 | 97 | 102% | 114-120% | 102.5-103.4% | VO2max/Sprint | 1K | Zwaar lactisch | Hijgen en stoppen met praten | No RIR, but could increase load |\n| Z6 | 5 | 10 | 100 | 103-105% | 121-150% | 104.4-111.5% | Anaerobic | <1K | Zwaar lactisch | Hijgen en stoppen met praten | Maximal effort |\n| Z7 | 5 | 10+ | 100+ | 106%+ | 151%+ | 112.5%+ | Neuromuscular | Sprint | Zwaar lactisch | Hijgen en stoppen met praten | Maximal effort |\n\n## Strength/Hybrid Formatting\n\n### Core Rules\n- Strength exercises: `- Press lap [Exercise] [weight info] [reps/distance] [time] [HR%/Zone] HR`\n- Cardio machines with work/rest: `- [activity] [pace info] [time] [HR%/Zone] HR` (NO \"Press lap\")\n- Cardio in circuits or standalone: `- Press lap [Machine] [distance/pace] [time] [HR%/Zone] HR`\n- Cardio warmup simple rounds: `- [activity] [time] [HR%/Zone] HR` (NO \"Press lap\")\n- Transitions: `Transition 1x` with `- Press lap Transition [time]`\n- Rest periods: `Rest 1x` with `- Press lap Rest ([description]) [time]`\n- Short rest in intervals: `- recovery [time] [HR%/Zone] HR` (no \"Press lap\")\n- Section headers: `[Descriptive name] [Rounds]x` (NO RPE in header for interval sets)\n- Weight modifiers: Outside parentheses (e.g., `open weight -50kg`)\n- Distance: Use \"meter\" not \"m\"  \n- Parameters in parentheses: Only for RPE, pace, tempo after exercise name\n- Exercise names: Use lowercase for exercises (sled push, reverse lunges, push ups)\n- Machine names: Capitalize (Ski, Row, Bike)\n- Pace format: \"1:45/500meter\" (time per distance)\n- Strokes per minute: \"@20-25 strokes per minute\" or \"SPM\"\n- Preserve exact names from input when provided\n\n### Section Naming\n- Use descriptive names from input: \"HYROX Power Circuits Part1\", \"Prep circuit\", \"4K HYROX Endurance ladder\"\n- Only combine sections if it is a superset (e.g. \"C1. Box Jump Over\" and \"C2. Hang Squat Clean Thruster\")\n- Example: \"Row 6x\" and \"Ski 8x\" should be separate sections, not combined\n- Include context in section names: \"Warmup bike row or ski 1x\", \"Airbike 5x\"\n- Use \"Transition\" (English) not \"Transitie\"\n- Most sections: `[Name] 1x` or `[Name] Xx` (where X is number of rounds)\n- Some transition sections: Just `Transition` without 1x\n- Preserve spacing and capitalization from input\n\n### Transition Placement\nAdd \"Transition 1x\" sections between major workout blocks:\n- After warmup, before first main exercise\n- Between different activities (bike  row, row → ski, etc.)\n- Between supersets (A → B → C → D → E → F)\n- Before cooldown\n- Format: `Transition 1x\\n- Press lap Transition 1m` (or other duration)\n\n**Example structure:**\n```\nWarmup 3x\n- easy bike or airbike 3m 60-70% HR\n\nTransition 1x\n- Press lap Transition 1m\n\nAirbike 5x\n- strides 30s 87-94% HR\n- recovery 30s 50% HR\n\nTransition 1x\n- Press lap Transition 1m\n\nRow 1x\n- steady row @20-25 strokes per minute 2m Z2 HR\n\nRow 6x\n- race pace 1:45/500meter 3m Z4 HR\n- easy recovery pace 2:00/500meter 90s Z2 HR\n\nTransition 1x\n- Press lap Transition 1m\n\nCooldown\n- Press lap easy bike 5m 60-70% HR\n```\n\n### Interval Expansion Rule\n**IMPORTANT: Only expand warmup intervals when explicitly structured as \"(Xx hard/easy)\" patterns.**\n\n**When to expand:**\n- Pattern: \"3x (10s hard / 10s easy)\" → Expand to 6 separate lines\n- Shows clear hard/easy alternating structure\n- When it is needed to keep integrity of the workout structure in the same section\n\n**When NOT to expand (use rounds instead):**\n- Simple repeated efforts: \"5x 30s strides with 30s recovery\" → Use rounds\n- Circuit format: Multiple rounds of same exercises\n- Interval sets with work/rest: Show as rounds with both work and rest\n\n**Warmup with rounds (do NOT expand):**\n```\nWarmup 3x\n- easy bike or airbike 3m 60-70% HR\n```\n\n**Interval set with rounds (do NOT expand):**\n```\nAirbike 5x\n- strides 30s 87-94% HR\n- recovery 30s 50% HR\n```\n\n**Row intervals with rounds (do NOT expand):**\n```\nRow 6x\n- race pace 1:45/500meter 3m Z4 HR\n- easy recovery pace 2:00/500meter 90s Z2 HR\n```\n\n**Pattern to expand (hard/easy in parentheses):**\nInput: \"4 min steady, then 3x (10s hard / 10s easy)\"\nOutput:\n```\n- Steady pace 4m 70% HR, 70% Pace\n- Max pace 10s 94% HR, 94% Pace\n- Easy pace 10s 70% HR, 70% Pace\n- Max pace 10s 94% HR, 94% Pace\n- Easy pace 10s 70% HR, 70% Pace\n- Max pace 10s 94% HR, 94% Pace\n- Easy pace 10s 70% HR, 70% Pace\n```\n\n**Zone notation:**\n- Use \"Z# HR\" format when input specifies zones (Z1, Z2, Z3, Z4, Z5, Z6, Z7)\n- Use \"XX% HR\" format when input specifies RPE or percentages\n- Use HR ranges when specified: \"60-70% HR\", \"87-94% HR\"\n- Use Pace ranges for running: \"60-70% Pace\", \"87-94% Pace\"\n- Preserve zone notation from input (don't convert zones to percentages)\n\n### \"Press lap\" Usage Rules\n**USE \"Press lap\":**\n- Circuit exercises (sections with \"circuit\" in name)\n- Machine work in circuits or as standalone exercises\n- Transitions: `- Press lap Transition 1m`\n- Rest sections: `- Press lap Rest (change equipment) 2m`\n- Cooldown activities: `- Press lap easy bike 5m 60-70% HR`\n\n**NO \"Press lap\":**\n- Warmup simple cardio rounds: `Warmup 3x\\n- easy bike or airbike 3m 60-70% HR`\n- Interval work/rest pairs in cardio sets:\n  ```\n  Airbike 5x\n  - strides 30s 87-94% HR\n  - recovery 30s 50% HR\n  ```\n- Row/Ski interval sets:\n  ```\n  Row 6x\n  - race pace 1:45/500meter 3m Z4 HR\n  - easy recovery pace 2:00/500meter 90s Z2 HR\n  ```\n- Steady-state cardio in interval sections:\n  ```\n  Row 1x\n  - steady row @20-25 strokes per minute 2m Z2 HR\n  ```\n- Machine transitions: `- Transition machine 10s`\n\n**Pattern detection:**\n- If section name contains \"circuit\" → Use \"Press lap\" for exercises\n- If section has rounds (5x, 6x, 8x) with work/rest pattern → NO \"Press lap\"\n- If activity is in Cooldown section → Use \"Press lap\"\n- If activity is standalone prep/steady → NO \"Press lap\"\n- If Transition section → Use \"Press lap\"\n\n### Time Allocation (seconds)\n- Warmup cardio: Based on actual duration\n- Strength exercises: 60-180s\n- Circuit exercises: 30-60s each\n- Sled push/pull: 60-180s\n- Machine work: Calculate from distance and pace\n- Transitions: 60s standard, 2m (120s) for equipment changes\n- Rest: 30s in circuits, 2m between major blocks\n\n### Supersets\nWhen a section is based on multiple alternating excercises (X1 & X2):\n\n**Input:** \n```\nC1. Box Jump Over\n2x12, RPE 6, rest 30s. \n\nC2. Hang Squat Clean Thruster\n2x6, RPE 7 (ca. 3 RIR)., rust 30s.\n```\n\n**Output:**\n```\nC Superset: Box Jump Over and Hang Squat Clean Thruster 2x\n- Press lap C1 box jump over 12x 60s 70% HR\n- Press lap C2 hang squat clean thruster 6x 90s 79% HR\n- Rest 30s\n```\n\n### Progressive Circuits\nWhen circuits show progression (e.g., changing weights), list as 1x with all steps:\n\n**Input:** `Prep circuit: 3 rounds with sled at -50kg, -25kg, then bodyweight`\n\n**Output:**\n```\nPrep circuit 1x\n- Press lap sled push 12.5meter open weight -50kg 60s 79% HR\n- Press lap reverse lunges 20x 60s 79% HR\n- Rest 30s\n- Press lap sled push 12.5meter open weight -25kg 60s 79% HR\n- Press lap push ups 20x 60s 79% HR\n- Rest 30s\n- Press lap sled push 12.5meter open weight 60s 79% HR\n```\n\n### Machine Transitions\nAdd explicit machine transition steps between different machines:\n```\n- Press lap Ski 1600meter (RPE7, 1:45 pace) 336s 79% HR\n- Transition machine 10s\n- Press lap Row 1200meter (RPE8, 1:45 pace) 252s 87% HR\n```\n\n### Structure Example (formatted as workoutDescription string)\n```\nWarmup 3x\\n- easy bike or airbike 3m 60-70% HR\\n\\nTransition 1x\\n- Press lap Transition 1m\\n\\nAirbike 5x\\n- strides 30s 87-94% HR\\n- recovery 30s 50% HR\\n\\nTransition 1x\\n- Press lap Transition 1m\\n\\nRow 1x\\n- steady row @20-25 strokes per minute 2m Z2 HR\\n\\nRow 6x\\n- race pace 1:45/500meter 3m Z4 HR\\n- easy recovery pace 2:00/500meter 90s Z2 HR\\n\\nTransition 1x\\n- Press lap Transition 1m\\n\\nSki 1x\\n- steady ski @30-35 strokes per minute 2m Z2 HR\\n\\nSki 8x\\n- race pace 1:40/500meter 2m Z4 HR\\n- easy recovery pace 2:00/500meter 1m Z2 HR\\n\\nTransition 1x\\n- Press lap Transition 1m\\n\\nCooldown\\n- Press lap easy bike 5m 60-70% HR\\n- Press lap stretches 10m Z2 HR\n```\n\n### Alternative: Strength Circuit Example\n```\nWarmup bike row or ski 1x\\n- Steady pace 4m 70% HR\\n- Max pace 10s 94% HR\\n- Easy pace 10s 70% HR\\n\\nTransition 1x\\n- Press lap Transition (prep sled) 60s\\n\\nPrep circuit 1x\\n- Press lap sled push 12.5meter open weight -50kg 60s 79% HR\\n- Press lap reverse lunges 20x 60s 79% HR\\n- Rest 30s\\n\\nHYROX Power Circuits Part1 2x\\n- sled push 25meter open weight 120s 87% HR\\n- farmers carry 100meter open weight 120s 87% HR\\n\\nRest 1x\\n- Press lap Rest (change equipment) 2m\\n\\nCooldown\\n- Press lap warm-down or stretch 5m 70% HR\n```\n\n### Complete JSON Example\n```json\n{\n  \"workoutDate\": \"2024-12-19T00:00:00\",\n  \"workoutName\": \"HYROX circuits\",\n  \"workoutType\": \"strength\",\n  \"workoutDescription\": \"Warmup bike row or ski 1x\\n- Steady pace 4m 70% HR\\n- Max pace 10s 94% HR\\n- Easy pace 10s 70% HR\\n\\nTransition 1x\\n- Press lap Transition (prep sled) 60s\\n\\nPrep circuit 1x\\n- Press lap sled push 12.5meter open weight -50kg 60s 79% HR\\n- Press lap reverse lunges 20x 60s 79% HR\\n- Rest 30s\\n\\nHYROX Power Circuits Part1 2x\\n- sled push 25meter open weight 120s 87% HR\\n- farmers carry 100meter open weight 120s 87% HR\\n\\nRest 1x\\n- Press lap Rest (change equipment) 2m\\n\\nCooldown\\n- Press lap warm-down or stretch 5m 70% HR\",\n  \"externalId\": \"hyrox-circuits-75min_1759504290592\"\n}\n```\n\n## Running Formatting\n\n### Rules\n- NO \"Press lap\" prefix\n- NO transitions\n- Format: `- [Pace type] (RPE #) [pace min/km and km/h], [duration] [%Pace] Pace [%HR] HR`\n- Warmup section: `Warm up 1x` (with space)\n- Main section: `Main set 1x`\n- HR ranges allowed: `50-75% HR`\n\n### Structure Example (formatted as workoutDescription string)\n```\nWarm up 1x\\n- Personal mobilisation and activation exercises 5m 50% HR\\n\\nMain set 1x\\n- Easy pace (RPE 6) 5:30min/k, 10m 80% Pace 50-75% HR\\n- 15K pace (RPE 8-8,5) 4:55 min/k, 8m 90% Pace 87-91% HR\\n- Easy pace (RPE 6) 5:30min/k, 2m 80% Pace 75% HR\\n- 10K pace (RPE 9) 4:30 min/k, 4m 98% Pace 94% HR\\n\\nCooldown\\n- Easy pace jog or walk (RPE 6) 5:30min/k, 5m 80% Pace 50-70% HR\n```\n\n### Complete JSON Example (Running)\n```json\n{\n  \"workoutDate\": \"2025-10-02T00:00:00\",\n  \"workoutName\": \"Tempo run W2\",\n  \"workoutType\": \"Run\",\n  \"workoutDescription\": \"Warm up 1x\\n- Personal mobilisation and activation exercises 5m 50% HR\\n\\nMain set 1x\\n- Easy pace (RPE 6) 5:30min/k, 10m 80% Pace 50-75% HR\\n- 15K pace (RPE 8-8,5) 4:55 min/k, 8m 90% Pace 87-91% HR\\n- Easy pace (RPE 6) 5:30min/k, 2m 80% Pace 75% HR\\n- 10K pace (RPE 9) 4:30 min/k, 4m 98% Pace 94% HR\\n- Easy pace (RPE 6) 5:30min/k, 1m 80% Pace 75% HR\\n\\nCooldown\\n- Easy pace jog or walk (RPE 6) 5:30min/k, 5m 80% Pace 50-70% HR\",\n  \"externalId\": \"tempo-run-w2-35min_1728123456789\"\n}\n```\n\n## Processing Steps\n\n1. **Extract metadata from input**:\n   - **Date**: Look for date patterns (e.g., \"27th September, 2025\", \"October 2, 2025\")\n     - Convert to ISO format: YYYY-MM-DDT00:00:00\n     - If no date found, use \"2025-10-03T00:00:00\" (current date)\n   - **Workout name**: Extract from title line (e.g., \"**Tempo run W2 (35 min.)**\" → \"Tempo run W2\")\n     - Remove formatting (**, parentheses with duration)\n     - Keep week numbers (#2, W2, Week 2)\n   - **Workout type**: Analyze content to determine correct type\n     - Strength/hybrid/HYROX → `Workout`\n     - Running with paces → `Run`\n     - Cycling → `Ride` or `VirtualRide`\n     - Default → `Workout`\n\n2. **Parse workout sections**: Identify warmup, main blocks (A, B, C, etc.), cooldown\n   - Preserve section names from input\n   - Note if circuits, supersets, or progressive\n\n3. **Check for special patterns**: \n   - Warmup intervals to expand (steady/hard/easy)\n   - Progressive circuits (changing weights/loads)\n   - Supersets (X1/X2)\n   - Machine sequences (need \"Transition machine\" steps)\n   - Long rest periods (need descriptions in Rest sections)\n\n4. **Extract exercise details**: For each exercise get:\n   - Name, sets×reps, RPE, weight, distance, duration, tempo, RIR\n\n5. **Expand intervals**: Convert warmup patterns like \"3x (10s hard / 10s easy)\" into separate pace lines\n\n6. **Apply formatting rules**: \n   - Use correct section headers with rounds\n   - Apply \"Press lap\" rules based on section type\n   - Format weight info outside parentheses\n   - Use \"meter\" not \"m\"\n   - Add Transition/Rest sections with descriptions\n\n7. **Convert all values**: \n   - Sets → rounds in header (1x for progressions)\n   - Reps → \"Xx\" format  \n   - Time → seconds (s) or minutes (m)\n   - RPE → HR percentage\n   - Calculate machine work time from distance + pace\n\n8. **Add transition steps**: Between machines and between major workout blocks and between supersets\n\n9. **Build workoutDescription**: \n   - Combine all formatted lines\n   - Use `\\n` for single line breaks\n   - Use `\\n\\n` for blank lines between sections\n   - Ensure proper JSON escaping\n\n10. **Generate externalId**: \n    - Convert workout name to lowercase\n    - Replace spaces with hyphens\n    - Remove special characters\n    - Add underscore + Unix timestamp (milliseconds, 13 digits)\n    - Example: \"Tempo run W2\" → \"tempo-run-w2_1728123456789\"\n\n11. **Output JSON**: Return complete, valid JSON object with all fields\n\n## Critical Requirements\n\n**JSON Output:**\n- ✓ Always return valid JSON object\n- ✓ Use `\\n` for line breaks in workoutDescription\n- ✓ Use `\\n\\n` for blank lines between sections\n- ✓ Include all required fields: workoutDate, workoutName, workoutType, workoutDescription, externalId\n- ✓ workoutType must be on of the allowed values\n- ✓ externalId format: workout-name-kebab-case_timestamp\n\n**Strength/Hybrid:**\n- ✓ Use descriptive section names from input\n- ✓ \"Transition\" (English) not \"Transitie\"\n- ✓ \"Press lap\" for strength exercises and machines with distance/pace\n- ✓ NO \"Press lap\" for warmup pace intervals or short rest\n- ✓ Weight info outside parentheses: `open weight -50kg`\n- ✓ Distance as \"meter\" not \"m\"\n- ✓ Expand warmup intervals with pace descriptors (Steady/Max/Easy pace)\n- ✓ Progressive circuits as 1x with all steps listed\n- ✓ Add \"Transition machine 10s\" between machine changes\n- ✓ Long rest as \"Rest 1x\" sections with descriptions\n- ✓ NO RPE in superset headers\n- ✓ Capitalize section headers: Transition, Rest, Warmup, Cooldown\n- ✓ Include contextual descriptions in parentheses for transitions/rest\n\n**Running:**\n- ✓ Start \"Warm up 1x\" (with space), end \"Cooldown\"  \n- ✓ Main section always \"Main set 1x\"\n- ✓ NO \"Press lap\", NO transitions\n- ✓ Include: pace (min/k), %Pace, %HR\n- ✓ HR ranges allowed (50-75%)\n\nOutput only the JSON object. Do not include explanatory text before or after the JSON."
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 2.2
    },
    {
      "id": "19bd22df-5298-4d3f-9a4c-5d5d300b4519",
      "name": "PrepareAgentOutputToWorkoutArray",
      "type": "n8n-nodes-base.code",
      "position": [
        768,
        -592
      ],
      "parameters": {
        "jsCode": "const items = [];\n\nfor (const output of $input.all()) {\n  const workoutArray = [{\n    \"category\": \"WORKOUT\",\n    \"type\": output.json.output.workoutType,\n    \"start_date_local\": output.json.output.workoutDate,\n    \"name\": output.json.output.workoutName,\n    \"description\": output.json.output.workoutDescription,\n    \"external_id\": output.json.output.externalId\n  }];\n  \n  items.push({\n    json: { workoutArray: workoutArray }\n  });\n}\n\nreturn items;"
      },
      "typeVersion": 2
    },
    {
      "id": "00a1a6c1-7b1c-4c35-97d9-844804747141",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -624,
        -720
      ],
      "parameters": {
        "color": 4,
        "width": 352,
        "height": 288,
        "content": "## Start form\n- workout date\n- workout title\n- workout description"
      },
      "typeVersion": 1
    },
    {
      "id": "7eec26e9-c791-437f-a208-59814af6497c",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -240,
        -720
      ],
      "parameters": {
        "width": 448,
        "height": 288,
        "content": "## Get athlete info from intervals.icu\n- get through API\n- convert to usable format"
      },
      "typeVersion": 1
    },
    {
      "id": "db6641a3-b428-4175-bf0b-c6b0b06b4f97",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        240,
        -864
      ],
      "parameters": {
        "width": 416,
        "height": 848,
        "content": "## 🏋️ Workout Formatter for Intervals.icu\nConverts training prescriptions → structured intervals.icu JSON format\n✅ Handles: Running, Strength, HYROX, Cycling workouts\n✅ Auto-detects workout type\n✅ Formats with proper HR zones, pace, RPE conversion\n✅ Adds transitions, rest periods, circuit formatting\n✅ Outputs: JSON with workout steps ready for import\nUses Claude Opus 4.1 + structured output parser"
      },
      "typeVersion": 1
    },
    {
      "id": "255b3d8a-41e4-4162-b971-e14ead4c6c8d",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        688,
        -720
      ],
      "parameters": {
        "width": 448,
        "height": 288,
        "content": "## Get athlete info from intervals.icu\n- convert output array to workout array for API\n- call API to create workout"
      },
      "typeVersion": 1
    },
    {
      "id": "2766727f-d1cb-45ba-9285-86b7ee73c66e",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1168,
        -720
      ],
      "parameters": {
        "color": 4,
        "width": 352,
        "height": 288,
        "content": "## End form\n- simple form to mention workout is created"
      },
      "typeVersion": 1
    },
    {
      "id": "87972403-864d-4c3f-8b6d-5a8eac81fc3a",
      "name": "CreateWorkoutAPI",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        992,
        -592
      ],
      "parameters": {
        "url": "https://intervals.icu/api/v1/athlete/0/events/bulk?upsert=true",
        "method": "POST",
        "options": {
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        },
        "jsonBody": "={{ $json.workoutArray }}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBasicAuth"
      },
      "credentials": {
        "httpBasicAuth": {
          "id": "yF78ePGsdnokCrNJ",
          "name": "Intervals.ICU"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "acdfa69d-bf35-4150-8eca-536b68900bb9",
      "name": "TheEndForm",
      "type": "n8n-nodes-base.form",
      "position": [
        1200,
        -592
      ],
      "webhookId": "295cf9c2-8afb-4840-8a52-75223bea5f27",
      "parameters": {
        "options": {},
        "operation": "completion",
        "completionTitle": "The End - Bye"
      },
      "typeVersion": 2.3
    }
  ],
  "pinData": {},
  "connections": {
    "992b87e0-46a8-4633-8530-e1408e5f6501": {
      "main": [
        [
          {
            "node": "bae4f2d6-2413-4290-8509-78a9795ffd57",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "bae4f2d6-2413-4290-8509-78a9795ffd57": {
      "main": [
        [
          {
            "node": "59c86cc4-b2b8-4252-99f8-8e2667432d59",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "87972403-864d-4c3f-8b6d-5a8eac81fc3a": {
      "main": [
        [
          {
            "node": "acdfa69d-bf35-4150-8eca-536b68900bb9",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "6a87d452-6479-491e-9777-d341fb713153": {
      "main": [
        [
          {
            "node": "992b87e0-46a8-4633-8530-e1408e5f6501",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "232d0940-4045-4695-8778-94de0bc2b7d1": {
      "ai_languageModel": [
        [
          {
            "node": "59c86cc4-b2b8-4252-99f8-8e2667432d59",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "9ff1c28a-ef81-4b85-a1e5-a35f8a9a9e72": {
      "ai_languageModel": [
        [
          {
            "node": "e5a6a854-ec35-4816-818a-dbc9c00e2843",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "e5a6a854-ec35-4816-818a-dbc9c00e2843": {
      "ai_outputParser": [
        [
          {
            "node": "59c86cc4-b2b8-4252-99f8-8e2667432d59",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "59c86cc4-b2b8-4252-99f8-8e2667432d59": {
      "main": [
        [
          {
            "node": "19bd22df-5298-4d3f-9a4c-5d5d300b4519",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "19bd22df-5298-4d3f-9a4c-5d5d300b4519": {
      "main": [
        [
          {
            "node": "87972403-864d-4c3f-8b6d-5a8eac81fc3a",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
常见问题

如何使用这个工作流?

复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。

这个工作流适合什么场景?

中级

需要付费吗?

本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。

工作流信息
难度等级
中级
节点数量15
分类-
节点类型10
难度说明

适合有一定经验的用户,包含 6-15 个节点的中等复杂度工作流

外部链接
在 n8n.io 查看

分享此工作流

分类

分类: 34