Образовательный проект

Создаем ИИ-ассистента с нуля. На примере легкой Nano-версии

Показываем на простом примере, как создать своего ИИ-помощника: от установки библиотек в Python до поиска нужной информации по документам и запуска веб-интерфейса.
Создаем ИИ-ассистента с нуля (тест)
Шаг 1 Установка библиотек. Собираем инструменты. Шаг 1
Разберем процесс создания простого ИИ-помощника, который работает в браузере, умеет искать данные по документам, считать формулы и говорить в роли разных экспертов.

Сначала устанавливаем несколько библиотек. Это как подготовить инструменты перед ремонтом. Например, одна библиотека нужна для понимания смысла текста, другая — для создания веб-интерфейса, третья — для анализа текста. Команда !pip -q install sentence-transformers gradio scikit-learn просит систему скачать и установить эти наборы инструментов.
Показать блок-схему шага
flowchart LR A[Старт] --> B[Устанавливаем
библиотеки] B --> C[Инструмент
для текста] B --> D[Инструмент
для веб-интерфейса] B --> E[Инструмент
для анализа текста] C --> F[Все инструменты
готовы] D --> F E --> F
Показать пример кода установки
# Установка необходимых библиотек
!pip -q install sentence-transformers gradio scikit-learn
Шаг 2 Готовим базовый набор функций. Шаг 2
После установки библиотек мы раскладываем инструменты по столу: импортируем модули для работы с файлами и операционной системой, математикой, поиском по тексту. NumPy потребуется для работы с массивами чисел, а requests — для запросов в интернет.
Показать блок-схему шага
flowchart LR A[Библиотеки
установлены] --> B[Импортируем
модули] B --> C[Модули для
файлов и ОС] B --> D[Модули для
математики и текста] B --> E[NumPy для чисел
и requests для сети] C --> F[Рабочее место
готово] D --> F E --> F
Показать пример кода импорта
import os, json, math, random, re, pathlib, textwrap
from typing import List, Dict, Tuple, Optional
import numpy as np
import requests
Шаг 3 Подгружаем данные. Шаг 3
Далее нужно определиться, откуда берём данные. Для простоты даем ассистенту ссылку на  JSON-файл в облаке — это адрес, где лежат тексты, с которыми будет работать интеллектуальный помощник. Формат JSON крайне удобен для таких программ: он хранит данные, структурированные так, что их легко разобрать: отдельно текст, автор, дата и другие поля.

В работе ИИ-ассистентов также используется формат Markdown (.md). Он больше подходит для людей, которые не владеют навыками программирования: фактически это обычный текст с лёгкой разметкой (заголовки и списки). При этом и языковые модели его хорошо понимают — структура документа читаема и понятна.
Показать блок-схему шага
flowchart LR A[Есть ссылка
на JSON] --> B[Скачиваем
файл] B --> C[Читаем JSON
в память] C --> D[Достаём
нужные тексты] D --> E[Сохраняем их
в архив ассистента]
Функция: FileIndex.build()
Показать пример работы с JSON
GOOGLE_DRIVE_JSON_URL = "https://drive.google.com/uc?export=download&id=..."

response = requests.get(GOOGLE_DRIVE_JSON_URL)
response.raise_for_status()
data = response.json()
# дальше FileIndex разбирает data и вытаскивает текст
Шаг 4 Подключаем Yandex GPT. Шаг 4
Подключаем к нашему ассистенту ИИ-провайдера. Класс LLMClient — это «оператор связи» между нашим кодом и Yandex GPT. Он читает ключи доступа, формирует запрос с сообщениями диалога и отправляет его в API Яндекса.

