Создаем ИИ-ассистента с нуля. На примере легкой Nano-версии
Шаг 1 Установка библиотек. Собираем инструменты. Шаг 1 ➤
Сначала устанавливаем несколько библиотек. Это как подготовить инструменты перед ремонтом. Например, одна библиотека нужна для понимания смысла текста, другая — для создания веб-интерфейса, третья — для анализа текста. Команда
!pip -q install sentence-transformers gradio scikit-learn просит систему
скачать и установить эти наборы инструментов.
↳ Показать блок-схему шага
библиотеки] B --> C[Инструмент
для текста] B --> D[Инструмент
для веб-интерфейса] B --> E[Инструмент
для анализа текста] C --> F[Все инструменты
готовы] D --> F E --> F
Показать пример кода установки
# Установка необходимых библиотек
!pip -q install sentence-transformers gradio scikit-learn
Шаг 2 Готовим базовый набор функций. Шаг 2 ➤
NumPy
потребуется для работы с массивами чисел, а requests — для запросов в интернет.
↳ Показать блок-схему шага
установлены] --> 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 ➤
В работе ИИ-ассистентов также используется формат Markdown (
.md). Он больше подходит для людей, которые не владеют навыками программирования: фактически это обычный текст с лёгкой разметкой
(заголовки и списки). При этом и языковые модели его хорошо понимают — структура документа
читаема и понятна.
↳ Показать блок-схему шага
на 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-ответ с текстом.
↳ Показать блок-схему шага
пользователя] --> 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 превращает
запрос в вектор и с помощью косинусного сходства находит несколько самых близких по смыслу документов.
↳ Показать блок-схему шага
документов] --> 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. Хотя его и называют калькулятором,
он умеет работать не только с числами, но и с текстами, логикой и простыми преобразованиями данных.
↳ Показать блок-схему шага
кодом или формулой] --> 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 ➤
В код встроены короткие примеры диалогов, которые подмешиваются в запрос, чтобы языковая модель лучше перенимала стиль нужного эксперта. Отдельно заданы общие правила: отвечать лаконично/подробно, опираться на факты и использовать результаты расчётов.
↳ Показать блок-схему шага
эксперта] --> 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 выдала полезный короткий ответ в нужной роли.
↳ Показать блок-схему шага
пользователя] --> 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, а ответ показывается в браузере.
↳ Показать блок-схему шага
готов] --> 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 смотрим, как работает ИИ-ассистент в целом.
Мы построили собственного ИИ-ассистента. Пользователь открывает веб-страницу, выбирает роль эксперта, пишет вопрос и нажимает кнопку.
По сути, это многопрофильный помощник: он может быть экономистом, переводчиком, международником или специалистом по данным, опирается на свои документы, умеет считать и создавать тексты.
↳ Показать общий пайплайн всего ИИ-ассистента
на сайте] 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) ключей.
Ваши данные и конфиденциальность
Контролируйте, какие сведения передаются ассистенту
Ассистент получает доступ ко всем данным и документам, которые вы ему предоставляете в процессе настройки и использования.
Не загружайте персональные данные, платёжные реквизиты, пароли, коммерческую тайну и иную конфиденциальную информацию, если у вас нет достаточных прав и необходимых организационных и технических мер защиты.
Перед использованием ассистента в рабочих системах убедитесь, что обработка данных соответствует требованиям применимого законодательства о защите информации и персональных данных, а также внутренним политикам вашей организации.
При работе с облачными сервисами проверьте корректность настроек доступа, шифрования, резервного копирования и журналирования действий.
Минимальные меры безопасности
Что следует добавить в реальных проектах