
Привет, хабр, меня зовут Кияшева Екатерина. Вообще я из QA, но сегодня сделаю шаг в сторону и разберу AI‑базу.
За последний год встречалась масса литературы об AI, она, или теоретическая — не ясно, как это пощупать, или углубленная в одну тему — не сразу ясно какую задачу решает. Эта статья — обзор теоретической базы, но с прикладным характером.
На ур. QA важно знать «как эта штука работает», чтоб осмысленно применять паттерны тест‑дизайна. На человеческом ур. надоела маска «волшебства» вокруг ИИ, хочется смахнуть ее и рассмотреть инструмент предметно: как устроен, какой бывает, через что с ним работать без VPN из России.
Модели
Определения и классификация
Модели бывают:
-
языковые;
-
аудио;
-
компьютерное зрение (Computer Vision — CV);
-
мультимодальные.
Как ясно из названий, они отличаются по типу модальностей, с которыми работают. Языковые — берут на вход текст, аудио — звуковую дорожку, CV — картинку. Мультимодальные умеют работать больше, чем с одним типом информации.
Примеры:
-
Оцифровка текста с фотографий — мультимодальная (CV + Языковая);
-
Генерация видео по фото — мультимодальная (CV + Аудио/Видео);
-
Генерация картинки по текстовому описанию — мультимодальная (Языковая + CV);
-
Генерация программного кода по текстовому описанию — языковая модель (LLM).
Отдельное направление работы с моделями — их обучение (Model Learning — ML). Берем данные, семейство функций по которым будет рассуждать модель, измеритель ошибок этих рассуждений, алгоритм отбора наилучшего рассуждения, запускаем процесс отбора. На тему ML есть отличная серия статей от автора @ysrgsyn, излагает доступно, по полочкам, небольшими частями.
Большие языковые модели (Large Language Model — LLM), всем известные GPT, Gemini, DeepSeek, Claude, Kimi, Qwen, Llama, и т.д. — предобученные языковые модели от разных производителей.
Классифицировать модели можно по функциям и по возможностям:
-
Функциональную классификацию удобно смотреть на Hugging Face: это “старая” площадка, задуманная, как аналог GitHub. Создана, когда модели имели свою специализацию: генерировать, суммаризировать, анализировать, работать с таблицами и т.д. Эти и многие др. функции со временем вобрали в себя LLM.
-
Инструментальную классификацию удобно смотреть на Ollama: более новая площадка, работает как менеджер моделей. В ее реестре есть всего три фильтра:
-
thinking — LLM функция мыслительного процесса Chain-of-Thought. Когда модель думает по цепочке, проверяет каждый вывод, прежде чем дать ответ.
-
tool — LLM функция вызова действия. Когда модель самостоятельно вызывает выданные ей инструменты, например пушить код или открывать браузер.
-
embeddings — тип модели, нацеленный на извлечение смыслов из текста. В отличие от LLM — эти модели возвращают на выход не текст, а координаты смысла.
-
Выбор модели
На сегодня есть целая линейка моделей с полным набором функциональностей и инструментов. Они конкурируют между собой: по интеллекту, стоимости, стабильности, ресурсоемкости. Ситуация непрерывно меняется, так что невозможно сказать, какая модель лучше. Но можно отслеживать динамику развития по лидербордам: следить за трендами изменений последних лет, оценивать на что упирает производитель; подбирать модель под свои нужды, опираясь на прогноз.
|
Лидерборд |
Тип модели |
Фокус сравнения |
|
Генеративная |
Бизнес-ориентированный. Динамика инвестиций в модели, их развитие, стоимость токенов, интеллектуальность, предсказуемость и т.д. Информация, чтобы подобрать баланс между стоимостью и возможностями модели. |
|
|
Генеративная |
Прикладной. Рейтинг моделей в разрезе способностей — Agentic, Coding, Multimodal, Reasoning и т.д. Информация, чтобы подобрать баланс между стоимостью, скоростью вместительностью в контексте прикладной задачи. |
|
|
Векторная |
Функциональный. Сравнение моделей по качеству понимания смыслов. Информация, чтобы подобрать баланс между точностью поиска, скоростью обработки и объемом векторных данных для построения систем RAG. |
Агенты
Агентные механизмы
Итак, современные нейронки умеют мыслить, кодировать, выполнять любые команды за человека, полностью самостоятельно. Рассмотрим подкапотное пространство: что, кроме модели, необходимо для этой деятельности.
LLM — форвард AI-активностей, она берет задачу пользователя, генерирует решение, что-то отвечает. Все модели живут одним действием. Осмысленный диалог, связный код, согласованная цепочка действий получается за счет агентных механизмов. Это специальная прослойка, которая вызывает LLM в цикле:
-
отправляет запрос,
-
записывает ответ в историю диалога,
-
на каждом круге дополняет историю последним запросом юзера и ответом LLM,
-
отправляет новый запрос с накопленной историей и доп. инструкциями — контекстом.
В простом виде взаимодействие с генеративной моделью выглядит как-то так:

