Skip to content

Commit 8a54af1

Browse files
committed
heuristic patterns update
1 parent 1bcf046 commit 8a54af1

File tree

2 files changed

+75
-71
lines changed

2 files changed

+75
-71
lines changed

app/questions_generator/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
`docker build -t vkr-generator .`
33

44
## Запуск
5-
`python run_docker.py <путь к файлу с текстом ВКР>`
5+
`python run_docker.py <путь к файлу с текстом ВКР>`
Lines changed: 74 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,36 @@
11
import re
22
from typing import List, Dict
3+
34
from nltk.tokenize import sent_tokenize, word_tokenize
45
from nltk.corpus import stopwords
56
from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
67

78

89
class VkrQuestionGenerator:
9-
"""
10-
Генератор вопросов по тексту ВКР.
11-
Основан на гибридном подходе: NLTK + rut5-base-multitask.
12-
"""
13-
def __init__(self, vkr_text: str, model_path: str):
10+
"""Гибридный генератор вопросов по ВКР: NLTK + rut5-base-multitask."""
11+
12+
SECTION_PATTERNS: Dict[str, str] = {
13+
"Введение": r"Введение.*?(?=\n[A-ZА-Я][^\n]*\n)",
14+
"Обзор предметной области": r"Обзор предметной области.*?(?=\n[A-ZА-Я][^\n]*\n)",
15+
"Постановка задачи": r"Постановка задачи.*?(?=\n[A-ZА-Я][^\n]*\n)",
16+
"Метод решения": r"Метод решения.*?(?=\n[A-ZА-Я][^\n]*\n)",
17+
"Исследования": r"Исследования.*?(?=\n[A-ZА-Я][^\n]*\n)",
18+
"Заключение": r"Заключение.*?(?=\n[A-ZА-Я][^\n]*\n)",
19+
"Приложения": r"Приложения.*?(?=\n[A-ZА-Я][^\n]*\n)",
20+
}
21+
22+
def __init__(self, vkr_text: str, model_path: str = "ai-forever/rut5-base-multitask"):
1423
self.vkr_text = vkr_text
1524
self.sentences = sent_tokenize(vkr_text)
1625
self.stopwords = set(stopwords.words("russian"))
1726

18-
# ---- Модель rut5 ----
27+
# Модель rut5-base-multitask для языкового оформления вопросов
1928
self.tokenizer = AutoTokenizer.from_pretrained(model_path, use_fast=False)
2029
self.model = AutoModelForSeq2SeqLM.from_pretrained(model_path)
2130

22-
# ---------------------------------------------------------
23-
# --- 1. ЭВРИСТИКА: Извлечение ключевых частей ВКР ---
24-
# ---------------------------------------------------------
25-
2631
def extract_section(self, title: str) -> str:
27-
"""
28-
Универсальный метод извлечения раздела по заголовку.
29-
"""
30-
pattern = rf"{title}.*?(?=\n[A-ZА-Я][^\n]*\n)"
32+
"""Извлекает раздел по шаблону заголовка."""
33+
pattern = self.SECTION_PATTERNS.get(title, rf"{title}.*?(?=\n[A-ZА-Я][^\n]*\n)")
3134
m = re.search(pattern, self.vkr_text, re.DOTALL | re.IGNORECASE)
3235
return m.group(0) if m else ""
3336

@@ -37,77 +40,86 @@ def extract_intro(self) -> str:
3740
def extract_conclusion(self) -> str:
3841
return self.extract_section("Заключение")
3942

40-
# ---------------------------------------------------------
41-
# --- 2. ЭВРИСТИКА: Поиск ключевых концепций ---
42-
# ---------------------------------------------------------
43-
4443
def extract_keywords(self, text: str) -> List[str]:
44+
"""Извлекает ключевые слова из текста."""
4545
tokens = word_tokenize(text.lower())
4646
return [
4747
t for t in tokens
4848
if t.isalnum() and t not in self.stopwords and len(t) > 4
4949
]
5050

51-
# ---------------------------------------------------------
52-
# --- 3. Генерация вопросов через rut5 (режим ask) ---
53-
# ---------------------------------------------------------
54-
5551
def llm_generate_question(self, text_fragment: str) -> str:
56-
"""
57-
Генерация вопроса по фрагменту текста через rut5 ask
58-
"""
52+
"""Генерирует формулировку вопроса через rut5 ask."""
5953
prompt = f"ask: {text_fragment}"
6054
enc = self.tokenizer(prompt, return_tensors="pt", truncation=True)
6155
out = self.model.generate(
6256
**enc,
6357
max_length=64,
6458
num_beams=5,
65-
early_stopping=True
59+
early_stopping=True,
6660
)
6761
return self.tokenizer.decode(out[0], skip_special_tokens=True)
6862

