Spaces:
Paused
Paused
PanarinI commited on
Commit ·
03793f6
1
Parent(s): 28d6562
...
Browse files- README.md +1 -1
- TASKS.md +2 -2
- app/__init__.py +0 -0
- app/instructions.py +62 -0
- app/knowledge_base/LP_shots.py +243 -0
- app/knowledge_base/Methods.md +1 -0
- app/knowledge_base/inspire.md +2 -0
- app/knowledge_base/lesson_frameworks.md +1 -0
- app/main.py +0 -245
- app/prompt_builder/__init__.py +0 -0
- app/prompt_builder/methodology.py +18 -0
- app/prompt_builder/prompt.py +39 -0
- app/test.py +0 -245
- app/test_gradio.py +5 -1
- app/test_gradio_test.py +300 -0
- docs/architecture.md +11 -0
README.md
CHANGED
|
@@ -5,7 +5,7 @@ colorFrom: gray
|
|
| 5 |
colorTo: pink
|
| 6 |
sdk: gradio
|
| 7 |
sdk_version: 5.25.2
|
| 8 |
-
app_file: app/
|
| 9 |
pinned: false
|
| 10 |
---
|
| 11 |
|
|
|
|
| 5 |
colorTo: pink
|
| 6 |
sdk: gradio
|
| 7 |
sdk_version: 5.25.2
|
| 8 |
+
app_file: app/test_gradio_test.py
|
| 9 |
pinned: false
|
| 10 |
---
|
| 11 |
|
TASKS.md
CHANGED
|
@@ -19,8 +19,8 @@
|
|
| 19 |
[ ] интеграция настроек
|
| 20 |
[ ] мотивационный блок в приложении - напр. цитата из педагогики, мотивационная, каждый раз новая
|
| 21 |
|
| 22 |
-
[ ]
|
| 23 |
-
|
| 24 |
## in progress
|
| 25 |
|
| 26 |
[ ] проверка различных основных учебников
|
|
|
|
| 19 |
[ ] интеграция настроек
|
| 20 |
[ ] мотивационный блок в приложении - напр. цитата из педагогики, мотивационная, каждый раз новая
|
| 21 |
|
| 22 |
+
[ ] можно превратить в интерактивный обучающий курс. Где части плана объясняются, поясняется методика, даются ссылки на видео с уроками и тд. Вдохновение: https://maxenglishcorner.com/lessons/teaching-ideas/teaching-ideas-7-lesson-planning-ppp-model/
|
| 23 |
+
[ ] эту идею описать в портфолио как продолжение MVP
|
| 24 |
## in progress
|
| 25 |
|
| 26 |
[ ] проверка различных основных учебников
|
app/__init__.py
ADDED
|
File without changes
|
app/instructions.py
CHANGED
|
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
def get_instructions(
|
| 3 |
+
methodology: str,
|
| 4 |
+
cefr: str,
|
| 5 |
+
topic: str,
|
| 6 |
+
goal: str,
|
| 7 |
+
age: str,
|
| 8 |
+
adults: bool,
|
| 9 |
+
level_match: int,
|
| 10 |
+
duration: int,
|
| 11 |
+
inventory: str,
|
| 12 |
+
hw_required: bool,
|
| 13 |
+
web_search: bool,
|
| 14 |
+
repetition: bool,
|
| 15 |
+
application: bool,
|
| 16 |
+
analysis: bool,
|
| 17 |
+
creativity: bool
|
| 18 |
+
) -> str:
|
| 19 |
+
"""Генерирует промпт для OpenAI на основе параметров урока."""
|
| 20 |
+
|
| 21 |
+
# Пример форматирования (можно менять как угодно)
|
| 22 |
+
prompt = f"""
|
| 23 |
+
Ты — преподаватель английского, работающий по методике **{methodology}**.
|
| 24 |
+
|
| 25 |
+
**Параметры урока:**
|
| 26 |
+
**Учебник**: {textbook}",
|
| 27 |
+
**CEFR-уровень**: {'определи по загруженной странице' if cefr.strip() == '' else cefr}",
|
| 28 |
+
**Тема**: {topic or 'определи по загруженной странице'}",
|
| 29 |
+
**Цель занятия**: {goal or 'определи по загруженной странице'}",
|
| 30 |
+
**Формат**: {format_type} ({num_students} {'ребёнок' if num_students == 1 else 'детей'})",
|
| 31 |
+
**Возраст**: {'взрослые' if adults else age}",
|
| 32 |
+
**Соответствие уровня**: {['below', 'on-level', 'above', 'mixed'][level_match]}",
|
| 33 |
+
**Длительность**: {duration} минут",
|
| 34 |
+
**Инвентарь**: {inventory if inventory else 'стандартный'}",
|
| 35 |
+
**Методика**: {methodology}",
|
| 36 |
+
**Дополнительная информация**: {extra_info}"
|
| 37 |
+
|
| 38 |
+
**Домашнее задание**: {'да' if hw_required else 'нет'}"
|
| 39 |
+
|
| 40 |
+
Сгенерируй план урока, учитывая эти параметры.
|
| 41 |
+
"""
|
| 42 |
+
return prompt
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
INSTRUCTIONS_1=f"""
|
| 46 |
+
Ты — AI-ассистент для учителей английского, экспертно владеющий методологией {methodology}].
|
| 47 |
+
Сначала спланируй логику урока по этапам, затем сгенерировать конспект».
|
| 48 |
+
|
| 49 |
+
Используй методику PPP (Presentation-Practice-Production) для построения урока для школьников. Урок должен состоять из трех этапов:
|
| 50 |
+
1. **Презентация**: Представь новую тему, объясни грамматические или лексические аспекты с примерами.
|
| 51 |
+
2. **Практика**: Подготовь упражнения для закрепления знаний, например, заполнение пропусков, тесты с вариантами ответа.
|
| 52 |
+
3. **Продукция**: Предложи задания, в которых ученики могут применить новые знания в реальных ситуациях, например, ролевые игры или диалоги.
|
| 53 |
+
|
| 54 |
+
Учитывай:
|
| 55 |
+
- Уровень учащихся (например, A1, B1, C1) и соответствующие задания.
|
| 56 |
+
- Возрастную категорию учащихся (от 10 до 18 лет).
|
| 57 |
+
- Формат занятия: индивидуальное или групповое.
|
| 58 |
+
- Длительность урока (например, 60 минут) и соответствующее количество заданий.
|
| 59 |
+
- Домашнее задание (если требуется).
|
| 60 |
+
- Дополнительные материалы (если нужно, используй интернет-ресурсы).
|
| 61 |
+
|
| 62 |
+
"""
|
app/knowledge_base/LP_shots.py
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
LP_PPP_1 = f"""
|
| 2 |
+
CLASS - - - Intermediate level, 4 students
|
| 3 |
+
THEME FOR CLASS - - - Pollution
|
| 4 |
+
TARGET LANGUAGE (TL) - - - 1st and 2nd conditonals
|
| 5 |
+
OBJECTIVE FOR CLASS - - - By the end of the class the students should be able to know the difference between
|
| 6 |
+
first and second conditionals and be able to use both forms while speaking about pollution.
|
| 7 |
+
MATERIALS - - -
|
| 8 |
+
● 3 pictures to be used in the CONTEXT stage (from Pixabay)
|
| 9 |
+
●Handout 1 - Reading: Article about air pollution plus corresponding reading exercises
|
| 10 |
+
citation: (This article was written by Katy Daigle and has been adapted from
|
| 11 |
+
https://www.sciencenewsforstudents.org/article/air-pollution-shortening-lives-worldwide)
|
| 12 |
+
●Handout 2 - Worksheet for PRACTICE activities with summary reference chart at top of page
|
| 13 |
+
|
| 14 |
+
---
|
| 15 |
+
1. CONTEXT or WARMER/THEME
|
| 16 |
+
|
| 17 |
+
OBJECTIVE(S)
|
| 18 |
+
●To get the students speaking and communicating a little in English.
|
| 19 |
+
●To orientate students to begin thinking of different kinds of pollution. There are three
|
| 20 |
+
pictures (one each of air, land and water pollution).
|
| 21 |
+
|
| 22 |
+
MATERIALS
|
| 23 |
+
3 pictures from Pixabay
|
| 24 |
+
(different kinds of pollution: air, land & water)
|
| 25 |
+
Students are placed into pairs (more intimate groups this way, and more students will be speaking) and given 3
|
| 26 |
+
pictures. Together they have to
|
| 27 |
+
1) Describe each picture individually
|
| 28 |
+
2) Decide what all three pictures have in common
|
| 29 |
+
Teacher monitors and helps out a little if there are problems with vocabulary
|
| 30 |
+
After the pair-work the teacher asks the students to say what they came up with
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
2. INTRODUCTION
|
| 34 |
+
|
| 35 |
+
OBJECTIVE(S)
|
| 36 |
+
● To pre-teach some vocabulary found in the reading (ex: lifespan)
|
| 37 |
+
●To orientate students to some of the content (concepts) in the article
|
| 38 |
+
|
| 39 |
+
MATERIALS
|
| 40 |
+
none, other than the whiteboard
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
●Elicit the meaning of ‘lifespan’ from the class
|
| 44 |
+
●Ask the students to answer the question written on the board:
|
| 45 |
+
What are some common causes for shorter life spans in some countries? (for example diet, pollution and cancer)
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
3. INPUT TASK 1: READING FOR GIST
|
| 49 |
+
|
| 50 |
+
OBJECTIVE(S)
|
| 51 |
+
● To have the students get a general idea of what the text is about
|
| 52 |
+
without getting bogged down with unfamiliar vocabulary or some unclear parts or ideas
|
| 53 |
+
MATERIALS Handout 1 with article and two sets of questions. One set is for
|
| 54 |
+
this task where the students have to decide which of the three given titles is most suitable
|
| 55 |
+
●The students are given a handout which contains both the article to read and two sets of questions. The students are
|
| 56 |
+
asked to fold their papers so they could focus completely on that first task (and not reading ahead to see what is next on
|
| 57 |
+
the handout).
|
| 58 |
+
●Students understand the task before they begin reading and are given only one minute to decide which title would be
|
| 59 |
+
best for the article.
|
| 60 |
+
●After one minute students are asked not only for their answer, but also why the other options were not correct or
|
| 61 |
+
suitable.
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
4. INPUT TASK 2: READING FOR DETAILS
|
| 65 |
+
|
| 66 |
+
OBJECTIVE(S) ●
|
| 67 |
+
To have the students get a better idea of what the text is about
|
| 68 |
+
by quickly scanning it for specific information
|
| 69 |
+
MATERIALS Handout 1 with article and two sets of questions. This task is the
|
| 70 |
+
second set of 3 questions found at the bottom of the article
|
| 71 |
+
|
| 72 |
+
● Students are told to look at Section 2 on their handout
|
| 73 |
+
●We go over the questions so the students understand what they have to read for
|
| 74 |
+
●Students work out the answers individually but can check with other students
|
| 75 |
+
●The answers are elicited and explained.
|
| 76 |
+
|
| 77 |
+
5. PRESENTATION
|
| 78 |
+
|
| 79 |
+
OBJECTIVE(S)
|
| 80 |
+
● To familiarize the students with how the first and second
|
| 81 |
+
conditionals are formed and to know what situations are appropriate to use which one
|
| 82 |
+
MATERIALS
|
| 83 |
+
The form, concepts and examples are written on the board
|
| 84 |
+
|
| 85 |
+
●Ask the students what they know about the grammatical structure of conditionals.
|
| 86 |
+
●Focus on what they know about the first and second conditionals and write the information on the board in a clear and
|
| 87 |
+
organized way (which resembles the chart in Handout 2.
|
| 88 |
+
●Review the key points by asking different students comprehension check questions to see what they understand and
|
| 89 |
+
how well they are retaining the key information.
|
| 90 |
+
|
| 91 |
+
6. PRACTICE 1 CORRECT SENTENCES
|
| 92 |
+
|
| 93 |
+
OBJECTIVE(S) ● To test for level of understanding as students apply some of the
|
| 94 |
+
recently reviewed key concepts by correcting 4 sentences with errors emphasizing those
|
| 95 |
+
points.
|
| 96 |
+
MATERIALS Handout 2 with a chart for reference of key idea (#1) and the task
|
| 97 |
+
of correcting the four sentences (#2)
|
| 98 |
+
|
| 99 |
+
●Point out the 4 sentences in Section 2 and tell the students to correct them.
|
| 100 |
+
●Monitor the students while they are working to see what they find difficult.
|
| 101 |
+
●Elicit the answers and their explanations / Also deal with their doubts as we go over their answers.
|
| 102 |
+
●Review the key points before going on to Practice 2
|
| 103 |
+
|
| 104 |
+
7a. PRACTICE 2a WRITE & USE QUESTIONS 1-ST CONDITIONAL
|
| 105 |
+
|
| 106 |
+
OBJECTIVE(S)
|
| 107 |
+
● To have the students come up with their own sentences using the
|
| 108 |
+
first conditional structure for an appropriate situation, matching the form with the function
|
| 109 |
+
● To have the students become familiar with incorporating the TL into their speaking
|
| 110 |
+
MATERIALS
|
| 111 |
+
Handout 2 Section #3
|
| 112 |
+
●Focus on only the first conditional as the TL (target language) and elicit the key points to orientate the students.
|
| 113 |
+
●Students look at Section #3 on Handout 2 and teacher elicits different possible questions they could ask in that form.
|
| 114 |
+
●Students individually write first conditional questions to ask a partner in the class. Teacher monitors and helps.
|
| 115 |
+
●Model the coming speaking application by having one student ask another his/her question and the partner replies.
|
| 116 |
+
Encourage them to explore a little further by changing it into a ‘mini one-minute conversation’ through asking followup questions and volunteering comments or details.
|
| 117 |
+
●Place students in pairs and have them take turns with their questions which lead them to their mini conversations.
|
| 118 |
+
●Give some feedback when they are finished, looking both at the target language used and their development of the
|
| 119 |
+
mini conversations.
|
| 120 |
+
|
| 121 |
+
7b. PRACTICE 2b WRITE & USE QUESTIONS 2ND CONDITIONAL
|
| 122 |
+
|
| 123 |
+
OBJECTIVE(S)
|
| 124 |
+
● To have the students come up with their own sentences using the
|
| 125 |
+
second conditional structure for an appropriate situation, matching the form with the
|
| 126 |
+
function
|
| 127 |
+
● To have the students become familiar with incorporating the TL into their speaking
|
| 128 |
+
MATERIALS Handout 2 Section #4
|
| 129 |
+
●Focus on only the second conditional and elicit the key points to orientate the students.
|
| 130 |
+
●Basically the same procedure as the previous exercise, but now for Section 4 and focusing on the second conditional.
|
| 131 |
+
|
| 132 |
+
7c. PRACTICE 3 PREPARE FOR DISCUSSION
|
| 133 |
+
|
| 134 |
+
OBJECTIVE(S)
|
| 135 |
+
● To help students prepare for coming discussion
|
| 136 |
+
MATERIALS Handout 2 Section #5
|
| 137 |
+
Some prompts in Section #6 they can use in their conversation
|
| 138 |
+
●Tell the students that soon they will have a discussion about the theme of air pollution. They are to talk about how
|
| 139 |
+
bad it is and what can be done about it. Elicit some possible sentences/questions for the two conditionals as examples.
|
| 140 |
+
●Students each write one first conditional sentence or question and another one in the second conditional that they
|
| 141 |
+
might use in that discussion.
|
| 142 |
+
●Teacher monitors and helps.
|
| 143 |
+
●Because there are only four students, place them all in the same group and tell them they are enjoying a cup of coffee
|
| 144 |
+
together. They are to imagine that the conversation has turned to air pollution and without referring to their notes, they
|
| 145 |
+
talk about how bad the situation is and speculate what could be done about it, using occasionally, if it’s appropriate, the
|
| 146 |
+
first or second conditional. The main focus is to have a conversation and that they should explore the theme.
|
| 147 |
+
●Go over the expressions in Section #6 that they can use while speaking and write them on the board as prompts
|
| 148 |
+
●Teacher listens and steps in, if necessary, to guide and encourage the students to explore the theme in their
|
| 149 |
+
conversation.
|
| 150 |
+
●Give some feedback when they are finished, looking both at the target language used and their development of the
|
| 151 |
+
discussion.
|
| 152 |
+
|
| 153 |
+
|
| 154 |
+
8. PRODUCTION
|
| 155 |
+
|
| 156 |
+
OBJECTIVE(S)
|
| 157 |
+
● To use the two conditionals in a Production speaking activity
|
| 158 |
+
MATERIALS
|
| 159 |
+
none
|
| 160 |
+
|
| 161 |
+
●Tell the students they are going to have one more conversation using the first and second conditional. This time it
|
| 162 |
+
will be about land and water pollution or pollution in general.
|
| 163 |
+
●Tell them they can think for a couple of minutes about what they may say or ask about that theme. Remind them of
|
| 164 |
+
some of the things that came up in the previous speaking activity. Also tell them that the teacher will not be
|
| 165 |
+
participating; that it will be just their conversation.
|
| 166 |
+
●Students speak for about 5 minutes.
|
| 167 |
+
●Give some feedback when they are finished, looking both at the target language used and their development of the
|
| 168 |
+
discussion.
|
| 169 |
+
"""
|
| 170 |
+
|
| 171 |
+
LP_PPP_2 = f"""
|
| 172 |
+
1 OBJECTIVES
|
| 173 |
+
By the end of the class the students should be able to use the past continuous while engaging in a conversation about what they did last weekend
|
| 174 |
+
|
| 175 |
+
2 CONTEXT
|
| 176 |
+
●Show some pictures of people doing fun or relaxing things and students describe them (learning the relevant vocabulary such as the action verbs – reading a book, playing tennis, etc)
|
| 177 |
+
●Then the students ask the teacher questions to find out what s/he did last weekend or typically does on the weekend. (It’s fine if the students don’t use the past continuous and they shouldn’t be expected to if they haven’t been taught it yet. In fact, the past continuous isn’t part of the lesson plan until later in the Presentation stage).
|
| 178 |
+
|
| 179 |
+
3 INPUT TASK 1: READING FOR GIST
|
| 180 |
+
Best title
|
| 181 |
+
●Write on the board 3 titles and tell the students they have to decide which one (A, B or C) is the most suitable for the reading.
|
| 182 |
+
●Give the students the reading and 60 seconds to complete the task.
|
| 183 |
+
●Ask the students to say their choice(s) and say why. When all the choices have been gone over, tell them the correct answer.
|
| 184 |
+
|
| 185 |
+
4 INPUT TASK 2: READING FOR SPECIFIC INFORMATION
|
| 186 |
+
●Give the students a handout with six sentences. All six sentences reflect what happened in the text they just read, but each sentence has one mistake. Students are asked to look at the example of the first sentence and make similar corrections for the remaining five. NOTE: These mistakes reflect understanding the contents of the text, NOT any grammar errors. Ex: Julie went to the market on Saturday. Friday
|
| 187 |
+
●Students complete task individually, compare answers with another student, then go over their answers as a class.
|
| 188 |
+
© maxenglishcorner.com 2020 PPP lesson planning example 2
|
| 189 |
+
|
| 190 |
+
5 PRESENTATION 1
|
| 191 |
+
PAST CONTINUOUS: Form & Ongoing Action in Past
|
| 192 |
+
●Write on the board two sentences found in the first paragraph of the text. Ex: There was a lot of traffic. Lots of people were going to the beach.
|
| 193 |
+
●Elicit the names of the two verb forms and write them above the examples.
|
| 194 |
+
●Elicit the structure of each. (regular verb in past simple form = verb + ED) (past continuous verb has 2 parts: be auxiliary in past + verb in ING form) ●Elicit how to create the negative forms and write those sentences on the board ●Elicit how to create a WH question (and answer) ●Elicit how to create a YES/NO question (and positive & negative answer) ●Elicit when to use the past continuous. (Focus on Concepts 1 & 2 but accept others) These concepts are explained in Part D of The PPP Model Explained and refer to particular situations that would be appropriate for using the past continuous. You want the students to understand that they can use the past continuous form when describing a general situation happening in the past (ex: It was raining yesterday), or when a particular action lasted for a period of time and you want to bring attention to that (ex: It was raining all night).
|
| 195 |
+
●Before continuing on to the Practice tasks, ask different students comprehension check questions, testing them on what they understand of what has been presented so far.
|
| 196 |
+
6 PRACTICE set 1
|
| 197 |
+
PRACTICE 1-1
|
| 198 |
+
●Give students worksheet. Explain exercise 1-1: There are 5 gap-fill sentences to complete, writing the past continuous form with the provided prompts. Ex: She __________ football yesterday. (not play) (She wasn’t playing football yesterday.)
|
| 199 |
+
●Students do it individually, check in pairs, then go over answers with class
|
| 200 |
+
PRACTICE 1-2
|
| 201 |
+
●Exercise 2 on worksheet. Students invent answers to respond to WH and Yes/No questions. Ex: What were they doing? __________ (They were sleeping. – for example) Was he studying? (+) __________ OR (-) __________ (Yes, he was. OR No, he wasn’t.) ●Model one or two examples first.
|
| 202 |
+
●As above, students do it individually, check in pairs, then the answers are gone over
|
| 203 |
+
PRACTICE 1-3
|
| 204 |
+
Have the students ask you past continuous questions about your last weekend. Encourage both WH & Yes/No questions. Allow other questions too, like past simple and present simple, and if they make mistakes, guide them by eliciting the right form of question to ask or the key elements which would help them decide which form to use.
|
| 205 |
+
7 PRESENTATION 2
|
| 206 |
+
PAST CONTINUOUS: Action happening at a specific time in the past
|
| 207 |
+
●Write on the board the following question and answer: What were you doing at ten o’clock last night? I was watching TV.
|
| 208 |
+
●Elicit if the action was for a duration (ex: for 3 hours) (No), a description in general (ex: the sun was shining) (No) or for an action happening (in progress) at a specific time in the past (Yes!)
|
| 209 |
+
© maxenglishcorner.com 2020 PPP lesson planning example 2
|
| 210 |
+
●Point out that this is a different function (situation to use the verb tense). Ask students if there are any changes to the form in the question or answer. (No)
|
| 211 |
+
●Draw a time line (see Concept #5) As in Presentation 1, the past continuous can be used in different situations. In this case it is for a continuous action happening at a certain point in time in the past.
|
| 212 |
+
●Elicit or provide more WH questions: - - What were you watching on TV (at that time)? - - Who were you watching TV with (at that time)? - - What were you doing at 11:00 last night?
|
| 213 |
+
●And elicit some Yes/No questions:
|
| 214 |
+
- - Were you watching TV at 10:30? (Yes, I was. // No, I wasn’t.) - - Were you eating at 6:15?
|
| 215 |
+
8 PRACTICE set 2
|
| 216 |
+
PRACTICE 2-1
|
| 217 |
+
●Students write questions to ask partner about last night (with specific times). If students seem to be able to handle it, ask them to write two WH questions and two Yes/No questions. If not, do 3 WH questions only. ●Teacher monitors and helps/guides when necessary.
|
| 218 |
+
PRACTICE 2-2
|
| 219 |
+
●Students are placed into pairs. One student is to ask a WH question, and the other answers it. Model first with two students so they are clear on the task instructions. Then students carry out the task and the teacher monitors. ●In the same pairs, students change roles.
|
| 220 |
+
PRACTICE 2-3
|
| 221 |
+
●If only WH questions were done previously, then repeat tasks 2-1 and 2-2 but for Yes/No questions this time.
|
| 222 |
+
9 PRESENTATION 3
|
| 223 |
+
Focus on enriching the students’ approach to speaking
|
| 224 |
+
●Teacher goes into ‘hot seat’ role where students have to ask WH and Yes/No questions about what s/he was doing yesterday. ●Teacher points out that simply asking a list of unrelated questions soon exhausts the activity. The students can enrich their speaking by 1) asking follow-up questions – not all of which have to be in the past continuous form (ex: Do you do that every night?) 2) making comments – (ex: That’s interesting. Me too. I was doing that too.) 3) volunteering information about themselves – I don’t like to watch TV. I prefer to do other things in my free time…..) ●Teacher chooses one student to be in the hot seat and the class asks that person about her/his day yesterday. The teacher encourages follow-up questions, comments from the class, as well as volunteering information about themselves.
|
| 225 |
+
© maxenglishcorner.com 2020 PPP lesson planning example 2 10 PRACTICE set 3
|
| 226 |
+
PRACTICE 3-1
|
| 227 |
+
●Students are divided into groups of 3 or 4, one person in each group is in the hot seat, and they ask and speak about yesterday. (Some questions and sentences are in the past continuous, but many aren’t.) Teacher monitors. ●After a few minutes the teacher tells the students to select a new person for the hot seat. Teacher monitors.
|
| 228 |
+
After a few more minutes teacher stops activity and addresses class, offering a little feedback (such as encouragement, advice and eliciting corrections to some mistakes).
|
| 229 |
+
PRACTICE 3-2 Dress Rehearsal for Production Speaking Activity
|
| 230 |
+
●Students stay in the same groups. Rather than focusing on just one person who is in a hot seat role, tell them to imagine they are having a coffee together and a short conversation about what they did yesterday. Encourage the people to all participate, volunteer information, make comments and ask questions, including follow-up questions.
|
| 231 |
+
●Teacher monitors and prompts them as the students attempt to have a conversation for five minutes.
|
| 232 |
+
●At the end of the activity, teacher gives some feedback such as tips, recognition of what they were doing well, some corrections on mistakes made when using the past simple or continuous, and more encouragement for everyone to participate in the conversation.
|
| 233 |
+
11 PRODUCTION
|
| 234 |
+
●The teacher changes the groups so there are new members in each group.
|
| 235 |
+
●The students are told to do the same thing as before, only now the theme of their conversation is to talk about last weekend. They can find out how their weekends were similar or different, and if the weekend was unusual or like every other weekend.
|
| 236 |
+
●This time the teacher does not intervene and s/he tells them so. They will have up to 5 minutes to speak about the theme and the teacher will not be participating or helping in any way. It is up to them to maintain the conversation.
|
| 237 |
+
●The students engage in their conversations the best they can.
|
| 238 |
+
12 FEEDBACK
|
| 239 |
+
●As with the dress rehearsal, the teacher gives feedback in the form of corrections, tips and reminders, and pointing out what the students did well.
|
| 240 |
+
HOMEWORK
|
| 241 |
+
●The students have to write 150 words about what they did last weekend, including at least one example of the past continuous for Concept 1* and at least one example of Concept 5*.
|
| 242 |
+
*NOTE: ‘Concept 1’ and ‘Concept 5’ are not the official names or numbers of these referenced situations. They were only used for the purposes of this article and can be seen as they were presented in The PPP Model Explained, Section D. They could very easily be called Situation 1 and Situation 2 or any other title that the teacher may prefer to use.
|
| 243 |
+
"""
|
app/knowledge_base/Methods.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
Метод PPP даёт прочный каркас, а гибкость в упражнениях позволяет адаптировать урок под возраст и уровень учеников.
|
app/knowledge_base/inspire.md
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
The theories behind it, the materials and activities that become part of it, the suggestions and the models one can follow, all have to deal with the inevitable:
|
| 2 |
+
that it is only a plan and whatever shape it takes, it is to be used in the classroom.
|
app/knowledge_base/lesson_frameworks.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
ва ва
|
app/main.py
CHANGED
|
@@ -1,245 +0,0 @@
|
|
| 1 |
-
import streamlit as st
|
| 2 |
-
from openai import OpenAI, BadRequestError
|
| 3 |
-
from pydantic import BaseModel
|
| 4 |
-
import requests
|
| 5 |
-
import logging
|
| 6 |
-
import os
|
| 7 |
-
from dotenv import load_dotenv
|
| 8 |
-
|
| 9 |
-
# 1. Конфигурация полей формы
|
| 10 |
-
FORM_CONFIG = {
|
| 11 |
-
"main_fields": {
|
| 12 |
-
"subject": {"label": "Предмет", "type": "text", "default": ""},
|
| 13 |
-
"textbook": {"label": "Название учебника", "type": "text", "default": ""},
|
| 14 |
-
"students": {"label": "Количество учеников", "type": "slider", "min": 1, "max": 40, "default": 10},
|
| 15 |
-
"methodology": {
|
| 16 |
-
"label": "Методика обучения",
|
| 17 |
-
"type": "selectbox",
|
| 18 |
-
"options": ["PPP", "TTT"],
|
| 19 |
-
"default": "PPP"
|
| 20 |
-
}
|
| 21 |
-
}
|
| 22 |
-
}
|
| 23 |
-
|
| 24 |
-
AUTO_DETECT_ENABLED = True # Флаг по умолчанию
|
| 25 |
-
|
| 26 |
-
# 2. Первая команда Streamlit (должна быть первой)
|
| 27 |
-
st.set_page_config(page_title="Генератор планов уроков", layout="wide")
|
| 28 |
-
|
| 29 |
-
# 3. CSS для отключения running состояний
|
| 30 |
-
st.markdown("""
|
| 31 |
-
<style>
|
| 32 |
-
[data-testid="stStatusWidget"] {
|
| 33 |
-
display: none !important;
|
| 34 |
-
}
|
| 35 |
-
.stTextInput, .stSlider, .stSelectbox {
|
| 36 |
-
opacity: 1 !important;
|
| 37 |
-
}
|
| 38 |
-
</style>
|
| 39 |
-
""", unsafe_allow_html=True)
|
| 40 |
-
|
| 41 |
-
# 4. Настройка логирования
|
| 42 |
-
logging.basicConfig(
|
| 43 |
-
level=logging.INFO,
|
| 44 |
-
format='%(asctime)s - %(levelname)s - %(message)s',
|
| 45 |
-
handlers=[logging.StreamHandler()]
|
| 46 |
-
)
|
| 47 |
-
logger = logging.getLogger(__name__)
|
| 48 |
-
|
| 49 |
-
# 5. Загрузка переменных окружения
|
| 50 |
-
load_dotenv()
|
| 51 |
-
API_KEY = os.getenv("API_KEY_openai")
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
if not API_KEY:
|
| 55 |
-
logger.error("API_KEY_openai не найден в переменных окружения")
|
| 56 |
-
st.error("Ошибка: API_KEY_openai не найден. Проверьте файл .env")
|
| 57 |
-
st.stop()
|
| 58 |
-
|
| 59 |
-
# 6. Инициализация клиента OpenAI
|
| 60 |
-
client = OpenAI(api_key=API_KEY)
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
# 7. Модель данных
|
| 64 |
-
class TextbookInfo(BaseModel):
|
| 65 |
-
subject: str = ""
|
| 66 |
-
textbook_name: str = ""
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
# 8. Функция загрузки изображения
|
| 70 |
-
def upload_to_catbox(file_bytes):
|
| 71 |
-
try:
|
| 72 |
-
with st.spinner("Загрузка изображения на сервер..."):
|
| 73 |
-
response = requests.post(
|
| 74 |
-
"https://catbox.moe/user/api.php",
|
| 75 |
-
files={"fileToUpload": file_bytes},
|
| 76 |
-
data={"reqtype": "fileupload"}
|
| 77 |
-
)
|
| 78 |
-
response.raise_for_status()
|
| 79 |
-
url = response.text.strip()
|
| 80 |
-
if not url.startswith("https://files.catbox.moe/"):
|
| 81 |
-
raise ValueError("Некорректный URL")
|
| 82 |
-
return url
|
| 83 |
-
except Exception as e:
|
| 84 |
-
logger.error(f"Ошибка загрузки: {e}")
|
| 85 |
-
st.error("Не удалось загрузить изображение")
|
| 86 |
-
return None
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
# 9. Функция парсинга ответа
|
| 90 |
-
def parse_response(text: str) -> tuple[str, str]:
|
| 91 |
-
subject = "Не определено"
|
| 92 |
-
textbook = "Не определено"
|
| 93 |
-
|
| 94 |
-
lines = text.lower().replace("*", "").strip().split('\n')
|
| 95 |
-
for line in lines:
|
| 96 |
-
if "предмет:" in line:
|
| 97 |
-
subject = line.split("предмет:")[1].strip().capitalize()
|
| 98 |
-
elif "учебник:" in line:
|
| 99 |
-
textbook = line.split("учебник:")[1].strip().capitalize()
|
| 100 |
-
|
| 101 |
-
return subject, textbook
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
# 10. Инициализация состояний
|
| 105 |
-
if "textbook_data" not in st.session_state:
|
| 106 |
-
st.session_state.textbook_data = TextbookInfo()
|
| 107 |
-
if "image_url" not in st.session_state:
|
| 108 |
-
st.session_state.image_url = None
|
| 109 |
-
if "form_values" not in st.session_state:
|
| 110 |
-
st.session_state.form_values = {
|
| 111 |
-
field: config["default"]
|
| 112 |
-
for field, config in FORM_CONFIG["main_fields"].items()
|
| 113 |
-
}
|
| 114 |
-
if "force_form_update" not in st.session_state:
|
| 115 |
-
st.session_state.force_form_update = True
|
| 116 |
-
|
| 117 |
-
# 11. Основной интерфейс
|
| 118 |
-
left_col, right_col = st.columns([2, 3])
|
| 119 |
-
|
| 120 |
-
with left_col:
|
| 121 |
-
st.header("Настройки")
|
| 122 |
-
AUTO_DETECT_ENABLED = st.toggle(
|
| 123 |
-
"Автоматическое распознавание учебника",
|
| 124 |
-
value=True,
|
| 125 |
-
key="auto_detect_toggle"
|
| 126 |
-
)
|
| 127 |
-
st.header("Загрузка учебника")
|
| 128 |
-
uploaded_file = st.file_uploader("Выберите изображение страницы", type=["jpg", "png", "jpeg"])
|
| 129 |
-
|
| 130 |
-
# Обработка загрузки изображения
|
| 131 |
-
if uploaded_file and not st.session_state.image_url:
|
| 132 |
-
image_url = upload_to_catbox(uploaded_file.getvalue())
|
| 133 |
-
if image_url:
|
| 134 |
-
st.session_state.image_url = image_url
|
| 135 |
-
st.success("Изображение загружено!")
|
| 136 |
-
|
| 137 |
-
if AUTO_DETECT_ENABLED: # Только если включено
|
| 138 |
-
with st.spinner("Анализ изображения..."):
|
| 139 |
-
try:
|
| 140 |
-
response = client.chat.completions.create(
|
| 141 |
-
model="gpt-4o-mini",
|
| 142 |
-
messages=[
|
| 143 |
-
{
|
| 144 |
-
"role": "user",
|
| 145 |
-
"content": [
|
| 146 |
-
{"type": "image_url", "image_url": {"url": image_url}},
|
| 147 |
-
{"type": "text", "text": """Определи предмет и учебник. Ответь строго в формате:
|
| 148 |
-
Предмет: [название]
|
| 149 |
-
Учебник: [название]"""}
|
| 150 |
-
]
|
| 151 |
-
}
|
| 152 |
-
]
|
| 153 |
-
)
|
| 154 |
-
|
| 155 |
-
if response.choices:
|
| 156 |
-
subject, textbook = parse_response(response.choices[0].message.content)
|
| 157 |
-
st.session_state.textbook_data = TextbookInfo(
|
| 158 |
-
subject=subject,
|
| 159 |
-
textbook_name=textbook
|
| 160 |
-
)
|
| 161 |
-
st.session_state.force_form_update = True
|
| 162 |
-
|
| 163 |
-
except Exception as e:
|
| 164 |
-
logger.error(f"Ошибка анализа: {e}")
|
| 165 |
-
st.error("Ошибка при анализе изображения")
|
| 166 |
-
else:
|
| 167 |
-
st.info("Автораспознавание отключено. Заполните данные вручную")
|
| 168 |
-
st.session_state.textbook_data = TextbookInfo()
|
| 169 |
-
st.session_state.force_form_update = True
|
| 170 |
-
|
| 171 |
-
# Форма с настройками
|
| 172 |
-
if st.session_state.image_url:
|
| 173 |
-
st.subheader("Данные учебника")
|
| 174 |
-
|
| 175 |
-
# Обновление значений формы при необходимости
|
| 176 |
-
if st.session_state.force_form_update:
|
| 177 |
-
st.session_state.form_values.update({
|
| 178 |
-
"subject": st.session_state.textbook_data.subject,
|
| 179 |
-
"textbook": st.session_state.textbook_data.textbook_name
|
| 180 |
-
})
|
| 181 |
-
st.session_state.force_form_update = False
|
| 182 |
-
|
| 183 |
-
with st.form(key="lesson_form"):
|
| 184 |
-
# Динамическое создание полей из конфига
|
| 185 |
-
for field, config in FORM_CONFIG["main_fields"].items():
|
| 186 |
-
if config["type"] == "text":
|
| 187 |
-
st.session_state.form_values[field] = st.text_input(
|
| 188 |
-
config["label"],
|
| 189 |
-
value=st.session_state.form_values[field],
|
| 190 |
-
key=f"form_{field}"
|
| 191 |
-
)
|
| 192 |
-
elif config["type"] == "slider":
|
| 193 |
-
st.session_state.form_values[field] = st.slider(
|
| 194 |
-
config["label"],
|
| 195 |
-
min_value=config["min"],
|
| 196 |
-
max_value=config["max"],
|
| 197 |
-
value=st.session_state.form_values[field],
|
| 198 |
-
key=f"form_{field}"
|
| 199 |
-
)
|
| 200 |
-
elif config["type"] == "selectbox":
|
| 201 |
-
st.session_state.form_values[field] = st.selectbox(
|
| 202 |
-
config["label"],
|
| 203 |
-
options=config["options"],
|
| 204 |
-
index=config["options"].index(st.session_state.form_values[field]),
|
| 205 |
-
key=f"form_{field}"
|
| 206 |
-
)
|
| 207 |
-
|
| 208 |
-
submitted = st.form_submit_button("Создать план урока")
|
| 209 |
-
|
| 210 |
-
if submitted:
|
| 211 |
-
if not st.session_state.form_values["subject"] or not st.session_state.form_values["textbook"]:
|
| 212 |
-
st.warning("Заполните все обязательные поля")
|
| 213 |
-
else:
|
| 214 |
-
with st.spinner("Генерация плана урока..."):
|
| 215 |
-
try:
|
| 216 |
-
response = client.chat.completions.create(
|
| 217 |
-
model="gpt-4o-mini",
|
| 218 |
-
messages=[
|
| 219 |
-
{
|
| 220 |
-
"role": "system",
|
| 221 |
-
"content": "Ты опытный учитель. Создай детальный план урока."
|
| 222 |
-
},
|
| 223 |
-
{
|
| 224 |
-
"role": "user",
|
| 225 |
-
"content": [
|
| 226 |
-
{"type": "image_url", "image_url": {"url": st.session_state.image_url}},
|
| 227 |
-
{"type": "text", "text": f"""
|
| 228 |
-
Создай план урока по методике {st.session_state.form_values['methodology']}
|
| 229 |
-
Детали:
|
| 230 |
-
- Предмет: {st.session_state.form_values['subject']}
|
| 231 |
-
- Учебник: {st.session_state.form_values['textbook']}
|
| 232 |
-
- Учеников: {st.session_state.form_values['students']}
|
| 233 |
-
"""}
|
| 234 |
-
]
|
| 235 |
-
}
|
| 236 |
-
]
|
| 237 |
-
)
|
| 238 |
-
right_col.markdown(response.choices[0].message.content)
|
| 239 |
-
except Exception as e:
|
| 240 |
-
st.error(f"Ошибка генерации: {e}")
|
| 241 |
-
|
| 242 |
-
with right_col:
|
| 243 |
-
st.header("План урока")
|
| 244 |
-
if not st.session_state.image_url:
|
| 245 |
-
st.info("Загрузите изображение учебника")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/prompt_builder/__init__.py
ADDED
|
File without changes
|
app/prompt_builder/methodology.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
METHODOLOGY_DATA = {
|
| 2 |
+
"PPP": {
|
| 3 |
+
"structure": "3 этапа: Presentation → Practice → Production",
|
| 4 |
+
"key_points": [
|
| 5 |
+
"Четкое разделение этапов",
|
| 6 |
+
"Контролируемая практика перед свободным использованием",
|
| 7 |
+
"Пример: Демонстрация диалога → Упражнения на подстановку → Ролевая игра"
|
| 8 |
+
]
|
| 9 |
+
},
|
| 10 |
+
"TTT": {
|
| 11 |
+
"structure": "Test → Teach → Test",
|
| 12 |
+
"key_points": [
|
| 13 |
+
"Диагностика текущего уровня перед обучением",
|
| 14 |
+
"Фокус на пробелах в знаниях",
|
| 15 |
+
"Пример: Предварительный квиз → Объяснение проблемных мест → Финальное задание"
|
| 16 |
+
]
|
| 17 |
+
}
|
| 18 |
+
}
|
app/prompt_builder/prompt.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
METHODOLOGY_TIPS = {
|
| 2 |
+
"PPP": """
|
| 3 |
+
[МЕТОДИКА PPP]
|
| 4 |
+
Обязательные этапы:
|
| 5 |
+
1. Presentation (5-10 мин): Покажи пример через диалог/текст
|
| 6 |
+
2. Practice (15 мин): Упражнения на подстановку/трансформацию
|
| 7 |
+
3. Production (20 мин): Ролевая игра или создание своего контента
|
| 8 |
+
Пример: стр. 45 учебника → упражнение 3A → написание диалога
|
| 9 |
+
""",
|
| 10 |
+
|
| 11 |
+
"TTT": """
|
| 12 |
+
[МЕТОДИКА TTT]
|
| 13 |
+
Обязательные этапы:
|
| 14 |
+
1. Test (10 мин): Диагностический тест
|
| 15 |
+
2. Teach (15 мин): Объяснение проблемных мест
|
| 16 |
+
3. Test (20 мин): Применение в новой ситуации
|
| 17 |
+
Пример: предтест на времена → объяснение Past Perfect → кейсы
|
| 18 |
+
"""
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def build_prompt(params):
|
| 23 |
+
base_prompt = f"""
|
| 24 |
+
СОЗДАЙ ПЛАН УРОКА по этим данным:
|
| 25 |
+
- Учебник: {params['textbook']}
|
| 26 |
+
- Страница: {params['image_url']} (все задания с этой страницы!)
|
| 27 |
+
- Уровень: {params['cefr']}
|
| 28 |
+
- Тема: {params['topic']}
|
| 29 |
+
- Цель: {params['goal']}
|
| 30 |
+
- Время: {params['duration']} мин
|
| 31 |
+
- Ученики: {params['num_students']} чел, возраст: {params['age']}
|
| 32 |
+
|
| 33 |
+
Особые указания:
|
| 34 |
+
"""
|
| 35 |
+
|
| 36 |
+
# Добавляем советы по методике
|
| 37 |
+
methodology_advice = METHODOLOGY_TIPS.get(params['methodology'], "")
|
| 38 |
+
|
| 39 |
+
return base_prompt + methodology_advice
|
app/test.py
CHANGED
|
@@ -1,245 +0,0 @@
|
|
| 1 |
-
import streamlit as st
|
| 2 |
-
from openai import OpenAI, BadRequestError
|
| 3 |
-
from pydantic import BaseModel
|
| 4 |
-
import requests
|
| 5 |
-
import logging
|
| 6 |
-
import os
|
| 7 |
-
from dotenv import load_dotenv
|
| 8 |
-
|
| 9 |
-
# 1. Конфигурация полей формы
|
| 10 |
-
FORM_CONFIG = {
|
| 11 |
-
"main_fields": {
|
| 12 |
-
"subject": {"label": "Предмет", "type": "text", "default": ""},
|
| 13 |
-
"textbook": {"label": "Название учебника", "type": "text", "default": ""},
|
| 14 |
-
"students": {"label": "Количество учеников", "type": "slider", "min": 1, "max": 40, "default": 10},
|
| 15 |
-
"methodology": {
|
| 16 |
-
"label": "Методика обучения",
|
| 17 |
-
"type": "selectbox",
|
| 18 |
-
"options": ["PPP", "TTT"],
|
| 19 |
-
"default": "PPP"
|
| 20 |
-
}
|
| 21 |
-
}
|
| 22 |
-
}
|
| 23 |
-
|
| 24 |
-
AUTO_DETECT_ENABLED = True # Флаг по умолчанию
|
| 25 |
-
|
| 26 |
-
# 2. Первая команда Streamlit (должна быть первой)
|
| 27 |
-
st.set_page_config(page_title="Генератор планов уроков", layout="wide")
|
| 28 |
-
|
| 29 |
-
# 3. CSS для отключения running состояний
|
| 30 |
-
st.markdown("""
|
| 31 |
-
<style>
|
| 32 |
-
[data-testid="stStatusWidget"] {
|
| 33 |
-
display: none !important;
|
| 34 |
-
}
|
| 35 |
-
.stTextInput, .stSlider, .stSelectbox {
|
| 36 |
-
opacity: 1 !important;
|
| 37 |
-
}
|
| 38 |
-
</style>
|
| 39 |
-
""", unsafe_allow_html=True)
|
| 40 |
-
|
| 41 |
-
# 4. Настройка логирования
|
| 42 |
-
logging.basicConfig(
|
| 43 |
-
level=logging.INFO,
|
| 44 |
-
format='%(asctime)s - %(levelname)s - %(message)s',
|
| 45 |
-
handlers=[logging.StreamHandler()]
|
| 46 |
-
)
|
| 47 |
-
logger = logging.getLogger(__name__)
|
| 48 |
-
|
| 49 |
-
# 5. Загрузка переменных окружения
|
| 50 |
-
load_dotenv()
|
| 51 |
-
API_KEY = os.getenv("API_KEY")
|
| 52 |
-
BASE_URL = os.getenv("BASE_URL")
|
| 53 |
-
|
| 54 |
-
if not API_KEY:
|
| 55 |
-
logger.error("API_KEY не найден в переменных окружения")
|
| 56 |
-
st.error("Ошибка: API_KEY не найден. Проверьте файл .env")
|
| 57 |
-
st.stop()
|
| 58 |
-
|
| 59 |
-
# 6. Инициализация клиента OpenAI
|
| 60 |
-
client = OpenAI(api_key=API_KEY, base_url=BASE_URL)
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
# 7. Модель данных
|
| 64 |
-
class TextbookInfo(BaseModel):
|
| 65 |
-
subject: str = ""
|
| 66 |
-
textbook_name: str = ""
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
# 8. Функция загрузки изображения
|
| 70 |
-
def upload_to_catbox(file_bytes):
|
| 71 |
-
try:
|
| 72 |
-
with st.spinner("Загрузка изображения на сервер..."):
|
| 73 |
-
response = requests.post(
|
| 74 |
-
"https://catbox.moe/user/api.php",
|
| 75 |
-
files={"fileToUpload": file_bytes},
|
| 76 |
-
data={"reqtype": "fileupload"}
|
| 77 |
-
)
|
| 78 |
-
response.raise_for_status()
|
| 79 |
-
url = response.text.strip()
|
| 80 |
-
if not url.startswith("https://files.catbox.moe/"):
|
| 81 |
-
raise ValueError("Некорректный URL")
|
| 82 |
-
return url
|
| 83 |
-
except Exception as e:
|
| 84 |
-
logger.error(f"Ошибка загрузки: {e}")
|
| 85 |
-
st.error("Не удалось загрузить изображение")
|
| 86 |
-
return None
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
# 9. Функция парсинга ответа
|
| 90 |
-
def parse_response(text: str) -> tuple[str, str]:
|
| 91 |
-
subject = "Не определено"
|
| 92 |
-
textbook = "Не определено"
|
| 93 |
-
|
| 94 |
-
lines = text.lower().replace("*", "").strip().split('\n')
|
| 95 |
-
for line in lines:
|
| 96 |
-
if "предмет:" in line:
|
| 97 |
-
subject = line.split("предмет:")[1].strip().capitalize()
|
| 98 |
-
elif "учебник:" in line:
|
| 99 |
-
textbook = line.split("учебник:")[1].strip().capitalize()
|
| 100 |
-
|
| 101 |
-
return subject, textbook
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
# 10. Инициализация состояний
|
| 105 |
-
if "textbook_data" not in st.session_state:
|
| 106 |
-
st.session_state.textbook_data = TextbookInfo()
|
| 107 |
-
if "image_url" not in st.session_state:
|
| 108 |
-
st.session_state.image_url = None
|
| 109 |
-
if "form_values" not in st.session_state:
|
| 110 |
-
st.session_state.form_values = {
|
| 111 |
-
field: config["default"]
|
| 112 |
-
for field, config in FORM_CONFIG["main_fields"].items()
|
| 113 |
-
}
|
| 114 |
-
if "force_form_update" not in st.session_state:
|
| 115 |
-
st.session_state.force_form_update = True
|
| 116 |
-
|
| 117 |
-
# 11. Основной интерфейс
|
| 118 |
-
left_col, right_col = st.columns([2, 3])
|
| 119 |
-
|
| 120 |
-
with left_col:
|
| 121 |
-
st.header("Настройки")
|
| 122 |
-
AUTO_DETECT_ENABLED = st.toggle(
|
| 123 |
-
"Автоматическое распознавание учебника",
|
| 124 |
-
value=True,
|
| 125 |
-
key="auto_detect_toggle"
|
| 126 |
-
)
|
| 127 |
-
st.header("Загрузка учебника")
|
| 128 |
-
uploaded_file = st.file_uploader("Выберите изображение страницы", type=["jpg", "png", "jpeg"])
|
| 129 |
-
|
| 130 |
-
# Обработка загрузки изображения
|
| 131 |
-
if uploaded_file and not st.session_state.image_url:
|
| 132 |
-
image_url = upload_to_catbox(uploaded_file.getvalue())
|
| 133 |
-
if image_url:
|
| 134 |
-
st.session_state.image_url = image_url
|
| 135 |
-
st.success("Изображение загружено!")
|
| 136 |
-
|
| 137 |
-
if AUTO_DETECT_ENABLED: # Только если включено
|
| 138 |
-
with st.spinner("Анализ изображения..."):
|
| 139 |
-
try:
|
| 140 |
-
response = client.chat.completions.create(
|
| 141 |
-
model="gpt-4o-mini",
|
| 142 |
-
messages=[
|
| 143 |
-
{
|
| 144 |
-
"role": "user",
|
| 145 |
-
"content": [
|
| 146 |
-
{"type": "image_url", "image_url": {"url": image_url}},
|
| 147 |
-
{"type": "text", "text": """Определи предмет и учебник. Ответь строго в формате:
|
| 148 |
-
Предмет: [название]
|
| 149 |
-
Учебник: [название]"""}
|
| 150 |
-
]
|
| 151 |
-
}
|
| 152 |
-
]
|
| 153 |
-
)
|
| 154 |
-
|
| 155 |
-
if response.choices:
|
| 156 |
-
subject, textbook = parse_response(response.choices[0].message.content)
|
| 157 |
-
st.session_state.textbook_data = TextbookInfo(
|
| 158 |
-
subject=subject,
|
| 159 |
-
textbook_name=textbook
|
| 160 |
-
)
|
| 161 |
-
st.session_state.force_form_update = True
|
| 162 |
-
|
| 163 |
-
except Exception as e:
|
| 164 |
-
logger.error(f"Ошибка анализа: {e}")
|
| 165 |
-
st.error("Ошибка при анализе изображения")
|
| 166 |
-
else:
|
| 167 |
-
st.info("Автораспознавание отключено. Заполните данные вручную")
|
| 168 |
-
st.session_state.textbook_data = TextbookInfo()
|
| 169 |
-
st.session_state.force_form_update = True
|
| 170 |
-
|
| 171 |
-
# Форма с настройками
|
| 172 |
-
if st.session_state.image_url:
|
| 173 |
-
st.subheader("Данные учебника")
|
| 174 |
-
|
| 175 |
-
# Обновление значений формы при необходимости
|
| 176 |
-
if st.session_state.force_form_update:
|
| 177 |
-
st.session_state.form_values.update({
|
| 178 |
-
"subject": st.session_state.textbook_data.subject,
|
| 179 |
-
"textbook": st.session_state.textbook_data.textbook_name
|
| 180 |
-
})
|
| 181 |
-
st.session_state.force_form_update = False
|
| 182 |
-
|
| 183 |
-
with st.form(key="lesson_form"):
|
| 184 |
-
# Динамическое создание полей из конфига
|
| 185 |
-
for field, config in FORM_CONFIG["main_fields"].items():
|
| 186 |
-
if config["type"] == "text":
|
| 187 |
-
st.session_state.form_values[field] = st.text_input(
|
| 188 |
-
config["label"],
|
| 189 |
-
value=st.session_state.form_values[field],
|
| 190 |
-
key=f"form_{field}"
|
| 191 |
-
)
|
| 192 |
-
elif config["type"] == "slider":
|
| 193 |
-
st.session_state.form_values[field] = st.slider(
|
| 194 |
-
config["label"],
|
| 195 |
-
min_value=config["min"],
|
| 196 |
-
max_value=config["max"],
|
| 197 |
-
value=st.session_state.form_values[field],
|
| 198 |
-
key=f"form_{field}"
|
| 199 |
-
)
|
| 200 |
-
elif config["type"] == "selectbox":
|
| 201 |
-
st.session_state.form_values[field] = st.selectbox(
|
| 202 |
-
config["label"],
|
| 203 |
-
options=config["options"],
|
| 204 |
-
index=config["options"].index(st.session_state.form_values[field]),
|
| 205 |
-
key=f"form_{field}"
|
| 206 |
-
)
|
| 207 |
-
|
| 208 |
-
submitted = st.form_submit_button("Создать план урока")
|
| 209 |
-
|
| 210 |
-
if submitted:
|
| 211 |
-
if not st.session_state.form_values["subject"] or not st.session_state.form_values["textbook"]:
|
| 212 |
-
st.warning("Заполните все обязательные поля")
|
| 213 |
-
else:
|
| 214 |
-
with st.spinner("Генерация плана урока..."):
|
| 215 |
-
try:
|
| 216 |
-
response = client.chat.completions.create(
|
| 217 |
-
model="gpt-4o-mini",
|
| 218 |
-
messages=[
|
| 219 |
-
{
|
| 220 |
-
"role": "system",
|
| 221 |
-
"content": "Ты опытный учитель. Создай детальный план урока."
|
| 222 |
-
},
|
| 223 |
-
{
|
| 224 |
-
"role": "user",
|
| 225 |
-
"content": [
|
| 226 |
-
{"type": "image_url", "image_url": {"url": st.session_state.image_url}},
|
| 227 |
-
{"type": "text", "text": f"""
|
| 228 |
-
Создай план урока по методике {st.session_state.form_values['methodology']}
|
| 229 |
-
Детали:
|
| 230 |
-
- Предмет: {st.session_state.form_values['subject']}
|
| 231 |
-
- Учебник: {st.session_state.form_values['textbook']}
|
| 232 |
-
- Учеников: {st.session_state.form_values['students']}
|
| 233 |
-
"""}
|
| 234 |
-
]
|
| 235 |
-
}
|
| 236 |
-
]
|
| 237 |
-
)
|
| 238 |
-
right_col.markdown(response.choices[0].message.content)
|
| 239 |
-
except Exception as e:
|
| 240 |
-
st.error(f"Ошибка генерации: {e}")
|
| 241 |
-
|
| 242 |
-
with right_col:
|
| 243 |
-
st.header("План урока")
|
| 244 |
-
if not st.session_state.image_url:
|
| 245 |
-
st.info("Загрузите изображение учебника")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/test_gradio.py
CHANGED
|
@@ -9,6 +9,8 @@ from docx import Document
|
|
| 9 |
import requests
|
| 10 |
from typing import Optional
|
| 11 |
|
|
|
|
|
|
|
| 12 |
# --- Настройка ---
|
| 13 |
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
| 14 |
load_dotenv()
|
|
@@ -59,6 +61,7 @@ def generate_lesson_plan(
|
|
| 59 |
duration: int,
|
| 60 |
inventory: str,
|
| 61 |
methodology: str,
|
|
|
|
| 62 |
hw_required: bool,
|
| 63 |
web_search: bool,
|
| 64 |
repetition: bool,
|
|
@@ -93,6 +96,7 @@ def generate_lesson_plan(
|
|
| 93 |
f"- **Длительность**: {duration} минут",
|
| 94 |
f"- **Инвентарь**: {inventory if inventory else 'стандартный'}",
|
| 95 |
f"- **Методика**: {methodology}",
|
|
|
|
| 96 |
# f"- **Таксономия**: {', '.join([level for level, checked in [('Повторение', repetition),
|
| 97 |
# ('Применение', application),
|
| 98 |
# ('Анализ', analysis),
|
|
@@ -130,7 +134,7 @@ def generate_lesson_plan(
|
|
| 130 |
# Вызов LLM
|
| 131 |
try:
|
| 132 |
response = client.responses.create(
|
| 133 |
-
instructions=
|
| 134 |
input=[{"role": "user", "content": input_content}],
|
| 135 |
model="gpt-4o-mini",
|
| 136 |
tools=tools or None,
|
|
|
|
| 9 |
import requests
|
| 10 |
from typing import Optional
|
| 11 |
|
| 12 |
+
import app.instructions
|
| 13 |
+
|
| 14 |
# --- Настройка ---
|
| 15 |
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
| 16 |
load_dotenv()
|
|
|
|
| 61 |
duration: int,
|
| 62 |
inventory: str,
|
| 63 |
methodology: str,
|
| 64 |
+
extra_info: str,
|
| 65 |
hw_required: bool,
|
| 66 |
web_search: bool,
|
| 67 |
repetition: bool,
|
|
|
|
| 96 |
f"- **Длительность**: {duration} минут",
|
| 97 |
f"- **Инвентарь**: {inventory if inventory else 'стандартный'}",
|
| 98 |
f"- **Методика**: {methodology}",
|
| 99 |
+
# f"- **Дополнительная информация**: {extra_info}"
|
| 100 |
# f"- **Таксономия**: {', '.join([level for level, checked in [('Повторение', repetition),
|
| 101 |
# ('Применение', application),
|
| 102 |
# ('Анализ', analysis),
|
|
|
|
| 134 |
# Вызов LLM
|
| 135 |
try:
|
| 136 |
response = client.responses.create(
|
| 137 |
+
instructions=app.instructions.INSTRUCTIONS_1,
|
| 138 |
input=[{"role": "user", "content": input_content}],
|
| 139 |
model="gpt-4o-mini",
|
| 140 |
tools=tools or None,
|
app/test_gradio_test.py
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import logging
|
| 3 |
+
import tempfile
|
| 4 |
+
from datetime import datetime
|
| 5 |
+
from dotenv import load_dotenv
|
| 6 |
+
from openai import OpenAI
|
| 7 |
+
import gradio as gr
|
| 8 |
+
from docx import Document
|
| 9 |
+
import requests
|
| 10 |
+
from typing import Optional
|
| 11 |
+
|
| 12 |
+
import app.instructions
|
| 13 |
+
|
| 14 |
+
# --- Настройка ---
|
| 15 |
+
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
| 16 |
+
load_dotenv()
|
| 17 |
+
api_key = os.getenv("API_KEY_openai")
|
| 18 |
+
client = OpenAI(api_key=api_key) if api_key else None
|
| 19 |
+
VS_ID = os.getenv("VECTOR_STORE_ID", "") # vector store for file_search if needed
|
| 20 |
+
|
| 21 |
+
# --- Утилиты ---
|
| 22 |
+
def upload_to_catbox(file_bytes: bytes) -> str:
|
| 23 |
+
"""Загружает файл на catbox.moe и возвращает URL"""
|
| 24 |
+
try:
|
| 25 |
+
response = requests.post(
|
| 26 |
+
"https://catbox.moe/user/api.php",
|
| 27 |
+
files={"fileToUpload": file_bytes},
|
| 28 |
+
data={"reqtype": "fileupload"},
|
| 29 |
+
timeout=10
|
| 30 |
+
)
|
| 31 |
+
response.raise_for_status()
|
| 32 |
+
return response.text.strip()
|
| 33 |
+
except Exception as e:
|
| 34 |
+
logging.error(f"Ошибка загрузки изображения: {e}")
|
| 35 |
+
raise gr.Error("Не удалось загрузить изображение")
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def generate_docx(text: str) -> str:
|
| 39 |
+
"""Сохраняет текст в .docx и возвращает путь"""
|
| 40 |
+
doc = Document()
|
| 41 |
+
for line in text.split("\n"):
|
| 42 |
+
doc.add_paragraph(line)
|
| 43 |
+
tmp = tempfile.gettempdir()
|
| 44 |
+
path = os.path.join(tmp, f"lesson_plan_{datetime.now().strftime('%Y%m%d%H%M%S')}.docx")
|
| 45 |
+
doc.save(path)
|
| 46 |
+
return path
|
| 47 |
+
|
| 48 |
+
all_inputs = [
|
| 49 |
+
image, # Gradio компонент, соответствует image_path в функциях
|
| 50 |
+
textbook,
|
| 51 |
+
cefr,
|
| 52 |
+
topic,
|
| 53 |
+
goal,
|
| 54 |
+
format_type,
|
| 55 |
+
num_students,
|
| 56 |
+
age,
|
| 57 |
+
adults,
|
| 58 |
+
level_match,
|
| 59 |
+
duration,
|
| 60 |
+
inventory,
|
| 61 |
+
methodology,
|
| 62 |
+
hw_required,
|
| 63 |
+
web_search,
|
| 64 |
+
repetition,
|
| 65 |
+
application,
|
| 66 |
+
analysis,
|
| 67 |
+
creativity
|
| 68 |
+
]
|
| 69 |
+
# --- Основная функция генерации ---
|
| 70 |
+
def generate_lesson_plan(
|
| 71 |
+
image_path: Optional[str],
|
| 72 |
+
textbook: str,
|
| 73 |
+
cefr: str,
|
| 74 |
+
topic: str,
|
| 75 |
+
goal: str,
|
| 76 |
+
format_type: str,
|
| 77 |
+
num_students: int,
|
| 78 |
+
age: str,
|
| 79 |
+
adults: bool,
|
| 80 |
+
level_match: int,
|
| 81 |
+
duration: int,
|
| 82 |
+
inventory: str,
|
| 83 |
+
methodology: str,
|
| 84 |
+
hw_required: bool,
|
| 85 |
+
web_search: bool,
|
| 86 |
+
repetition: bool,
|
| 87 |
+
application: bool,
|
| 88 |
+
analysis: bool,
|
| 89 |
+
creativity: bool
|
| 90 |
+
) -> str:
|
| 91 |
+
|
| 92 |
+
# Валидация API клиента
|
| 93 |
+
if not client:
|
| 94 |
+
raise gr.Error("API ключ не настроен")
|
| 95 |
+
# Проверка наличия изображения
|
| 96 |
+
if not image_path:
|
| 97 |
+
return "❗ Загрузите фото страницы учебника для генерации урока"
|
| 98 |
+
|
| 99 |
+
# Загружаем изображение и получаем URL
|
| 100 |
+
try:
|
| 101 |
+
with open(image_path, "rb") as f:
|
| 102 |
+
image_url = upload_to_catbox(f.read())
|
| 103 |
+
except Exception as e:
|
| 104 |
+
logging.error(f"Image upload error: {e}")
|
| 105 |
+
raise gr.Error("Не удалось загрузить изображение")
|
| 106 |
+
|
| 107 |
+
# Собираем текстовый prompt
|
| 108 |
+
lesson_params = {
|
| 109 |
+
'methodology': methodology,
|
| 110 |
+
'Учебник': textbook,
|
| 111 |
+
'Уровень учебника по CEFR': cefr,
|
| 112 |
+
'Тема': topic,
|
| 113 |
+
'Цель': goal,
|
| 114 |
+
'Количество учеников': num_students,
|
| 115 |
+
'Возраст': 'Взрослые' if adults else age,
|
| 116 |
+
'level_match': ['below', 'on-level', 'above', 'mixed'][level_match],
|
| 117 |
+
'duration': duration,
|
| 118 |
+
'inventory': inventory,
|
| 119 |
+
'hw_required': hw_required
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
# Получаем готовый промпт
|
| 123 |
+
from app.prompt_builder.prompt import build_prompt
|
| 124 |
+
full_prompt = build_prompt(lesson_params)
|
| 125 |
+
|
| 126 |
+
# Собираем входные данные согласно API
|
| 127 |
+
input_content = [
|
| 128 |
+
{
|
| 129 |
+
"type": "input_text",
|
| 130 |
+
"text": full_prompt
|
| 131 |
+
},
|
| 132 |
+
{
|
| 133 |
+
"type": "input_image",
|
| 134 |
+
"image_url": image_url,
|
| 135 |
+
"detail": "high"
|
| 136 |
+
}
|
| 137 |
+
]
|
| 138 |
+
|
| 139 |
+
# Опции инструментов
|
| 140 |
+
tools = []
|
| 141 |
+
tool_choice = None
|
| 142 |
+
if web_search:
|
| 143 |
+
tools.append({
|
| 144 |
+
"type": "web_search_preview",
|
| 145 |
+
"search_context_size": "medium",
|
| 146 |
+
"user_location": {"type": "approximate", "country": "RU"}
|
| 147 |
+
})
|
| 148 |
+
tool_choice = {"type": "web_search_preview"}
|
| 149 |
+
|
| 150 |
+
# Вызов LLM
|
| 151 |
+
try:
|
| 152 |
+
response = client.responses.create(
|
| 153 |
+
input=input_content,
|
| 154 |
+
model="gpt-4o-mini",
|
| 155 |
+
tools=tools or None,
|
| 156 |
+
tool_choice=tool_choice,
|
| 157 |
+
max_output_tokens=2000,
|
| 158 |
+
stream=False
|
| 159 |
+
)
|
| 160 |
+
return response.output_text
|
| 161 |
+
|
| 162 |
+
except Exception as e:
|
| 163 |
+
logging.error(f"Generation error: {e}")
|
| 164 |
+
raise gr.Error(f"Ошибка генерации: {e}")
|
| 165 |
+
|
| 166 |
+
|
| 167 |
+
# --- Gradio UI ---
|
| 168 |
+
with gr.Blocks(title="AI-Генератор уроков по фото учебника") as app:
|
| 169 |
+
advanced_settings_visible = gr.State(False) # Добавляем состояние для видимости блока
|
| 170 |
+
with gr.Row():
|
| 171 |
+
with gr.Column(scale=1): # левый блок
|
| 172 |
+
image = gr.Image(label="Фото страницы учебника*", type="filepath")
|
| 173 |
+
|
| 174 |
+
# Блок 1: Учебник
|
| 175 |
+
with gr.Column(variant="panel"):
|
| 176 |
+
gr.Markdown("### 📚 Учебник", elem_classes=["block-title"])
|
| 177 |
+
textbook = gr.Textbox(label="Название учебника", placeholder="English File Beginner")
|
| 178 |
+
cefr = gr.Dropdown(label="CEFR-уровень", choices=["", "A1", "A2", "B1", "B2", "C1", "C2"],
|
| 179 |
+
value="", info="Выберите уровень")
|
| 180 |
+
|
| 181 |
+
# Блок 2: Класс
|
| 182 |
+
with gr.Column(variant="panel"):
|
| 183 |
+
gr.Markdown("### 👥 Класс", elem_classes=["block-title"])
|
| 184 |
+
format_type = gr.Radio(label="Формат занятия*", choices=["Индивидуальное", "Групповое"],
|
| 185 |
+
value="Групповое")
|
| 186 |
+
|
| 187 |
+
with gr.Group(visible=True) as group_settings:
|
| 188 |
+
num_students = gr.Slider(label="Количество учеников*", minimum=1, maximum=40,
|
| 189 |
+
value=10, step=1, info="Для групповых занятий")
|
| 190 |
+
|
| 191 |
+
with gr.Group() as age_group:
|
| 192 |
+
adults = gr.Checkbox(label="Взрослые")
|
| 193 |
+
age = gr.Textbox(label="Возраст*", placeholder="10–11", interactive=True)
|
| 194 |
+
|
| 195 |
+
level_match = gr.Slider(label="Соответствие уровня учебника", minimum=0, maximum=3,
|
| 196 |
+
step=1, value=1, info="0=ниже,1=соответствует,2=выше,3=смешанный")
|
| 197 |
+
|
| 198 |
+
# Блок 3: Занятие
|
| 199 |
+
with gr.Column(variant="panel"):
|
| 200 |
+
gr.Markdown("### 📄 Занятие", elem_classes=["block-title"])
|
| 201 |
+
topic = gr.Textbox(label="Тема", placeholder="Daily routines")
|
| 202 |
+
goal = gr.Textbox(label="Цель", placeholder="Практика Present Simple")
|
| 203 |
+
duration = gr.Slider(label="Длительность (мин)*", minimum=30, maximum=180, value=60, step=5)
|
| 204 |
+
inventory = gr.Textbox(label="Инвентарь", placeholder="Карточки, проектор...")
|
| 205 |
+
|
| 206 |
+
# Блок 4: Методика
|
| 207 |
+
with gr.Column(variant="panel"):
|
| 208 |
+
gr.Markdown("### 🎓 Методика", elem_classes=["block-title"])
|
| 209 |
+
methodology = gr.Dropdown(label="Методика преподавания*",
|
| 210 |
+
choices=["PPP (Presentation-Practice-Production)",
|
| 211 |
+
"TTT (Test-Teach-Test)"],
|
| 212 |
+
value="PPP (Presentation-Practice-Production)")
|
| 213 |
+
|
| 214 |
+
advanced_btn = gr.Button(value="➕ Продвинутые настройки", size="sm")
|
| 215 |
+
|
| 216 |
+
# Блок продвинутых настроек, изначально скрыт
|
| 217 |
+
with gr.Column(visible=False) as advanced_block:
|
| 218 |
+
gr.Markdown("**💡 Уровни задач (таксономия):**")
|
| 219 |
+
repetition = gr.Checkbox(label="Повторение")
|
| 220 |
+
application = gr.Checkbox(label="Применение")
|
| 221 |
+
analysis = gr.Checkbox(label="Анализ")
|
| 222 |
+
creativity = gr.Checkbox(label="Творчество")
|
| 223 |
+
|
| 224 |
+
hw_required = gr.Checkbox(label="Домашнее задание")
|
| 225 |
+
web_search = gr.Checkbox(label="Доп. материалы из интернета")
|
| 226 |
+
|
| 227 |
+
btn = gr.Button("Сгенерировать план", variant="primary", size="lg")
|
| 228 |
+
|
| 229 |
+
with gr.Column(scale=2): # правый блок
|
| 230 |
+
output = gr.Markdown("## План урока появится здесь...")
|
| 231 |
+
download_btn = gr.DownloadButton(label="⬇️ Скачать .docx", visible=False)
|
| 232 |
+
|
| 233 |
+
|
| 234 |
+
|
| 235 |
+
# --- Логика интерфейса ---
|
| 236 |
+
def toggle_advanced_settings(visible):
|
| 237 |
+
return gr.update(visible=not visible), not visible
|
| 238 |
+
|
| 239 |
+
|
| 240 |
+
advanced_btn.click(
|
| 241 |
+
fn=toggle_advanced_settings,
|
| 242 |
+
inputs=[advanced_settings_visible],
|
| 243 |
+
outputs=[advanced_block, advanced_settings_visible]
|
| 244 |
+
)
|
| 245 |
+
|
| 246 |
+
# Функция для переключения видимости полей в зависимости от формата занятия
|
| 247 |
+
def toggle_format(selected_format):
|
| 248 |
+
return gr.update(visible=selected_format == "Групповое")
|
| 249 |
+
format_type.change(fn=toggle_format, inputs=format_type, outputs=group_settings)
|
| 250 |
+
|
| 251 |
+
# Функция для переключения поля возраста
|
| 252 |
+
def toggle_age(adult_checked):
|
| 253 |
+
return gr.update(interactive=not adult_checked)
|
| 254 |
+
adults.change(fn=toggle_age, inputs=adults, outputs=age)
|
| 255 |
+
|
| 256 |
+
|
| 257 |
+
# Коллбек генерации
|
| 258 |
+
def on_generate(
|
| 259 |
+
image_path: Optional[str], # Переименовано из image
|
| 260 |
+
textbook: str,
|
| 261 |
+
cefr: str,
|
| 262 |
+
topic: str,
|
| 263 |
+
goal: str,
|
| 264 |
+
format_type: str,
|
| 265 |
+
num_students: int,
|
| 266 |
+
age: str,
|
| 267 |
+
adults: bool,
|
| 268 |
+
level_match: int,
|
| 269 |
+
duration: int,
|
| 270 |
+
inventory: str,
|
| 271 |
+
methodology: str,
|
| 272 |
+
hw_required: bool,
|
| 273 |
+
web_search: bool,
|
| 274 |
+
repetition: bool,
|
| 275 |
+
application: bool,
|
| 276 |
+
analysis: bool,
|
| 277 |
+
creativity: bool
|
| 278 |
+
):
|
| 279 |
+
# Проверка обязательных полей
|
| 280 |
+
if not image_path or (not adults and not age):
|
| 281 |
+
return gr.update(value="❗ Заполните обязательные поля (отмечены *)"), gr.update(visible=False)
|
| 282 |
+
|
| 283 |
+
# Собираем все аргументы в словарь
|
| 284 |
+
kwargs = locals()
|
| 285 |
+
|
| 286 |
+
# Генерация плана
|
| 287 |
+
text = generate_lesson_plan(**kwargs)
|
| 288 |
+
|
| 289 |
+
# Создание DOCX
|
| 290 |
+
docx_path = generate_docx(text) if not text.startswith("❗") else None
|
| 291 |
+
return gr.update(value=text), gr.update(visible=bool(docx_path), value=docx_path)
|
| 292 |
+
|
| 293 |
+
btn.click(
|
| 294 |
+
fn=on_generate,
|
| 295 |
+
inputs=all_inputs,
|
| 296 |
+
outputs=[output, download_btn]
|
| 297 |
+
)
|
| 298 |
+
|
| 299 |
+
if __name__ == "__main__":
|
| 300 |
+
app.launch()
|
docs/architecture.md
CHANGED
|
@@ -28,6 +28,17 @@
|
|
| 28 |
|
| 29 |
|
| 30 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
|
| 32 |
|
| 33 |
|
|
|
|
| 28 |
|
| 29 |
|
| 30 |
|
| 31 |
+
Собираем все параметры в all_inputs (это список Gradio-компонентов).
|
| 32 |
+
|
| 33 |
+
Передаём их в on_generate через inputs=all_inputs.
|
| 34 |
+
|
| 35 |
+
Внутри on_generate используем locals(), чтобы автоматически получить словарь всех аргументов.
|
| 36 |
+
|
| 37 |
+
Распаковываем их в generate_lesson_plan(**kwargs).
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
|
| 42 |
|
| 43 |
|
| 44 |
|