API — это способ для одной программы разговаривать с другой по заранее понятным правилам. По сути это переписка между двумя программами в заранее оговорённом формате: мы собираем данные в JSON, отправляем их на сервер и получаем JSON-ответ с текстом.
Показать блок-схему шага
flowchart LR A[Вопрос
пользователя] --> B[Собираем
сообщения] B --> C[Делаем запрос
к API Яндекса] C --> D[Отправляем JSON
на сервер] D --> E[Модель Яндекс GPT
пишет ответ] E --> F[Забираем текст
ответа] F --> G[Отдаём текст
пользователю] C --> H[Если ошибка –
даём запасной ответ]
Функции: LLMClient.chat_messages(), LLMClient._yandex_chat(), LLMClient._fallback_answer()
Показать фрагмент LLMClient
class LLMClient:
    def __init__(self):
        self.yc_key = os.getenv("YC_API_KEY", "").strip()
        self.yc_folder = os.getenv("YC_FOLDER_ID", "").strip()

    def chat_messages(self, messages, model_yandex="yandexgpt-lite",
                      temperature=0.3, max_tokens=1500) -> str:
        try:
            if self.yc_key and self.yc_folder:
                return self._yandex_chat(messages, model_yandex, temperature, max_tokens)
        except Exception:
            return self._fallback_answer(messages)
        return self._fallback_answer(messages)
Шаг 5 Косинусное сходство: как ИИ понимает, что брать из текстов. Шаг 5
ИИ предстоит найти нужные вам данные, чаще всего — текст. Для этого нужно упаковать все тексты в числа: модель делает числовой смысловой отпечаток для каждого текста. Если продвинутой модели нет, можно использовать запасной вариант — считать частоту совпадения слов.

Функция _cosine сравнивает два таких набора чисел и говорит, насколько они похожи: ближе к 1 — тексты похожи, ближе к 0 — говорят о разном. Внутри ассистента специальный класс FileIndex хранит все тексты и их числовые копии; метод search превращает запрос в вектор и с помощью косинусного сходства находит несколько самых близких по смыслу документов.
Показать блок-схему шага
flowchart LR A[Тексты
документов] --> B[Переводим тексты
в числа] B --> C[Числовые
векторы текстов] D[Вопрос
пользователя] --> E[Переводим вопрос
в числа] E --> F[Числовой
вектор вопроса] C --> G[Сравниваем
векторы] F --> G G --> H[Получаем число
похожести от 0 до 1] H --> I[Берём самые
похожие тексты]
Функции: _encode(), _cosine(), FileIndex.search()
Показать фрагмент кода
def _encode(texts: List[str]) -> np.ndarray:
    if _USE_ST:
        v = _MODEL.encode(texts, normalize_embeddings=True, show_progress_bar=False)
        return np.array(v, dtype=np.float32)
    # запасной режим TF-IDF ...

def _cosine(a: np.ndarray, b: np.ndarray) -> float:
    na = np.linalg.norm(a) + 1e-9
    nb = np.linalg.norm(b) + 1e-9
    return float(np.dot(a, b) / (na * nb))

class FileIndex:
    def search(self, query: str, top_k: int = 3) -> List[Dict]:
        if not self.docs:
            return []
        qv = _encode([query])[0]
        scores = [_cosine(qv, self.vectors[i]) for i in range(len(self.docs))]
        idx = np.argsort(scores)[::-1][:top_k]
        return [{"score": float(scores[i]), **self.docs[i]} for i in idx]
Шаг 6 Встроенный «калькулятор». Шаг 6
Функцию pal_run можно представить как маленький встроенный «калькулятор», который понимает простые команды на языке Python. Хотя его и называют калькулятором, он умеет работать не только с числами, но и с текстами, логикой и простыми преобразованиями данных.
Показать блок-схему шага
flowchart LR A[Вопрос с
кодом или формулой] --> B[Берём текст
кода] B --> C[Выполняем код
в песочнице] C --> D[Ищем переменную
result] D --> E[Возвращаем
значение result] C --> F[Если ошибка –
пишем текст ошибки]
Функция: pal_run()
Показать код pal_run
def pal_run(code: str) -> str:
    loc = {}
    try:
        exec(code, {"__builtins__": __builtins__, "math": math, "np": np}, loc)
        return str(loc.get("result", ""))  # ожидается, что код положит что-то в result
    except Exception as e:
        return f"(Ошибка PAL: {e})"
Шаг 7 Даем ИИ роль эксперта. Шаг 7
ИИ-ассистенту можно назначить роль: экономист, переводчик, международник, специалист по данным. Для каждой роли заранее описано, как именно стоит отвечать: на что опираться, что уточнять, чего избегать.