Пользователь написал свой запрос (user-prompt) ➔ агент взял запрос + контекст, сформировал все в промпт и отправил ➔ LLM сгенерировала ответ ➔ агент, получив ответ, пополнил контекст и вернул ответ пользователю.
О правилах и практиках составления промтов есть отличный ресурс Prompt Engineering Guide. Там доступно и по шагам излагаются паттерны промптинга, актуальные для любой модели. Паттерн-исключение “Chain-of-Thought”: неактуален и даже вреден в работе с thinking моделями, поскольку механизм “думать по шагам” в них встроен.
Агентные механизмы обогащают, суммаризируют, сегментируют, классифицируют накопленный контекст, чтобы каждый новый ответ казался продолжением предыдущего взаимодействия. Так что, когда говорят, у LLM короткая память, на самом деле это ошибка. В реальности памяти нет вообще, короткая память у агентного механизма, которым выполняется работа с моделью.
Задача агентного механизма
-
проконтролировать заполненность контекстного окна, чтобы не получить ошибку переполнения вроде ‘400: «Request payload size exceeds the limit’,
-
сформировать такой промпт, чтобы ответ модели был предельно уместным с учетом предыдущего контекста.
Далее я буду использовать примеры кода, надерганные из личных экспериментов. Это фрагменты и винегрет из разных библиотек. Прошу воспринимать, как псевдокод, только для передачи мысли.
Генеративная функция
Уместность ответа регулируется не только контекстном, но и параметрами. Через параметры можно управлять ресурсоемкостью и софт характеристиками ответа. Рассмотрим типовой запрос в LLM с параметрами ответа.
Пример работы с параметрами
# Формируем историю сообщенийmessages = [ {"role": "system", "content": "<системная инструкция>"},]# Получаем и добавляем в историю user-promptquery = "<запрос пользователя>"messages.append({"role": "user", "content": query})# Создаем финальный промпт с параметрами для запроса в LLMpayload = { "model": "deepseek-v4-flash", "messages": messages, "stream": True, "options": { "temperature": 0.1, "top_k": 400, "top_p": 0.5, "min_p": 0.05, "repeat_penalty": 1.1, "presence_penalty": 0.0, "frequency_penalty": 0.0, "num_predict": 2048, "stop": ["Пользователь"] }}# Отправляем финальный промпт в LLMtry: response = requests.post( "https://ollama.com/api/chat", headers=headers, json=payload, timeout=120 )# Извлекаем ответ модели и добавляем в историю data = response.json() answer = data["message"]["content"] messages.append({"role": "assistant", "content": answer})
UX-параметры:
-
stream: True — модель будет набирать ответ в режиме реального времени, при false вернула бы его целиком;
-
Num_predict (или max_token): 2048 — ограничение длины ответа (токенов, шт.), позволяет управлять стоимостью работы с моделью;
-
stop: [«Пользователь»] — модель остановит набор и отправит ответ, как только напишет стоп-слово. Здесь дополнительная защита от бесконечной переписки самой с собой.
Модель набирает ответ, последовательно выбирая токены с наибольшей вероятностью. Вероятность — это производная величина, числовая оценка того, каким именно должно быть продолжение.
Вычисляется вероятность примерно так: сначала модель определяет смысловой “вес” токенов (логиты), потом Softmax-функцией пересчитывает вес в вероятности. По ходу вычисления, можно подкручивать веса, так чтоб модель увеличивала и уменьшала диапазон вероятных продолжений.
Штраф-параметры (подкручивают логиты):
-
repeat_penalty: 1.1 — “мягкий цензор”: уменьшает вес слов, уже встречавшихся в контексте, в 1,1 раза. При repat_penalty = 1, никакого воздействия, при repat_penalty меньше 1 слово-дубликат увеличивает вес;
-
presence_penalty: 0.0 — “штраф за факт”: уменьшает вес слова-дубликата на 0,1;
-
frequency_penalty: 0.0 — “штраф за частоту”: уменьшает вес слова-дубликата пропорционально количеству повторений. Если слово встречается 5 раз, штраф будет 0,5.
Параметр креативности (подкручивает логиты):
-
temperature: 0,1 — управляет “азартом”: здесь модель заостряет вероятности выбора, возводит их в 10 степень (=1/0,1), так что разница между вариантами ответов становится более видимой. При температуре больше 1 наоборот бы сглаживала в сторону уравнения.
Параметры адекватности (фильтрует вероятности):
-
top_k: 400 — ограничивает выборку “штуками”: модель смотрит только на 400 самых релевантных вариантов;
-
top_p: 0.5 — ограничивает выборку “качеством”: оставляет варианты, чья суммарная вероятность достигает 50%;
-
min_p: 0.05 — отсекает «мусор»: выбрасывает токены, вероятность которых меньше 5% от вероятности самого лучшего варианта.
Параметры работают друг за другом, сначала группа штрафов, потом температура, и в конце осечка по адекватности.
Комбинации на примерах: top_p: 0,1 и temperature:2 — вернет самые необычные ответы из самых адекватных; temperature:0 вернет самый логичный ответ; temperature:1,2 и min_p: 0,4 вернет что-то на грани безумия, но без откровенной дичи.
MCP: выполнение действий
LLM с функцией tool, умеет вызывать действие. Для этого программируем в агентной прослойке обертки для выполнения действий — тулы, передаем в модель набор тулов вместе с промптом. В ответ нейронка сама будет выбирать инструмент и возвращать команды для активации того или иного действия.
MCP (Model Context Protocol) — способ пакетировать тулы в комплекты: унифицированные, переносимые, безопасные. Собираем комплект, запускаем на сервере и юзаем его различными моделям по API.
Пример работы механизма LLM tool
# Формируем список доступных инструментов для LLM с call-to-action инструкциейtools = []for t in mcp_tools: tools.append({ "type": "function", "function": { "name": t["<название инструмента>"], "description": t.get("<инструкция, когда использовать>"), "parameters": t.get("inputSchema", {"type": "object", "properties": {}, "required": []}), }, })# Формируем финальный промпт со списком доступных инструментовpayload = { "model": "deepseek-v4-flash", "messages": messages, "tools": tools, # Передача перечня инструментов "stream": False, "options": { "temperature": 0.1, "num_predict": 2048, "num_ctx": 16000 },}# Отправляем финальный промпт в LLMtry: resp = requests.post( "https://ollama.com/api/chat", headers=headers, json=payload, timeout=120 ) resp.raise_for_status() msg = resp.json()["message"] # Если модель вызвала инструмент - исполняем его if msg.get("tool_calls"): for tc in msg["tool_calls"]: name = tc["function"]["name"] args = tc["function"]["arguments"] result = mcp_client.call_tool(name, args)
RAG: внешняя память и семантический поиск
RAG (Retrieval-Augmented Generation) — LLM дает ответы не из своей нейросети, а какие-то конкретные, из кастомной базы знаний. Актуально, когда база знаний существенно больше, чтоб влезть в один промпт целиком и существенно отличается, чтобы удовлетвориться предобученным ответом LLM.
Общая схема работы: агентный механизм получает запрос юзера ➔ делает выборку из БД ➔ добавляет в промпт результат выборки ➔ отправляет обогащенный промпт в генеративную модель.
Центральное звено в схеме занимает векторная база данных. База может быть любой, но векторная получила большое распространение благодаря функции семантического поиска. Данные в ней хранятся в виде коллекций, есть поддержка функции “перевода” текста в смыслы (эмбеддинг). Рассмотрим типовую схему данных векторной БД.
-
Ids — идентификатор элемента в коллекции;
-
Embeddings — массив чисел, смысловой вектор элемента, чтобы искать кусок текста по смыслу;
-
Metadatas — служебная информация, любой набор маркеров текста, чтобы искать текст по кастомным значимым признакам;
-
Documents — исходный кусок текста сохраненный в БД, именно он эмбедится в вектора и потом передается в LLM в виде промпта.
Непосредственный перевод текста в смыслы выполняет векторная модель. На вход она получает текст в виде токенов ➔ определяет координаты этого текста на каждой своей оси ➔ возвращает массив координат (вектор).
Например, на вход имеем слово “кот”. Векторная модель ищет его в осях координат:
-
Живое — неживое,
-
Пушистое — гладкое,
-
Сладкое — соленое,
-
Мягкое — твердое,
-
И т.д.
Размерность модели = количество измерительных осей. Чем больше размерность, тем больше признаков, по которым модель будет оценивать слово.
Работа с векторной БД глобально состоит из 2 частей:
1. Индексация: порезать исходный текст, перевести в смыслы и сохранить в базу данных
# Режем текст на куски, каждый кусок - один элемент коллекции в ChromaDB…..# Создаем/вызываем коллекцию и передаем в нее функцию эмбеддинга embed_fn = LocalEmbeddingFunction(embed_model) chroma_client = chromadb.PersistentClient(path=vector_store_path) try: collection = chroma_client.get_collection(name=collection_name, embedding_function=embed_fn) logger.info(" Reusing existing collection '%s'", collection_name) except Exception: collection = chroma_client.create_collection( name=collection_name, embedding_function=embed_fn, metadata={"hnsw:space": "cosine"}, ) # Добавляем элементы в коллекцию, эмбеддинг происходит автоматическиcollection.add(ids=ids, metadatas=metadatas, documents=documents)
2. Поиск: находить в бд куски по смыслу или тексту, и подставлять в промпт.
# Получаем запрос пользователяquery = "<текст вопроса пользователя>"n_results = 600 #орграничитель первичной выборки в шт.# Эмбеддим запрос пользователя и выбираем n_result с наиближайшими векторамиresults = collection.query(query_texts=[query], n_results=n_results, include=["documents", "distances"])# Фильтруем полученную выборку по дистанции м-у векторами запроса и выборки# отбрасываем все, что больше max_distance filtered_docs = [] for doc, dist in zip(results['documents'][0], results['distances'][0]): if dist <= max_distance: filtered_docs.append(doc)# Фильтруем вектора в оставшейся выборке по релевантностиfinal_context = filtered_docs[:top_k]# Добавляем в итоговый промпт текст Documents из выборкиcontext_text = "\n\n--- {{заголовок ответа}} ---\n\n".join(final_context)prompt = f"""КОНТЕКСТ:{context_text}ВОПРОС:{query}"""# Отправляем промпт в LLM...
NLP: дешифровка подтекста
NLP (Natural Language Processing) — извлечение из текста непрямых посланий. Возможно замечали, как LLM меняет тон разговора, когда ей пишешь весело или грубо — это проявление NLP.
Небольшой пример из жизни: на скрине финальное сообщение из цепочки запросов о стандартах ISO (perplexity).