69-
# ---------------------------------------------------------
70-
# --- 4. ЭВРИСТИЧЕСКИЕ ШАБЛОНЫ (из документа) ---
71-
# ---------------------------------------------------------
72-
7363
def heuristic_questions(self) -> List[str]:
74-
"""
75-
Генерация вопросов по эвристикам из загруженных PDF.
76-
"""
64+
"""Эвристики, завязанные на структуру ВКР."""
7765
intro = self.extract_intro()
66+
overview = self.extract_section("Обзор предметной области")
67+
objectives = self.extract_section("Постановка задачи")
68+
method = self.extract_section("Метод решения")
69+
research = self.extract_section("Исследования")
7870
conc = self.extract_conclusion()
79-
keywords = self.extract_keywords(self.vkr_text)
71+
apps = self.extract_section("Приложения")
8072

81-
q = []
73+
q: List[str] = []
8274

83-
# --- По связям между разделами ---
75+
# Введение ↔ Заключение
8476
if intro and conc:
85-
q.append("Как сформулированные во введении задачи связаны с выводами работы?")
86-
87-
# --- По выводам ---
88-
if conc:
89-
q.append("На основании каких данных был сделан ключевой вывод в заключении?")
90-
91-
# --- Общие вопросы (из документа) ---
77+
q.append(
78+
"Как цель и задачи, сформулированные во введении, отражены в итоговых выводах заключения?"
79+
)
80+
81+
# Обзор предметной области
82+
if overview:
83+
q.append(
84+
"Какие термины и подходы из обзора предметной области легли в основу формальной постановки задачи?"
85+
)
86+
87+
# Постановка задачи
88+
if objectives:
89+
q.append(
90+
"В каких требованиях к решению, указанных в постановке задачи, находят отражение цели работы?"
91+
)
92+
93+
# Метод решения
94+
if method:
95+
q.append(
96+
"Как архитектура и алгоритмы, описанные в разделе «Метод решения», обеспечивают достижение поставленных требований?"
97+
)
98+
99+
# Исследования
100+
if research:
101+
q.append(
102+
"Какие количественные или качественные свойства решения подтверждены в разделе «Исследования» и как они связаны с задачами введения?"
103+
)
104+
105+
# Приложения
106+
if apps:
107+
q.append(
108+
"Какие дополнительные материалы из приложений необходимы для проверки воспроизводимости результатов?"
109+
)
110+
111+
# Обязательные общие вопросы
92112
q.extend([
93-
"Есть ли опенсорс аналоги упомянутых решений?",
94-
"В чем практическая значимость представленного метода?",
95-
"Какие ограничения имеет разработанный подход?",
96-
"Для каких дополнительных задач можно применить полученные результаты?",
113+
"Как практическая значимость работы следует из задач и результатов исследования?",
114+
"Какие ограничения метода решения указаны в тексте и как они влияют на достижение цели?",
97115
])
98116

99117
return q
100118

101-
# ---------------------------------------------------------
102-
# --- 5. Гибридная генерация: LLM + эвристики ---
103-
# ---------------------------------------------------------
104-
105-
def generate_llm_questions(self, count=5) -> List[str]:
106-
"""
107-
Генерация N вопросов через rut5 по ключевым фрагментам документа.
108-
"""
109-
q = []
110-
fragments = self.sentences[:40] # первые ~40 предложений для контекста
119+
def generate_llm_questions(self, count: int = 5) -> List[str]:
120+
"""Генерирует N вопросов через rut5 по ключевым фрагментам документа."""
121+
q: List[str] = []
122+
fragments = self.sentences[:40]
111123

112124
step = max(1, len(fragments) // count)
113125

@@ -117,26 +129,18 @@ def generate_llm_questions(self, count=5) -> List[str]:
117129
llm_q = self.llm_generate_question(frag)
118130
if len(llm_q) > 10:
119131
q.append(llm_q)
120-
except:
132+
except Exception: # noqa: BLE001
121133
continue
122134

123135
if len(q) >= count:
124136
break
125137

126138
return q
127139

128-
# ---------------------------------------------------------
129-
# --- 6. Главный метод ---
130-
# ---------------------------------------------------------
131-
132140
def generate_all(self) -> List[str]:
133-
"""
134-
Генерирует полный набор вопросов:
135-
- эвристические
136-
- модельные (LLM)
137-
"""
138-
result = []
141+
"""Генерирует полный набор вопросов: эвристики + LLM."""
142+
result: List[str] = []
139143
result.extend(self.heuristic_questions())
140-
result.extend(["Начало rut5-base-multitask вопросов"])
144+
result.extend(["--- rut5-base-multitask вопросы ---"])
141145
result.extend(self.generate_llm_questions(count=10))
142-
return list(dict.fromkeys(result)) # убрать дубли
146+
return list(dict.fromkeys(result))

0 commit comments

Comments
 (0)