В код встроены короткие примеры диалогов, которые подмешиваются в запрос, чтобы языковая модель лучше перенимала стиль нужного эксперта. Отдельно заданы общие правила: отвечать лаконично/подробно, опираться на факты и использовать результаты расчётов.
Показать блок-схему шага
flowchart LR A[Выбираем роль
эксперта] --> B[Берём описание
эксперта] B --> C[Берём примеры
ответов] C --> D[Формируем стиль
и тон ответа] E[Общие правила
BASE_SYSTEM] --> D D --> F[Модель понимает,
как отвечать]
Структуры: EXPERT_PROFILES, FEW_SHOT, BASE_SYSTEM
Показать пример профилей
EXPERT_PROFILES = {
    "Экономист": "...",
    "Переводчик": "...",
    "Международник": "...",
    "Специалист по работе с данными": "..."
}

BASE_SYSTEM = (
    "Отвечайте лаконично. Используйте ФАКТЫ при их наличии..."
)
Шаг 8 Собираем всё воедино. Шаг 8
Теперь нужно собрать разные части ИИ-ассистента вместе. Класс NanoAssistant соединяет доступ к языковой модели, поиск по документам, профили экспертов и PAL-вычисления. Он умеет искать факты, выполнять код и собирать запрос так, чтобы Yandex GPT выдала полезный короткий ответ в нужной роли.
Показать блок-схему шага
flowchart LR A[Вопрос
пользователя] --> B[Ищем факты
в документах] A --> C[Ищем в вопросе
код или формулу] C --> D[Если есть код –
запускаем pal_run] B --> E[Собираем блок
FACTS] D --> F[Собираем блок
PAL_RESULT] G[Роль эксперта
и примеры] --> H[Готовим контекст
для модели] E --> H F --> H H --> I[Отправляем запрос
в Яндекс GPT] I --> J[Получаем
ответ пользователю]
Класс: NanoAssistant, метод: answer()
Показать фрагмент NanoAssistant
class NanoAssistant:
    def __init__(self, expert_key: str = "Экономист"):
        self.llm = LLMClient()
        self.index = FileIndex()
        self.index.build()
        self.expert_key = expert_key

    def set_expert(self, expert_key: str):
        self.expert_key = expert_key

    def answer(self, query: str, try_pal: bool = True, use_fewshot: bool = True) -> str:
        facts = self._build_facts_block(query, top_k=3)
        pal_out = ""
        # логика PAL и сборки messages
        return self.llm.chat_messages(messages)
Шаг 9 Создаём удобный интерфейс. Шаг 9
Интерфейс — одна из важнейших деталей всей программы. В хорошем ИИ-ассистенте есть поле для вопроса, выбор роли эксперта и кнопка «Ответить», которая обращается к NanoAssistant, а ответ показывается в браузере.
Показать блок-схему шага
flowchart LR A[NanoAssistant
готов] --> B[Создаём функцию
ui_answer] B --> C[Строим страницу
в Gradio] C --> D[Поле для
вопроса] C --> E[Выбор роли
и флажков] C --> F[Кнопка
«Ответить»] F --> G[Вызываем
ui_answer] G --> H[Показываем ответ
на странице]
Функция: ui_answer(), блок: gr.Blocks(...)
Показать фрагмент Gradio-интерфейса
import gradio as gr

assistant = NanoAssistant(expert_key="Экономист")

def ui_answer(query, try_pal, expert_key, use_fewshot):
    assistant.set_expert(expert_key)
    return assistant.answer(query, try_pal=try_pal, use_fewshot=use_fewshot)

with gr.Blocks(title="AI Nano ассистент") as demo:
    gr.Markdown("## AI Nano\nВыберите профиль эксперта и задайте вопрос.")
    with gr.Row():
        expert_key = gr.Dropdown(choices=list(EXPERT_PROFILES.keys()),
                                 value="Экономист",
                                 label="Профиль эксперта")
        use_fewshot = gr.Checkbox(label="Включить примеры", value=True)
        try_pal = gr.Checkbox(label="Включить PAL (расчеты на Python)", value=True)
    inp = gr.Textbox(label="Ваш вопрос", lines=6)
    out = gr.Markdown()
    gr.Button("Ответить").click(ui_answer,
                                inputs=[inp, try_pal, expert_key, use_fewshot],
                                outputs=[out])

demo.launch(share=False)

Подводим итоги

В шаге 10 смотрим, как работает ИИ-ассистент в целом.

