Пошаговое руководство по созданию ИИ-репетитора с верификацией ответов на Python. Узнайте, как использовать 50+ академических движков и RAG.
Представьте: студент задает вопрос ChatGPT, получает красивый ответ, но на экзамене проваливается. Почему? Потому что языковые модели угадывают, а не вычисляют. Они не проверяют свои ответы, не знают вашу учебную программу и могут уверенно выдать ложь. В этой статье я покажу, как построить ИИ-репетитора, который не просто генерирует, а верифицирует каждый ответ с помощью академических движков — от SymPy до ChemPy. Вы узнаете, как реализовать каскадный поиск, подключить 50+ проверочных движков и создать офлайн-приложение, которое реально помогает студентам.
Система состоит из нескольких ключевых компонентов: FastAPI для бэкенда, LLM для генерации, векторная база Qdrant для семантического поиска, Meilisearch для ключевых слов, и главное — движок верификации, который пересчитывает ответы. Студент задает вопрос, система ищет релевантный контекст, генерирует ответ, а затем проверяет его через математические, физические, химические и инженерные библиотеки. Если ответ неверен — исправляет и объясняет ошибку.
Пользовательский вопрос проходит через 5 этапов поиска: сначала ищем в конспектах (векторный поиск), затем в учебной программе (ключевой поиск), потом в академических источниках (гибридный), далее в интернете, и в последнюю очередь — прямой вызов LLM. На каждом этапе контекст добавляется в запрос, а финальный ответ проверяется движками.
Секретное оружие UniUI — это перекрестная проверка каждого сгенерированного ответа. LLM генерирует ответ, затем определяется предметная область, и ответ передается в соответствующие библиотеки: SymPy для математики, ChemPy для химии, PyNiteFEA для инженерии. Если результат расходится — система возвращает исправленный ответ с объяснением.
Допустим, студент просит вычислить интеграл от x². LLM может дать неправильный ответ, но SymPy вычислит точно:
import sympy as sp
import numpy as np
from typing import Dict, Any, Optional
def verify_math_answer(question: str, llm_answer: str) -> Dict[str, Any]:
"""
Проверяет ответ по математике с помощью SymPy.
Возвращает: {"verified": bool, "correct_answer": str, "derivation": str, "confidence": float}
"""
try:
# Шаг 1: Парсим вопрос и извлекаем выражение
# Шаг 2: Преобразуем в выражение SymPy
# Шаг 3: Вычисляем правильный ответ
# Шаг 4: Сравниваем с ответом LLM
# Шаг 5: Возвращаем результат
x = sp.Symbol('x')
expression = sp.integrate(x**2, x) # Результат: x**3/3
return {
"verified": True,
"correct_answer": str(expression),
"derivation": "∫x²dx = x³/3",
"confidence": 1.0
}
except Exception as e:
return {
"verified": False,
"correct_answer": None,
"error": str(e),
"confidence": 0.0
}
Этот код — лишь иллюстрация. В реальной системе вы парсите вопрос, извлекаете математическое выражение, вычисляете его и сравниваете с ответом LLM. Если расхождение — исправляете.
Перед генерацией ответа система ищет контекст. Это критично для точности. Вот как реализован каждый этап.
def vector_search(query: str, faculty: Optional[str] = None) -> List[Dict]:
"""Поиск семантически похожего контента в Qdrant."""
embedding = get_embedding(query)
filter_condition = None
if faculty:
filter_condition = {"must": [{"key": "faculty", "match": {"value": faculty}}]}
results = qdrant_client.search(
collection_name="uniui_documents",
query_vector=embedding,
query_filter=filter_condition,
limit=10,
score_threshold=0.7
)
return results
def keyword_search(query: str) -> List[Dict]:
"""Поиск по ключевым словам в Meilisearch."""
results = meilisearch_client.index("curriculum").search(
query,
{"attributesToRetrieve": ["title", "content", "course_code"], "limit": 10}
)
return results["hits"]
def hybrid_search(query: str) -> List[Dict]:
"""Комбинация векторного и ключевого поиска через RRF."""
vector_results = vector_search(query)
keyword_results = keyword_search(query)
fused = reciprocal_rank_fusion(vector_results, keyword_results, alpha=60)
return fused[:10]
def internet_search(query: str) -> List[Dict]:
"""Поиск в интернете через Exa или SerpAPI."""
try:
results = exa_client.search(query, type="neural", num_results=5)
return results["results"]
except Exception:
return jina_search(query)
def direct_llm(query: str) -> str:
"""Генерация ответа напрямую из LLM без контекста."""
response = groq_client.chat.completions.create(
model="llama-3.3-70b-versatile",
messages=[
{"role": "system", "content": "Вы строгий академический репетитор. Отвечайте точно."},
{"role": "user", "content": query}
]
)
return response.choices[0].message.content
Теперь объединим все в единый эндпоинт. Маршрутизатор принимает вопрос, выполняет каскадный поиск, генерирует ответ и верифицирует его.
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
from typing import List, Optional
from academic_pipeline.backend.verifiers import verify_answer
from academic_pipeline.backend.retrieval import hybrid_search
from academic_pipeline.backend.llm import call_llm
router = APIRouter()
class AskRequest(BaseModel):
question: str
faculty: Optional[str] = None
course_code: Optional[str] = None
class AskResponse(BaseModel):
answer: str
verified: bool
verified_answer: Optional[str] = None
sources: List[Dict[str, str]]
engine_used: str
confidence: float
@router.post("/ask", response_model=AskResponse)
async def ask_question(request: AskRequest):
"""
Главный эндпоинт для вопросов.
1. Поиск контекста (5-этапный каскад)
2. Генерация ответа LLM с контекстом
3. Верификация ответа академическими движками
4. Возврат проверенного результата
"""
# Шаг 1: Поиск контекста
contexts = hybrid_search(request.question)
if not contexts:
# Этап 5: Прямой вызов LLM
llm_answer = await direct_llm(request.question)
sources = []
else:
# Этапы 1-4: LLM с контекстом
llm_answer, sources = await generate_with_context(request.question, contexts)
# Шаг 2: Верификация ответа
verification_result = await verify_answer(
question=request.question,
answer=llm_answer,
faculty=request.faculty
)
# Шаг 3: Формирование ответа
return AskResponse(
answer=verification_result.get("answer", llm_answer),
verified=verification_result.get("verified", False),
verified_answer=verification_result.get("correct_answer"),
sources=sources,
engine_used=verification_result.get("engine_used", "groq"),
confidence=verification_result.get("confidence", 0.0)
)
Реестр верификаторов позволяет легко добавлять новые предметные движки. Используйте декоратор для регистрации функций.
from typing import Dict, Any, Optional
import importlib
import inspect
VERIFIERS = {}
def register_verifier(subject: str):
def decorator(func):
VERIFIERS[subject] = func
return func
return decorator
async def verify_answer(question: str, answer: str, faculty: Optional[str] = None) -> Dict[str, Any]:
"""Верифицирует ответ, используя подходящий движок."""
subject = detect_subject(question, faculty)
verifier = VERIFIERS.get(subject)
if not verifier:
return {"verified": False, "answer": answer, "engine_used": "none", "confidence": 0.0}
try:
result = await verifier(question, answer)
result["engine_used"] = subject
return result
except Exception as e:
return {"verified": False, "answer": answer, "engine_used": subject, "confidence": 0.0, "error": str(e)}
Для маршрутизации к правильному движку используйте ключевые слова.
KEYWORDS = {
"math": ["интеграл", "производная", "математика", "матрица", "уравнение"],
"physics": ["сила", "скорость", "энергия", "импульс", "гравитация"],
"chemistry": ["моль", "реакция", "кислота", "щелочь", "стехиометрия"],
# ... другие предметы
}
def detect_subject(question: str, faculty: Optional[str] = None) -> str:
# Простой анализ ключевых слов
for subject, keywords in KEYWORDS.items():
if any(kw in question.lower() for kw in keywords):
return subject
return "general"
Вы можете начать с малого: возьмите один предмет, например математику, и подключите SymPy для верификации. Создайте FastAPI-эндпоинт, который принимает вопрос, генерирует ответ через Groq и проверяет его. Затем добавьте поиск по учебным материалам через Qdrant. Постепенно расширяйте до 50+ движков. Главное — не давайте студентам непроверенные ответы. Начните с простого прототипа, протестируйте его на реальных вопросах и улучшайте. Уже через неделю у вас будет работающий ИИ-репетитор, которому можно доверять.
Хочешь закрепить знания на практике?
Решай задачи на Algolit — интерактивная платформа для обучения
Начать бесплатно →