В сообщении нет прямой информации, только подтекст. Что тут видно: подтекст считали LLM и агентный механизм:
-
LLM пересобрала смысловые акценты и выдала, наконец, точный ответ,
-
Агентный механизм проверил культуру взаимодействия.
Базовая утилитарная функция NLP в агентах — собирать данные о пользователе: как зовут, где находится, чем занимается, что умеет, чем интересуется, и т.д. Все это позволяет формировать по-настоящему персонифицированные промпты, так чтобы LLM буквально видела, кому отвечает.
Общая схема работы: агентный механизм парсит сообщение юзера ➔ извлекает из сообщения важный подтекст ➔ маркирует его и сохраняет в базу данных ➔ берет из базы информацию с наибольшим весом и добавляет ее в промпт в виде инструкции ➔ отправляет промт в LLM.
Центральное звено в схеме занимают NLP-экстракторы: паттерны и NER модели, которые могут разбивать и нормализовать текст, маркировать в нем важную информацию о пользователе.
Пример работы с экстракторами
# Инициализируем NER нейронку Natashasegmenter = Segmenter() # функция разбивки на предложения и словаmorph_vocab = MorphVocab() # словарь словоформemb = NewsEmbedding() # функция-эмбеддер текстаmorph_tagger = NewsMorphTagger(emb) # морфологический словарь ner_tagger = NewsNERTagger(emb) # NER экстракторNER_TYPES = {"PER": "person", "ORG": "company", "LOC": "place"} #словарь маркеров# Получаем запрос пользователяtexts = [ "Привет! Меня зовут Маша, я — frontend-разработчик.", "Работаю в Яндексе, интересуюсь React.", "Сейчас делаю проект Grafika.", "React — это библиотека для UI.", "Кстати, переезжаю в новый офис, буду ближе к команде.",]# Разбираем и маркируем запрос по NER признакамfor text in texts: doc = Doc(text) doc.segment(segmenter) # разбиваем текст на слова doc.tag_morph(morph_tagger) # извелкаем ключевые слова doc.tag_ner(ner_tagger) # ищем слова про персону, компанию, локацию seen = set() entities = [] for span in doc.spans: # перебираем все слова ntype = NER_TYPES.get(span.type) # получаем NER маркер слова if not ntype: continue span.normalize(morph_vocab) # приводим слово к начальной форме key = (span.normal.lower(), ntype) if key not in seen: # дополняем массив словом и его маркером seen.add(key) entities.append({"name": span.normal, "type": ntype})
В качестве хранилища — используются графовые базы данных. В отличие от реляционных (SQL) и документ-ориентированных (NoSQL), они умеют в нелинейные связи: позволяют связывать между собой объекты любого типа и легко доставать эти объекты по коротким описаниям связей.
Ниже пример-представление статьи “Чит-лист функционального тестирования, памятка тестировщику” в графовой базе. Здесь:
-
“Статья” главное звено, от нее идут связи к векторам — слоям тестирования: Данные, Операции, Функциональности, Технология,
-
“Вектора” связаны с сетами: группами тестов с общим признаком в конкретном слое,
-
“Сеты” связаны с “Тестами”: конкретными проверками внутри сета
-
“Теги” воспроизводят нелинейные связи к любым объектом из иерархии.

