3.3 Механизм генерации квиза ИИ
Эта глава собирает воедино рабочий генератор квизов на базе ИИ: мы настраиваем окружение и доступ к внешним сервисам, готовим датасет с предметами/категориями/фактами, проектируем промпт так, чтобы вопросы точно соответствовали выбранной категории, и связываем всё в пайплайн на LangChain. Начинаем с подготовки окружения и ключей; для чистоты вывода несущественные предупреждения можно подавить.
# Импортируем библиотеку warnings для управления предупреждающими сообщениями
import warnings
# Игнорируем все предупреждающие сообщения для обеспечения чистого вывода во время выполнения
warnings.filterwarnings('ignore')
# Загружаем API-ключи для сторонних сервисов, используемых в проекте
from utils import get_circle_ci_api_key, get_github_api_key, get_openai_api_key
# Получаем отдельные API-ключи для CircleCI, GitHub и OpenAI
circle_ci_api_key = get_circle_ci_api_key()
github_api_key = get_github_api_key()
openai_api_key = get_openai_api_key()
Далее формируем основу приложения — компактный датасет, из которого будут собираться вопросы: фиксируем предметы, категории и факты, на базе которых конструируются квизы.
# Определяем шаблон для структурирования вопросов квиза
quiz_question_template = "{question}"
# Инициализируем банк квизов с предметами, категориями и фактами
quiz_bank = """
Вот три новых вопроса квиза, следующих заданному формату:
1. Предмет: Исторический конфликт
Категории: История, Политика
Факты:
- Начался в 1914 году и закончился в 1918 году
- Вовлек два крупных альянса: Союзники и Центральные державы
- Известен обширным использованием окопной войны на Западном фронте
2. Предмет: Революционная коммуникационная технология
Категории: Технологии, История
Факты:
- Изобретена Александром Грэхемом Беллом в 1876 году
- Революционизировала дальнюю связь
- Первые переданные слова были "Мистер Уотсон, идите сюда, я хочу вас видеть"
3. Предмет: Знаковая американская достопримечательность
Категории: География, История
Факты:
- Подарена Соединенным Штатам Францией в 1886 году
- Символизирует свободу и демократию
- Расположена на острове Свободы в гавани Нью-Йорка
"""
Чтобы вопросы были релевантны выбранной пользователем категории, проектируем подробный шаблон промпта: от выбора категории через банк предметов до формулирования вопросов в установленном формате.
# Определяем разделитель для разделения различных частей промпта квиза
section_delimiter = "####"
# Создаем детальный шаблон промпта, направляющий ИИ для генерации пользовательских квизов
quiz_generation_prompt_template = f"""
Инструкции для генерации пользовательского квиза:
Каждый вопрос разделен четырьмя хештегами, т.е. {section_delimiter}
Пользователь выбирает категорию для своего квиза. Убедитесь, что вопросы релевантны выбранной категории.
Шаг 1:{section_delimiter} Определите выбранную пользователем категорию из следующего списка:
* Культура
* Наука
* Искусство
Шаг 2:{section_delimiter} Выберите до двух предметов, которые соответствуют выбранной категории из банка квизов:
{quiz_bank}
Шаг 3:{section_delimiter} Создайте квиз на основе выбранных предметов, формулируя три вопроса на предмет.
Формат квиза:
Вопрос 1:{section_delimiter} <Вставьте вопрос 1>
Вопрос 2:{section_delimiter} <Вставьте вопрос 2>
Вопрос 3:{section_delimiter} <Вставьте вопрос 3>
"""
С этим шаблоном переходим к LangChain: формируем ChatPrompt, выбираем модель и парсер, чтобы приводить ответ к читаемому виду.
# Импортируем необходимые компоненты из LangChain для структурирования промптов и взаимодействия с ИИ-моделью
from langchain.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langchain.schema.output_parser import StrOutputParser
# Преобразуем детальный промпт генерации квиза в структурированный формат для ИИ
structured_chat_prompt = ChatPromptTemplate.from_messages([("user", quiz_generation_prompt_template)])
# Выбираем языковую модель для генерации вопросов квиза
language_model = ChatOpenAI(model="gpt-4o-mini", temperature=0)
# Настраиваем парсер вывода для преобразования ответа ИИ в читаемый формат
response_parser = StrOutputParser()
Теперь связываем всё через expression language LangChain в единый конвейер, получая воспроизводимую генерацию.
# Соединяем структурированный промпт, языковую модель и парсер вывода для формирования пайплайна генерации квиза
quiz_generation_pipeline = structured_chat_prompt | language_model | response_parser
# Выполняем пайплайн для генерации квиза (пример выполнения не показан)
Далее инкапсулируем настройку и запуск генерации квиза в одну повторно используемую функцию. Это повышает модульность и упрощает поддержку. generate_quiz_assistant_pipeline
объединяет создание промпта, выбор модели и парсинг в единый рабочий процесс.
Краткий обзор: generate_quiz_assistant_pipeline
универсальна и позволяет подставлять разные шаблоны и конфигурации (модели/парсеры). Определение функции:
from langchain.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langchain.schema.output_parser import StrOutputParser
def generate_quiz_assistant_pipeline(
system_prompt_message,
user_question_template="{question}",
selected_language_model=ChatOpenAI(model="gpt-4o-mini", temperature=0),
response_format_parser=StrOutputParser()):
"""
Собирает компоненты, необходимые для генерации квизов через процесс на базе ИИ.
Параметры:
- system_prompt_message: Сообщение, содержащее инструкции или контекст для генерации квиза.
- user_question_template: Шаблон для структурирования пользовательских вопросов, по умолчанию простой заполнитель.
- selected_language_model: ИИ-модель, используемая для генерации контента, с указанной моделью по умолчанию.
- response_format_parser: Механизм для парсинга ответа ИИ-модели в желаемый формат.
Возвращает:
Пайплайн LangChain, который при выполнении генерирует квиз на основе предоставленного системного сообщения и пользовательского шаблона.
"""
# Создаем структурированный чат-промпт из системного и пользовательского сообщений
structured_chat_prompt = ChatPromptTemplate.from_messages([
("system", system_prompt_message),
("user", user_question_template),
])
# Собираем чат-промпт, языковую модель и парсер вывода в единый пайплайн
quiz_generation_pipeline = structured_chat_prompt | selected_language_model | response_format_parser
return quiz_generation_pipeline
Практическое использование. Функция скрывает сложность сборки компонентов: достаточно вызвать generate_quiz_assistant_pipeline
с нужными аргументами, чтобы генерировать квизы по темам/категориям и легко встраивать это в более крупные системы. Несколько практических советов:
- Настройка — используйте параметры для гибкой настройки процесса.
- Выбор ИИ-модели — экспериментируйте с моделями по качеству/креативности.
- Дизайн шаблонов — продумывайте
user_question_template
иsystem_prompt_message
. - Обработка ошибок — учитывайте лимиты API и неожиданные ответы.
Включение этой функции в ваш проект упрощает создание квизов на базе ИИ, позволяя создавать инновационные образовательные инструменты и интерактивный контент.
Чтобы добавить оценку качества, введём функцию evaluate_quiz_content
: она проверяет, содержит ли сгенерированный квиз ожидаемые ключевые слова по теме — это важно для релевантности и корректности контента в учебных сценариях.
Теперь про оценку содержимого. Функция интегрируется с пайплайном генерации: принимает системное сообщение (инструкции/контекст), конкретный запрос (например, квиз по теме) и список ожидаемых слов/фраз, которые должны встретиться в результате. Определение функции:
def evaluate_quiz_content(
system_prompt_message,
quiz_request_question,
expected_keywords,
user_question_template="{question}",
selected_language_model=ChatOpenAI(model="gpt-4o-mini", temperature=0),
response_format_parser=StrOutputParser()):
"""
Оценивает сгенерированный контент квиза, чтобы убедиться, что он включает ожидаемые ключевые слова или фразы.
Параметры:
- system_prompt_message: Инструкции или контекст для генерации квиза.
- quiz_request_question: Конкретный вопрос или запрос для генерации квиза.
- expected_keywords: Список слов или фраз, которые должны быть включены в контент квиза.
- user_question_template: Шаблон для структурирования пользовательских вопросов, с заполнителем по умолчанию.
- selected_language_model: ИИ-модель, используемая для генерации контента, с указанной моделью по умолчанию.
- response_format_parser: Механизм для парсинга ответа ИИ-модели в желаемый формат.
Вызывает:
- AssertionError: Если ни одно из ожидаемых ключевых слов не найдено в сгенерированном контенте квиза.
"""
# Используем функцию-помощник для генерации контента квиза на основе предоставленного вопроса
generated_content = generate_quiz_assistant_pipeline(
system_prompt_message,
user_question_template,
selected_language_model,
response_format_parser).invoke({"question": quiz_request_question})
print(generated_content)
# Проверяем, что сгенерированный контент включает хотя бы одно из ожидаемых ключевых слов
assert any(keyword.lower() in generated_content.lower() for keyword in expected_keywords), \
f"Ожидалось, что сгенерированный квиз будет содержать одно из '{expected_keywords}', но этого не произошло."
Рассмотрим пример: генерируем и оцениваем квиз по науке.
# Определяем системное сообщение (или шаблон промпта), конкретный вопрос и ожидаемые ключевые слова
system_prompt_message = quiz_generation_prompt_template # Предполагается, что эта переменная определена ранее в вашем коде
quiz_request_question = "Generate a quiz about science."
expected_keywords = ["renaissance innovator", "astronomical observation tools", "natural sciences"]
# Вызываем функцию оценки с параметрами тестового случая
evaluate_quiz_content(
system_prompt_message,
quiz_request_question,
expected_keywords
)
Этот пример показывает, как с помощью evaluate_quiz_content
убедиться, что в научном квизе есть релевантные темы (фигуры, инструменты, понятия). Полезные практики:
- Выбор ключевых слов — они должны быть достаточно конкретны, но оставлять пространство для вариативности.
- Широкая проверка — используйте несколько наборов ключей для разных тем.
- Итеративный подход — улучшайте шаблон/параметры/датасет по результатам оценки.
Структурированное тестирование помогает поддерживать качество и находить точки улучшения релевантности и вовлеченности.
Чтобы обработать запросы вне области компетенции, введём функцию evaluate_request_refusal
, которая тестирует корректный отказ в неуместных сценариях. Это важно для доверия и пользовательского опыта (UX): функция симулирует случаи, когда система должна отказать (по критериям релевантности/ограничений), и проверяет, что вернётся ожидаемое сообщение об отказе. Определение функции:
def evaluate_request_refusal(
system_prompt_message,
invalid_quiz_request_question,
expected_refusal_response,
user_question_template="{question}",
selected_language_model=ChatOpenAI(model="gpt-4o-mini", temperature=0),
response_format_parser=StrOutputParser()):
"""
Оценивает ответ системы, чтобы убедиться, что она корректно отказывает в выполнении недействительных или неприменимых запросов.
Параметры:
- system_prompt_message: Инструкции или контекст для генерации квиза.
- invalid_quiz_request_question: Запрос, который система должна отклонить.
- expected_refusal_response: Ожидаемый ответ, указывающий на отказ системы в выполнении запроса.
- user_question_template: Шаблон для структурирования пользовательских вопросов, с заполнителем по умолчанию.
- selected_language_model: ИИ-модель, используемая для генерации контента, с указанной моделью по умолчанию.
- response_format_parser: Механизм для парсинга ответа ИИ-модели в желаемый формат.
Вызывает:
- AssertionError: Если ответ системы не содержит ожидаемого сообщения об отказе.
"""
# Изменяем порядок параметров, чтобы он соответствовал ожидаемому порядку в `generate_quiz_assistant_pipeline`
generated_response = generate_quiz_assistant_pipeline(
system_prompt_message,
user_question_template,
selected_language_model,
response_format_parser).invoke({"question": invalid_quiz_request_question})
print(generated_response)
# Проверяем, что ответ системы содержит ожидаемое сообщение об отказе
assert expected_refusal_response.lower() in generated_response.lower(), \
f"Ожидалось, что система откажет с сообщением '{expected_refusal_response}', но получен ответ: {generated_response}"
Чтобы проиллюстрировать работу evaluate_request_refusal
, рассмотрим сценарий, когда генератор квизов должен отказать в создании квиза, поскольку запрос выходит за рамки компетенции или не поддерживается текущей конфигурацией.
# Определяем системное сообщение (или шаблон промпта), запрос, который должен быть отклонен, и ожидаемое сообщение об отказе
system_prompt_message = quiz_generation_prompt_template # Предполагается, что эта переменная определена ранее в вашем коде
invalid_quiz_request_question = "Generate a quiz about Rome."
expected_refusal_response = "I'm sorry, but I can't generate a quiz about Rome at this time."
# Выполняем функцию оценки отказа с указанными параметрами
evaluate_request_refusal(
system_prompt_message,
invalid_quiz_request_question,
expected_refusal_response
)
Этот пример демонстрирует способность функции тестировать реакцию генератора квизов на запрос, который должен быть отклонен: проверяя наличие ожидаемого сообщения об отказе, убеждаемся, что система ведёт себя корректно при столкновении с запросами, которые она не может выполнить. Рекомендации и советы:
- Четкие сообщения об отказе: Разрабатывайте четкие и информативные сообщения об отказе, помогающие пользователям понять, почему их запрос не может быть выполнен.
- Всестороннее тестирование: Используйте различные тестовые сценарии, включая запросы по неподдерживаемым темам или форматам, чтобы тщательно оценить логику отказа системы.
- Уточнение и обратная связь: На основе результатов тестирования дорабатывайте логику отказа и сообщения для повышения понимания и удовлетворенности пользователей.
- Учитывайте пользовательский опыт: Хотя отказ иногда необходим, рассмотрите возможность предоставления альтернативных предложений или рекомендаций для поддержания позитивного взаимодействия с пользователем.
Реализация и тестирование сценариев отказа гарантируют, что генератор квизов сможет корректно обрабатывать широкий спектр пользовательских запросов, поддерживая надежность и доверие пользователей даже тогда, когда он не может предоставить запрошенный контент.
Для адаптации предоставленного шаблона к практическому тестовому сценарию, сфокусированному на создании квиза по научной тематике, разработана функция test_science_quiz
. Эта функция призвана оценить, действительно ли сгенерированные ИИ-вопросы квиза сосредоточены вокруг ожидаемых научных тем или предметов. Интегрируя функцию evaluate_quiz_content
, мы можем убедиться, что квиз включает определенные ключевые слова или темы, характерные для научной категории.
Наконец, адаптируем evaluate_quiz_content
для научного тест‑кейса: функция проверяет, соответствует ли сгенерированный контент ожидаемым научным темам. Определение функции для тестирования научного квиза:
def test_science_quiz():
"""
Тестирует способность генератора квизов создавать вопросы, связанные с наукой, проверяя включение ожидаемых предметов.
"""
# Определяем запрос для генерации вопроса квиза
question_request = "Generate a quiz question."
# Список ожидаемых ключевых слов или предметов, которые указывают на соответствие квиза научным темам
expected_science_subjects = ["physics", "chemistry", "biology", "astronomy"]
# Системное сообщение или шаблон промпта, настроенный для генерации квиза
system_prompt_message = quiz_generation_prompt_template # This should be defined earlier in your code
# Вызываем функцию оценки с параметрами, специфичными для науки
evaluate_quiz_content(
system_prompt_message=system_prompt_message,
quiz_request_question=question_request,
expected_keywords=expected_science_subjects
)
Эта функция инкапсулирует логику проверки: при запросе по науке контент должен содержать ожидаемые научные темы/ключевые слова; вызов test_science_quiz
имитирует запрос и проверяет наличие научных тем — важный индикатор корректной генерации. Уточняйте список ключевых слов под домен и охват тем, расширяйте покрытие тестами для других категорий (история/география/искусство) и анализируйте неудачи: сопоставляйте ожидания с результатом и улучшайте логику/датасет. Структурированное тестирование помогает поддерживать качество и находить точки улучшения релевантности и вовлечённости.
Напоследок — взгляд на CI/CD: файл .circleci/config.yml
в корне репозитория описывает пайплайн (сборка/тестирование/развёртывание) в YAML. Ниже — эскиз для Python‑проекта с автотестами:
version: 2.1
orbs:
python: circleci/python@1.2.0 # Use the Python orb to simplify your config
jobs:
build-and-test:
docker:
- image: cimg/python:3.8 # Specify the Python version
steps:
- checkout # Check out the source code
- restore_cache: # Restore cache to save time on dependencies installation
keys:
- v1-dependencies-{{ checksum "requirements.txt" }}
- v1-dependencies-
- run:
name: Install Dependencies
command: pip install -r requirements.txt
- save_cache: # Cache dependencies to speed up future builds
paths:
- ./venv
key: v1-dependencies-{{ checksum "requirements.txt" }}
- run:
name: Run Tests
command: pytest # Or any other command to run your tests
workflows:
version: 2
build_and_test:
jobs:
- build-and-test
Ключевые элементы: version
— версия конфигурации (обычно 2.1); orbs
— переиспользуемые блоки, здесь python
помогает с настройкой сред; jobs
— набор задач, у нас единая build-and-test
; docker
— образ для запуска (например, cimg/python:3.8
); steps
— последовательность действий (checkout, кэш, установка зависимостей, тесты); workflows
— связывает задачи в общий процесс и запускает их по правилу.
Для настройки под себя: подберите версию Python в docker
, замените pytest
на свою команду тестов и добавьте дополнительные шаги (БД, переменные среды и т. п.) как новые - run:
. После коммита файла .circleci/config.yml
интегрированный CircleCI обнаружит конфигурацию и будет запускать пайплайн при каждом коммите в соответствии с правилами.
Теоретические вопросы:
- Каковы необходимые компоненты для настройки среды генератора квизов на базе ИИ?
- Опишите, как структурировать набор данных для генерации вопросов квиза. Включите примеры категорий и фактов.
- Как инженерия промптов влияет на генерацию кастомизированных квизов? Приведите пример шаблона промпта.
- Объясните роль LangChain в структурировании промптов для обработки ИИ-моделью.
- Что составляет пайплайн генерации квиза в контексте использования expression language LangChain?
- Как можно обеспечить релевантность и точность сгенерированного контента квиза с помощью функций оценки?
- Опишите метод тестирования способности системы отказывать в генерации квиза при определенных условиях.
- Как можно протестировать сгенерированные ИИ-вопросы квиза на соответствие ожидаемым научным темам или предметам?
- Опишите основные компоненты файла конфигурации CircleCI для проекта на Python, включая автоматический запуск тестов.
- Обсудите важность кастомизации файла конфигурации CircleCI для соответствия специфическим потребностям проекта.
Практические задания:
-
Создайте датасет квиза: Определите словарь Python под названием
quiz_bank
, который представляет собой коллекцию вопросов для квиза, каждый из которых содержит предметы, категории и факты, аналогичные приведенному примеру. Убедитесь, что ваш словарь обеспечивает легкий доступ к предметам, категориям и фактам. -
Генерация вопросов квиза с использованием промптов: Разработайте функцию
generate_quiz_questions(category)
, которая принимает категорию (например, "История", "Технологии") в качестве входных данных и возвращает список сгенерированных вопросов для квиза на основе предметов и фактов из словаряquiz_bank
. Используйте строковые операции или шаблонные строки для конструирования вопросов квиза. -
Реализация структурирования промптов LangChain: Имитируйте процесс использования возможностей LangChain, определив функцию
structure_quiz_prompt(quiz_questions)
, которая принимает список вопросов для квиза и возвращает структурированный чат-промпт в формате, аналогичном описанному, без фактической интеграции LangChain. -
Пайплайн генерации квиза: Создайте функцию Python под названием
generate_quiz_pipeline()
, которая имитирует создание и выполнение пайплайна генерации квиза, используя заполнители для компонентов LangChain. Функция должна выводить сообщение, имитирующее выполнение пайплайна. -
Повторно используемая функция генерации квизов: Реализуйте функцию Python
generate_quiz_assistant_pipeline(system_prompt_message, user_question_template="{question}")
, которая имитирует сборку компонентов, необходимых для генерации квизов. Используйте форматирование строк для конструирования детального промпта на основе входных данных. -
Оценка сгенерированного контента квиза: Напишите функцию
evaluate_quiz_content(generated_content, expected_keywords)
, которая принимает сгенерированный контент квиза и список ожидаемых ключевых слов в качестве входных данных и проверяет, содержит ли контент какое-либо из ключевых слов. Вызовите ошибку утверждения с пользовательским сообщением, если ни одно из ключевых слов не найдено. -
Обработка неверных запросов квиза: Разработайте функцию
evaluate_request_refusal(invalid_request, expected_response)
, которая имитирует оценку ответа системы на неверный запрос квиза. Функция должна проверять, соответствует ли сгенерированный ответ об отказе ожидаемому ответу об отказе. -
Science Quiz Evaluation Test: Develop a Python function
test_science_quiz()
that uses theevaluate_quiz_content
function to test if a generated science quiz includes questions related to expected scientific topics, such as "physics" or "chemistry".