Зачем я это пишу
Запустил недавно прод-агента на Claude — небольшой, для бытовой задачи у знакомой. По дороге понял несколько вещей, которые сначала казались мне очевидными «после интернета», а потом, когда сел и проверил на реальной задаче, начали играть иначе. Хочется поделиться разбором — может, кому-то сэкономит неделю-другую на тех же развилках.
Сразу честно: я не «академический» разработчик. До последнего года писал на Python в основном чтобы прочитать чужой скрипт и подправить отступ. То, что вы увидите в коде ниже — собирал руками, лезя в документацию по каждому слою. Поэтому это не «гайд от эксперта», а скорее дневник развилок — где я выбрал так и так и почему. Если у вас опыт глубже и я где-то перегнул палку или упустил — буду благодарен за поправки в комментариях, для меня это самая ценная часть таких публикаций.
Никаких ссылок, кодов промокодов и «закажите у нас» — просто разбор.
TL;DR
Сделал ИИ-агента для небольшой студии дизайна интерьера. Делает две вещи:
-
Чат на сайте — отвечает на типовые вопросы про услуги, тарифы, сроки, мягко переводит на встречу с дизайнером.
-
Эндпоинт
/brief— клиент заполняет короткий бриф через форму, на выходе получает три заметно разных концепции интерьера: палитра, материалы, мебель, свет, четыре фото из Pexels на каждую.
Стек: Claude Haiku 4.5 для чата, Claude Sonnet 4.6 для генерации концепций, FastAPI на Railway, Pexels API для фото, статичный фронт на . Без LangChain, без LlamaIndex, без векторной базы, без RAG-фреймворка. Один файл main.py на 712 строк, два промпта, четыре эндпоинта.
В статье — почему именно этого хватило, какие развилки прошёл, на каких граблях постоял и где этот подход начнёт ломаться.
Контекст
Заказ: у знакомой — небольшая студия дизайна интерьера. Один дизайнер, проектов в работе одновременно три-четыре. Бюджет на разработку — несколько рабочих дней (это я сам себе ставил, у нас был дружеский договор). Боль на входе воронки понятная: клиенты пишут в WhatsApp в одиннадцать вечера, утром половина уже у конкурентов, потому что никто им не ответил. И вторая боль — клиент часто сам не понимает, чего хочет, и приходит на встречу «расскажите мне что-нибудь».
Из этих двух болей выросли две сущности:
-
Чат-агент — отвечает мгновенно ночью, не оставляет сообщения «висеть до утра».
-
/brief— генератор трёх концепций по короткому брифу. Не заменяет дизайнера, а готовит первую встречу: клиент приходит уже с понятием, какое направление откликается.
Это не «универсальный бот для всего». Это две узкие функции с понятной границей ответственности. Дальше будет важно, почему именно эта узость позволила обойтись без фреймворков.
Развилка №1: брать ли LangChain
Начинал я с распространённого представления, что любой ИИ-агент = LangChain (для оркестрации) + векторная база (Chroma/Qdrant/Pinecone) для контекста + RAG-цепочка для актуальных данных + агент-роутер с тулами. Так пишут в большинстве гайдов и так делают «взрослые». Хотел тащить это в проект, потому что страшно было пилить «не как у людей».
Прежде чем тащить — сел и расписал, что из этого реально решает мою задачу. Получилось вот что:
-
Оркестрация цепочек? У меня одна цепочка: сообщение → один запрос к LLM → ответ. Никаких ветвлений, параллельных шагов, ретраев с разными моделями. LangChain тут — обёртка вокруг
httpx.post(). -
RAG для тарифов и услуг? Весь контент студии помещается в 2,5 тысячи токенов системного промпта. Это меньше, чем один embedding-запрос в Pinecone. И главное — этот контент не меняется каждую неделю. Тарифы — раз в квартал, услуги ещё реже.
-
Векторная база? Под что? Под историю диалога — сессии короткие, 5–10 сообщений, помещаются в окно. Под услуги — их семь штук. Под похожих клиентов — у дизайнера 30 проектов в год, векторный поиск тут неуместен по размерности.
-
Агент-роутер с тулами? Чат не должен никуда ходить во внешний мир — ни в календарь, ни в CRM, ни в базу. У
/briefесть один тул (поиск фото в Pexels), и он жёстко привязан к структуре ответа, не выбирается агентом.
Когда сложил это в табличку, оказалось — из всех абстракций LangChain я бы использовал ровно одну, ChatAnthropic как обёртку над HTTP-клиентом. И эта обёртка добавляет: зависимость на ~500 МБ, новый слой с собственными багами, версионную нестабильность (LangChain известен breaking changes между минорными релизами), новый язык описания цепочек, который пришлось бы учить.
В обмен — chain.invoke() вместо прямого POST. Всё.
Никакого LangChain.
Это не значит, что LangChain — плохой инструмент. Это значит, что для узких задач с одной цепочкой он избыточен. Граница, на которой он начинает окупаться: 3+ параллельных цепочки, реальный роутинг между моделями, сложный multi-step агент с тулами. Если у вас одно «вопрос → ответ» — берите голый HTTP.
Это не моё гениальное озарение — это просто результат честного разбора задачи. Если у вас контр-пример, когда LangChain заметно окупился именно на простой задаче, очень интересно его услышать, я могу что-то упускать.
Системный промпт вместо RAG
Раз весь контент студии помещается в SYSTEM, я просто положил его туда. Структура промпта — не «творческий текст», а строгий FAQ-документ. Ниже укороченный фрагмент (полный — около 5500 токенов вместе с эталонами ответов):
Ты — ассистент студии дизайна интерьера. Отвечай вежливо, по делу,
в спокойном профессиональном тоне. Без лишних эмодзи. Только на русском.
Короткие чёткие ответы.
О СТУДИИ:
- Дизайн интерьера жилых пространств в Москве
- Сайт студии и контакты — см. правила маршрутизации ниже
УСЛУГИ:
1. Функциональное планирование — планировочные решения, перепланировки
2. Дизайн-проект — цельный визуальный образ, материалы, рабочая логика
3. Рабочая документация — чертежи и технические решения
...
ТАРИФЫ:
Три тарифа с разной глубиной проработки. Включают замер, фотофиксацию
и договор. Подробности — на сайте студии (правила маршрутизации ниже).
ПРАВИЛА:
- Вопрос о записи/консультации → дать контакт студии
- Вопрос о цене → объяснить тариф и что входит, без точных цифр
если клиент не уточнил параметры объекта
- Хочет посмотреть варианты / не знает чего хочет → предложи /brief
- Не придумывай информацию которой нет выше
Подсвечу важное: разделы написаны человеческим языком в правильной структуре. «УСЛУГИ» — нумерованный список с одной строкой описания. «ТАРИФЫ» — словесное описание уровней без точных сумм в промпте (это спорное решение, у нас тарифы менялись пару раз за квартал, и я не хотел перекатывать SYSTEM каждый раз). «ПРАВИЛА» — однозначная маршрутизация типовых интентов.
Модель в такой структуре не теряется. Когда клиент спрашивает «сколько стоит планировка», Haiku видит, что «планировочное решение» упомянуто в услугах, и отвечает связно. Если бы тот же контент я залил в Chroma чанками по 512 токенов и доставал через RAG, я бы получил тот же результат, но: с задержкой на embedding-запрос, риском «не доехал нужный чанк», необходимостью поддерживать векторную базу как отдельный сервис. Зачем — я честно не нашёл ответа.
Эндпоинт /chat целиком
Это полный эндпоинт чата. Он короче, чем README типичного LangChain-стартера:
@app.post("/chat")
async def chat(request: Request):
body = await request.json()
messages = body.get("messages", [])
if not messages:
return JSONResponse({"error": "messages is empty"}, status_code=400)
async with httpx.AsyncClient(timeout=60) as client:
response = await client.post(
"https://api.anthropic.com/v1/messages",
headers={
"x-api-key": ANTHROPIC_API_KEY,
"anthropic-version": "2023-06-01",
"content-type": "application/json",
},
json={
"model": "claude-haiku-4-5-20251001",
"max_tokens": 2000,
"system": [{
"type": "text",
"text": SYSTEM,
"cache_control": {"type": "ephemeral"},
}],
"messages": messages,
},
)
data = response.json()
text = "".join(
b.get("text", "") for b in data.get("content", []) if b.get("type") == "text"
).strip()
return JSONResponse({"reply": text, "usage": data.get("usage")})
Разберу несколько решений по этому коду:
Модель claude-haiku-4-5-20251001. Самый маленький Claude — быстрый, дешёвый, по нашим задачам (короткие ответы по плотному FAQ) не уступает Sonnet. Время первого токена — ~600 мс, полного ответа — 1–2 секунды. Стоимость на нашем трафике — около копейки за диалог.
max_tokens=2000. Запас, который реально не используется — типичный ответ помещается в 100–300 токенов. Поставил с резервом на случай «расскажи подробно про каждый тариф».
system как список объектов с cache_control. Это включение prompt caching у Anthropic. Если SYSTEM-промпт не меняется между запросами, API его кэширует, повторные запросы считают только новые сообщения. Хит даёт скидку 90% на input-токены и режет латентность первого токена в 2 раза. Про грабли этого механизма дальше отдельный раздел.
messages приходит целиком от клиента. История диалога хранится во фронтенде в localStorage, не на сервере. Это осознанное упрощение: пока нет аутентификации и нет необходимости хранить переписку, не хочется мучиться с базой и доступами. Когда понадобится — добавлю SQLAlchemy и одну таблицу.
Никакого retry, fallback, circuit breaker. Anthropic стабилен — за квартал ~99,95% успешных запросов. Заворачивать в tenacity с пятью попытками — преждевременная оптимизация. Если упадёт — клиент перезагрузит страницу.
Это «минимально достаточный код». Один файл, одна функция, прямой запрос. Тесты пишутся за пять минут — мокаешь httpx.AsyncClient и проверяешь, что отправляется правильный body.
Главное открытие: FAQ-эталоны вместо чёрного списка запретов
Самой нетривиальной частью оказалось не научить ассистента отвечать, а научить молчать. Первые версии фантазировали: обещали невозможные сроки, путали тарифы, со второго сообщения начинали продавать встречу. То, что для меня казалось «модель должна сама понять, что это плохо» — она не понимала.
Первый порыв — добавить чёрный список:
ЗАПРЕТЫ:
- Не обещай сроки меньше 2 месяцев
- Не предлагай встречу до того, как узнал площадь
- Не используй слова «гарантирую», «точно», «безусловно»
- Не упоминай конкурентов
- Не давай скидок
- Не пиши маркированными списками, только связным текстом
...
Это не работало. К концу второй недели у меня было 23 пункта запретов. Ассистент всё равно их нарушал через раз, а сам промпт превратился в нечитаемое полотно «не делай X». И ещё тонкая вещь, которую я заметил: чёрные списки в принципе ухудшают качество ответов — модель будто фокусируется на «как обойти запреты» вместо «как помочь клиенту».
Переломный момент — переход на FAQ-эталоны в самом теле SYSTEM. Вместо «не делай так» — «вот эталонный ответ на типовой вопрос». Структура такая:
ВОПРОС: Сколько стоит проект двушки?
ОТВЕТ-ЭТАЛОН: «Зависит от тарифа и площади. У нас три уровня
проработки. Если назовёте метраж, могу подсказать ориентир по
каждому. Если интересно, могу подсказать, какой подходит под
вашу задачу».
ВОПРОС: Когда вы свободны на консультацию?
ОТВЕТ-ЭТАЛОН: «Запись на встречу — через контакт студии,
дизайнер ответит в течение дня и согласует время напрямую».
ВОПРОС: А вы можете спроектировать всё без меня, я не понимаю в дизайне?
ОТВЕТ-ЭТАЛОН: «Полностью без вас — не получится, ваши предпочтения
и образ жизни нужны как материал. Но если непонятно с чего начать,
у нас на сайте есть бесплатная концепция — за минуту собирается
три направления, можно посмотреть, что откликается».
Эта схема резко улучшила сдержанность. Модель видит готовый паттерн — длину ответа, тон, структуру, способ обработки ситуации — и воспроизводит его на похожих вопросах. Чёрный список из 23 пунктов схлопнулся в 4 правила маршрутизации (раздел «ПРАВИЛА» выше).
Почему это работает? Моя рабочая гипотеза: LLM — это машина имитации паттернов, а не машина следования инструкциям. Когда говоришь «не делай X», у модели нет образца того, что делать вместо X — она просто стохастически снижает вероятность токенов, связанных с X. Когда даёшь эталонный пример, она тиражирует его стилистически. Эталон — это положительный пример, чёрный список — отрицательный, и для имитативных моделей положительные примеры эффективнее.
Это не моё открытие — про важность few-shot examples писано тысячу раз. Что было откровением лично для меня — что 10–15 эталонов внутри SYSTEM работают лучше, чем 50 запретов и 5 эталонов отдельно. Эталоны делают всю тяжёлую работу.
Грабли с порогом кэширования 4096 у Haiku 4.5
Anthropic в 2024 году выкатил prompt caching. Если SYSTEM-промпт длинный и стабильный, API его кэширует на сервере, повторные запросы платят только за uncached input. На длинных промптах — до 90% экономии и латентность первого токена режется в 2 раза.
Я разметил кэширование в /chat сразу:
"system": [{
"type": "text",
"text": SYSTEM,
"cache_control": {"type": "ephemeral"},
}],
И смотрю на usage в ответах API — cache_creation_input_tokens и cache_read_input_tokens стабильно нули. Кэш не работает. Я месяц думал, что у меня баг в коде. Проверял версии API, разрешения, ставил debug-логи. Никаких ошибок.
Оказалось — у Haiku 4.5 минимальный размер кэшируемого блока 4096 токенов. У Sonnet и Opus — 1024. Если блок короче — Anthropic просто тихо игнорирует cache_control, без warning, без ошибки в ответе. Нигде это не подсвечено крупно в документации, нашёл в одной строке в таблице параметров кэширования.
Мой SYSTEM на момент проблемы был ~2500 токенов. Для Sonnet кэш бы работал, для Haiku — нет. Было два варианта:
-
Раздуть SYSTEM до 4096+, добавив FAQ-эталоны и подробные правила. Это улучшает качество ответов в любом случае (см. предыдущий раздел), плюс активирует кэш.
-
Переключиться на Sonnet. Дороже, медленнее, для нашего чата избыточен.
Выбрал первый. Сейчас SYSTEM — ~5500 токенов, кэш активен. На каждом запросе вижу:
"usage": {
"input_tokens": 47,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 5483,
"output_tokens": 178
}
Экономия на 1000 диалогах: вместо 5,5 млн input-токенов почти всё считается как cached, цена режется примерно в 10 раз.
Что забрать: если активируете caching на Haiku — следите, чтобы блок был ≥4096 токенов, иначе тихо ничего не работает. Для Sonnet и Opus порог 1024. Это, мне кажется, должно быть в документации красным жирным шрифтом, но пока нет.
Эндпоинт /brief: структурированный ответ от Sonnet 4.6
Второй эндпоинт сложнее. От /chat отличается двумя вещами:
-
здесь нужен Sonnet 4.6 (не Haiku) — задача креативная, нужен качественный язык и заметная разница между концепциями;
-
ответ должен быть строго JSON-массивом, без markdown-обёрток, парсимый напрямую.
Промпт начинается так:
Ты — арт-директор студии интерьерного дизайна.
Студия: тёплый минимализм, Japandi, скандинавский, функциональное
проектирование жилых пространств.
Тебе приходит бриф клиента. Твоя задача — выдать ТРИ заметно разных
концепт-направления для обсуждения с клиентом.
Отвечай СТРОГО валидным JSON-массивом без markdown-обёртки и без
комментариев. Никакого текста до или после JSON.
Структура каждого направления:
{
"name": "Название концепции (2-4 слова, на русском)",
"tagline": "Одна строка — суть атмосферы",
"palette": [
{"hex": "#XXXXXX", "name": "название цвета"},
... ровно 5 цветов
],
"materials": [...5-6 материалов с конкретикой...],
"furniture": [...5-6 предметов...],
"lighting": "температура, источники, акценты",
"mood": "что чувствует человек, время дня, звуки",
"image_prompts": [...4 prompt-а на английском...],
"pexels_queries": [...4 поисковых запроса для Pexels...]
}
Несмотря на жёсткие инструкции «без markdown», Sonnet периодически оборачивает ответ в ```json … ```. Не всегда, в 5–10% случаев. Бороться с этим через prompt engineering можно — но дешевле просто прибрать на парсинге:
def _parse_concepts(text: str):
text = text.strip()
if text.startswith("```"):
text = re.sub(r"^```(?:json)?s*", "", text)
text = re.sub(r"s*```$", "", text)
return json.loads(text)
Три строки регэкспа решают проблему, на которую можно было бы потратить часы тюнинга температуры и формулировок. Принцип, который я для себя сформулировал: не воюй с моделью там, где можно простить ей слабость в коде.
Pexels вместо генерации картинок: почему живое фото бьёт DALL-E
Концепция без визуала — это полотно текста, которое клиент не дочитает. С визуалом — оживает. Первый вариант, который я попробовал — генерация картинок через DALL-E 3 / Midjourney через API. Получалось красиво, но узнаваемо неживо: у AI-генерации в 2026 году всё ещё характерные артефакты — слишком чистая геометрия, неестественные текстуры, странные углы между плинтусом и стеной. Любой, кто видел реальный интерьер на фото, считывает «это не настоящее» за секунду.
Переключился на Pexels API. Это бесплатный сток с реальной фотографией: 200 запросов/час, 20 000/месяц, без оплаты для коммерческого использования.
Логика: Sonnet кроме описаний выдаёт 4 коротких поисковых запроса для каждой концепции:
"pexels_queries": [
"japandi living room",
"warm wood texture",
"linen sofa detail",
"beige minimalist interior"
]
И параллельно через asyncio.gather тянем 4 фото:
async def fetch_pexels_set(queries, prompts, randomize=False):
queries = (queries or [])[:4]
async with httpx.AsyncClient() as client:
results = await asyncio.gather(
*[fetch_pexels(client, q, randomize=randomize) for q in queries]
)
return [
{
"prompt": prompts[i],
"query": queries[i],
"url": (r or {}).get("url"),
"photographer": (r or {}).get("photographer"),
"page": (r or {}).get("page"),
}
for i, r in enumerate(results)
]
В каждой концепции — 4 живых фото по теме, с указанием фотографа и ссылкой на оригинал (этого требует лицензия Pexels).
Что забрать: для интерьеров, фуд-фото, путешествий, моды — берите Pexels/Unsplash вместо генерации. Дешевле, быстрее, главное — выглядит как настоящее. Генерация выигрывает там, где нужна абсолютная уникальность сцены (фантастика, дизайн упаковки, иллюстрации к чему-то нестандартному), а не там, где нужна правдоподобность.
Микро-урок про форму: чипсы вместо текстовых полей
Маленький, но важный UX-урок — не про модели, про форму. Изначально все поля брифа были textarea — «опишите образ жизни», «опишите свет в комнате», «что вам не нравится». Около половины клиентов застревали на «образе жизни» и закрывали вкладку. Текстовое поле в форме — это психологический барьер: надо сформулировать, проверить грамматику, бояться написать глупость.
Заменил почти все поля на чипсы — кнопки с предзаготовленными вариантами. Для «Кто живёт»: «один», «пара», «пара + ребёнок», «пара + 2 детей», «несколько поколений». Для «Бюджет»: «эконом», «средний», «комфорт», «премиум». Для «Стиль-ориентир»: «минимализм», «Japandi», «скандинавский», «современная классика», «лофт», «эклектика», «не знаю».
Чипсы дополнены опциональным текстовым полем. Заполняемость формы выросла в 2,5–3 раза. Сигнал для модели чуть упростился, но это компенсируется тем, что до неё вообще доходит больше клиентов.
Принцип, который я выписал себе на полях: снижайте сопротивление на входе в форму даже ценой потери богатства данных. 100 коротких ответов лучше, чем 30 длинных, потому что моделям и людям одинаково удобнее работать с короткими.
Минимальный стек целиком
┌─ Frontend ───────────────────────────────────────────┐
│ Статичный HTML/Next.js, лежит на VPS │
│ Виджет чата — fixed-кнопка справа снизу │
│ Страница /brief — форма с чипсами + результат │
└──────────────────────────────────────────────────────┘
│ POST
▼
┌─ Backend ────────────────────────────────────────────┐
│ FastAPI на Railway, один файл main.py (712 строк) │
│ │
│ POST /chat → Claude Haiku 4.5 │
│ POST /brief → Claude Sonnet 4.6 + Pexels │
│ POST /regenerate-images → только Pexels │
└──────────────────────────────────────────────────────┘
│
┌──────────┴──────────┐
▼ ▼
Anthropic Pexels
(LLM) (фото)
Никаких баз данных. Никаких очередей. Никаких воркеров. Деплой — git push, Railway сам ребилдит контейнер за минуту-две.
Где этот подход начнёт ломаться
Чтобы статья не выглядела «как мы выкинули весь enterprise и заиграли» — честно скажу, где минимально достаточный перестаёт быть достаточным.
1. Если контент перестаёт помещаться в SYSTEM. Допустим, появилось 500 кейсов, каждый со своим описанием, и хочется, чтобы агент про них вспоминал в нужный момент. 500 кейсов в SYSTEM не лезут — приходит RAG. Тогда же приходит embedding, векторная база, индексация, инвалидация при обновлении. Это уже отдельная инфраструктура.
2. Если появляется multi-step рассуждение. Например, «собери концепцию, проверь, нет ли в ней цветов из стоп-листа, если есть — переделай, потом подбери фото». Сейчас одношаговый pipeline. Если шагов становится 3+ — стоит подумать про оркестрацию (LangGraph, или просто аккуратный async/await).
3. Если нужны тулы. Например, агент смотрит в Google Calendar дизайнера, находит свободные слоты, предлагает три времени. Это тулы. Можно руками через Anthropic function calling. Если тулов 2–3 — руками. Если 10+ — есть смысл смотреть в сторону агентских фреймворков, но я бы сначала попробовал Claude Tool Use напрямую.
4. Если появляется состояние между сессиями. История диалога, профиль клиента, прогресс воронки — это БД, скорее всего Postgres + ORM. У меня этого нет осознанно: история в localStorage, новый клиент = новый разговор.
5. Если нужна команда из трёх и более человек. Минимально достаточный код легко поддерживать одному. Когда подключаются ещё двое — нужны соглашения, шаблоны, абстракции. Тут LangChain/типизированные цепочки/Pydantic-схемы окупаются. Не из-за технических причин, а из-за общего языка в команде.
«Минимально достаточный» — это не идеология. Это состояние, в котором находится проект сейчас. Через год эта же кодовая база может потребовать LangChain — и это будет нормально. Главное — не тащить его за полгода до того момента, когда он действительно нужен.
Что бы сделал по-другому
Если бы запускался сегодня с нуля:
-
Логи запросов в SQLite с самого начала. Сейчас история не сохраняется нигде, и через два месяца не могу пересмотреть «как же там ассистент ответил тому клиенту, который потом стал заказчиком». Пять строк кода, надо было делать сразу.
-
Метрика прерванных диалогов. Знать, на каком сообщении клиент закрыл виджет — золотой источник для тюнинга промпта. Веду эту работу глазами по выгрузкам, что не очень.
-
A/B на SYSTEM-промпте. Правки промпта летят в прод без сравнения с предыдущей версией. Один эндпоинт с двумя версиями и подсчётом конверсии — пара часов работы, окупится за месяц.
-
Не пытался бы сделать «универсальный бот». Узкий ассистент с понятной границей ответственности — это единственное, что у меня уверенно заработало. Любая попытка «давайте он ещё и Х» приводила к падению качества по основной задаче.
Вместо вывода
Когда смотришь на современный AI-ландшафт, кажется, что любой агент = LangChain + векторка + RAG + observability + оркестрация. Иногда это правда. Чаще — это гипотеза, которая не выдерживает встречи с реальной задачей.
Стоит честно сесть и спросить себя по каждому слою: что именно эта абстракция решает в моей задаче и сколько мне будет стоить её отсутствие. У меня по большинству слоёв стоимость отсутствия оказалась — ноль. И когда я перестал тащить их «потому что так делают все», вылезло, что вся работа делается в одном файле, читается за вечер и спокойно поддерживается одним человеком.
Это не призыв всё писать руками — фреймворки существуют не просто так. Это призыв не путать сложность инструмента со сложностью задачи. Иногда задача — простая, и тогда простой стек её решает быстрее, дешевле и надёжнее.
Буду рад содержательным комментариям. Особенно интересны два кейса: где LangChain у вас действительно окупился — нужны контр-примеры, чтобы лучше калибровать совет; и где минимально достаточный стек у вас, наоборот, треснул через полгода — на каких именно границах. Спасибо, если дочитали.
Автор: eignatiev
