блоки 2 и 3 доработки интерфейса системы тестирования
This commit is contained in:
@@ -290,13 +290,80 @@ def improve_test_full(test_title: str, test_description: str, questions: list[di
|
||||
return {'items': items}
|
||||
|
||||
|
||||
def generate_question_hint(
|
||||
*,
|
||||
question_text: str,
|
||||
options: list[dict],
|
||||
) -> str:
|
||||
"""Универсальная подсказка к вопросу: 2–4 предложения, объясняет правильный ответ."""
|
||||
cfg = get_llm_config()
|
||||
if cfg is None:
|
||||
return ''
|
||||
correct_list = '; '.join(o['text'] for o in options if o.get('isCorrect'))
|
||||
all_list = '; '.join(o['text'] for o in options)
|
||||
system = (
|
||||
'Ты опытный преподаватель. Отвечай по-русски, кратко (2–4 предложения), '
|
||||
'без markdown и без вступлений. Объясни почему правильный вариант — правильный.'
|
||||
)
|
||||
user = (
|
||||
f'Вопрос: {question_text}\n'
|
||||
f'Варианты: {all_list}\n'
|
||||
f'Правильный ответ: {correct_list or "—"}\n\n'
|
||||
'Дай краткое объяснение для подсказки во всплывающем окне.'
|
||||
)
|
||||
try:
|
||||
raw = chat_completion_text_content(cfg, system, user, 0.3, as_json=False)
|
||||
return (raw or '').strip()
|
||||
except Exception as e:
|
||||
import logging
|
||||
logging.getLogger(__name__).warning('generate_question_hint failed: %s', e)
|
||||
return ''
|
||||
|
||||
|
||||
def explain_answer(
|
||||
*,
|
||||
question_text: str,
|
||||
options: list[dict],
|
||||
selected_texts: list[str],
|
||||
is_correct: bool,
|
||||
) -> str:
|
||||
"""Генерирует короткое объяснение результата ответа на вопрос (для попапа подсказки)."""
|
||||
cfg = get_llm_config()
|
||||
if cfg is None:
|
||||
return ''
|
||||
correct_list = '; '.join(o['text'] for o in options if o.get('isCorrect'))
|
||||
sel_list = '; '.join(selected_texts) if selected_texts else '(ничего не выбрано)'
|
||||
verdict = 'верно' if is_correct else 'неверно'
|
||||
system = (
|
||||
'Ты опытный преподаватель. Отвечай по-русски, кратко (2–4 предложения). '
|
||||
'Объясни почему правильный ответ именно такой, без лишней воды и без markdown.'
|
||||
)
|
||||
user = (
|
||||
f'Вопрос: {question_text}\n'
|
||||
f'Правильный ответ: {correct_list or "—"}\n'
|
||||
f'Ответ ученика ({verdict}): {sel_list}\n\n'
|
||||
'Дай краткое объяснение для подсказки во всплывающем окне.'
|
||||
)
|
||||
try:
|
||||
raw = chat_completion_text_content(cfg, system, user, 0.3, as_json=False)
|
||||
return (raw or '').strip()
|
||||
except Exception as e:
|
||||
import logging
|
||||
logging.getLogger(__name__).warning('explain_answer failed: %s', e)
|
||||
return ''
|
||||
|
||||
|
||||
def generate_or_rephrase_question(
|
||||
test_title: str,
|
||||
test_description: str,
|
||||
question_text: str,
|
||||
options_count: Any,
|
||||
has_multiple_answers: bool,
|
||||
mode: str | None = None,
|
||||
existing_options: list[dict] | None = None,
|
||||
) -> dict:
|
||||
import json as _json
|
||||
|
||||
cfg = _require_cfg()
|
||||
try:
|
||||
n = int(float(options_count))
|
||||
@@ -308,6 +375,43 @@ def generate_or_rephrase_question(
|
||||
topic = (((test_title or '').strip() or 'Тест') + '. ' + (test_description or '').strip()).strip()
|
||||
qt = (question_text or '').strip()
|
||||
|
||||
# ── Режим дистракторов: есть вопрос + часть вариантов пуста ─────────────
|
||||
if qt and mode == 'distractors' and existing_options:
|
||||
filled = [o for o in existing_options if (o.get('text') or '').strip()]
|
||||
empty_count = len([o for o in existing_options if not (o.get('text') or '').strip()] )
|
||||
if empty_count > 0:
|
||||
filled_lines = '\n'.join(
|
||||
f'- {"✓" if o.get("isCorrect") else "✗"} {o["text"]}'
|
||||
for o in filled
|
||||
) or '(нет)'
|
||||
system = (
|
||||
'Ты составитель учебных тестов. Отвечай ТОЛЬКО JSON: '
|
||||
f'{{"options": [{{"text": string, "isCorrect": false}}, ...]}} — '
|
||||
f'ровно {empty_count} объекта в массиве. '
|
||||
'Все тексты на русском, без нумерации, без кавычек.'
|
||||
)
|
||||
user = (
|
||||
f'Тема теста: {topic}\n\n'
|
||||
f'Вопрос: {qt}\n\n'
|
||||
f'Уже заполненные варианты:\n{filled_lines}\n\n'
|
||||
f'Придумай ровно {empty_count} правдоподобных, но НЕВЕРНЫХ дистракторов '
|
||||
f'(isCorrect: false), которые не повторяют уже существующие варианты '
|
||||
f'и выглядят похоже на реальные ответы.'
|
||||
)
|
||||
raw = chat_completion_text_content(cfg, system, user, 0.45)
|
||||
parsed = parse_json_from_llm_text(raw)
|
||||
opts = []
|
||||
if isinstance(parsed, dict):
|
||||
opts = parsed.get('options') or []
|
||||
elif isinstance(parsed, list):
|
||||
opts = parsed
|
||||
opts = [
|
||||
{'text': str(o.get('text') or '').strip(), 'isCorrect': False}
|
||||
for o in opts if (o.get('text') or '').strip()
|
||||
][:empty_count]
|
||||
return {'mode': 'distractors', 'text': qt, 'options': opts}
|
||||
|
||||
# ── Режим улучшения: вопрос есть → только переформулировать текст ────────
|
||||
if qt:
|
||||
system = (
|
||||
'Ты редактор учебных материалов. Отвечай ТОЛЬКО JSON: {"text": string} — '
|
||||
@@ -325,6 +429,7 @@ def generate_or_rephrase_question(
|
||||
raise LlmError('Пустой text в ответе модели.', code='llm_shape')
|
||||
return {'mode': 'rephrase', 'text': text}
|
||||
|
||||
# ── Полная генерация: вопрос пуст ────────────────────────────────────────
|
||||
system = (
|
||||
'Ты составитель тестов. Отвечай ТОЛЬКО JSON: {"text", "hasMultipleAnswers", '
|
||||
'"options": [{ "text", "isCorrect" }]}. Все на русском.'
|
||||
|
||||
Reference in New Issue
Block a user