Мы построили собственного ИИ-ассистента. Пользователь открывает веб-страницу, выбирает роль эксперта, пишет вопрос и нажимает кнопку.

По сути, это многопрофильный помощник: он может быть экономистом, переводчиком, международником или специалистом по данным, опирается на свои документы, умеет считать и создавать тексты.

Показать общий пайплайн всего ИИ-ассистента
flowchart TD U[Пользователь] --> UI[Поле для вопроса
на сайте] UI --> NA[Ассистент
получает вопрос] NA --> IDX[Ассистент ищет
похожие тексты] NA --> PAL[Ассистент считает
формулы, если они есть] NA --> PROF[Ассистент смотрит,
какой эксперт выбран] IDX --> CTX[Собираем всё
в один запрос] PAL --> CTX PROF --> CTX CTX --> LLM[Посылаем запрос
в Яндекс GPT] LLM --> RESP[Получаем ответ
от модели] RESP --> UI2[Показываем ответ
на сайте] UI2 --> U2[Пользователь читает
и решает, что делать дальше]

Демонстрация ответа в интерфейсе

Готовый ответ возвращается в веб-интерфейс, отображается в браузере, и пользователь может задать следующий вопрос или сменить профиль эксперта.

Готовые вопросы:
Выберите вопрос или введите свой.
Я покажу соответствующий ответ из встроенной базы знаний.
Сейчас ассистент знает ответы только на несколько заранее заданных вопросов — их можно расширить прямо в коде дашборда.

Важная информация и отказ от ответственности

Важно

Только для учебных и демонстрационных целей

Не используйте этот пример «как есть» в продакшене

Этот ассистент и сопутствующий код предоставляются «как есть» и предназначены исключительно для обучения и демонстрации. Они не проходят промышленный аудит безопасности, нагрузочное тестирование и проверку на соответствие требованиям информационной безопасности.

Использование данного примера в реальной среде без доработки и проверки может привести к утечке данных, финансовым потерям и другим неблагоприятным последствиям.

Вы самостоятельно оцениваете риски и несёте полную ответственность за использование этого примера в своих проектах. Перед внедрением в рабочие системы проконсультируйтесь с квалифицированными специалистами по разработке и информационной безопасности и проведите независимый аудит.

+

Ключи доступа и API-ключи

Обращайтесь с ними как с паролями и реквизитами

API-ключи, токены и другие реквизиты доступа позволяют выполнять действия от имени вашего аккаунта, в том числе тратить средства и получать данные.

Никогда не оставляйте ключи в коде, который доступен третьим лицам (репозитории, фронтенд-код, общие файлы). Не загружайте файлы с ключами в открытый доступ или в непроверенные сервисы.

Храните секреты в специальных защищённых хранилищах на стороне сервера (хранилища секретов, KMS и т. п.), ограничивайте права каждого ключа по принципу минимально необходимых прав и регулярно проводите ротацию и отзыв (revocation) ключей.

+

Ваши данные и конфиденциальность

Контролируйте, какие сведения передаются ассистенту

Ассистент получает доступ ко всем данным и документам, которые вы ему предоставляете в процессе настройки и использования.

Не загружайте персональные данные, платёжные реквизиты, пароли, коммерческую тайну и иную конфиденциальную информацию, если у вас нет достаточных прав и необходимых организационных и технических мер защиты.

Перед использованием ассистента в рабочих системах убедитесь, что обработка данных соответствует требованиям применимого законодательства о защите информации и персональных данных, а также внутренним политикам вашей организации.

При работе с облачными сервисами проверьте корректность настроек доступа, шифрования, резервного копирования и журналирования действий.

+

Минимальные меры безопасности

Что следует добавить в реальных проектах

Всегда используйте HTTPS (TLS) вместо HTTP для шифрования трафика между клиентом и сервером.
Регулярно обновляйте зависимости, библиотеки и платформу, устанавливайте исправления безопасности.
Ведите журналы (логирование) и при необходимости настраивайте аудит действий пользователей и сервисов.
Настройте ограничения доступа и права по принципу минимально необходимых прав для пользователей, сервисных аккаунтов и ключей.
Корректно настройте CORS и другие механизмы контроля доступа к API, ограничивая вызовы только доверенными источниками.
+