command_parser
Парсинг текстовых команд Яндекс Алисы в структурированные действия.
Преобразует естественно-языковые запросы пользователя (добавление, просмотр, выполнение, удаление и перенос задач) в объекты ParseResult с определённым действием и параметрами.
1""" 2Парсинг текстовых команд Яндекс Алисы в структурированные действия. 3 4Преобразует естественно-языковые запросы пользователя (добавление, 5просмотр, выполнение, удаление и перенос задач) в объекты ParseResult 6с определённым действием и параметрами. 7""" 8 9import re 10 11from dataclasses import dataclass, field 12from typing import Optional 13from langfuse import observe 14 15 16@dataclass 17class ParseResult: 18 """ 19 Результат парсинга команды пользователя. 20 21 Содержит идентификатор действия и сопутствующие параметры: 22 текст задачи, ключевое слово даты, ID задачи и ключевое слово для поиска. 23 """ 24 25 action: str 26 task_text: str = "" 27 date_keyword: Optional[str] = None 28 task_id: Optional[int] = None 29 keyword: str = "" 30 31 32class CommandParser: 33 """ 34 Парсер голосовых команд для навыка управления задачами. 35 36 Анализирует входящий текст команды и оригинальное высказывание, 37 определяет намерение пользователя и извлекает параметры 38 (текст задачи, дату, ID задачи). 39 """ 40 41 @observe(name="Parse command") 42 def parse(self, command: str, original_utterance: str) -> Optional[ParseResult]: 43 """ 44 Анализирует команду пользователя и возвращает структурированный результат. 45 46 Определяет тип действия (ping, add_task, list_tasks, complete, 47 delete, move_one, move_all, help, what) на основе регулярных выражений. 48 49 Args: 50 command: Нормализованная команда (в нижнем регистре) 51 original_utterance: Оригинальное высказывание пользователя 52 53 Returns: 54 Optional[ParseResult]: Результат парсинга или None, если команда не распознана 55 """ 56 if original_utterance == "ping": 57 return ParseResult(action="ping") 58 59 if re.search(r"\b(добавь|добавить|создай|новая\s+задача)\b", command): 60 return self._parse_add(command, original_utterance) 61 62 if re.search(r"\b(какие\s+задачи|покажи\s+список|список\s+задач)\b", command) or re.search( 63 r"\b(дело|дела)\b", command 64 ): 65 return self._parse_list(command) 66 67 if re.search(r"\b(помощь|help)\b", command): 68 return ParseResult(action="help") 69 70 if command.startswith("что "): 71 return self._parse_what(command) 72 73 if re.search(r"\b(выполн(?:ен[аыо])?|сделал(?:а)?|готово|пометь|отметь)\b", command): 74 return self._parse_task_id_command(command, action="complete") 75 76 if re.search(r"\b(удали|удалить|убери|убрать)\b", command): 77 return self._parse_task_id_command(command, action="delete") 78 79 if re.search(r"\bперенеси\s+задачу|перенос\s+задачи\b", command): 80 return self._parse_move_one(command) 81 82 if re.search(r"\bперенеси|перенос\b", command): 83 return self._parse_move_all(command) 84 85 return None 86 87 def _parse_add(self, command: str, original_utterance: str) -> ParseResult: 88 """ 89 Парсит команду добавления задачи. 90 91 Определяет дату (сегодня/завтра/послезавтра) и текст задачи 92 из оригинального высказывания пользователя. 93 94 Args: 95 command: Нормализованная команда 96 original_utterance: Оригинальное высказывание 97 98 Returns: 99 ParseResult: Действие add_task с текстом задачи и датой, 100 либо no_task_text если текст задачи не обнаружен 101 """ 102 date_keyword = None 103 task_text = "" 104 105 date_patterns = [ 106 (r"\bна\s+завтра\b\s+(.+)", "завтра"), 107 (r"\bна\s+сегодня\b\s+(.+)", "сегодня"), 108 (r"\bна\s+послезавтра\b\s+(.+)", "послезавтра"), 109 ] 110 111 for pattern, date in date_patterns: 112 match = re.search(pattern, original_utterance) 113 if match: 114 date_keyword = date 115 task_text = match.group(1).strip() 116 break 117 118 if not task_text: 119 for trigger in ["добавь задачу", "добавить задачу", "создай задачу"]: 120 if trigger in original_utterance: 121 remaining = original_utterance.split(trigger, 1)[1].strip() 122 if remaining: 123 task_text = remaining 124 break 125 else: 126 task_text = command 127 128 task_text = re.sub(r"[.,!?]+$", "", task_text).strip() 129 130 if not task_text: 131 return ParseResult(action="no_task_text") 132 133 return ParseResult(action="add_task", task_text=task_text, date_keyword=date_keyword) 134 135 def _parse_list(self, command: str) -> ParseResult: 136 """ 137 Парсит команду просмотра списка задач. 138 139 Определяет дату, на которую пользователь хочет увидеть задачи. 140 141 Args: 142 command: Нормализованная команда 143 144 Returns: 145 ParseResult: Действие list_tasks с указанной датой 146 """ 147 date_keyword = "сегодня" 148 if re.search(r"\bна\s+завтра\b", command): 149 date_keyword = "завтра" 150 elif re.search(r"\bна\s+послезавтра\b", command): 151 date_keyword = "послезавтра" 152 return ParseResult(action="list_tasks", date_keyword=date_keyword) 153 154 def _parse_what(self, command: str) -> ParseResult: 155 """ 156 Парсит команду поиска задач по ключевому слову (например "что купить?"). 157 158 Извлекает ключевое слово (первое слово после "что") и дату 159 для фильтрации задач. 160 161 Args: 162 command: Нормализованная команда 163 164 Returns: 165 ParseResult: Действие what с ключевым словом и датой 166 """ 167 date_keyword = "сегодня" 168 if re.search(r"\bна\s+завтра\b", command): 169 date_keyword = "завтра" 170 elif re.search(r"\bна\s+послезавтра\b", command): 171 date_keyword = "послезавтра" 172 173 parts = command.split() 174 keyword = parts[1] if len(parts) >= 2 else "" 175 return ParseResult(action="what", keyword=keyword, date_keyword=date_keyword) 176 177 def _parse_task_id_command(self, command: str, action: str) -> ParseResult: 178 """ 179 Парсит команду, требующую ID задачи (выполнение/удаление). 180 181 Извлекает числовой ID задачи из команды. 182 183 Args: 184 command: Нормализованная команда 185 action: Тип действия (complete или delete) 186 187 Returns: 188 ParseResult: Действие с указанным типом и ID задачи, 189 либо no_task_id если ID не найден 190 """ 191 match = re.search(r"(\d+)", command) 192 if not match: 193 return ParseResult(action="no_task_id") 194 return ParseResult(action=action, task_id=int(match.group(1))) 195 196 def _parse_move_one(self, command: str) -> ParseResult: 197 """ 198 Парсит команду переноса одной задачи на другую дату. 199 200 Извлекает ID задачи и целевую дату (завтра/послезавтра). 201 202 Args: 203 command: Нормализованная команда 204 205 Returns: 206 ParseResult: Действие move_one с ID задачи и датой, 207 либо no_task_id если ID не найден 208 """ 209 date_keyword = "завтра" 210 if re.search(r"\bна\s+завтра\b", command): 211 date_keyword = "завтра" 212 elif re.search(r"\bна\s+послезавтра\b", command): 213 date_keyword = "послезавтра" 214 215 match = re.search(r"(\d+)", command) 216 if not match: 217 return ParseResult(action="no_task_id") 218 return ParseResult( 219 action="move_one", task_id=int(match.group(1)), date_keyword=date_keyword 220 ) 221 222 def _parse_move_all(self, command: str) -> ParseResult: 223 """ 224 Парсит команду переноса всех задач на другую дату. 225 226 Определяет целевую дату (завтра/послезавтра) для массового переноса. 227 228 Args: 229 command: Нормализованная команда 230 231 Returns: 232 ParseResult: Действие move_all с целевой датой 233 """ 234 date_keyword = "завтра" 235 if re.search(r"\bна\s+завтра\b", command): 236 date_keyword = "завтра" 237 elif re.search(r"\bна\s+послезавтра\b", command): 238 date_keyword = "послезавтра" 239 return ParseResult(action="move_all", date_keyword=date_keyword)
17@dataclass 18class ParseResult: 19 """ 20 Результат парсинга команды пользователя. 21 22 Содержит идентификатор действия и сопутствующие параметры: 23 текст задачи, ключевое слово даты, ID задачи и ключевое слово для поиска. 24 """ 25 26 action: str 27 task_text: str = "" 28 date_keyword: Optional[str] = None 29 task_id: Optional[int] = None 30 keyword: str = ""
Результат парсинга команды пользователя.
Содержит идентификатор действия и сопутствующие параметры: текст задачи, ключевое слово даты, ID задачи и ключевое слово для поиска.
33class CommandParser: 34 """ 35 Парсер голосовых команд для навыка управления задачами. 36 37 Анализирует входящий текст команды и оригинальное высказывание, 38 определяет намерение пользователя и извлекает параметры 39 (текст задачи, дату, ID задачи). 40 """ 41 42 @observe(name="Parse command") 43 def parse(self, command: str, original_utterance: str) -> Optional[ParseResult]: 44 """ 45 Анализирует команду пользователя и возвращает структурированный результат. 46 47 Определяет тип действия (ping, add_task, list_tasks, complete, 48 delete, move_one, move_all, help, what) на основе регулярных выражений. 49 50 Args: 51 command: Нормализованная команда (в нижнем регистре) 52 original_utterance: Оригинальное высказывание пользователя 53 54 Returns: 55 Optional[ParseResult]: Результат парсинга или None, если команда не распознана 56 """ 57 if original_utterance == "ping": 58 return ParseResult(action="ping") 59 60 if re.search(r"\b(добавь|добавить|создай|новая\s+задача)\b", command): 61 return self._parse_add(command, original_utterance) 62 63 if re.search(r"\b(какие\s+задачи|покажи\s+список|список\s+задач)\b", command) or re.search( 64 r"\b(дело|дела)\b", command 65 ): 66 return self._parse_list(command) 67 68 if re.search(r"\b(помощь|help)\b", command): 69 return ParseResult(action="help") 70 71 if command.startswith("что "): 72 return self._parse_what(command) 73 74 if re.search(r"\b(выполн(?:ен[аыо])?|сделал(?:а)?|готово|пометь|отметь)\b", command): 75 return self._parse_task_id_command(command, action="complete") 76 77 if re.search(r"\b(удали|удалить|убери|убрать)\b", command): 78 return self._parse_task_id_command(command, action="delete") 79 80 if re.search(r"\bперенеси\s+задачу|перенос\s+задачи\b", command): 81 return self._parse_move_one(command) 82 83 if re.search(r"\bперенеси|перенос\b", command): 84 return self._parse_move_all(command) 85 86 return None 87 88 def _parse_add(self, command: str, original_utterance: str) -> ParseResult: 89 """ 90 Парсит команду добавления задачи. 91 92 Определяет дату (сегодня/завтра/послезавтра) и текст задачи 93 из оригинального высказывания пользователя. 94 95 Args: 96 command: Нормализованная команда 97 original_utterance: Оригинальное высказывание 98 99 Returns: 100 ParseResult: Действие add_task с текстом задачи и датой, 101 либо no_task_text если текст задачи не обнаружен 102 """ 103 date_keyword = None 104 task_text = "" 105 106 date_patterns = [ 107 (r"\bна\s+завтра\b\s+(.+)", "завтра"), 108 (r"\bна\s+сегодня\b\s+(.+)", "сегодня"), 109 (r"\bна\s+послезавтра\b\s+(.+)", "послезавтра"), 110 ] 111 112 for pattern, date in date_patterns: 113 match = re.search(pattern, original_utterance) 114 if match: 115 date_keyword = date 116 task_text = match.group(1).strip() 117 break 118 119 if not task_text: 120 for trigger in ["добавь задачу", "добавить задачу", "создай задачу"]: 121 if trigger in original_utterance: 122 remaining = original_utterance.split(trigger, 1)[1].strip() 123 if remaining: 124 task_text = remaining 125 break 126 else: 127 task_text = command 128 129 task_text = re.sub(r"[.,!?]+$", "", task_text).strip() 130 131 if not task_text: 132 return ParseResult(action="no_task_text") 133 134 return ParseResult(action="add_task", task_text=task_text, date_keyword=date_keyword) 135 136 def _parse_list(self, command: str) -> ParseResult: 137 """ 138 Парсит команду просмотра списка задач. 139 140 Определяет дату, на которую пользователь хочет увидеть задачи. 141 142 Args: 143 command: Нормализованная команда 144 145 Returns: 146 ParseResult: Действие list_tasks с указанной датой 147 """ 148 date_keyword = "сегодня" 149 if re.search(r"\bна\s+завтра\b", command): 150 date_keyword = "завтра" 151 elif re.search(r"\bна\s+послезавтра\b", command): 152 date_keyword = "послезавтра" 153 return ParseResult(action="list_tasks", date_keyword=date_keyword) 154 155 def _parse_what(self, command: str) -> ParseResult: 156 """ 157 Парсит команду поиска задач по ключевому слову (например "что купить?"). 158 159 Извлекает ключевое слово (первое слово после "что") и дату 160 для фильтрации задач. 161 162 Args: 163 command: Нормализованная команда 164 165 Returns: 166 ParseResult: Действие what с ключевым словом и датой 167 """ 168 date_keyword = "сегодня" 169 if re.search(r"\bна\s+завтра\b", command): 170 date_keyword = "завтра" 171 elif re.search(r"\bна\s+послезавтра\b", command): 172 date_keyword = "послезавтра" 173 174 parts = command.split() 175 keyword = parts[1] if len(parts) >= 2 else "" 176 return ParseResult(action="what", keyword=keyword, date_keyword=date_keyword) 177 178 def _parse_task_id_command(self, command: str, action: str) -> ParseResult: 179 """ 180 Парсит команду, требующую ID задачи (выполнение/удаление). 181 182 Извлекает числовой ID задачи из команды. 183 184 Args: 185 command: Нормализованная команда 186 action: Тип действия (complete или delete) 187 188 Returns: 189 ParseResult: Действие с указанным типом и ID задачи, 190 либо no_task_id если ID не найден 191 """ 192 match = re.search(r"(\d+)", command) 193 if not match: 194 return ParseResult(action="no_task_id") 195 return ParseResult(action=action, task_id=int(match.group(1))) 196 197 def _parse_move_one(self, command: str) -> ParseResult: 198 """ 199 Парсит команду переноса одной задачи на другую дату. 200 201 Извлекает ID задачи и целевую дату (завтра/послезавтра). 202 203 Args: 204 command: Нормализованная команда 205 206 Returns: 207 ParseResult: Действие move_one с ID задачи и датой, 208 либо no_task_id если ID не найден 209 """ 210 date_keyword = "завтра" 211 if re.search(r"\bна\s+завтра\b", command): 212 date_keyword = "завтра" 213 elif re.search(r"\bна\s+послезавтра\b", command): 214 date_keyword = "послезавтра" 215 216 match = re.search(r"(\d+)", command) 217 if not match: 218 return ParseResult(action="no_task_id") 219 return ParseResult( 220 action="move_one", task_id=int(match.group(1)), date_keyword=date_keyword 221 ) 222 223 def _parse_move_all(self, command: str) -> ParseResult: 224 """ 225 Парсит команду переноса всех задач на другую дату. 226 227 Определяет целевую дату (завтра/послезавтра) для массового переноса. 228 229 Args: 230 command: Нормализованная команда 231 232 Returns: 233 ParseResult: Действие move_all с целевой датой 234 """ 235 date_keyword = "завтра" 236 if re.search(r"\bна\s+завтра\b", command): 237 date_keyword = "завтра" 238 elif re.search(r"\bна\s+послезавтра\b", command): 239 date_keyword = "послезавтра" 240 return ParseResult(action="move_all", date_keyword=date_keyword)
Парсер голосовых команд для навыка управления задачами.
Анализирует входящий текст команды и оригинальное высказывание, определяет намерение пользователя и извлекает параметры (текст задачи, дату, ID задачи).
42 @observe(name="Parse command") 43 def parse(self, command: str, original_utterance: str) -> Optional[ParseResult]: 44 """ 45 Анализирует команду пользователя и возвращает структурированный результат. 46 47 Определяет тип действия (ping, add_task, list_tasks, complete, 48 delete, move_one, move_all, help, what) на основе регулярных выражений. 49 50 Args: 51 command: Нормализованная команда (в нижнем регистре) 52 original_utterance: Оригинальное высказывание пользователя 53 54 Returns: 55 Optional[ParseResult]: Результат парсинга или None, если команда не распознана 56 """ 57 if original_utterance == "ping": 58 return ParseResult(action="ping") 59 60 if re.search(r"\b(добавь|добавить|создай|новая\s+задача)\b", command): 61 return self._parse_add(command, original_utterance) 62 63 if re.search(r"\b(какие\s+задачи|покажи\s+список|список\s+задач)\b", command) or re.search( 64 r"\b(дело|дела)\b", command 65 ): 66 return self._parse_list(command) 67 68 if re.search(r"\b(помощь|help)\b", command): 69 return ParseResult(action="help") 70 71 if command.startswith("что "): 72 return self._parse_what(command) 73 74 if re.search(r"\b(выполн(?:ен[аыо])?|сделал(?:а)?|готово|пометь|отметь)\b", command): 75 return self._parse_task_id_command(command, action="complete") 76 77 if re.search(r"\b(удали|удалить|убери|убрать)\b", command): 78 return self._parse_task_id_command(command, action="delete") 79 80 if re.search(r"\bперенеси\s+задачу|перенос\s+задачи\b", command): 81 return self._parse_move_one(command) 82 83 if re.search(r"\bперенеси|перенос\b", command): 84 return self._parse_move_all(command) 85 86 return None
Анализирует команду пользователя и возвращает структурированный результат.
Определяет тип действия (ping, add_task, list_tasks, complete, delete, move_one, move_all, help, what) на основе регулярных выражений.
Arguments:
- command: Нормализованная команда (в нижнем регистре)
- original_utterance: Оригинальное высказывание пользователя
Returns:
Optional[ParseResult]: Результат парсинга или None, если команда не распознана