Spaces:
Sleeping
Sleeping
andrewchernish1-ui
commited on
Commit
·
c46cc16
1
Parent(s):
9dab459
feat: restore 3 questions per block
Browse files- app/agent.py +47 -17
- app/main.py +1 -1
- app/report.py +24 -63
app/agent.py
CHANGED
|
@@ -18,23 +18,53 @@ class InterviewAgent:
|
|
| 18 |
TOPIC_QUESTIONS = {
|
| 19 |
BlockKey.health: [
|
| 20 |
(
|
| 21 |
-
"
|
| 22 |
"Были ли у вас травмы и есть ли движения, которые вызывают дискомфорт?",
|
| 23 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
),
|
| 25 |
],
|
| 26 |
BlockKey.goals: [
|
| 27 |
(
|
| 28 |
-
"
|
| 29 |
-
"Какая главная цель на ближайшие 8
|
| 30 |
-
"Хотите уточнить
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
),
|
| 32 |
],
|
| 33 |
BlockKey.readiness: [
|
| 34 |
(
|
| 35 |
-
"
|
| 36 |
-
"Сколько раз в неделю готовы
|
| 37 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
),
|
| 39 |
],
|
| 40 |
}
|
|
@@ -169,7 +199,7 @@ class InterviewAgent:
|
|
| 169 |
"не травмирован",
|
| 170 |
]
|
| 171 |
if self._has_any(text, injury_keywords) or self._has_any(text, injury_negations):
|
| 172 |
-
topics.add("
|
| 173 |
|
| 174 |
medical_keywords = [
|
| 175 |
"диагноз",
|
|
@@ -186,7 +216,7 @@ class InterviewAgent:
|
|
| 186 |
"кардио",
|
| 187 |
]
|
| 188 |
if self._has_any(text, medical_keywords):
|
| 189 |
-
topics.add("
|
| 190 |
|
| 191 |
recovery_keywords = [
|
| 192 |
"сон",
|
|
@@ -200,7 +230,7 @@ class InterviewAgent:
|
|
| 200 |
"перегруз",
|
| 201 |
]
|
| 202 |
if self._has_any(text, recovery_keywords):
|
| 203 |
-
topics.add("
|
| 204 |
|
| 205 |
if block == BlockKey.goals:
|
| 206 |
goal_keywords = [
|
|
@@ -217,7 +247,7 @@ class InterviewAgent:
|
|
| 217 |
"hyrox",
|
| 218 |
]
|
| 219 |
if self._has_any(text, goal_keywords):
|
| 220 |
-
topics.add("
|
| 221 |
|
| 222 |
metrics_keywords = [
|
| 223 |
"кг",
|
|
@@ -244,7 +274,7 @@ class InterviewAgent:
|
|
| 244 |
"взятие",
|
| 245 |
]
|
| 246 |
if self._has_any(text, metrics_keywords):
|
| 247 |
-
topics.add("
|
| 248 |
|
| 249 |
constraints_keywords = [
|
| 250 |
"без",
|
|
@@ -257,7 +287,7 @@ class InterviewAgent:
|
|
| 257 |
"огранич",
|
| 258 |
]
|
| 259 |
if self._has_any(text, constraints_keywords):
|
| 260 |
-
topics.add("
|
| 261 |
|
| 262 |
if block == BlockKey.readiness:
|
| 263 |
frequency_keywords = [
|
|
@@ -273,7 +303,7 @@ class InterviewAgent:
|
|
| 273 |
]
|
| 274 |
frequency_regex = r"\b\d+\s*(раз|р\.)\b"
|
| 275 |
if self._has_any(text, frequency_keywords) or re.search(frequency_regex, text):
|
| 276 |
-
topics.add("
|
| 277 |
|
| 278 |
equipment_keywords = [
|
| 279 |
"дом",
|
|
@@ -295,7 +325,7 @@ class InterviewAgent:
|
|
| 295 |
"дорож",
|
| 296 |
]
|
| 297 |
if self._has_any(text, equipment_keywords):
|
| 298 |
-
topics.add("
|
| 299 |
|
| 300 |
format_keywords = [
|
| 301 |
"онлайн",
|
|
@@ -314,7 +344,7 @@ class InterviewAgent:
|
|
| 314 |
"видео",
|
| 315 |
]
|
| 316 |
if self._has_any(text, format_keywords):
|
| 317 |
-
topics.add("
|
| 318 |
|
| 319 |
return topics
|
| 320 |
|
|
|
|
| 18 |
TOPIC_QUESTIONS = {
|
| 19 |
BlockKey.health: [
|
| 20 |
(
|
| 21 |
+
"health_injuries",
|
| 22 |
"Были ли у вас травмы и есть ли движения, которые вызывают дискомфорт?",
|
| 23 |
+
"Хотите добавить детали по травмам или дискомфорту?",
|
| 24 |
+
),
|
| 25 |
+
(
|
| 26 |
+
"health_medical",
|
| 27 |
+
"Есть ли медицинские ограничения или диагнозы, которые нужно учитывать?",
|
| 28 |
+
"Есть ли ещё важные медицинские моменты?",
|
| 29 |
+
),
|
| 30 |
+
(
|
| 31 |
+
"health_recovery",
|
| 32 |
+
"Как вы обычно переносите нагрузку и восстанавливаетесь после тренировок?",
|
| 33 |
+
"Хотите уточнить восстановление и самочувствие?",
|
| 34 |
),
|
| 35 |
],
|
| 36 |
BlockKey.goals: [
|
| 37 |
(
|
| 38 |
+
"goals_primary",
|
| 39 |
+
"Какая главная цель на ближайшие 8–12 недель?",
|
| 40 |
+
"Хотите уточнить цель?",
|
| 41 |
+
),
|
| 42 |
+
(
|
| 43 |
+
"goals_metrics",
|
| 44 |
+
"По каким признакам вы поймёте, что цель достигнута?",
|
| 45 |
+
"Хотите уточнить признаки результата?",
|
| 46 |
+
),
|
| 47 |
+
(
|
| 48 |
+
"goals_constraints",
|
| 49 |
+
"Есть ли то, чего вы точно не хотите в тренировках?",
|
| 50 |
+
"Есть ли ещё ограничения или нежелательные моменты?",
|
| 51 |
),
|
| 52 |
],
|
| 53 |
BlockKey.readiness: [
|
| 54 |
(
|
| 55 |
+
"readiness_schedule",
|
| 56 |
+
"Сколько раз в неделю готовы тренироваться?",
|
| 57 |
+
"Хотите уточнить частоту занятий?",
|
| 58 |
+
),
|
| 59 |
+
(
|
| 60 |
+
"readiness_location",
|
| 61 |
+
"Где вы планируете тренироваться?",
|
| 62 |
+
"Есть ли дополнительные локации?",
|
| 63 |
+
),
|
| 64 |
+
(
|
| 65 |
+
"readiness_format",
|
| 66 |
+
"Какой формат занятий вам удобнее (онлайн/офлайн)?",
|
| 67 |
+
"Хотите уточнить формат занятий?",
|
| 68 |
),
|
| 69 |
],
|
| 70 |
}
|
|
|
|
| 199 |
"не травмирован",
|
| 200 |
]
|
| 201 |
if self._has_any(text, injury_keywords) or self._has_any(text, injury_negations):
|
| 202 |
+
topics.add("health_injuries")
|
| 203 |
|
| 204 |
medical_keywords = [
|
| 205 |
"диагноз",
|
|
|
|
| 216 |
"кардио",
|
| 217 |
]
|
| 218 |
if self._has_any(text, medical_keywords):
|
| 219 |
+
topics.add("health_medical")
|
| 220 |
|
| 221 |
recovery_keywords = [
|
| 222 |
"сон",
|
|
|
|
| 230 |
"перегруз",
|
| 231 |
]
|
| 232 |
if self._has_any(text, recovery_keywords):
|
| 233 |
+
topics.add("health_recovery")
|
| 234 |
|
| 235 |
if block == BlockKey.goals:
|
| 236 |
goal_keywords = [
|
|
|
|
| 247 |
"hyrox",
|
| 248 |
]
|
| 249 |
if self._has_any(text, goal_keywords):
|
| 250 |
+
topics.add("goals_primary")
|
| 251 |
|
| 252 |
metrics_keywords = [
|
| 253 |
"кг",
|
|
|
|
| 274 |
"взятие",
|
| 275 |
]
|
| 276 |
if self._has_any(text, metrics_keywords):
|
| 277 |
+
topics.add("goals_metrics")
|
| 278 |
|
| 279 |
constraints_keywords = [
|
| 280 |
"без",
|
|
|
|
| 287 |
"огранич",
|
| 288 |
]
|
| 289 |
if self._has_any(text, constraints_keywords):
|
| 290 |
+
topics.add("goals_constraints")
|
| 291 |
|
| 292 |
if block == BlockKey.readiness:
|
| 293 |
frequency_keywords = [
|
|
|
|
| 303 |
]
|
| 304 |
frequency_regex = r"\b\d+\s*(раз|р\.)\b"
|
| 305 |
if self._has_any(text, frequency_keywords) or re.search(frequency_regex, text):
|
| 306 |
+
topics.add("readiness_schedule")
|
| 307 |
|
| 308 |
equipment_keywords = [
|
| 309 |
"дом",
|
|
|
|
| 325 |
"дорож",
|
| 326 |
]
|
| 327 |
if self._has_any(text, equipment_keywords):
|
| 328 |
+
topics.add("readiness_location")
|
| 329 |
|
| 330 |
format_keywords = [
|
| 331 |
"онлайн",
|
|
|
|
| 344 |
"видео",
|
| 345 |
]
|
| 346 |
if self._has_any(text, format_keywords):
|
| 347 |
+
topics.add("readiness_format")
|
| 348 |
|
| 349 |
return topics
|
| 350 |
|
app/main.py
CHANGED
|
@@ -12,7 +12,7 @@ from .stt.whisper import WhisperService
|
|
| 12 |
from .agent import InterviewAgent
|
| 13 |
|
| 14 |
settings = get_settings()
|
| 15 |
-
session_manager = SessionManager(ttl_minutes=settings.session_ttl_minutes, questions_per_block=
|
| 16 |
whisper_service = WhisperService(settings.whisper_model)
|
| 17 |
email_client = EmailClient(settings)
|
| 18 |
llm_client = LocalLLM(settings.llm_model, settings.llm_local_path)
|
|
|
|
| 12 |
from .agent import InterviewAgent
|
| 13 |
|
| 14 |
settings = get_settings()
|
| 15 |
+
session_manager = SessionManager(ttl_minutes=settings.session_ttl_minutes, questions_per_block=3)
|
| 16 |
whisper_service = WhisperService(settings.whisper_model)
|
| 17 |
email_client = EmailClient(settings)
|
| 18 |
llm_client = LocalLLM(settings.llm_model, settings.llm_local_path)
|
app/report.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
| 1 |
from __future__ import annotations
|
| 2 |
|
| 3 |
from datetime import datetime
|
| 4 |
-
import re
|
| 5 |
|
| 6 |
from .models import SessionState
|
| 7 |
|
|
@@ -30,16 +29,24 @@ def build_markdown_report(state: SessionState) -> str:
|
|
| 30 |
)
|
| 31 |
lines.append("")
|
| 32 |
lines.append("## Здоровье")
|
| 33 |
-
lines.append(f"-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
lines.append("")
|
| 35 |
lines.append("## Цели")
|
| 36 |
-
lines.append(f"-
|
|
|
|
|
|
|
| 37 |
lines.append("")
|
| 38 |
lines.append("## Режим и формат")
|
| 39 |
-
lines.append(f"-
|
|
|
|
|
|
|
| 40 |
lines.append("")
|
| 41 |
lines.append("## Рекомендации")
|
| 42 |
-
for item in _build_recommendations(full_text):
|
| 43 |
lines.append(f"- {item}")
|
| 44 |
lines.append("")
|
| 45 |
lines.append("---")
|
|
@@ -61,7 +68,9 @@ def _answer(answers_by_topic: dict[str, str], topic: str) -> str:
|
|
| 61 |
return answers_by_topic.get(topic, "Нет ответа")
|
| 62 |
|
| 63 |
|
| 64 |
-
def _build_recommendations(
|
|
|
|
|
|
|
| 65 |
recommendations: list[str] = []
|
| 66 |
|
| 67 |
if _has_any(
|
|
@@ -92,72 +101,24 @@ def _build_recommendations(full_text: str) -> list[str]:
|
|
| 92 |
"При наличии медицинских ограничений согласовать план с врачом."
|
| 93 |
)
|
| 94 |
|
| 95 |
-
|
| 96 |
-
"кг",
|
| 97 |
-
"килограмм",
|
| 98 |
-
"процент",
|
| 99 |
-
"объем",
|
| 100 |
-
"см",
|
| 101 |
-
"сантиметр",
|
| 102 |
-
"показател",
|
| 103 |
-
"результат",
|
| 104 |
-
"цифр",
|
| 105 |
-
"повтор",
|
| 106 |
-
"минут",
|
| 107 |
-
"секунд",
|
| 108 |
-
"рекорд",
|
| 109 |
-
"присед",
|
| 110 |
-
"тяга",
|
| 111 |
-
"жим",
|
| 112 |
-
"рывок",
|
| 113 |
-
"взятие",
|
| 114 |
-
"тест",
|
| 115 |
-
]
|
| 116 |
-
has_metrics = _has_any(full_text, metrics_keywords)
|
| 117 |
-
if has_metrics:
|
| 118 |
recommendations.append(
|
| 119 |
-
"
|
| 120 |
)
|
| 121 |
else:
|
| 122 |
recommendations.append(
|
| 123 |
-
"
|
| 124 |
)
|
| 125 |
|
| 126 |
-
|
| 127 |
-
"в неделю",
|
| 128 |
-
"раз в неделю",
|
| 129 |
-
"раза в неделю",
|
| 130 |
-
"раз",
|
| 131 |
-
"дни",
|
| 132 |
-
"время",
|
| 133 |
-
"смен",
|
| 134 |
-
"работ",
|
| 135 |
-
"семь",
|
| 136 |
-
]
|
| 137 |
-
has_schedule = _has_any(full_text, schedule_keywords) or re.search(r"\b\d+\s*(раз|р\.)\b", full_text)
|
| 138 |
-
if has_schedule:
|
| 139 |
-
recommendations.append("Зафиксировать расписание и частоту тренировок.")
|
| 140 |
-
else:
|
| 141 |
recommendations.append("Уточнить удобные дни и частоту тренировок.")
|
| 142 |
-
|
| 143 |
-
format_keywords = [
|
| 144 |
-
"онлайн",
|
| 145 |
-
"офлайн",
|
| 146 |
-
"оффлайн",
|
| 147 |
-
"очно",
|
| 148 |
-
"индивиду",
|
| 149 |
-
"групп",
|
| 150 |
-
"чат",
|
| 151 |
-
"созвон",
|
| 152 |
-
"звон",
|
| 153 |
-
"видео",
|
| 154 |
-
"голос",
|
| 155 |
-
]
|
| 156 |
-
has_format = _has_any(full_text, format_keywords)
|
| 157 |
-
if has_format:
|
| 158 |
-
recommendations.append("Зафиксировать формат занятий и канал связи.")
|
| 159 |
else:
|
|
|
|
|
|
|
|
|
|
| 160 |
recommendations.append("Согласовать формат занятий и канал связи.")
|
|
|
|
|
|
|
| 161 |
|
| 162 |
if not recommendations:
|
| 163 |
recommendations.append(
|
|
|
|
| 1 |
from __future__ import annotations
|
| 2 |
|
| 3 |
from datetime import datetime
|
|
|
|
| 4 |
|
| 5 |
from .models import SessionState
|
| 6 |
|
|
|
|
| 29 |
)
|
| 30 |
lines.append("")
|
| 31 |
lines.append("## Здоровье")
|
| 32 |
+
lines.append(f"- Травмы и дискомфорт: {_answer(answers_by_topic, 'health_injuries')}")
|
| 33 |
+
lines.append(f"- Медицинские ограничения: {_answer(answers_by_topic, 'health_medical')}")
|
| 34 |
+
lines.append(
|
| 35 |
+
f"- Восстановление после нагрузок: {_answer(answers_by_topic, 'health_recovery')}"
|
| 36 |
+
)
|
| 37 |
lines.append("")
|
| 38 |
lines.append("## Цели")
|
| 39 |
+
lines.append(f"- Главная цель: {_answer(answers_by_topic, 'goals_primary')}")
|
| 40 |
+
lines.append(f"- Признаки результата: {_answer(answers_by_topic, 'goals_metrics')}")
|
| 41 |
+
lines.append(f"- Нежелательные моменты: {_answer(answers_by_topic, 'goals_constraints')}")
|
| 42 |
lines.append("")
|
| 43 |
lines.append("## Режим и формат")
|
| 44 |
+
lines.append(f"- Частота тренировок: {_answer(answers_by_topic, 'readiness_schedule')}")
|
| 45 |
+
lines.append(f"- Локация: {_answer(answers_by_topic, 'readiness_location')}")
|
| 46 |
+
lines.append(f"- Формат занятий: {_answer(answers_by_topic, 'readiness_format')}")
|
| 47 |
lines.append("")
|
| 48 |
lines.append("## Рекомендации")
|
| 49 |
+
for item in _build_recommendations(full_text, answers_by_topic):
|
| 50 |
lines.append(f"- {item}")
|
| 51 |
lines.append("")
|
| 52 |
lines.append("---")
|
|
|
|
| 68 |
return answers_by_topic.get(topic, "Нет ответа")
|
| 69 |
|
| 70 |
|
| 71 |
+
def _build_recommendations(
|
| 72 |
+
full_text: str, answers_by_topic: dict[str, str]
|
| 73 |
+
) -> list[str]:
|
| 74 |
recommendations: list[str] = []
|
| 75 |
|
| 76 |
if _has_any(
|
|
|
|
| 101 |
"При наличии медицинских ограничений согласовать план с врачом."
|
| 102 |
)
|
| 103 |
|
| 104 |
+
if _answer(answers_by_topic, "goals_metrics") == "Нет ответа":
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
recommendations.append(
|
| 106 |
+
"Согласовать критерии результата и контрольные точки."
|
| 107 |
)
|
| 108 |
else:
|
| 109 |
recommendations.append(
|
| 110 |
+
"Зафиксировать критерии результата и контрольные точки."
|
| 111 |
)
|
| 112 |
|
| 113 |
+
if _answer(answers_by_topic, "readiness_schedule") == "Нет ответа":
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 114 |
recommendations.append("Уточнить удобные дни и частоту тренировок.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 115 |
else:
|
| 116 |
+
recommendations.append("Зафиксировать расписание и частоту тренировок.")
|
| 117 |
+
|
| 118 |
+
if _answer(answers_by_topic, "readiness_format") == "Нет ответа":
|
| 119 |
recommendations.append("Согласовать формат занятий и канал связи.")
|
| 120 |
+
else:
|
| 121 |
+
recommendations.append("Зафиксировать формат занятий и канал связи.")
|
| 122 |
|
| 123 |
if not recommendations:
|
| 124 |
recommendations.append(
|