Типичная картина: студент написал в чат поддержки «Не могу сдать задание 5.3 — пишет ошибку сериализации». Через 15 минут приходит ответ от первой линии: «Попробуйте перезагрузить страницу». Еще через час — «Обратитесь к методисту». Знакомо? Большинство EdTech-платформ тонут в тикетах, где 80% вопросов однотипны. И тут ИИ-агент выглядит спасителем. Но обычно его внедрение превращается в эпопею с LangChain, Pinecone, Kubernetes и командой из пяти человек.
На самом деле для прототипа достаточно одного файла на Python, API Gemini и JSON-планировщика. Без фреймворков. Без векторных баз. За два часа.
Почему так много хлама?
Рынок AI-агентов для EdTech перенасыщен. Каждый второй стартап продает «интеллектуального тьютора на RAG». Но RAG (Retrieval-Augmented Generation) — это оверинжиниринг для 80% задач поддержки. Студенту не нужен поиск по 10 000 документов — ему нужно:
- получить ответ по конкретному заданию из базы знаний курса;
- понять, почему упал его код (если курс по программированию);
- узнать статус проверки работы;
- связаться с живым преподавателем, если ответ не найден.
Это четыре сценария. Их можно реализовать на чистом Python с одним промптом и JSON-ответом. Никаких эмбеддингов, никаких векторных баз. Если вам интересно, как вообще выглядит создание AI-агента с нуля, советую почитать гайд по выбору инструментов и созданию ИИ-агента для IT-компании — там описана общая методология.
Архитектура: агент как конечный автомат
Мы строим не бота, а агента. Разница принципиальная. Бот — это if-else на промптах: «Если спросили про задание 5.3, верни ответ из базы». Агент же сам принимает решение, какое действие выполнить, на основе текущего контекста.
Архитектура простая:
- Пользователь пишет сообщение.
- Агент формирует промпт с инструкциями, историей диалога и доступными действиями.
- Gemini возвращает JSON с выбранным действием и аргументами.
- Python код выполняет действие (ищет в базе, вызывает API, эскалирует).
- Результат возвращается пользователю.
Этот цикл — суть агента. Мы не используем фреймворки в духе LangChain или CrewAI. Кстати, в статье «Когда фреймворки слишком сложны» хорошо показано, что альтернативы без фреймворков не только существуют, но и часто работают эффективнее.
Gemini в роли мозга
Почему именно Gemini? У Gemini классный JSON mode: достаточно добавить системный промпт с требованием вернуть JSON определенной схемы, и модель (особенно свежие версии, такие как Gemini 2.5 Pro или Gemini 3 Flash, о возможностях которой мы недавно писали) отдает структурированный ответ без галлюцинаций по формату. Это позволяет нам намертво зашить логику агента прямо в промпт, не выходя из Python.
Кстати, для доступа к Gemini из регионов с ограничениями можно использовать AITunnel — это единый API-шлюз, предоставляющий стабильный доступ к мощнейшим мировым нейросетям через один эндпоинт. Пара строк кода — и Gemini доступен.
Пишем код: EdTechAgent
Создадим класс EdTechAgent. Конструктор принимает API-ключ Gemini, путь к файлу с базой знаний (простой JSON) и список ответственных за эскалацию.
import json
import google.generativeai as genai
from typing import Dict, Optional
class EdTechAgent:
def __init__(self, api_key: str, knowledge_base_path: str, escalation_contact: str):
genai.configure(api_key=api_key)
self.model = genai.GenerativeModel('gemini-2.5-pro')
with open(knowledge_base_path, 'r', encoding='utf-8') as f:
self.knowledge_base = json.load(f)
self.escalation_contact = escalation_contact
self.conversation_history = []
База знаний — это словарь, где ключи — темы (например, 'assignment_5.3'), а значения — готовые ответы или ссылки на материалы. Никаких векторных индексов.
1 Промпт с JSON-схемой
Ключевая часть — системный промпт, который заставляет Gemini возвращать JSON строго определённого формата. Вот как он выглядит:
SYSTEM_PROMPT = """Ты — ИИ-куратор образовательной платформы.
Твоя задача — помочь студенту. У тебя есть три действия:
1. answer — дать ответ на вопрос, если он есть в базе знаний.
2. escalate — передать вопрос живому преподавателю, если ответа нет.
3. fetch_external — запросить данные из внешнего API (статус проверки и т.п.).
Верни JSON с ключами:
"action": "answer" | "escalate" | "fetch_external",
"reason": "краткое обоснование",
"data": {} # для answer: {"text": "..."}; для fetch_external: {"endpoint": "...", "params": {}}; для escalate: {"contact": "...", "summary": "..."}
База знаний:
{knowledge_base}
История диалога:
{history}
Вопрос: {question}
"""
Грабли: если не указать примеры корректного JSON, Gemini может начать галлюцинировать поля. Поэтому в продакшене лучше добавить в промпт 1-2 примера (few-shot).
2 Обработка ответа и выполнение действия
Метод process_message принимает текст вопроса, вызывает модель, парсит JSON и выполняет действие.
def process_message(self, question: str) -> str:
kb_json = json.dumps(self.knowledge_base, ensure_ascii=False, indent=2)
history_str = chr(10).join([f'{msg["role"]}: {msg["text"]}' for msg in self.conversation_history])
prompt = SYSTEM_PROMPT.format(
knowledge_base=kb_json,
history=history_str,
question=question
)
response = self.model.generate_content(prompt)
# Принудительно заставляем модель вернуть JSON
response.candidates[0].safety_settings = None # упрощённо
try:
decision = json.loads(response.text)
except json.JSONDecodeError:
# Fallback — эскалация
decision = {"action": "escalate", "reason": "Ошибка парсинга ответа модели", "data": {}}
if decision['action'] == 'answer':
answer = decision['data'].get('text', 'Извините, не могу найти ответ.')
self.conversation_history.append({'role': 'assistant', 'text': answer})
return answer
elif decision['action'] == 'fetch_external':
endpoint = decision['data']['endpoint']
params = decision['data'].get('params', {})
# имитация вызова API
result = self._call_external_api(endpoint, params)
self.conversation_history.append({'role': 'assistant', 'text': result})
return result
elif decision['action'] == 'escalate':
summary = decision['data'].get('summary', question)
# здесь можно отправить тикет
msg = f'Передал вопрос живому куратору. {summary}'
self.conversation_history.append({'role': 'assistant', 'text': msg})
return msg
else:
return 'Неизвестное действие. Попробуйте переформулировать вопрос.'
3 Запуск агента
if __name__ == '__main__':
agent = EdTechAgent(
api_key='YOUR_GEMINI_KEY',
knowledge_base_path='knowledge.json',
escalation_contact='teacher@edtech.ru'
)
while True:
user_input = input('Студент: ')
if user_input == 'exit':
break
print('Агент:', agent.process_message(user_input))
Что внутри knowledge.json?
Файл выглядит примерно так:
{
"assignment_5.3": {
"error_serialization": "Ошибка возникает, если не закрыть файл после записи. Добавьте вызов file.close() или используйте контекстный менеджер with.",
"common_questions": [
{"q": "не могу сдать", "a": "Проверьте, что файл сохранён в кодировке UTF-8."}
]
},
"payment_issue": {
"invoice": "Счёт можно скачать в личном кабинете в разделе 'Платежи'."
}
}
Если запрос студента не совпадает ни с одним ключом, Gemini принимает решение об эскалации. Всё как мы запланировали.
Но что, если вопрос про код, который написал студент?
Тут вступает в дело fetch_external. Агент может вызвать внешний API (например, sandbox-сервер с кодом студента), получить ошибку и уже с ней вернуться к Gemini для генерации ответа. Это уже более продвинутая логика, похожая на концепцию математического агента Aletheia, о котором мы писали ранее — там Gemini проверяет свои же рассуждения. Здесь агент может попросить Gemini проанализировать вывод ошибки.
Типичные ошибки и как их обходить
Ошибка 1: Не парсить JSON из ответа Gemini как строку, а полагаться на structured output. Gemini 2.5 Pro поддерживает response_mime_type='application/json'. Используйте это.
Вот как правильно:
response = self.model.generate_content(
prompt,
generation_config=genai.types.GenerationConfig(
response_mime_type='application/json'
)
)
Это исключает ошибки парсинга (почти).
Ошибка 2: Забывать про контекстное окно. История диалога растёт, и вы превышаете лимит токенов. Решение: обрезать историю до последних N сообщений или суммаризировать.
Ошибка 3: Жёстко зашивать базу знаний в промпт. Если база большая (больше 50 000 токенов), модель не сможет её переварить. Но для EdTech-поддержки по одному курсу база редко превышает 20-30 страниц текста — это влезает в контекст Gemini 2.5 Pro (2 млн токенов).
Куда двигаться дальше?
Наш прототип — это минимально жизнеспособный продукт. Следующие шаги: добавить возможность агента выполнять код в песочнице (как в Gemini 2.5 Computer Use), интегрировать с LMS через API, реализовать self-healing (повторный вызов Gemini при неудаче). И да, если вы захотите перейти на полностью локальное решение без облака, есть альтернатива — сборка локального AI-агента без облака и VPN.
Но самое главное — не усложняйте. Если вы держите базу знаний в JSON и используете Gemini с JSON mode, вы уже получите агента, способного решить 80% проблем студентов. А для остальных 20% у вас есть эскалация.
FAQ: вопросы, которые вы зададите
Почему не использовать LangChain?
LangChain удобен для сложных цепочек, но для простого агента-куратора он добавляет лишнюю сложность. Наш код выполняется, не требуя установки десятков зависимостей. Если вы только начинаете, лучше написать свой велосипед — так вы поймёте, что происходит под капотом.
А как быть с галлюцинациями?
Gemini в JSON mode уменьшает вероятность галлюцинаций, но не исключает. Лучшая защита — держать ответы в базе знаний и заставлять модель цитировать источник. Также можно добавить второй проход: другой промпт проверяет ответ на соответствие базе.
Сколько это стоит?
Gemini 2.5 Pro стоит около $1.25 за 1 млн входных токенов и $5 за 1 млн выходных. Для чата поддержки с 1000 студентов в день, каждый диалог 10 сообщений — это $5-10 в день. Дешевле живого оператора. А если использовать Gemini 3 Flash (она упоминалась в нашей статье), стоимость будет кратно ниже.
Что насчёт безопасности?
Не передавайте в промпт чувствительные данные студентов. Gemini не должен видеть пароли, персональные данные. Используйте анонимизацию. Для полного контроля — локальный агент, о котором мы писали отдельно.
В конечном счёте, создание ИИ-агента не требует магии. Просто Python, хороший промпт и умение слушать свою модель. Попробуйте — удивитесь, как мало нужно для большого результата.