feat: мини-игры, premium подписка, улучшенные контексты
Мини-игры (/games): - Speed Round: 10 раундов, 10 секунд на ответ, очки за скорость - Match Pairs: 5 слов + 5 переводов, соединить пары Premium-функции: - Поля is_premium и premium_until для пользователей - AI режим проверки ответов (учитывает синонимы) - Batch проверка всех ответов одним запросом Улучшения: - Примеры использования для всех добавляемых слов - Разбиение переводов по запятой на отдельные записи - Полные предложения в контекстах (без ___) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -398,6 +398,8 @@ class AIService:
|
||||
"word": "исходное слово",
|
||||
"translation": "перевод",
|
||||
"transcription": "транскрипция (IPA или ромадзи для японского)",{furigana_instruction}
|
||||
"example": "короткий пример использования на {source_lang}",
|
||||
"example_translation": "перевод примера на {translation_lang}"
|
||||
}},
|
||||
...
|
||||
]
|
||||
@@ -405,7 +407,7 @@ class AIService:
|
||||
Важно:
|
||||
- Верни только JSON массив, без дополнительного текста
|
||||
- Сохрани порядок слов как в исходном списке
|
||||
- Для каждого слова укажи точный перевод и транскрипцию"""
|
||||
- Для каждого слова укажи точный перевод, транскрипцию и короткий пример"""
|
||||
|
||||
try:
|
||||
logger.info(f"[AI Request] translate_words_batch: {len(words)} words, {source_lang} -> {translation_lang}")
|
||||
@@ -498,6 +500,156 @@ class AIService:
|
||||
"score": 0
|
||||
}
|
||||
|
||||
async def check_translation(
|
||||
self,
|
||||
word: str,
|
||||
correct_translation: str,
|
||||
user_answer: str,
|
||||
source_lang: str = "en",
|
||||
target_lang: str = "ru",
|
||||
user_id: Optional[int] = None
|
||||
) -> Dict:
|
||||
"""
|
||||
Проверить перевод слова с помощью ИИ (для мини-игр)
|
||||
|
||||
Args:
|
||||
word: Оригинальное слово
|
||||
correct_translation: Эталонный перевод
|
||||
user_answer: Ответ пользователя
|
||||
source_lang: Язык оригинального слова
|
||||
target_lang: Язык перевода
|
||||
user_id: ID пользователя в БД
|
||||
|
||||
Returns:
|
||||
Dict с результатом проверки
|
||||
"""
|
||||
prompt = f"""Проверь перевод слова.
|
||||
|
||||
Слово ({source_lang}): {word}
|
||||
Эталонный перевод ({target_lang}): {correct_translation}
|
||||
Ответ пользователя: {user_answer}
|
||||
|
||||
Определи, правильный ли перевод пользователя. Учитывай:
|
||||
- Синонимы и близкие по смыслу слова
|
||||
- Разные формы слова (единственное/множественное число)
|
||||
- Небольшие опечатки
|
||||
|
||||
Верни JSON:
|
||||
{{
|
||||
"is_correct": true/false,
|
||||
"feedback": "краткое пояснение (почему верно/неверно, какой вариант лучше)"
|
||||
}}"""
|
||||
|
||||
try:
|
||||
logger.info(f"[AI Request] check_translation: word='{word}', user='{user_answer}'")
|
||||
|
||||
messages = [
|
||||
{"role": "system", "content": "Ты - лингвист, проверяющий переводы. Будь справедлив и учитывай синонимы."},
|
||||
{"role": "user", "content": prompt}
|
||||
]
|
||||
|
||||
response_data = await self._make_request(messages, temperature=0.2, user_id=user_id)
|
||||
|
||||
result = json.loads(response_data['choices'][0]['message']['content'])
|
||||
logger.info(f"[AI Response] check_translation: is_correct={result.get('is_correct', False)}")
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[AI Error] check_translation: {type(e).__name__}: {str(e)}")
|
||||
# В случае ошибки делаем простое сравнение
|
||||
is_correct = user_answer.lower().strip() == correct_translation.lower().strip()
|
||||
return {
|
||||
"is_correct": is_correct,
|
||||
"feedback": ""
|
||||
}
|
||||
|
||||
async def check_translations_batch(
|
||||
self,
|
||||
answers: List[Dict],
|
||||
source_lang: str = "en",
|
||||
target_lang: str = "ru",
|
||||
user_id: Optional[int] = None
|
||||
) -> List[Dict]:
|
||||
"""
|
||||
Проверить несколько переводов одним запросом (для мини-игр)
|
||||
|
||||
Args:
|
||||
answers: Список словарей с ключами: word, correct_translation, user_answer
|
||||
source_lang: Язык оригинальных слов
|
||||
target_lang: Язык перевода
|
||||
user_id: ID пользователя в БД
|
||||
|
||||
Returns:
|
||||
Список словарей с результатами проверки
|
||||
"""
|
||||
if not answers:
|
||||
return []
|
||||
|
||||
# Формируем список для проверки
|
||||
answers_text = ""
|
||||
for i, ans in enumerate(answers, 1):
|
||||
answers_text += f"{i}. {ans['word']} → эталон: {ans['correct_translation']} | ответ: {ans['user_answer']}\n"
|
||||
|
||||
prompt = f"""Проверь переводы слов с {source_lang} на {target_lang}.
|
||||
|
||||
{answers_text}
|
||||
|
||||
Для каждого слова определи, правильный ли перевод. Учитывай:
|
||||
- Синонимы и близкие по смыслу слова
|
||||
- Разные формы слова
|
||||
- Небольшие опечатки
|
||||
|
||||
Верни JSON массив:
|
||||
[
|
||||
{{"index": 1, "is_correct": true/false, "feedback": "краткое пояснение", "user_answer_meaning": "что означает ответ пользователя на {source_lang}, если это валидное слово"}},
|
||||
...
|
||||
]
|
||||
|
||||
user_answer_meaning - переведи ответ пользователя обратно на {source_lang}, чтобы показать что он на самом деле написал. Если ответ бессмысленный - оставь пустым."""
|
||||
|
||||
try:
|
||||
logger.info(f"[AI Request] check_translations_batch: {len(answers)} answers")
|
||||
|
||||
messages = [
|
||||
{"role": "system", "content": "Ты - лингвист, проверяющий переводы. Будь справедлив и учитывай синонимы. Отвечай только JSON массивом."},
|
||||
{"role": "user", "content": prompt}
|
||||
]
|
||||
|
||||
response_data = await self._make_request(messages, temperature=0.2, user_id=user_id)
|
||||
|
||||
result = json.loads(response_data['choices'][0]['message']['content'])
|
||||
logger.info(f"[AI Response] check_translations_batch: {len(result)} results")
|
||||
|
||||
# Преобразуем в удобный формат
|
||||
results_map = {r['index']: r for r in result}
|
||||
final_results = []
|
||||
|
||||
for i, ans in enumerate(answers, 1):
|
||||
if i in results_map:
|
||||
final_results.append({
|
||||
'is_correct': results_map[i].get('is_correct', False),
|
||||
'feedback': results_map[i].get('feedback', ''),
|
||||
'user_answer_meaning': results_map[i].get('user_answer_meaning', '')
|
||||
})
|
||||
else:
|
||||
# Fallback на простое сравнение
|
||||
is_correct = ans['user_answer'].lower().strip() == ans['correct_translation'].lower().strip()
|
||||
final_results.append({'is_correct': is_correct, 'feedback': '', 'user_answer_meaning': ''})
|
||||
|
||||
return final_results
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[AI Error] check_translations_batch: {type(e).__name__}: {str(e)}")
|
||||
# В случае ошибки делаем простое сравнение для всех
|
||||
return [
|
||||
{
|
||||
'is_correct': ans['user_answer'].lower().strip() == ans['correct_translation'].lower().strip(),
|
||||
'feedback': '',
|
||||
'user_answer_meaning': ''
|
||||
}
|
||||
for ans in answers
|
||||
]
|
||||
|
||||
async def generate_fill_in_sentence(self, word: str, learning_lang: str = "en", translation_lang: str = "ru", user_id: Optional[int] = None) -> Dict:
|
||||
"""
|
||||
Сгенерировать предложение с пропуском для заданного слова
|
||||
@@ -649,15 +801,17 @@ class AIService:
|
||||
"results": [
|
||||
{{
|
||||
"sentence": "предложение (с ___ для fill_blank)",
|
||||
"full_sentence": "полное предложение БЕЗ пропуска",
|
||||
"answer": "слово для пропуска (только для fill_blank)",
|
||||
"translation": "перевод на {translation_lang}"
|
||||
"translation": "ПОЛНЫЙ перевод предложения на {translation_lang} (БЕЗ пропусков, БЕЗ слов на {learning_lang})"
|
||||
}}
|
||||
]
|
||||
}}
|
||||
|
||||
Важно:
|
||||
- Для fill_blank: замени целевое слово на ___, укажи answer
|
||||
- Для fill_blank: замени целевое слово на ___, укажи answer и full_sentence
|
||||
- Для sentence_translate: просто предложение со словом, answer не нужен
|
||||
- translation должен быть ПОЛНЫМ переводом на {translation_lang}, без ___ и без слов на {learning_lang}
|
||||
- Предложения должны быть простыми (5-10 слов)
|
||||
- Контекст должен подсказывать правильное слово{furigana_instruction}
|
||||
- Верни результаты В ТОМ ЖЕ ПОРЯДКЕ что и задания"""
|
||||
@@ -842,7 +996,8 @@ class AIService:
|
||||
"word": "слово на {learning_lang} (в базовой форме)",
|
||||
"translation": "перевод на {translation_lang}",
|
||||
"transcription": "транскрипция в IPA (для английского) или хирагана (для японского)",
|
||||
"context": "предложение из текста на {learning_lang}, где используется это слово"
|
||||
"example": "предложение из текста на {learning_lang}, где используется это слово",
|
||||
"example_translation": "перевод этого предложения на {translation_lang}"
|
||||
}}
|
||||
]
|
||||
}}
|
||||
|
||||
Reference in New Issue
Block a user