토론

러닝 플랜 로드 체커 - AI 스킬 for 훈련 플랜 리뷰

Wikiprompt, 무료 프롬프트 백과사전에서

Fatih Kadir Akın

2026년 10월 9일

러닝 플랜 로드 체커 - AI 스킬 for 훈련 플랜 리뷰 러닝 훈련 계획을 수립하고 검토하기 위한 완전한 AI 스킬로, 위험한 부하 점프를 확인하는 Python 스크립트와 상세한 워크플로우를 포함합니다.

프롬프트 내용저장

🌐
--- name: running-plan-load-checker description: 러닝 훈련 계획을 만들고 검토합니다 - 5K, 10K, 하프 마라톤, 마라톤 목표를 위해 - 주간 볼륨 점프, 롱런 비율과 진행, 하드 데이와 연속 하드 세션, 휴식과 컷백 주, 피크 시점, 그리고 테이퍼를 검증된 스크립트로 확인한 후, 위험을 평이한 언어로 설명하고 더 안전한 주간 계획을 제안합니다. 러너가 계획을 공유하거나 "이 계획이 너무 과한가요?", "12주 하프 마라톤 계획을 만들어 주세요", 또는 "마일리지를 어떻게 늘려야 하나요?"라고 물을 때 사용하세요. # 러닝 플랜 로드 체커 당신은 일반 러너가 레이스 데이에 건강하게 도달하도록 돕습니다. 점진적으로 진행되는 계획을 만들고, 기존 계획에서 대부분의 과사용 부상을 일으키는 로드 실수를 검토합니다: 너무 많이, 너무 빨리, 회복이 너무 적게. 당신은 코치의 조수이지 의사가 아닙니다. 통증, 질병, 의학적 상태는 전문가에게 가야 합니다(`references/safety-and-red-flags.md` 참조). ## 이 스킬의 파일 - `scripts/check_plan.py` - 계획 표(Markdown 또는 CSV)를 파싱하고 주별 로드 진행을 확인합니다 (Python 3 표준 라이브러리만 사용) - `references/training-principles.md` - 진행, 강도 균형, 롱런, 컷백 주, 피크 및 테이퍼 - `references/safety-and-red-flags.md` - 언제 멈추고, 전문가를 만나거나, 계획을 조정해야 하는지 - `templates/training-plan.md` - 스크립트가 읽는 계획 표 형식, 그리고 검토 레이아웃 - `examples/example-half-marathon-review.md` - 초안 계획을 검토하고 수정한 예시 ## 워크플로우 ### 1. 러너의 상황 파악 한 개의 짧은 메시지로 다음을 묻거나 확인하세요: - 목표 레이스, 거리, 날짜 (또는 사용 가능한 주 수) - 현재 러닝: 지난 4주간 주간 킬로미터, 주당 러닝 횟수, 최근 최장 러닝 거리 - 경험 수준 및 지난 1년간 부상 이력 - 가능한 요일, 그리고 반드시 비어 있어야 하는 요일 - 목표: 편안한 완주, 또는 시간 목표 러너가 건강 상태가 있거나, 부상에서 복귀 중이거나, 임신 중이거나, 운동을 처음 시작하는 경우, 계획을 따르기 전에 의사와 상담할 것을 권장하세요. ### 2. 계획을 표 형식으로 변환 `templates/training-plan.md` 사용: 세션당 한 행씩 `week`, `day`, `type`, `km` 포함. 스크립트가 아는 유형 단어 사용 (easy, recovery, long, tempo, intervals, hills, fartlek, race, cross, strength, rest). ### 3. 체커 실행 ```bash python3 scripts/check_plan.py plan.md --race half --level intermediate --current-km 20 python3 scripts/check_plan.py plan.csv --race marathon --level beginner --current-km 30 --json ``` 주간 표는 km, 러닝 횟수, 비러닝 일수, 롱런, 롱런 비율, 하드 데이, 그리고 이전 3주 중 최고치 대비 변화를 보여줍니다. 발견 사항은 HIGH (계획 사용 전 수정 필요) 또는 WARN (검토 및 정당화 필요)입니다. 종료 코드 1은 최소 하나의 HIGH 발견을 의미합니다. 스크립트를 실행할 수 없으면 `references/training-principles.md`를 사용하여 동일한 검사를 수동으로 적용하고 그렇게 말하세요. ### 4. 수정 및 설명 각 발견 사항에 대해 문제를 설명만 하지 말고 계획을 변경하세요: 점프를 완만하게 하고, 컷백 주를 삽입하고, 하드 세션을 분리하고, 피크를 이동하고, 레이스 주를 단축하세요. HIGH 발견이 없을 때까지 체커를 다시 실행하고, 남은 WARN은 이유와 함께만 유지하세요 (예: 익숙한 볼륨으로 돌아가는 경험 많은 러너). ### 5. 보고 `templates/training-plan.md`의 검토 레이아웃을 사용하세요, `examples/example-half-marathon-review.md`에서처럼: 무엇이 바뀌었고 왜, 최종 계획, 생활에 변수가 생겼을 때 조정하는 방법, 안전 수칙. ## 규칙 - 러너가 마일을 사용하지 않는 한 거리는 km로 표시; 한 계획에서 단위를 혼용하지 마세요. - 대부분의 러닝은 가볍게 느껴져야 합니다 (대화 가능). 페이스만이 아니라 노력과 말하기 테스트로 강도를 설명하세요. - 다이어트, 보충제, 약물을 처방하지 마세요. - 날카롭거나, 악화되거나, 절뚝거리게 만드는 통증을 참으며 훈련하라고 러너에게 절대 말하지 마세요. - 한 주를 놓치는 것은 정상입니다: 앞으로 건너뛰지 말고 이전 주를 반복하세요. FILE:references/training-principles.md # 체커가 사용하는 훈련 원칙 이것들은 레크리에이션 러너를 위한 보수적인 경험 법칙입니다. 위험을 찾기 위한 지침이지 법칙이 아닙니다; 경험 많은 러너는 예외를 정당화할 수 있습니다. ## 주간 볼륨 진행 - 각 주를 **이전 3주 중 최고치**와 비교하세요, 직전 주만이 아니라, 그래서 컷백 주 이전의 볼륨으로 돌아가는 것이 불이익을 받지 않습니다. - 일반적인 안전한 증가: 초보자는 약 10퍼센트까지, 중급 러너는 12퍼센트, 상급 러너는 15퍼센트. 스크립트는 이 이상을 경고하고 25, 30, 또는 35퍼센트 이상의 점프를 HIGH로 평가합니다. - 작은 절대 변화(3 km 이하)는 무시됩니다, 15 km 주의 10퍼센트는 너무 작아서 중요하지 않기 때문입니다. - 1주차는 러너가 이미 하는 것과 가깝게 시작해야 합니다. 현재 볼륨보다 훨씬 높게 시작하는 것이 가장 흔한 계획 실수입니다. ## 컷백 주 - 3~4주마다, 볼륨을 약 20~30퍼센트 줄이고 롱런을 줄이세요. - 스크립트는 15퍼센트 이상 감소 없이 5주 연속 빌딩 후 경고합니다 (테이퍼 주 제외). ## 롱런 - 롱런은 지구력을 키우지만, 주를 지배해서는 안 됩니다. 사용된 비율 제한: 주 30 km 미만 시 50퍼센트, 60 km 미만 시 45퍼센트, 그 이상 시 40퍼센트. 레이스 주는 제외됩니다. - 롱런은 한 번에 약 1~3 km씩 늘리세요. 스크립트는 이전 최고치보다 3 km 이상 그리고 20퍼센트 이상 성장할 때 경고합니다. - 레이스 주 전 최소 최장 러닝: 5K는 6 km, 10K는 10 km, 하프 마라톤은 16 km, 마라톤은 28 km (초보 마라톤 계획은 종종 30~32 km에서 피크). ## 강도 균형 - 러닝 시간의 약 80퍼센트는 가볍게, 20퍼센트는 중간 또는 힘들게. - 하드 세션: 템포 또는 역치, 인터벌, 힐, 파틀렉, 프로그레션 런, 레이스. 주당 최대 하드 데이: 초보자 1, 중급 2, 상급 3. - 하드 세션 또는 하드 세션과 롱런을 연속된 날에 두지 마세요, 러너가 경험이 많고 의도적이지 않다면 (스크립트가 경고합니다). - 스트라이드(가벼운 러닝 후 짧고 편안한 가속)는 가벼운 것으로 간주됩니다. ## 휴식 - 초보자와 중급자는 주당 최소 하루는 러닝을 하지 않아야 합니다; 3~4회 러닝 계획에서는 이틀 또는 사흘이 정상입니다. 크로스 트레이닝과 근력 운동은 표에서 비러닝 일수로 간주됩니다. ## 피크 및 테이퍼 - 하프 마라톤 및 마라톤: 가장 큰 주는 레이스 주 2~3주 전에 와야 합니다 (스크립트는 마지막 2주 내 피크를 HIGH로 표시합니다). - 테이퍼: 마지막 1~3주 동안 볼륨을 점진적으로 줄이면서 짧고 날카로운 러닝을 유지하세요. 레이스 주에는, 레이스 자체 외에 피크 볼륨의 약 40~60퍼센트를 목표로 하세요 (스크립트는 60퍼센트 이상을 경고합니다). ## 단위 - 1 마일 = 1.609 km. 확인 전에 전체 계획을 변환하세요; 스크립트는 킬로미터만 읽습니다. FILE:references/safety-and-red-flags.md # 안전 및 위험 신호 계획을 만들거나 검토할 때마다 이 목록을 사용하세요. 의심스러우면, 러너가 의사, 물리치료사, 또는 스포츠 의학 전문가를 만나도록 권장하세요. 진단하지 마세요. ## 즉시 달리기를 멈추고 의료 도움을 받으세요 - 가슴 통증, 압박감, 또는 조임; 실신 또는 실신 직전; 비정상적인 호흡 곤란; 빠르거나 불규칙한 심장 박동. - 열 질환 징후: 혼란, 땀 멈춤, 메스꺼움 또는 구토, 매우 높은 체온. ## 세션을 멈추고 휴식한 후, 지속되면 검진 받기 - 날카롭거나, 뼈의 한 지점에 국한되거나, 절뚝거리게 만드는 통증. - 달릴수록 악화되거나, 다음 날 아침 더 심한 통증. - 부기, 저림, 또는 밤 통증. - 며칠 휴식 후에도 지속되는 통증. ## 계획 줄이기 또는 조정 - 며칠 이상 비정상적으로 피곤함, 수면 부족, 과민함, 또는 안정 시 심박수가 확실히 평소보다 높음: 추가 가벼운 날을 가지세요. - 목 위의 가벼운 질병 (콧물: 가벼운 러닝은 괜찮을 수 있음; 목 아래 질병 (열, 가슴 울혈, 위장병) 또는 열: 휴식. - 한 주 놓침: 마지막 완료 주를 반복하세요. 2주 이상 놓침: 볼륨을 2주 전으로 낮추세요. - 더위, 고도, 언덕: 페이스가 아닌 노력으로 달리고, 매우 더운 날에는 세션을 줄이세요. ## 계획 시작 전 의학적 검진 요청 - 운동을 처음 시작하거나, 오랜 휴식 후 복귀하거나, 40세 이상이고 이전에 활동적이지 않았던 경우. - 알려진 심장, 폐, 대사 질환, 또는 급성 심장 문제의 가족력. - 임신 또는 최근 출산. - 최근 부상, 특히 뼈 스트레스 부상. ## 연료 및 수분 보충 (일반적인 내용만) - 약 75~90분 이상의 러닝에서는, 레이스 데이에 처음이 아니라 훈련 중에 물과 약간의 탄수화물을 휴대하는 연습을 하세요. - 특정 영양, 보충제, 또는 체중 조언은 등록 영양사 또는 의사에게 속합니다. ## 말하는 방법 명확하고 친절하게: "그런 종류의 통증은 멈추고 다음 달리기 전에 검진을 받아야 하는 이유입니다. 계획은 기다릴 것입니다; 이후 주를 조정할 수 있습니다." FILE:templates/training-plan.md # 훈련 계획 표 (scripts/check_plan.py 입력) 세션당 한 행. 휴식일은 유형 `rest` 및 km `0`으로 나열하거나 생략할 수 있습니다. | week | day | type | km | notes | |---|---|---|---|---| | 1 | Tue | easy | 5 | conversational pace | | 1 | Thu | tempo | ​6 | ​2x8 min comfortably hard, 2 min jog between | | ​1 | Sat | long | ​10 | easy, practice drinking | | ​1 | Sun | cross | 0 | bike or swim30 to45 min, optional | 열: - `week`: 1, 2, 3 ... - `day`: Mon to Sun (또는 1~7) - `type`: easy, recovery, long, tempo, threshold, intervals, hills, fartlek, progression, race, cross, strength, rest - `km`: 킬로미터 거리 (rest, cross, strength는 0) - `notes`: 선택 사항; 운동 세부 정보 실행: `python3 scripts/check_plan.py plan.md --race <5k|10k|half|marathon> --level <beginner|intermediate|advanced> --current-km <km>` --- # 계획 검토: <러너> - <레이스> (<날짜>) **러너:** <현재 km/주, 주당 러닝 횟수, 최근 최장 러닝, 수준, 부상 이력> **목표:** <완주 / 시간 목표> **주 수:** <n> **가능한 요일:** <요일> ## 체커 결과 (수정 전) <HIGH 및 WARN 발견 사항, 간단히> ## 변경 사항 및 이유 1. <변경> - <발견 사항> 해결; <한 줄 이유> ## 최종 계획 <위 형식의 표> ## 체커 결과 (수정 후) <"발견 사항 0" 또는 허용 가능한 이유와 함께 남은 WARN> ## 생활에 변수가 생겼을 때 조정하는 방법 - 러닝 놓침: <규칙> - 한 주 놓침: <규칙> - 몸이 피곤함: <규칙> ## 안전 수칙 - <references/safety-and-red-flags.md의 관련 사항> FILE:examples/example-half-marathon-review.md # 예시: 10주 하프 마라톤 초안 검토 **러너:** "저는 주당 약 20 km, 3회 러닝, 최장 러닝 9 km를 합니다. 온라인에서 이 10주 하프 마라톤 계획을 찾았습니다. 괜찮은가요?" 중급, 지난 1년간 부상 없음, 목표는 강하게 완주. 가능한 요일: 화, 수, 목, 토, 일. **초안 계획 (요약):** 1~9주는 24~44 km로 증가, 롱런은 12~20 km로 주당 1 km씩 증가, 매주 템포 또는 인터벌 (2주차 추가 인터벌 및 6주차 힐 포함), 컷백 주 없음, 10주차 일요일 레이스. **명령:** ```bash python3 scripts/check_plan.py draft.md --race half --level intermediate --current-km20 ``` **출력:** ``` WEEK KM RUNS REST LONG LONG% HARD CHANGE 1 24 3 4 12 50% 1 - 2 31 4 3 13 42% 2 +29% 3 28 3 4 14 50% 1 -10% 4 34 4 3 15 44% 1 +10% 5 37 4 3 16 43% 1 +9% 6 39 4 3 17 44% 2 +5% 7 40 4 3 18 45% 1 +3% 8 42 4 3 19 45% 1 +5% 9 44 4 3 20 45% 1 +5% 10 40.1 4 3 21.1 53% 2 -9% 발견 사항: 6 [HIGH] 9주차: 피크 볼륨 (44 km)이 9주차에 해당, 레이스 주 10주차에 너무 가까움; 피크는 2~3주 전이어야 함 [WARN] 1주차: 1주차는 24 km로 현재 주 20 km 대비 [WARN] 2주차: 볼륨 31 km는 최근 최대 24 km보다 29% 높음 (12% 이하 목표) [WARN] 2주차: 하드 또는 롱 세션이 연속된 날에 있음: 3-4 [WARN] 5주차: 컷백 주 없이 5주 연속 (3~4주마다 볼륨 20~30% 감소 필요. ) [WARN] 6주차: 하드 또는 롱 세션이 연속된 날에 있음: 4-5, 5-6 ``` --- # 계획 검토: 하프 마라톤 12주 **러너:** 20 km/주, 3회 러닝, 최근 최장 러닝 9 km, 중급, 최근 부상 없음 **목표:** 강하게 완주 **주 수:** 12 (레이스를 12주차로 옮기고 2주 일찍 시작하여) **가능한 요일:** 화, 수, 목, 토, 일 ## 체커 결과 (수정 전) 1 HIGH (레이스 전 주에 피크가 있어 테이퍼 없음) 및 5 WARN (시작이 너무 높음, 2주차 29% 점프, 2주 및 6주차 연속 하드 데이, 컷백 주 없음). ) ## 변경 사항 및 이유 1. 1주차를 21 km로 시작 - 현재 20 km에 가깝게, 1주차에 몸에 충격을 주지 않기 위해. 2. 주당 약 2 km씩 증가 (6~10%) - 2주차 점프 제거. 3. 4주 및 8주차에 컷백 주 (약 20% 감소, 롱런 10 km로 복귀) - 훈련을 몸이 흡수하게 함. 4. 목요일에 주당 한 번의 퀄리티 세션, 롱런 전후로 절대 배치하지 않음 - 연속 하드 데이 해결. 5. 10주차에 피크 (36 km,, 롱런 16 km),, 이후 테이퍼 주 (26 km) 및 가벼운 레이스 주 - 레이스를 컨디션 좋은 상태로 달림. 6. 2주차부터 수요일에 네 번째 짧은 가벼운 러닝 추가 - 롱런을 더 무겁게 만들지 않고 볼륨 추가. ## 최종 계획 | week | km | Tue | Wed | Thu | Sat | Sun | |---|---|---|---|---|---|---| | 1 | ​21 | easy5 | - | easy6 + strides | long10 | - | | 2 | ​23 | easy5 | easy3 | tempo5(2x8 min) | long10 | - | | ​3 | ​25 | easy6 | easy3 | tempo5(2x10 min) | long11 | - | | ​4 | ​20 | easy5 | - | easy5 | long10 | - | | ​5 | ​27 | easy6 | easy4 | intervals6(5x1 km) | long11 | - | | ​6 | ​29 | easy6 | easy4 | tempo7(3x10 min) | long12 | - | | ​7 | ​31 | easy6 | easy5 | intervals7(6x1 km) | long13 | - | | ​8 | ​25 | easy6 | easy4 | easy5 | long10 | - | | ​9 | ​34 | easy7 | easy5 | tempo7(2x15 min) | long15 | - | | ​10 | ​36 | easy7 | easy6 | intervals7(5x1.6 km) | long16 | - | | ​11 | ​26 | easy6 | easy4 | tempo5(20 min at goal pace) | long11 | - | | ​12 | ​30.1 | easy5 + strides | - | easy4 | - | RACE21.1 | 5주차에는 월요일에 선택적 40분 자전거 타기도 있습니다. ## 체커 결과 (수정 후) `python3 scripts/check_plan.py revised.md --race half --level intermediate --current-km20` -> `발견 사항: 0`, 종료 코드 0. ## 생활에 변수가 생겼을 때 조정하는 방법 - 러닝 놓침: 건너뛰고 다음 날에 억지로 넣지 마세요. - 한 주 놓침: 완료한 마지막 주를 반복한 후 계속하세요. - 이틀 이상 몸이 피곤하거나 아픔: 퀄리티 세션을 가벼운 러닝이나 휴식으로 대체하세요. ## 안전 수칙 - 날카롭거나, 한 지점이거나, 절뚝거리게 만드는 통증, 또는 다음 날 아침 더 심한 통증이 있으면 멈추고 검진을 받으세요. - 9주차부터 롱런에서 물 (및 약간의 탄수화물 간식) 마시는 연습을 하여, 레이스 데이에 놀라움이 없게 하세요. FILE:scripts/check_plan.py #!/usr/bin/env python3 """위험한 로드 점프 및 구조 누락에 대해 러닝 훈련 계획을 확인합니다. 사용법: python3 check_plan.py plan.md [--race5k|10k|half|marathon] [--level beginner|intermediate|advanced] [--current-km20] [--json] python3 check_plan.py plan.csv ... cat plan.md | python3 check_plan.py - ... 계획은 Markdown 표 또는 CSV이며, 최소한 week, day, type 및 km (허용: distance, dist) 열이 있는 헤더 행을 포함합니다. 선택 열은 무시됩니다. 세션당 한 행; 휴식일은 나열하거나 생략할 수 있습니다. day: Mon..Sun, Monday..Sunday 또는 1..7 type: easy, recovery, long, tempo, threshold, intervals, hills, fartlek, progression, race, cross, strength, rest (다른 것은 easy로 간주) km: 숫자 (rest, cross 및 strength는 0 사용) 주별 검사: 이전 3주 중 최고치 대비 볼륨 증가, 주 중 롱런 비율 (30 km 미만 시 50% 제한, 60 km 미만 시 45%, 그 이상 시 40%; 레이스 주 제외), 롱런 점프, 하드 데이 수, 연속된 날의 하드 또는 롱 세션, 휴식일, 컷백 주. 계획 수준 검사: 첫 주 대비 현재 주간 볼륨, 레이스 거리 대비 최장 러닝, 레이스 데이에 너무 가까운 피크 주, 테이퍼. 종료 코드: 0 HIGH 발견 없음, 1 최소 하나의 HIGH 발견, 2 사용법 또는 입력 오류. 표준 라이브러리만 사용. """ import csv import io import json import re import sys DAYS = {"mon":1, "tue":2, "wed":3, "thu":4, "fri":5, "sat":6, "sun":7} HARD = {"tempo", "threshold", "intervals", "interval", "hills", "hill", "fartlek", "progression", "race", "speed", "track"} NON_RUN = {"rest", "cross", "strength", "off", "yoga", "bike", "swim"} RACE_KM = {"5k":5.0, "10k":10.0, "half":21.1, "marathon":42.2} MIN_LONG = {"5k":6, "10k":10,, "half":16,, "marathon":28} LIMITS = { # 주간 증가 경고, 주간 증가 HIGH, 최대 하드 데이 "beginner":(0.10,,0.25,,1), "intermediate":(0.12,,0.30,,2), "advanced":(0.15,,0.35,,3), } def long_share_limit(week_km): """저볼륨 주는 자연스럽게 롱런 비율이 더 큽니다.""" return0.50 if week_km <30 else0.45 if week_km <60 else0.40 def usage(msg): print(f"error: {msg}\n", file=sys.stderr) print(__doc__.strip().split("\n\n")[1], file=sys.stderr) sys.exit(2) def read_rows(text): lines = [ln for ln in text.splitlines() if ln.strip()] if not lines: return [] if lines[0].lstrip().startswith("|") or sum(ln.count("|") >=3 for ln in lines) > len(lines) /2: rows = [] for ln in lines: if "|" notin ln or re.match(r"^\s*\|?\s*:?-{2,}", ln): continue rows.append([c.strip() for c in ln.strip().strip("|").split("|")]) else: rows = list(csv.reader(io.StringIO("\n".join(lines)))) header = [h.strip().lower() for h in rows[0]] out = [] for r in rows[1:]: out.append({header[i]: (r[i].strip() if i < len(r) else "") for i in range(len(header))}) return out def num(value): m = re.search(r"\d+(?:[.,]\d+)?", value or "") return float(m.group(0.replace(",", ".")) if m else0.0 def parse(rows): if not rows: raise ValueError("no table rows found") keys = rows[0].keys() kcol = next((k for k in ("km", "distance", "dist", "distance_km") if k in keys), None) for need, col in (("week", "week" in keys), ("day", "day" in keys), ("type", "type" in keys), ("km", kcol)): if not col: raise ValueError(f"missing column '{need}' (found: {', '.join(keys)})") sessions = [] for i, r in enumerate(rows,2): w = num(r["week"]) d = r["day"].strip().lower() day = DAYS.get(d[:3]) if d[:3] in DAYS else (int(d) if d.isdigit()and1 <= int(d) <=7 else None) if not w or day is None: raise ValueError(f"row {i}: cannot read week/day from {r['week']!r}/{r['day']!r}") t = (r["type"].strip().lower().split() or ["easy"])[0] km = num(r[kcol]) sessions.append({"week": int(w), "day": day, "type": t, "km":0.0 if t in NON_RUN else km}) return sessions def analyze(sessions, race, level, current_km): warn_up, high_up, max_hard = LIMITS[level] weeks = {} for s in sessions: weeks.setdefault(s["week"], []).append(s) findings, table = [], [] def add(sev, week, msg): findings.append({"severity": sev, "week": week, "message": msg}) prev_long = [] week_nums = sorted(weeks) for idx, w in enumerate(week_nums: ss = sorted(weeks[w], key=lambda s: s["day"]) runs = [s for s in ss if s["km"] >0] total = round(sum(s["km"] for s in runs),1) run_days = sorted({s["day"] for s in runs}) longest = max((s["km"] for s in runs), default=0.0) hard_days = sorted({s["day"] for s in runs if s["type"] in HARD}) stress_days = sorted({s["day"] for s in runs if s["type"] in HARD or s["type"] == "long"}) ref = max((r["km"] for r in table[-3:]), default=None) change = None if not ref else (total - ref) / ref row = {"week": w, "km": total, "runs": len(runs, "rest_days":7 - len(run_days, "long_km": longest, "long_share": round(longest / total,2) if total else0.0, "hard_days": len(hard_days), "change_vs_recent_max": None if change is None else round(change *100)} table.append(row) if change is not Noneand total - ref >3: if change > high_up: add("HIGH", w,, f"volume {total:g} kmis {change:.0%} abovethe recent max of {ref:g} km(limit {high_up:.0%})") elif change > warn_up: add("WARN", w,, f"volume {total:g} kmis {change:.0%} abovethe recent max of {ref:g} km(aim for {warn_up:.0%} or less)") is_race_week = bool(race)and idx == len(week_nums) -1 long_share = long_share_limit(total) share = round(longest / total,,2) if total else0.0 if total >=15and not is_race_weekand share > long_share: sev = "HIGH" if share > long_share +0.15 else "WARN" add(sev, w,, f"long run {longest:g} kmis {share:.0%} ofthe week's {total:g} km(aim for {long_share:.0%} or less)") if prev_longand longest > max(prev_long) +3and longest > max(prev_long) *1.2and not is_race_week: : add("WARN", w,, f"longest run jumps to {longest:g} km from a previous best of {max(prev_long):g} km(add1 to3 km at a time)") prev_long.append(longest) if len(hard_days) > max_hard: : add("WARN", w,, f"{len(hard_days)} hard days(days {', '.join(map(str, hard_days))}); {level} plans usually have at most {max_hard}") b2b = [(a,, b) for a,, b in zip(stress_days,, stress_days[1:]) if b - a ==1] if b2b:: add("WARN", w,, "hard or long sessionson back-to-back days: " + ", ".join(f"{a}-{b}" for a,, b in b2b)) if len(run_days) ==7and level != "advanced": : add("WARN", w,, "no rest day this week") # 컷백 주: 빌딩 중 5주 연속으로, 직전 주 대비 최소 15% 이상 낮은 주가 하나 있어야 함 build = [r for r in table] if raceand len(build) >=3: build = build[:-2] # 테이퍼 제외 streak =0 for i,, r in enumerate(build: if iand build[i -1]["km"]and r["km"] <= build[i -1]["km"] *0.85: streak =0 else: streak +=1 if streak ==5: add("WARN", r["week"],, "five weeksin a row without a cutback week(drop volume20 to30% every3 to4 weeks)") streak =0 if current_km is not Noneand table:: first = table[0]["km"] if current_km ==0and first >10: add("HIGH", table[0]["week"],, f"week1 starts at {first:g} km from no running; begin with run-walk sessions") elif current_kmand (first - current_km) / current_km > high_upand first - current_km >3: add("HIGH", table[0]["week"],, f"week1 is {first:g} kmbut current volumeis {current_km:g} km/week({(first - current_km) / current_km:.0%} jump)") elif current_kmand (first - current_km) / current_km > warn_upand first - current_km >3: add("WARN", table[0]["week"],, f"week1 is {first:g} km versus current {current_km:g} km/week") if raceand table:: peak = max(table,, key=lambda r: r["km"]) last = table[-1]["week"] race_rows = [s for s in sessions if s["type"] == "race"] if not race_rows:: add("INFO", last,,, "no sessionwith type 'race';the last week is treated as race week") build_long = max((r["long_km"] for r in table[:-1]),, default=0) if build_long < MIN_LONG[race]:: add("HIGH" if racein ("half",, "marathon") else "WARN",, None,, f"longest training run before race weekis {build_long:g} km;for a {race} aim for at least {MIN_LONG[race]} km") if len(table) >=3and racein ("half",, "marathon")and peak["week"] >= last -1: add("HIGH", peak["week"],,, f"peak volume({peak['km']:g} km) falls in week {peak['week']}, too close to race week {last}; peak2 to3 weeks out") if len(table) >=2:: race_km = sum(s["km"] for s in sessions if s["type"] == "race") race_week_other = table[-1]["km"] - race_km if peak["km"]and race_week_other >0.6 * peak["km"]:: add("WARN", last,,,, f"race week has {race_week_other:g} km besidesthe race({race_week_other / peak['km']:.0%} of peak); taper to about40 to60%") return table,, findings def main(argv): opts = {"race": None,, "level": "intermediate",, "current": None,,, "json": False} paths = [] it = iter(argv) for a in it: if a == "--race": opts["race"] = (next(it,,, "") or "").lower() if opts["race"] notin RACE_KM:: usage("--race must be5k,,,10k,,, half or marathon") elif a == "--level": opts["level"] = (next(it,,, "") or "").lower() if opts["level"] notin LIMITS:: usage("--level must be beginner,,, intermediate or advanced") elif a == "--current-km": v = next(it,,, "") try: opts["current"] = float(v) except ValueError: usage("--current-km needs a number") elif a == "--json": opts["json"] = True elif a.startswith("--"): usage(f"unknown option {a}") else: paths.append(a) if len(paths) !=1: usage("give exactly one plan file,, or - for stdin") try: text = sys.stdin.read() if paths[0] == "-" else open(paths[0],, encoding="utf-8").read() sessions = parse(read_rows(text)) except (OSError,, ValueError) as e: print(f"error: {e}",, file=sys.stderr) return2 table,, findings = analyze(sessions,, opts["race"],,, opts["level"],,, opts["current"]) order = {"HIGH":0,,, "WARN":1,,, "INFO":2} findings.sort(key=lambda f: (order[f["severity"]],,, f["week"] or0)) if opts["json"]: print(json.dumps({"weeks": table,,, "findings": findings},,, indent=2)) else: print(f"Plan: {len(table)} weeks,,, {sum(r['km'] for r in table):g} km total | level={opts['level']}" f" race={opts['race'] or '-'} current={opts['current'] if opts['current'] is not None else '-'} km/week") print(f"\n{'WEEK':>4} {'KM':>6} {'RUNS':>4} {'REST':>4} {'LONG':>5} {'LONG%':>5} {'HARD':>4} {'CHANGE':>7}") for r in table:: ch = "-" if r["change_vs_recent_max"] is None else f"{r['change_vs_recent_max']:+d}%" print(f"{r['week']:>4} {r['km']:>6g} {r['runs']:>4} {r['rest_days']:>4} {r['long_km']:>5g} " f"{r['long_share']:>5.0%} {r['hard_days']:>4} {ch:>7}") print(f"\nFindings: {len(findings)}") for f in findings:: where = f"week {f['week']}" if f["week"] else "plan" print(f" [{f['severity']}] {where}: {f['message']}") if not findings:: print(" none - load progression looks reasonable") return1 if any(f["severity"] == "HIGH" for f in findings) else0 if __name__ == "__main__": sys.exit(main(sys.argv[1:]))))

전체 프롬프트를 보려면 로그인하세요

Continue with:

By logging in, you agree to our Terms of Use and Privacy Policy

사용법

이 프롬프트는 productivity와 함께 사용하도록 설계되었습니다. 위의 프롬프트 내용을 복사하여 원하는 AI 도구에 붙여넣으세요.

최상의 결과를 얻으려면 자리 표시자(대괄호 또는 대문자로 표시)를 특정 요구 사항으로 사용자 지정할 수 있습니다.

참고 자료

분류:productivity| prompts.chat| running| training-plan

토론