Контекст
Резюмируем:
-
LLM живет одним запросом, она генерирует ответ ровно на тот запрос, какой получила, каждый раз, как в первый раз,
-
Уместность ответов и эффект связанного диалога создается через промпт, который обогащается после каждого взаимодействия (запроса — ответа),
-
Промпт — это инструкция с запросом юзера И контекстом (информацией о юзере, историей диалога, кастомными данными юзера, параметрами генерации ответа, системными инструкциями для LLM о форме, тоне и ограничениях ведения диалога).
Промпт должен быть достаточно точным, чтобы создавать эффект связанного процесса и не слишком большой, чтобы уместиться в контекстное окно модели.
Рассмотрим несколько известных подходов сокращения длины контекста:
-
Buffer Memory — хранит последние N сообщений истории диалога, при достижении границ контекстного окна старая история стирается в пользу новой.
-
Summary Memory — раз в N сообщений суммаризирует историю диалога в краткий пересказ. За счет этого может хранить более длинную историю, но не очень конкретную — детали и полутона стираются.
-
Buffer + Summary — свежую историю диалога хранит в исходном виде, а старые сообщения суммаризирует в конспект. Компромисс между длинной контекста и его точностью.
-
Vector Memory — сохраняет ключевые участки диалога в векторную базу данных, по запросу пользователя может найти их семантическим поиском и добавить в контекст фрагментарно.
-
Graph Memory — управляет данными о пользователе через структуру связей. Извлекает характеристики юзера из подтекста, сохраняет факты в графовую БД, связывает их между собой и ранее добавленным фактами, назначает связям веса. Чем актуальней информация — тем больше связей с бОльшим весом она имеет. В промпт отправляет факты с наибольшим весом. Для поддержания актуальности — у фактов, в которым долго не обращались, переписывает веса в меньшую сторону или совсем обрывает связи.
-
Episodic vs Semantic — ранжирует данные о пользователе на постоянные характеристики и события. Постоянные характеристики всегда имеют высокий вес, событийные данные со временем теряют в весе и перестают добавляться в контекст.
Агентное ПО
Сейчас наблюдается чехарда с терминами, поэтому здесь изложу свою логику и классификацию. Споры, вопросы, уточнения — приветствуются.
Агентное ПО — программа, которая взаимодействует с моделью и организовывает контекст. Глобально выделяется 2 класса:
-
бизнес-приложения — модель подключается для выполнения бизнес — функции, например записи в парикмахерскую, консультирование, на простые служебные операции.
-
сервисные “приложения” — модель подключается для автоматизации производственных процессов, разработки другого ПО или других агентов. Слово “приложение” здесь условно, поскольку это ПО активно прорастает в операционные системы и железо.
Оба типа под капотом содержат агентные механизмы, различаются по цели использования, арсеналу и сложности агентных механик. Отдельного внимания требуют сервисные приложения.
|
Тип |
Комментарии |
Примеры |
|
Чат асисстент |
Чаты с функциями настройки диалога, внешней памятью RAG, действиями в интернете. Автоматизируют работу с информацией: ее поиском, анализом, преобразованием, позволяют кастомизировать контекст под себя. |
Чат-ассистенты |
|
Конструктор-среда |
Среда разработки в виде конструктора. Содержит пред подготовленные модули-коннекторы для интеграции с нейронкой, различными корпоративными системами и между собой. Упрощает автоматизацию производственных процессов: проверку, преобразование, передачу информацию по цепочке. Действия реализуются через API. |
|
|
CLI-среды |
Открытая среда разработки, заточенная под создание агентов. Из коробки: подстраивается под пользователя (формирует контекст), содержит инструменты bash и web (MCP), знает известные языки разработки (механизм LSP). Позволяет: донастраивать контекст (AGENTS.md, SKILLS, COMMAND) подключать собственные инструменты (MCP, LSP) чтобы создавать ролевую модель из агентов и делегировать им любые операции. Арсенал позволяет делать любые агентные механизмы, разрабатывать агентный слой любой сложности. |
|
|
IDE-среды |
Среда разработки с GUI интерфейсом, изначально заточенная под написание кода. Теперь IDE- среды перенимают все больше функциональностей CLI-среды и вероятно станут одним и тем же. |
OpenCode Hermes |
Все сервисные приложения имеют собственную базу подключенных нейронок: покупаешь подписку на использование и получаешь доступ ко всему реестру моделей (из России и в некоторых инструментах почти ко всему).
Бизнес-приложениям необходимо специальное подключение. Нужно выбрать модель, определиться, где она будет хоститься, оценить на перспективу, сколько это будет стоить. Ценообразование напрямую зависит от хостинга:
-
Локальное — требует покупки и обслуживания своих стоек, важна ресурсоемкость модели;
-
Облачное — плата за использование серверов хостера;
-
Онлайн — оплата потраченных токенов.
|
Провайден |
Комментарий |
|
Площадка — экосистема. Модели можно скачать, использовать удаленно на ресурсах Huggin Face или онлайн. Варианты использования зависят от провайдера и модели. |
|
|
Площадка — аппстор. Модели можно скачать в локаль или использовать облачное хранилище олламы. |
|
|
Провайдер моделей. Подписка позволяет использовать модели из реестра онлайн, плата взимается за токены. |
ссылка на оригинал статьи https://habr.com/ru/articles/1054412/