<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>LLM on kenji.blog</title><link>http://kenji.blog/ru/categories/llm/</link><description>Recent content in LLM on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>ru</language><copyright>kenjinote</copyright><lastBuildDate>Fri, 11 Sep 2026 16:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/ru/categories/llm/index.xml" rel="self" type="application/rss+xml"/><item><title>Как быстрее всего настроить TinyLLaMA в локальной (on-premises) среде</title><link>http://kenji.blog/ru/p/tinyllama-on-premises-fast-tuning-guide/</link><pubDate>Fri, 11 Sep 2026 16:00:00 +0900</pubDate><guid>http://kenji.blog/ru/p/tinyllama-on-premises-fast-tuning-guide/</guid><description>&lt;img src="http://kenji.blog/p/tinyllama-on-premises-fast-tuning-guide/img/eyecatch.jpg" alt="Featured image of post Как быстрее всего настроить TinyLLaMA в локальной (on-premises) среде" />&lt;h2 id="1-введение-почему-именно-сейчас-tinyllama-и-локальная-среда">1. Введение: Почему именно сейчас TinyLLaMA и локальная среда?
&lt;/h2>&lt;p>Эволюция больших языковых моделей (LLM) идет с невероятной скоростью, и вместе с этим количество параметров моделей продолжает разрастаться до сотен миллиардов. Хотя сверхгигантские модели, такие как GPT-4 и Claude 3, обладают непревзойденной производительностью, вычислительные затраты на инференс и обучение, а также проблемы безопасности и конфиденциальности данных при использовании внешних API стали серьезным препятствием для бизнеса. В частности, при работе с высококонфиденциальными внутренними данными компании или личной информацией, отправка данных в публичные API LLM в облаке часто недопустима с точки зрения соблюдения нормативных требований (таких как GDPR, APPI и др.).&lt;/p>
&lt;p>Именно поэтому в центре внимания оказались &lt;strong>малые языковые модели (SLM: Small Language Models)&lt;/strong> и &lt;strong>локальная эксплуатация в on-premises среде&lt;/strong>. Среди них «&lt;strong>TinyLLaMA&lt;/strong>» при компактном размере всего 1.1B (1,1 миллиарда) параметров была предварительно обучена на огромном наборе данных объемом около 3 триллионов токенов, демонстрируя поразительную производительность по сравнению с моделями того же класса.&lt;/p>
&lt;p>В этой статье мы представляем полное руководство по файнтюнингу (тонкой настройке) TinyLLaMA в локальной среде (на локальном сервере или рабочей станции) «максимально быстро и эффективно» для ваших собственных специализированных задач. Мы всесторонне рассмотрим всё: от математических основ до новейших технологий оптимизации и конкретного кода реализации на PyTorch.&lt;/p>
&lt;hr>
&lt;h2 id="2-архитектура-и-особенности-tinyllama">2. Архитектура и особенности TinyLLaMA
&lt;/h2>&lt;p>TinyLLaMA следует архитектуре LLaMA (Large Language Model Meta AI), разработанной компанией Meta. При ограничении количества параметров до 1.1B она использует тот же технологический стек, что и LLaMA 2, что обеспечивает ей чрезвычайно высокую совместимость в экосистеме.&lt;/p>
&lt;h3 id="основные-компоненты-архитектуры">Основные компоненты архитектуры
&lt;/h3>&lt;ol>
&lt;li>&lt;strong>RMSNorm (Root Mean Square Normalization):&lt;/strong>
Это метод нормализации, который опускает вычитание среднего значения из традиционного вычисления LayerNorm, тем самым повышая эффективность вычислений. Это увеличивает пропускную способность при сохранении стабильности обучения.&lt;/li>
&lt;li>&lt;strong>Функция активации SwiGLU:&lt;/strong>
В сети прямого распространения (Feed Forward Network, FFN) вместо традиционных ReLU или GELU используется SwiGLU. Математически это выражается следующим образом:
$$ \text{SwiGLU}(x, W, V) = \text{Swish}(xW) \otimes (xV) $$
Здесь $\otimes$ обозначает поэлементное произведение (произведение Адамара), а функция Swish — это $\text{Swish}(z) = z \cdot \sigma(\beta z)$. Это значительно повышает выразительность.&lt;/li>
&lt;li>&lt;strong>RoPE (Rotary Position Embedding):&lt;/strong>
Это метод, сочетающий преимущества абсолютного и относительного позиционного кодирования. Он обладает высокой обобщающей способностью даже при увеличении длины последовательности.&lt;/li>
&lt;li>&lt;strong>Grouped Query Attention (GQA):&lt;/strong>
Это промежуточный подход между Multi-Head Attention (MHA) и Multi-Query Attention (MQA), который значительно увеличивает скорость инференса и экономит пропускную способность памяти за счет группировки голов ключей (key) и значений (value).&lt;/li>
&lt;/ol>
&lt;p>Следующая диаграмма Mermaid показывает общий поток данных TinyLLaMA и структуру блока Transformer.&lt;/p>
&lt;div class="mermaid">graph TD
A["Входной текст"] --> B["Токенизатор (BPE)"]
B --> C["Слой Embedding"]
C --> D["Блоки Transformer (x22 слоя для TinyLLaMA)"]
D --> E["RMSNorm (Финальный)"]
E --> F["Линейная проекция (Размер словаря)"]
F --> G["Выходные вероятности (Softmax)"]
subgraph "Анатомия блока Transformer"
D1["Входное скрытое состояние"] --> D2["RMSNorm"]
D2 --> D3["Grouped Query Attention (GQA)"]
D3 --> D4["Остаточное сложение (Residual Add)"]
D4 --> D5["RMSNorm"]
D5 --> D6["SwiGLU FFN"]
D6 --> D7["Остаточное сложение (Residual Add)"]
D7 --> D8["Выход на следующий слой"]
D1 -.-> D4
D4 -.-> D7
end&lt;/div>
&lt;hr>
&lt;h2 id="3-прорыв-в-файнтюнинге-lora-и-qlora">3. Прорыв в файнтюнинге: LoRA и QLoRA
&lt;/h2>&lt;p>Для выполнения полного файнтюнинга параметров в локальной среде, даже для модели 1.1B, требуются десятки гигабайт видеопамяти (VRAM) для хранения состояний оптимизатора и градиентов. Для эффективного обучения с ограниченными ресурсами необходимы методы &lt;strong>PEFT (Parameter-Efficient Fine-Tuning)&lt;/strong>, такие как «&lt;strong>LoRA&lt;/strong>» и её квантованное расширение «&lt;strong>QLoRA&lt;/strong>».&lt;/p>
&lt;h3 id="31-математические-основы-lora-low-rank-adaptation">3.1 Математические основы LoRA (Low-Rank Adaptation)
&lt;/h3>&lt;p>LoRA — это метод, который фиксирует (замораживает) предварительно обученную матрицу весов и аппроксимирует обновление весов ($\Delta W$) как произведение двух небольших матриц низкого ранга.&lt;/p>
&lt;p>Пусть предварительно обученные веса равны $W_0 \in \mathbb{R}^{d \times k}$. При полном файнтюнинге обновляется сама $W_0$ до $W_0 + \Delta W$, но в LoRA матрица обновлений $\Delta W$ разлагается следующим образом:&lt;/p>
$$ \Delta W = B \times A $$
&lt;p>Здесь $B \in \mathbb{R}^{d \times r}$, $A \in \mathbb{R}^{r \times k}$, а $r$ — это гиперпараметр, называемый рангом (Rank), который является очень маленьким значением (обычно 8, 16, 32 и т.д.), удовлетворяющим условию $r \ll \min(d, k)$.&lt;/p>
&lt;p>Вычисление прямого прохода выглядит следующим образом:&lt;/p>
$$ h = W_0 x + \Delta W x = W_0 x + B A x $$
&lt;p>В начальном состоянии матрица $A$ инициализируется случайным образом с нормальным (гауссовым) распределением, а матрица $B$ инициализируется нулевой матрицей. В результате $\Delta W$ в начале обучения равна нулю, и мы можем начать обучение, полностью сохраняя выходные данные базовой модели.&lt;/p>
&lt;div class="mermaid">graph LR
X["Входной вектор x"] --> W0["Замороженные предварительно обученные веса (W_0)"]
X --> A["Обучаемая матрица LoRA A (r x k)"]
A --> B["Обучаемая матрица LoRA B (d x r)"]
W0 --> Add["Сложение векторов"]
B --> Add
Add --> Y["Выходной вектор h"]&lt;/div>
&lt;h3 id="32-инновационность-qlora-quantized-lora">3.2 Инновационность QLoRA (Quantized LoRA)
&lt;/h3>&lt;p>QLoRA развивает подход LoRA дальше, квантуя базовую модель $W_0$ с 4-битной точностью (NormalFloat 4, NF4) и загружая её в память. Это кардинально снижает потребление VRAM.&lt;/p>
&lt;p>В QLoRA интегрированы три ключевые технологии:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Квантование 4-bit NormalFloat (NF4):&lt;/strong> Теоретически оптимальный тип данных, оптимизированный для весов, распределенных по нормальному закону.&lt;/li>
&lt;li>&lt;strong>Двойное квантование (Double Quantization):&lt;/strong> Квантование самих констант квантования (масштабных коэффициентов) для дополнительной экономии памяти.&lt;/li>
&lt;li>&lt;strong>Paged Optimizers:&lt;/strong> Механизм, использующий функцию объединенной памяти (unified memory) NVIDIA для временной выгрузки состояний оптимизатора в оперативную память CPU (RAM) при нехватке VRAM.&lt;/li>
&lt;/ol>
&lt;p>Благодаря этому тюнинг, который обычно требует от 16 до 24 ГБ VRAM, может быть легко выполнен даже на потребительских GPU (таких как RTX 3060 12GB или RTX 4070).&lt;/p>
&lt;hr>
&lt;h2 id="4-требования-к-оборудованию-и-настройка-в-локальной-среде">4. Требования к оборудованию и настройка в локальной среде
&lt;/h2>&lt;p>Требования к оборудованию для тюнинга TinyLLaMA (1.1B) с помощью QLoRA очень низки.&lt;/p>
&lt;h3 id="рекомендуемые-характеристики-оборудования">Рекомендуемые характеристики оборудования
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>GPU:&lt;/strong> NVIDIA RTX 3060 (12GB), RTX 3090/4090 (24GB) или NVIDIA A10G/A100 и т.д. Минимальный объем VRAM для работы — 8 ГБ, но для увеличения размера батча рекомендуется от 12 ГБ и выше.&lt;/li>
&lt;li>&lt;strong>CPU:&lt;/strong> Современный процессор с 8 и более ядрами (Intel Core i7/i9, AMD Ryzen 7/9).&lt;/li>
&lt;li>&lt;strong>RAM:&lt;/strong> Не менее 32 ГБ (важно при использовании Paged Optimizers как место для выгрузки из VRAM).&lt;/li>
&lt;li>&lt;strong>Накопитель:&lt;/strong> NVMe SSD (для ускорения загрузки датасетов и сохранения модели).&lt;/li>
&lt;/ul>
&lt;h3 id="настройка-программной-среды">Настройка программной среды
&lt;/h3>&lt;p>Процедура настройки предполагается для среды Ubuntu 22.04 LTS. Мы будем использовать Python 3.10 или новее.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Создание и активация виртуальной среды&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">python3 -m venv tinyllama_env
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">source&lt;/span> tinyllama_env/bin/activate
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Установка PyTorch (для CUDA 12.1)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Установка библиотек, связанных с трансформерами&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pip install transformers datasets peft trl accelerate bitsandbytes
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;h2 id="5-методы-оптимизации-для-самого-быстрого-тюнинга">5. Методы оптимизации для самого быстрого тюнинга
&lt;/h2>&lt;p>Для завершения тюнинга «максимально быстро» недостаточно просто запустить скрипт, необходимо комбинировать следующие методы оптимизации.&lt;/p>
&lt;h3 id="51-flash-attention-2">5.1 Flash Attention 2
&lt;/h3>&lt;p>Стандартный механизм внимания (Attention) имеет временную и пространственную сложность $O(N^2)$ для длины последовательности $N$. Flash Attention 2 оптимизирует доступ к памяти между SRAM и HBM (High Bandwidth Memory) GPU, устраняя узкое место ввода-вывода (IO) без сокращения вычислений, что в разы увеличивает скорость обучения и кардинально снижает потребление памяти.&lt;/p>
&lt;h3 id="52-gradient-checkpointing-чекпойнтинг-градиентов">5.2 Gradient Checkpointing (Чекпойнтинг градиентов)
&lt;/h3>&lt;p>Это метод, при котором в VRAM сохраняются не все промежуточные активации, вычисленные при прямом проходе, а лишь некоторые из них; остальные пересчитываются при обратном проходе, когда это необходимо. Время вычислений увеличивается примерно на 20%, но потребление памяти значительно сокращается, что позволяет установить больший размер батча (batch size) и в итоге повысить общую пропускную способность.&lt;/p>
&lt;h3 id="53-обучение-со-смешанной-точностью-mixed-precision-training-и-bfloat16">5.3 Обучение со смешанной точностью (Mixed Precision Training) и Bfloat16
&lt;/h3>&lt;p>Для максимального использования тензорных ядер (Tensor Cores) GPU вычисления при обучении выполняются в &lt;code>bfloat16&lt;/code> (Brain Floating Point). По сравнению с &lt;code>float16&lt;/code> длина бита экспоненты у него такая же, как у &lt;code>float32&lt;/code>, поэтому риск переполнения и потери значимости (overflow/underflow) крайне низок, что делает обучение стабильным.&lt;/p>
&lt;hr>
&lt;h2 id="6-практика-код-файнтюнинга-tinyllama-с-помощью-qlora">6. Практика: Код файнтюнинга TinyLLaMA с помощью QLoRA
&lt;/h2>&lt;p>Теперь давайте разберем скрипт PyTorch для самого быстрого тюнинга, который включает в себя все вышеупомянутые оптимизации. Здесь мы будем использовать &lt;code>SFTTrainer&lt;/code> из библиотеки Hugging Face &lt;code>trl&lt;/code> (Transformer Reinforcement Learning).&lt;/p>
&lt;h3 id="61-подготовка-набора-данных-и-загрузка-модели">6.1 Подготовка набора данных и загрузка модели
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">torch&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">datasets&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">load_dataset&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">transformers&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">AutoModelForCausalLM&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">AutoTokenizer&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">BitsAndBytesConfig&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">TrainingArguments&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">peft&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">LoraConfig&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">get_peft_model&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">prepare_model_for_kbit_training&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">trl&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">SFTTrainer&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1. Указание модели и токенизатора&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model_id&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;TinyLlama/TinyLlama-1.1B-Chat-v1.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 2. Настройка 4-битного квантования для QLoRA&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">bnb_config&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">BitsAndBytesConfig&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">load_in_4bit&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">bnb_4bit_use_double_quant&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">bnb_4bit_quant_type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;nf4&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">bnb_4bit_compute_dtype&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">torch&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">bfloat16&lt;/span> &lt;span class="c1"># Вычисления производятся в bfloat16&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 3. Загрузка модели (Включение Flash Attention 2)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Loading model...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">AutoModelForCausalLM&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_pretrained&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model_id&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">quantization_config&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">bnb_config&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">device_map&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;auto&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">use_flash_attention_2&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span> &lt;span class="c1"># Ключ к максимальной скорости&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 4. Загрузка токенизатора&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">tokenizer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">AutoTokenizer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_pretrained&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model_id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">trust_remote_code&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">tokenizer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">pad_token&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">tokenizer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">eos_token&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">tokenizer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">padding_side&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;right&amp;#34;&lt;/span> &lt;span class="c1"># Устанавливаем right для предотвращения багов при обучении fp16/bf16&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="62-применение-адаптера-lora-и-форматирование-набора-данных">6.2 Применение адаптера LoRA и форматирование набора данных
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 5. Подготовка к k-bit обучению и включение чекпойнтинга градиентов&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">gradient_checkpointing_enable&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">prepare_model_for_kbit_training&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 6. Настройка LoRA&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">peft_config&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">LoraConfig&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">r&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">16&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># Ранг&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">lora_alpha&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">32&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># Масштабный коэффициент&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">lora_dropout&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">0.05&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">bias&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;none&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">task_type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;CAUSAL_LM&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">target_modules&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;q_proj&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;k_proj&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;v_proj&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;o_proj&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;gate_proj&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;up_proj&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;down_proj&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="c1"># Ориентация на все линейные слои улучшает производительность&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">get_peft_model&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">peft_config&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">print_trainable_parameters&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Пример вывода: trainable params: 14,286,848 || all params: 1,114,335,232 || trainable%: 1.282%&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 7. Загрузка набора данных (В качестве примера используется японский датасет инструкций)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># На практике здесь загружается локальный приватный JSONL файл и т.д.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">dataset&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">load_dataset&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;kunishou/databricks-dolly-15k-ja&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">split&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;train&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">format_instruction&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sample&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> Форматирует строку в соответствии с форматом ChatML или шаблоном промпта
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&amp;lt;|im_start|&amp;gt;user&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">sample&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;instruction&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">sample&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;input&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">prompt&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">sample&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;input&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">prompt&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&amp;lt;|im_end|&amp;gt;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">&amp;lt;|im_start|&amp;gt;assistant&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">sample&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;output&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;lt;|im_end|&amp;gt;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">prompt&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">dataset&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dataset&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">map&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">format_instruction&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="63-выполнение-обучения">6.3 Выполнение обучения
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 8. Настройка аргументов обучения&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">training_args&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">TrainingArguments&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">output_dir&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;./tinyllama-lora-output&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">per_device_train_batch_size&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">8&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># Увеличьте, если есть свободная VRAM&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">gradient_accumulation_steps&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># Эффективный размер батча = 8 * 2 = 16&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">optim&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;paged_adamw_32bit&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># Экономия VRAM с помощью Paged Optimizer&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">save_steps&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">100&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">logging_steps&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">10&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">learning_rate&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">2e-4&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fp16&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">False&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">bf16&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># Обучение со смешанной точностью (bfloat16)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">max_grad_norm&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">0.3&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">max_steps&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">500&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># 500 шагов для тестирования. В реальности указывайте количество эпох&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">warmup_ratio&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">0.03&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">group_by_length&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">lr_scheduler_type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;cosine&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 9. Запуск обучения с помощью SFTTrainer&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">trainer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">SFTTrainer&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">train_dataset&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">dataset&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">peft_config&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">peft_config&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dataset_text_field&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">max_seq_length&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1024&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># Настройте в соответствии с ожидаемой длиной входа&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tokenizer&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">tokenizer&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">args&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">training_args&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Starting training...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">trainer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">train&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 10. Сохранение адаптера LoRA&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">trainer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">save_pretrained&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;./tinyllama-lora-final&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">tokenizer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">save_pretrained&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;./tinyllama-lora-final&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Training complete and model saved.&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;h2 id="7-оценка-производительности-и-устранение-неполадок">7. Оценка производительности и устранение неполадок
&lt;/h2>&lt;p>Вот частые проблемы и их решения при запуске обучения в локальной среде.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Возникает ошибка OOM (Out Of Memory):&lt;/strong>
&lt;ul>
&lt;li>Уменьшите &lt;code>per_device_train_batch_size&lt;/code> до &lt;code>1&lt;/code>.&lt;/li>
&lt;li>Увеличьте &lt;code>gradient_accumulation_steps&lt;/code>, чтобы сохранить эффективный размер батча.&lt;/li>
&lt;li>Сократите &lt;code>max_seq_length&lt;/code> с &lt;code>2048&lt;/code> до &lt;code>1024&lt;/code> или &lt;code>512&lt;/code>.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Loss (Потеря) не снижается или расходится:&lt;/strong>
&lt;ul>
&lt;li>Возможно, скорость обучения (&lt;code>learning_rate&lt;/code>) слишком высока. Попробуйте снизить её с &lt;code>2e-4&lt;/code> до примерно &lt;code>5e-5&lt;/code>.&lt;/li>
&lt;li>Если используется Float16 вместо Bfloat16, возможно, происходит потеря значимости градиента (underflow). Проверьте наличие &lt;code>bf16=True&lt;/code>.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>При инференсе генерируются непонятные символы:&lt;/strong>
&lt;ul>
&lt;li>Убедитесь, что &lt;code>padding_side=&amp;quot;right&amp;quot;&lt;/code> настроен правильно. Также необходимо проверить, соответствует ли формат датасета (специальные токены вроде &lt;code>&amp;lt;|im_start|&amp;gt;&lt;/code>) формату базовой модели при предварительном обучении.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="8-развертывание-модели-deployment-после-тюнинга">8. Развертывание модели (Deployment) после тюнинга
&lt;/h2>&lt;p>После завершения тюнинга сохраняется не &amp;ldquo;вся базовая модель&amp;rdquo;, а только &amp;ldquo;&lt;strong>адаптер LoRA (веса разницы)&lt;/strong>&amp;rdquo; размером от нескольких до десятков мегабайт. Для быстрого инференса эти веса LoRA необходимо слить (merge) с исходной базовой моделью и экспортировать как единую модель.&lt;/p>
&lt;h3 id="скрипт-слияния-модели">Скрипт слияния модели
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">torch&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">peft&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">AutoPeftModelForCausalLM&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">transformers&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">AutoTokenizer&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">output_dir&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;./tinyllama-lora-final&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Загрузка модели и адаптера в формате FP16/BF16&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">AutoPeftModelForCausalLM&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_pretrained&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">output_dir&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">device_map&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;auto&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">torch_dtype&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">torch&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">bfloat16&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">tokenizer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">AutoTokenizer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_pretrained&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">output_dir&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Слияние и сохранение весов&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">merged_model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">model&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">merge_and_unload&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">merged_model&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">save_pretrained&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;./tinyllama-merged&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">safe_serialization&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">tokenizer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">save_pretrained&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;./tinyllama-merged&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Model merged and saved successfully!&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="запуск-сверхбыстрого-сервера-инференса-с-помощью-vllm">Запуск сверхбыстрого сервера инференса с помощью vLLM
&lt;/h3>&lt;p>Для максимизации скорости инференса (токенов в секунду) при развертывании в локальной среде настоятельно рекомендуется использовать &lt;strong>vLLM&lt;/strong> или &lt;strong>TGI (Text Generation Inference)&lt;/strong> вместо стандартного &lt;code>pipeline&lt;/code> от Hugging Face. vLLM использует технологию PagedAttention для предотвращения фрагментации памяти GPU и кардинально увеличивает пропускную способность при параллельных запросах.&lt;/p>
&lt;p>Следующая диаграмма Mermaid показывает пайплайн от обучения до развертывания сервера инференса.&lt;/p>
&lt;div class="mermaid">graph TD
A["Сырые приватные данные"] --> B["Предобработка и форматирование (JSONL)"]
B --> C["Файнтюнинг QLoRA (SFTTrainer)"]
C --> D["Веса адаптера LoRA (.safetensors)"]
D --> E["Слияние с базовой TinyLLaMA 1.1B"]
E --> F["Слитая модель (Merged Model)"]
F --> G["Развертывание через сервер vLLM"]
G --> H["Конечная точка API / UI (например, Чат-бот)"]&lt;/div>
&lt;p>Запуск API-сервера с использованием vLLM выполняется всего одной командой:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">python -m vllm.entrypoints.openai.api_server &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --model ./tinyllama-merged &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --host 0.0.0.0 &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --port &lt;span class="m">8000&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --max-model-len &lt;span class="m">2048&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --dtype bfloat16
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Теперь в вашей локальной среде развернута конечная точка, совместимая с OpenAI API, что позволяет безопасно и быстро использовать локальный ИИ.&lt;/p>
&lt;hr>
&lt;h2 id="9-заключение">9. Заключение
&lt;/h2>&lt;p>В этой статье мы рассмотрели методы максимально быстрого и экономичного с точки зрения памяти файнтюнинга в локальной среде легкой, но высокопроизводительной модели «TinyLLaMA» с 1.1B параметров.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>LoRA / QLoRA&lt;/strong> делают возможным полноценный тюнинг LLM даже на потребительских GPU.&lt;/li>
&lt;li>Использование &lt;strong>Flash Attention 2&lt;/strong> и &lt;strong>Gradient Checkpointing&lt;/strong> позволяет предельно оптимизировать время обучения и потребление VRAM.&lt;/li>
&lt;li>Развертывание с помощью &lt;strong>vLLM&lt;/strong> обеспечивает высокую пропускную способность даже в рабочей (production) среде.&lt;/li>
&lt;/ul>
&lt;p>Эксплуатация локальной LLM в on-premises среде не только защищает конфиденциальность данных, но и становится мощнейшим инструментом для создания по низким затратам специализированного ИИ, адаптированного для конкретных областей (юриспруденция, медицина, внутренние правила компании и т.д.). Пожалуйста, используйте это руководство для создания вашей собственной специализированной версии TinyLLaMA.&lt;/p></description></item><item><title>Разработка небольших ИИ-моделей (таких как TinyLLaMA) на C++</title><link>http://kenji.blog/ru/p/cpp-small-ai-model-tinyllama-dev-guide/</link><pubDate>Fri, 11 Sep 2026 14:00:00 +0900</pubDate><guid>http://kenji.blog/ru/p/cpp-small-ai-model-tinyllama-dev-guide/</guid><description>&lt;img src="http://kenji.blog/p/cpp-small-ai-model-tinyllama-dev-guide/img/eyecatch.jpg" alt="Featured image of post Разработка небольших ИИ-моделей (таких как TinyLLaMA) на C++" />&lt;h1 id="руководство-по-разработке-небольших-ии-моделей-таких-как-tinyllama-на-c">Руководство по разработке небольших ИИ-моделей (таких как TinyLLaMA) на C++
&lt;/h1>&lt;p>В последние годы стремительно растет интерес к запуску больших языковых моделей (LLM) в локальных средах. В частности, небольшие модели, такие как TinyLLaMA (1,1 млрд параметров), могут осуществлять вывод с практической скоростью даже на периферийных устройствах с ограниченными ресурсами и обычных ноутбуках (включая среды Windows). В то время как разработка с использованием Python и PyTorch является мейнстримом, для достижения максимальной производительности и экономии памяти де-факто стандартом становится комбинация C++ и основанной на C тензорной библиотеки &amp;ldquo;ggml&amp;rdquo;.&lt;/p>
&lt;p>В этой статье будет представлено очень подробное пошаговое руководство по разработке для создания механизма вывода с нуля (или глубокого понимания внутренней структуры существующего llama.cpp) для загрузки TinyLLaMA и генерации текста с использованием C++.&lt;/p>
&lt;hr>
&lt;h2 id="1-почему-c-и-ggml">1. Почему C++ и ggml?
&lt;/h2>&lt;p>На этапе обучения ИИ Python имеет подавляющее преимущество благодаря своей гибкости и богатой экосистеме. Однако на этапах развертывания и &amp;ldquo;вывода&amp;rdquo; (Inference) C++ становится мощным выбором по следующим причинам:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Снижение накладных расходов&lt;/strong>: Можно полностью исключить глобальную блокировку интерпретатора (GIL) и накладные расходы времени выполнения Python.&lt;/li>
&lt;li>&lt;strong>Эффективность памяти и выделение арены&lt;/strong>: Поскольку выделение и освобождение памяти можно контролировать вручную, предотвращаются непредсказуемые всплески, вызванные сборкой мусора.&lt;/li>
&lt;li>&lt;strong>Прямой доступ к оборудованию&lt;/strong>: Можно напрямую вызывать встроенные функции SIMD (Intrinsics), такие как AVX-512, AVX2, ARM NEON, чтобы максимально использовать вычислительную мощность процессора.&lt;/li>
&lt;li>&lt;strong>Отсутствие зависимостей&lt;/strong>: ggml — это библиотека C/C++ с нулевыми зависимостями (Zero dependencies), которую можно легко собрать даже в среде MSVC на Windows, если есть компилятор.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="2-общий-обзор-архитектуры">2. Общий обзор архитектуры
&lt;/h2>&lt;p>Общий поток конвейера вывода показан на диаграмме Mermaid ниже. Это серия процессов, начиная с введенного пользователем текста и заканчивая генерацией следующего токена.&lt;/p>
&lt;div class="mermaid">graph TD
A["Введенный пользователем текст"] --> B["Токенизатор BPE"]
B --> C["Массив ID токенов"]
C --> D["Поиск в слое Embedding"]
D --> E["Блоки Transformer"]
E --> F["RMSNorm"]
F --> G["Слой LM Head"]
G --> H["Массив логитов"]
H --> I["Модуль Sampler"]
I --> J["ID следующего токена"]
J --> K["Детокенизатор"]
K --> L["Фрагмент выходного текста"]
J -.-> |"Добавить в контекст"| C&lt;/div>
&lt;p>Поскольку это авторегрессионная модель, выведенный токен снова добавляется в контекст и циркулирует как входные данные для прогнозирования следующего токена (пунктирная линия на диаграмме).&lt;/p>
&lt;hr>
&lt;h2 id="3-формат-модели-и-отображение-памяти-mmap">3. Формат модели и отображение памяти (mmap)
&lt;/h2>&lt;p>Самым большим препятствием при работе с весами гигантских нейронных сетей являются дисковый ввод-вывод и потребление памяти. В реализации на C++ это решается с помощью &lt;strong>отображения памяти (mmap)&lt;/strong>.&lt;/p>
&lt;h3 id="31-механизм-отображения-памяти-и-реализация-в-windows">3.1 Механизм отображения памяти и реализация в Windows
&lt;/h3>&lt;p>С помощью mmap содержимое файла можно напрямую отобразить в виртуальное адресное пространство процесса.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Zero-copy (Нулевое копирование)&lt;/strong>: Данные считываются непосредственно с диска в страничный кэш ядра, и лишнее копирование в пользовательское пространство не происходит.&lt;/li>
&lt;li>&lt;strong>Загрузка по требованию (Page Fault)&lt;/strong>: В тот момент, когда процессор фактически обращается к этому адресу памяти, происходит ошибка страницы (page fault), и в физическую память загружается только необходимый фрагмент (обычно 4 КБ).&lt;/li>
&lt;/ul>
&lt;p>В среде Windows вместо POSIX &lt;code>mmap&lt;/code> используются API Win32 &lt;code>CreateFileMapping&lt;/code> и &lt;code>MapViewOfFile&lt;/code>.&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant OS["ОС Windows"]
participant RAM["Физическая память"]
participant App["Приложение C++"]
App->>OS: "CreateFileMapping / MapViewOfFile"
OS-->>App: "Указатель адреса виртуальной памяти"
App->>App: "Чтение тензорных данных по указателю"
OS->>RAM: "Ошибка страницы / Загрузка страницы с диска"
RAM-->>App: "Данные готовы для вычислений SIMD"&lt;/div>
&lt;h3 id="32-бинарная-структура-формата-gguf">3.2 Бинарная структура формата GGUF
&lt;/h3>&lt;p>&lt;strong>GGUF (GPT-Generated Unified Format)&lt;/strong>, преобразованный из таких форматов, как &lt;code>.safetensors&lt;/code> на Hugging Face, является идеальным форматом для вывода. Он имеет строгую бинарную структуру, как показано ниже:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Magic Bytes&lt;/strong>: &lt;code>0x46554747&lt;/code> (GGUF).&lt;/li>
&lt;li>&lt;strong>Version&lt;/strong>: Номер версии формата.&lt;/li>
&lt;li>&lt;strong>Tensor Count &amp;amp; Metadata Count&lt;/strong>: Количество тензоров и количество пар ключ-значение метаданных.&lt;/li>
&lt;li>&lt;strong>Metadata (Key-Value Pairs)&lt;/strong>: Ключи с префиксом длины строки и типизированные значения.&lt;/li>
&lt;li>&lt;strong>Tensor Info&lt;/strong>: Имя каждого тензора, количество измерений, тип данных (FP16, Q4_K и т.д.) и позиция смещения в файле.&lt;/li>
&lt;li>&lt;strong>Padding&lt;/strong>: Заполнение (padding), вставляемое для того, чтобы тензорные данные были выровнены по определенной границе (обычно 32 или 64 байта). Это важно для быстрого доступа к памяти с помощью инструкций SIMD (особенно AVX).&lt;/li>
&lt;li>&lt;strong>Tensor Data&lt;/strong>: Массив выровненных фактических данных весов.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="4-математические-основы-tinyllama-и-алгоритмы-c">4. Математические основы TinyLLaMA и алгоритмы C++
&lt;/h2>&lt;p>TinyLLaMA включает в себя несколько продвинутых архитектурных хитростей для повышения эффективности. Мы объясним математические выражения, необходимые для их правильной реализации на C++.&lt;/p>
&lt;h3 id="41-rmsnorm-нормализация-среднеквадратичного-значения">4.1 RMSNorm (Нормализация среднеквадратичного значения)
&lt;/h3>&lt;p>Затраты на вычисления снижаются за счет отказа от центрирования по среднему значению из LayerNorm и выполнения только масштабирования дисперсии.&lt;/p>
$$ \text{RMSNorm}(x) = \frac{x}{\sqrt{\frac{1}{d}\sum_{i=1}^{d} x_i^2 + \epsilon}} \odot \gamma $$
&lt;p>Где $d$ — количество измерений, а $\gamma$ — обученный тензор масштабирования.
При реализации на C++ сначала быстро вычисляется сумма квадратов массива с помощью &lt;code>_mm256_fmadd_ps&lt;/code> из AVX2 и оптимизируется путем умножения на обратный квадратный корень (например, инструкция &lt;code>_mm256_rsqrt_ps&lt;/code>).&lt;/p>
&lt;h3 id="42-rope-rotary-position-embedding">4.2 RoPE (Rotary Position Embedding)
&lt;/h3>&lt;p>Это метод, который применяет информацию о позиции токена в виде вращения в тензорном пространстве. Его можно рассматривать как вращение на комплексной плоскости, и к соседней паре измерений $(x_1, x_2)$ вектора $x$ применяется следующее вращение:&lt;/p>
$$ \text{RoPE}(x, m) = \begin{pmatrix} x_{1} \cos(m\theta) - x_{2} \sin(m\theta) \\ x_{1} \sin(m\theta) + x_{2} \cos(m\theta) \end{pmatrix} $$
&lt;p>Где $m$ — абсолютный индекс позиции токена, а $\theta$ — предварительно вычисленная базовая частота. В ggml это выполняется параллельно просто путем добавления оператора &lt;code>ggml_rope&lt;/code> во время построения графа вывода.&lt;/p>
&lt;h3 id="43-grouped-query-attention-gqa">4.3 Grouped-Query Attention (GQA)
&lt;/h3>&lt;p>В обычном Multi-Head Attention (MHA) количество голов для Query, Key и Value одинаково. Однако TinyLLaMA использует &lt;strong>Grouped-Query Attention (GQA)&lt;/strong>, чтобы радикально снизить пропускную способность памяти и потребление кэша KV.&lt;/p>
$$ \text{Attention}(Q, K, V) = \text{softmax}\left(\frac{Q K^T}{\sqrt{d_k}}\right) V $$
&lt;p>В GQA несколько голов Query совместно используют одну голову Key/Value. В реализации C++ перед выполнением матричного умножения &lt;code>ggml_mul_mat&lt;/code> требуется операция широковещательной рассылки (broadcast) тензора KV в соответствии с количеством Query.&lt;/p>
&lt;h3 id="44-функция-активации-swiglu">4.4 Функция активации SwiGLU
&lt;/h3>&lt;p>В слоях Feed-Forward Network (FFN) вместо GELU используется SwiGLU.&lt;/p>
$$ \text{SwiGLU}(x) = \text{Swish}(x W_{\text{gate}}) \otimes (x W_{\text{up}}) $$
$$ \text{Swish}(z) = z \cdot \sigma(z) = z \cdot \frac{1}{1 + e^{-z}} $$
&lt;p>В вычислительном графе это выражается путем объединения оператора &lt;code>ggml_silu&lt;/code> и &lt;code>ggml_mul&lt;/code>.&lt;/p>
&lt;hr>
&lt;h2 id="5-построение-вычислительного-графа-и-управление-памятью-с-помощью-ggml">5. Построение вычислительного графа и управление памятью с помощью ggml
&lt;/h2>&lt;p>ggml использует подход &amp;ldquo;Define-and-Run&amp;rdquo; (определи и запусти), при котором строится статический вычислительный граф для вывода, а затем вычисляется (evaluate).&lt;/p>
&lt;h3 id="51-ggml_context-и-распределитель-арены-arena-allocator">5.1 ggml_context и распределитель арены (Arena Allocator)
&lt;/h3>&lt;p>Самая уникальная особенность ggml — это &amp;ldquo;выделение арены&amp;rdquo; (arena allocation), которое вообще не выполняет динамическое выделение памяти (&lt;code>malloc&lt;/code> или &lt;code>new&lt;/code>) внутри цикла вывода.
При инициализации выделяется огромная непрерывная область памяти (арена), и указатель этой области увеличивается (increment) каждый раз при вызове &lt;code>ggml_new_tensor&lt;/code> и т.д. Когда один шаг вывода завершен, достаточно сбросить указатель выделения в исходное положение, и выделение памяти для следующего шага вывода мгновенно завершается.&lt;/p>
&lt;h3 id="52-конкретный-пример-построения-графа">5.2 Конкретный пример построения графа
&lt;/h3>&lt;p>Для каждого шага вывода в памяти собирается следующий вычислительный граф.&lt;/p>
&lt;div class="mermaid">graph TD
A["Входные ID токенов"] --> B["Поиск Embed"]
B --> C["ggml_rms_norm"]
C --> D["Проекции Q / K / V"]
D --> E["Позиционное ggml_rope"]
E --> F["Сохранение в кэш KV"]
E --> G["Загрузка из кэша KV"]
G --> H["Self Attention"]
H --> I["Масштабирование и Softmax"]
I --> J["Вывод Attention"]
J --> K["Выходная проекция"]
K --> L["Добавление Residual"]&lt;/div>
&lt;hr>
&lt;h2 id="6-квантование-quantization-и-оптимизация-windows--simd">6. Квантование (Quantization) и оптимизация Windows / SIMD
&lt;/h2>&lt;p>Если обрабатывать TinyLLaMA (1.1B) в формате FP16, потребуется около 2,2 ГБ памяти, но с помощью 4-битного квантования (например, Q4_K) её можно радикально сжать примерно до 600 МБ.&lt;/p>
&lt;h3 id="61-архитектура-блочного-квантования">6.1 Архитектура блочного квантования
&lt;/h3>&lt;p>ggml не квантует весь тензор единообразно, а делает это по «блокам».
В формате &lt;code>Q4_0&lt;/code> 32 значения FP16 объединяются в один блок.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Масштабный коэффициент (Scale factor)&lt;/strong>: 1 значение FP16 (2 байта)&lt;/li>
&lt;li>&lt;strong>Квантованные данные&lt;/strong>: 32 4-битных значения (16 байт)
Это сводит к минимуму влияние локальных выбросов.&lt;/li>
&lt;/ul>
&lt;h3 id="62-ускорение-скалярного-произведения-с-помощью-avx2">6.2 Ускорение скалярного произведения с помощью AVX2
&lt;/h3>&lt;p>При сборке для новейших процессоров x86 в среде Windows использование флагов компилятора, таких как &lt;code>/arch:AVX2&lt;/code>, обеспечивает обработку SIMD в следующем потоке:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Загрузка&lt;/strong>: Загрузка 4-битных квантованных данных из памяти в 256-битный регистр AVX.&lt;/li>
&lt;li>&lt;strong>Распаковка&lt;/strong>: Развертывание 4-битных значений в Int8 или Int16 с помощью битовых масок и операций сдвига.&lt;/li>
&lt;li>&lt;strong>Деквантование (Обратное квантование)&lt;/strong>: Умножение на масштабный коэффициент для преобразования в число с плавающей запятой.&lt;/li>
&lt;li>&lt;strong>Операция FMA&lt;/strong>: Параллельное выполнение операций умножения с накоплением (multiply-add) со значениями активации с помощью &lt;code>_mm256_fmadd_ps&lt;/code> (Fused Multiply-Add).&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="7-детали-реализации-кэша-kv">7. Детали реализации кэша KV
&lt;/h2>&lt;p>В авторегрессионной генерации «кэш KV» является важной функцией для пропуска вычисления Key и Value для прошлых токенов.&lt;/p>
&lt;p>Ключевые моменты при реализации на C++:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Предварительное выделение тензора&lt;/strong>: Инициализируйте огромный тензор для кэша KV на максимальную длину контекста (например, 2048 токенов) (рекомендуется FP16).&lt;/li>
&lt;li>&lt;strong>Копирование со смещением&lt;/strong>: Когда выполняются вычисления для позиции токена $N$, векторы K и V, полученные на этом шаге, сохраняются в $N$-й строке тензора кэша KV с помощью &lt;code>ggml_cpy&lt;/code> и т.д.&lt;/li>
&lt;li>&lt;strong>Создание представления (view) во время Attention&lt;/strong>: При вычислении Attention создается «представление» (view), указывающее только на часть токенов от 0 до $N$, которое передается в матричное умножение.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="8-токенизатор-bpe-и-декодирование">8. Токенизатор BPE и декодирование
&lt;/h2>&lt;p>Входная строка обрабатывается как последовательность байтов UTF-8 и сравнивается с заранее определенным словарем. В C++ для ускорения поиска по словарю реализуются алгоритмы с использованием &lt;strong>Trie-дерева (префиксного дерева)&lt;/strong> или очереди с приоритетом.&lt;/p>
&lt;p>Из логитов (logits), выводимых из LM Head, вероятности масштабируются с использованием параметра Temperature, кандидаты сужаются с помощью методов Top-K или Top-P (Nucleus Sampling), а затем случайные числа используются для определения конечного следующего токена.&lt;/p>
&lt;hr>
&lt;h2 id="9-настройка-проекта-c-среда-windows--powershell">9. Настройка проекта C++ (среда Windows / PowerShell)
&lt;/h2>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cmake" data-lang="cmake">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cmake_minimum_required&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">VERSION&lt;/span> &lt;span class="s">3.14&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">project&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">TinyLLaMACpp&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">CMAKE_CXX_STANDARD&lt;/span> &lt;span class="s">17&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Настройки оптимизации и флага AVX2 для Windows (MSVC)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">if&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">MSVC&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">add_compile_options&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">/O2&lt;/span> &lt;span class="s">/arch:AVX2&lt;/span> &lt;span class="s">/fp:fast&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">add_link_options&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">/STACK:8388608&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">else&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">add_compile_options&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">-O3&lt;/span> &lt;span class="s">-march=native&lt;/span> &lt;span class="s">-ffast-math&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">endif&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">add_library&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">ggml&lt;/span> &lt;span class="s">OBJECT&lt;/span> &lt;span class="s">ggml/ggml.c&lt;/span> &lt;span class="s">ggml/ggml-alloc.c&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">target_compile_definitions&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">ggml&lt;/span> &lt;span class="s">PRIVATE&lt;/span> &lt;span class="s">GGML_USE_AVX2&lt;/span> &lt;span class="s">GGML_USE_F16C&lt;/span> &lt;span class="s">GGML_USE_FMA&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">add_executable&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">main&lt;/span> &lt;span class="s">main.cpp&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">target_link_libraries&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">main&lt;/span> &lt;span class="s">ggml&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Пример команды сборки в PowerShell:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="n">mkdir&lt;/span> &lt;span class="n">build&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd &lt;/span>&lt;span class="n">build&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">cmake&lt;/span> &lt;span class="p">..&lt;/span> &lt;span class="n">-G&lt;/span> &lt;span class="s2">&amp;#34;Visual Studio 17 2022&amp;#34;&lt;/span> &lt;span class="n">-A&lt;/span> &lt;span class="n">x64&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">cmake&lt;/span> &lt;span class="p">-&lt;/span>&lt;span class="n">-build&lt;/span> &lt;span class="p">.&lt;/span> &lt;span class="p">-&lt;/span>&lt;span class="n">-config&lt;/span> &lt;span class="n">Release&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;h2 id="10-заключение">10. Заключение
&lt;/h2>&lt;p>Реализация механизма вывода для небольших ИИ-моделей, таких как TinyLLaMA, с нуля с использованием C++ и ggml — это отличная возможность раскрыть черный ящик глубокого обучения и оценить красоту низкоуровневого управления аппаратным обеспечением. Давайте прокладывать путь в будущее периферийного ИИ (Edge AI), в полной мере наслаждаясь сутью системного программирования, такой как загрузка без копирования с использованием отображения памяти, оптимизация SIMD и создание кэша KV.&lt;/p></description></item><item><title>【Введение в реализацию RAG】Как заставить локальный ИИ читать ваши документы</title><link>http://kenji.blog/ru/p/rag-local-ai-implementation-guide/</link><pubDate>Fri, 11 Sep 2026 13:00:00 +0900</pubDate><guid>http://kenji.blog/ru/p/rag-local-ai-implementation-guide/</guid><description>&lt;img src="http://kenji.blog/p/rag-local-ai-implementation-guide/img/eyecatch.jpg" alt="Featured image of post 【Введение в реализацию RAG】Как заставить локальный ИИ читать ваши документы" />&lt;h1 id="введение">Введение
&lt;/h1>&lt;p>В последние годы развитие больших языковых моделей (LLM) поражает воображение: множество ИИ, во главе с ChatGPT и Claude, проникают в нашу жизнь и работу. Однако у обычных LLM есть очевидная слабость: они знают только &amp;ldquo;публичную информацию на момент обучения&amp;rdquo;. Естественно, они не могут ответить на вопросы, касающиеся &amp;ldquo;приватных документов&amp;rdquo;, таких как внутренние правила компании, личные заметки или неопубликованные проектные материалы. Если попытаться заставить их ответить, возрастает риск генерации правдоподобной лжи, не соответствующей действительности (галлюцинаций).&lt;/p>
&lt;p>В связи с этим сегодня в мире взрывными темпами распространяется технологическая архитектура &lt;strong>RAG (Retrieval-Augmented Generation: генерация с дополненной выборкой)&lt;/strong>. Использование RAG позволяет динамически предоставлять LLM собственные знания из внешней базы данных, на основе которых можно генерировать точные и обоснованные ответы.&lt;/p>
&lt;p>Кроме того, при работе с корпоративными данными или личной конфиденциальной информацией передача данных в облачные API, такие как OpenAI, часто недопустима с точки зрения политики безопасности. Именно здесь возникает потребность в создании &amp;ldquo;Локального RAG&amp;rdquo; в сочетании с &lt;strong>локальным ИИ&lt;/strong> (LLM, полностью работающей на вашем ПК или локальном сервере).&lt;/p>
&lt;p>В этой статье мы подробно рассмотрим все: от базовой теории RAG до конкретных методов реализации локального RAG с использованием Python, математической основы (механизм векторного поиска) и продвинутых методов запуска системы в рабочую среду.&lt;/p>
&lt;hr>
&lt;h1 id="1-общая-архитектура-rag">1. Общая архитектура RAG
&lt;/h1>&lt;p>RAG — это не единая модель ИИ, а системная архитектура, в которой взаимодействуют несколько компонентов. Она условно делится на две фазы: &amp;ldquo;фаза поглощения (загрузка данных)&amp;rdquo; и &amp;ldquo;фаза поиска и генерации&amp;rdquo;.&lt;/p>
&lt;p>Следующая диаграмма Mermaid показывает общую картину системы RAG.&lt;/p>
&lt;div class="mermaid">graph TD
subgraph "Фаза поглощения (Предварительная подготовка)"
Doc["Собственные документы (PDF, TXT и т.д.)"] --> Loader["Загрузчик документов"]
Loader --> Splitter["Разделение текста (Чанкинг)"]
Splitter --> EmbedModel1["Модель встраивания (Embedding)"]
EmbedModel1 --> VectorDB["Векторная база данных"]
end
subgraph "Фаза вывода (При запросе пользователя)"
User["Вопрос пользователя (Запрос)"] --> EmbedModel2["Модель встраивания (Embedding)"]
EmbedModel2 --> QueryVector["Вектор запроса"]
QueryVector --> Search["Поиск по сходству (Векторный поиск)"]
VectorDB --> Search
Search --> Context["Извлечение связанных фрагментов (Контекст)"]
User --> PromptBuilder["Построение промпта"]
Context --> PromptBuilder
PromptBuilder --> LocalLLM["Локальная LLM"]
LocalLLM --> Answer["Генерация окончательного ответа"]
end&lt;/div>
&lt;h2 id="фаза-поглощения-предварительная-подготовка">Фаза поглощения (Предварительная подготовка)
&lt;/h2>&lt;ol>
&lt;li>&lt;strong>Загрузка документов&lt;/strong>: Загрузка неструктурированных данных, таких как PDF, Word, текстовые файлы и т.д.&lt;/li>
&lt;li>&lt;strong>Чанкинг (разделение текста)&lt;/strong>: Разделение длинных текстов на осмысленные фрагменты (чанки) для того, чтобы они поместились в ограничение на ввод LLM (контекстное окно) и для повышения точности поиска.&lt;/li>
&lt;li>&lt;strong>Эмбеддинг (векторизация)&lt;/strong>: Передача разделенных чанков в модель встраивания (Embedding Model) и преобразование их в массивы чисел (векторы) размерностью от сотен до тысяч.&lt;/li>
&lt;li>&lt;strong>Сохранение в базу данных&lt;/strong>: Сохранение преобразованных векторов и связывание их с исходными текстовыми данными в векторной базе данных (Vector DB).&lt;/li>
&lt;/ol>
&lt;h2 id="фаза-вывода-во-время-выполнения">Фаза вывода (Во время выполнения)
&lt;/h2>&lt;ol>
&lt;li>&lt;strong>Векторизация запроса&lt;/strong>: Векторизация вопроса пользователя с использованием той же модели встраивания, что и при предварительной подготовке.&lt;/li>
&lt;li>&lt;strong>Поиск по сходству&lt;/strong>: Расчет сходства между вектором запроса и векторами документов в базе данных для получения нескольких наиболее семантически близких (релевантных) фрагментов текста.&lt;/li>
&lt;li>&lt;strong>Построение промпта&lt;/strong>: Объединение полученных связанных текстов в качестве &amp;ldquo;контекста (фоновых знаний)&amp;rdquo; с вопросом пользователя для создания промпта ввода в LLM.&lt;/li>
&lt;li>&lt;strong>Генерация ответа&lt;/strong>: Получив расширенный промпт, LLM генерирует ответ на основе предоставленной контекстной информации.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h1 id="2-глубокое-понимание-векторного-поиска-и-встраиваний-embeddings">2. Глубокое понимание векторного поиска и встраиваний (Embeddings)
&lt;/h1>&lt;p>В основе RAG лежит &amp;ldquo;векторный поиск (семантический поиск)&amp;rdquo;. В отличие от традиционного поиска по ключевым словам (например, BM25), который основан на точном совпадении и частоте слов, векторный поиск основан на &amp;ldquo;смысловом сходстве&amp;rdquo;. Например, даже если используются разные слова, такие как &amp;ldquo;собака&amp;rdquo; и &amp;ldquo;щенок&amp;rdquo;, &amp;ldquo;ПК&amp;rdquo; и &amp;ldquo;компьютер&amp;rdquo;, они будут найдены, если их значения близки.&lt;/p>
&lt;h2 id="что-такое-модель-встраивания-embedding-model">Что такое модель встраивания (Embedding Model)?
&lt;/h2>&lt;p>Модель встраивания — это нейронная сеть, которая принимает на вход текст на естественном языке и выводит плотный вектор (Dense Vector) фиксированной длины. Обычные модели (например, &lt;code>text-embedding-3-small&lt;/code> или модель с открытым исходным кодом &lt;code>multilingual-e5-large&lt;/code>) отображают текст в векторы вещественных чисел размерностью 384 или 1024.&lt;/p>
&lt;p>В этом многомерном пространстве (скрытом пространстве) они обучены так, что чем ближе по смыслу предложения, тем меньше расстояние между ними в координатном пространстве.&lt;/p>
&lt;h2 id="математическая-основа-расчета-сходства-косинусное-сходство">Математическая основа расчета сходства: Косинусное сходство
&lt;/h2>&lt;p>Когда векторная база данных ищет связанные документы, наиболее часто используемой метрикой расстояния является &lt;strong>косинусное сходство (Cosine Similarity)&lt;/strong>. В отличие от евклидова расстояния (абсолютного расстояния в пространстве), косинусное сходство фокусируется на &amp;ldquo;угле между двумя векторами&amp;rdquo;. Поскольку оно менее подвержено влиянию длины предложения (нормы вектора), оно очень хорошо подходит для расчета сходства текстов.&lt;/p>
&lt;p>В виде формулы косинусное сходство между векторами $\mathbf{A}$ и $\mathbf{B}$ выглядит следующим образом:&lt;/p>
$$ \text{Cosine Similarity}(\mathbf{A}, \mathbf{B}) = \cos(\theta) = \frac{\mathbf{A} \cdot \mathbf{B}}{\|\mathbf{A}\| \|\mathbf{B}\|} = \frac{\sum_{i=1}^{n} A_i B_i}{\sqrt{\sum_{i=1}^{n} A_i^2} \sqrt{\sum_{i=1}^{n} B_i^2}} $$
&lt;ul>
&lt;li>$\mathbf{A} \cdot \mathbf{B}$ обозначает скалярное произведение (Dot Product).&lt;/li>
&lt;li>$\|\mathbf{A}\|$ обозначает L2-норму (длину) вектора $\mathbf{A}$.&lt;/li>
&lt;li>$n$ — это размерность вектора.&lt;/li>
&lt;/ul>
&lt;p>Косинусное сходство принимает значения от -1 до 1.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Ближе к 1&lt;/strong>: направления двух векторов почти совпадают (очень схожи по смыслу)&lt;/li>
&lt;li>&lt;strong>Ближе к 0&lt;/strong>: два вектора перпендикулярны (не связаны)&lt;/li>
&lt;li>&lt;strong>Ближе к -1&lt;/strong>: два вектора указывают в противоположные стороны (противоположные по смыслу)&lt;/li>
&lt;/ul>
&lt;p>Современные векторные БД (Chroma, FAISS, Qdrant и т.д.) используют алгоритм приближенного поиска ближайших соседей (ANN), называемый HNSW (Hierarchical Navigable Small World), который оптимизирован для поиска документов с высоким косинусным сходством среди миллионов векторных данных за миллисекунды.&lt;/p>
&lt;hr>
&lt;h1 id="3-технологический-стек-для-создания-локального-rag">3. Технологический стек для создания локального RAG
&lt;/h1>&lt;p>Для создания полностью локального RAG, не зависящего от облака, мы будем использовать экосистему с открытым исходным кодом. Ниже представлен рекомендуемый технологический стек.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Языковая модель (LLM)&lt;/strong>
&lt;ul>
&lt;li>Инструмент: &lt;code>Ollama&lt;/code> или &lt;code>Llama.cpp&lt;/code>&lt;/li>
&lt;li>Модель: легкие и высокопроизводительные открытые модели, такие как &lt;code>Llama-3-8B-Instruct&lt;/code>, &lt;code>Gemma-2-9B-It&lt;/code>, &lt;code>Qwen2-7B-Instruct&lt;/code>. Для задач на японском языке подходят модели, настроенные на японский язык, такие как &lt;code>Llama-3-ELYZA-JP-8B&lt;/code>.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Модель встраивания (Embedding)&lt;/strong>
&lt;ul>
&lt;li>Модель: &lt;code>intfloat/multilingual-e5-large&lt;/code> или &lt;code>BAAI/bge-m3&lt;/code>. При локальном запуске обычно их скачивают с Hugging Face и запускают с помощью Sentence-Transformers.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Векторная база данных (Vector DB)&lt;/strong>
&lt;ul>
&lt;li>&lt;code>ChromaDB&lt;/code>: основана на Python, очень проста в настройке. Идеально подходит для локальной разработки.&lt;/li>
&lt;li>&lt;code>FAISS&lt;/code>: высокоскоростная библиотека векторного поиска, разработанная Meta.&lt;/li>
&lt;li>&lt;code>Qdrant&lt;/code> / &lt;code>Milvus&lt;/code>: для более масштабных проектов и рабочих сред.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Фреймворк оркестрации&lt;/strong>
&lt;ul>
&lt;li>&lt;code>LangChain&lt;/code>: фактический стандарт для связывания (Chain) компонентов.&lt;/li>
&lt;li>&lt;code>LlamaIndex&lt;/code>: фреймворк для подключения данных, специально ориентированный на RAG.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>В этот раз мы реализуем систему, используя самую простую в освоении комбинацию: &lt;strong>LangChain + ChromaDB + Ollama + HuggingFaceEmbeddings&lt;/strong>.&lt;/p>
&lt;hr>
&lt;h1 id="4-учебное-руководство-по-реализации-создание-полного-локального-rag-на-python">4. Учебное руководство по реализации: Создание полного локального RAG на Python
&lt;/h1>&lt;p>Начиная с этого момента, мы будем создавать локальный RAG, написав фактический код на Python. Пожалуйста, предварительно установите Ollama на свой ПК и запустите его в фоновом режиме. Также загрузите модель в Ollama (например: &lt;code>ollama run llama3&lt;/code>).&lt;/p>
&lt;h2 id="шаг-1-установка-необходимых-библиотек">Шаг 1: Установка необходимых библиотек
&lt;/h2>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">pip install langchain langchain-community langchain-huggingface
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pip install chromadb sentence-transformers pypdf
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="шаг-2-обзор-кода-реализации">Шаг 2: Обзор кода реализации
&lt;/h2>&lt;p>Ниже представлен полный Python-скрипт для загрузки PDF-файла, его векторизации и ответов на вопросы с помощью локальной LLM.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;span class="lnt">64
&lt;/span>&lt;span class="lnt">65
&lt;/span>&lt;span class="lnt">66
&lt;/span>&lt;span class="lnt">67
&lt;/span>&lt;span class="lnt">68
&lt;/span>&lt;span class="lnt">69
&lt;/span>&lt;span class="lnt">70
&lt;/span>&lt;span class="lnt">71
&lt;/span>&lt;span class="lnt">72
&lt;/span>&lt;span class="lnt">73
&lt;/span>&lt;span class="lnt">74
&lt;/span>&lt;span class="lnt">75
&lt;/span>&lt;span class="lnt">76
&lt;/span>&lt;span class="lnt">77
&lt;/span>&lt;span class="lnt">78
&lt;/span>&lt;span class="lnt">79
&lt;/span>&lt;span class="lnt">80
&lt;/span>&lt;span class="lnt">81
&lt;/span>&lt;span class="lnt">82
&lt;/span>&lt;span class="lnt">83
&lt;/span>&lt;span class="lnt">84
&lt;/span>&lt;span class="lnt">85
&lt;/span>&lt;span class="lnt">86
&lt;/span>&lt;span class="lnt">87
&lt;/span>&lt;span class="lnt">88
&lt;/span>&lt;span class="lnt">89
&lt;/span>&lt;span class="lnt">90
&lt;/span>&lt;span class="lnt">91
&lt;/span>&lt;span class="lnt">92
&lt;/span>&lt;span class="lnt">93
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">os&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_community.document_loaders&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">PyPDFLoader&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_text_splitters&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">RecursiveCharacterTextSplitter&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_huggingface&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">HuggingFaceEmbeddings&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_community.vectorstores&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Chroma&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_community.llms&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Ollama&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_core.prompts&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">PromptTemplate&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain.chains&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">RetrievalQA&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 1. Загрузка документов&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Загрузка документов...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Укажите путь к PDF, который нужно загрузить&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">file_path&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;sample_company_policy.pdf&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">loader&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">PyPDFLoader&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">file_path&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">documents&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">loader&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">load&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 2. Разделение на чанки (Text Splitting)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Разделение на фрагменты подходящего размера, не нарушая смысла текста&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">text_splitter&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">RecursiveCharacterTextSplitter&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">chunk_size&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">500&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># Максимальное количество символов в одном чанке&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">chunk_overlap&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">50&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># Количество перекрывающихся символов между чанками (предотвращает потерю контекста)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">separators&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;。&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;、&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34; &amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">chunks&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">text_splitter&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">split_documents&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">documents&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Разделено на &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">chunks&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> чанков.&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 3. Инициализация модели встраивания (Local HuggingFace Model)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Использование мультиязычной модели&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Загрузка модели встраивания...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">embeddings&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">HuggingFaceEmbeddings&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model_name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;intfloat/multilingual-e5-large&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model_kwargs&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s1">&amp;#39;device&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;cpu&amp;#39;&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="c1"># Используйте &amp;#39;cuda&amp;#39; или &amp;#39;mps&amp;#39;, если есть GPU&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 4. Создание векторной базы данных (Chroma)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Создание векторной базы данных...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">persist_directory&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;./chroma_db&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">vectorstore&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Chroma&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_documents&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">documents&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">chunks&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">embedding&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">embeddings&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">persist_directory&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">persist_directory&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Создание ретривера (поисковика). Настройка на получение топ-3 связанных документов&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">retriever&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">vectorstore&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">as_retriever&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">search_kwargs&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;k&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 5. Инициализация локальной LLM (Ollama)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Подключение к локальной LLM...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Предварительно загрузите модель, например, с помощью &amp;#39;ollama pull llama3&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llm&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Ollama&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;llama3&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 6. Определение шаблона промпта&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">prompt_template&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Вы - превосходный ассистент, хорошо разбирающийся в правилах и внутренней информации компании.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">Используя ТОЛЬКО следующий контекст (фоновую информацию), подробно ответьте на вопрос пользователя.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">Если ответ не может быть найден в контексте, не стройте догадок и честно ответьте «Я не могу найти ответ в предоставленной информации».
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">【Контекст】
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&lt;/span>&lt;span class="si">{context}&lt;/span>&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">【Вопрос】
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&lt;/span>&lt;span class="si">{question}&lt;/span>&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">【Ответ】:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">PROMPT&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">PromptTemplate&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">template&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">prompt_template&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">input_variables&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;context&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;question&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 7. Создание цепочки RAG&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">qa_chain&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">RetrievalQA&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_chain_type&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llm&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">llm&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">chain_type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;stuff&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">retriever&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">retriever&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">return_source_documents&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># Настройка возврата источников информации&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">chain_type_kwargs&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;prompt&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">PROMPT&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 8. Выполнение вопроса&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">query&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;Расскажите об условиях оплаты транспортных расходов при удаленной работе.&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">Вопрос: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">query&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">qa_chain&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="s2">&amp;#34;query&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">query&lt;/span>&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;【Ответ】&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;result&amp;#39;&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">---&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;【Использованные источники】&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">doc&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;source_documents&amp;#39;&lt;/span>&lt;span class="p">]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;- Страница &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">doc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">metadata&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;page&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;неизвестно&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">doc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">page_content&lt;/span>&lt;span class="p">[:&lt;/span>&lt;span class="mi">50&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;__main__&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">main&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="объяснение-ключевых-моментов-кода">Объяснение ключевых моментов кода
&lt;/h2>&lt;ol>
&lt;li>&lt;strong>RecursiveCharacterTextSplitter&lt;/strong>:
Это наиболее рекомендуемый разделитель для естественного языка. Он пытается разделить текст в порядке: абзац (&lt;code>\n\n&lt;/code>), строка (&lt;code>\n&lt;/code>), точка (&lt;code>。&lt;/code>), чтобы разделить текст таким образом, чтобы он уместился в заданный &lt;code>chunk_size&lt;/code>, сохраняя при этом смысловые блоки насколько это возможно. Установив &lt;code>chunk_overlap&lt;/code>, вы предотвращаете потерю информации на границах контекста.&lt;/li>
&lt;li>&lt;strong>HuggingFaceEmbeddings&lt;/strong>:
&lt;code>intfloat/multilingual-e5-large&lt;/code> — это очень мощная многоязычная модель встраивания с открытым исходным кодом. Вы можете векторизовать текст оффлайн в локальной памяти, не используя облачные API (например, &lt;code>text-embedding-ada-002&lt;/code> от OpenAI).&lt;/li>
&lt;li>&lt;strong>ChromaDB&lt;/strong>:
Она работает в оперативной памяти или локальном хранилище (на базе SQLite), поэтому вам не нужно настраивать сложный сервер базы данных. Указав &lt;code>persist_directory&lt;/code>, вы можете пропустить процесс векторизации при повторном выполнении и загрузить базу данных с диска.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h1 id="5-продвинутые-методы-rag-advanced-rag-techniques">5. Продвинутые методы RAG (Advanced RAG Techniques)
&lt;/h1>&lt;p>Базовая система RAG (Naive RAG), созданная в приведенном выше руководстве, также будет работать, но если в рабочей среде требуется высокая точность ответов, необходимо внедрить следующие продвинутые методы.&lt;/p>
&lt;h2 id="51-гибридный-поиск-hybrid-search">5.1 Гибридный поиск (Hybrid Search)
&lt;/h2>&lt;p>Векторный поиск хорошо справляется с пониманием &amp;ldquo;смысла&amp;rdquo;, но он может плохо справляться со строгим поиском по ключевым словам, таким как &amp;ldquo;определенные имена собственные&amp;rdquo;, &amp;ldquo;номера моделей продуктов&amp;rdquo; и &amp;ldquo;идентификаторы сотрудников&amp;rdquo;.
Следовательно, параллельно выполняя &lt;strong>семантический поиск&lt;/strong> (с помощью векторного поиска) и &lt;strong>поиск по ключевым словам&lt;/strong> (с использованием алгоритмов типа BM25), а затем оценивая и объединяя результаты обоих подходов (используя методы типа Reciprocal Rank Fusion, RRF), можно радикально сократить количество пропусков в поиске.&lt;/p>
&lt;h2 id="52-повторное-ранжирование-re-ranking">5.2 Повторное ранжирование (Re-ranking)
&lt;/h2>&lt;p>Векторный поиск работает быстро, но он не обязательно оценивает точную контекстную релевантность. Общий конвейер для повышения точности поиска выглядит следующим образом:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Начальный поиск (First-stage Retrieval)&lt;/strong>: Широкий и поверхностный поиск из векторной БД, извлечение примерно 20–30 связанных чанков.&lt;/li>
&lt;li>&lt;strong>Повторная оценка (Re-ranking)&lt;/strong>: Использование другой, более &amp;ldquo;тяжелой&amp;rdquo; модели машинного обучения, называемой Cross-Encoder (например, &lt;code>bge-reranker&lt;/code>), для подачи пар запроса пользователя и извлеченного чанка и пересчета баллов семантической релевантности.&lt;/li>
&lt;li>&lt;strong>Отбор&lt;/strong>: Только верхние 3–5 с наивысшими баллами передаются в промпт LLM в качестве окончательного контекста.&lt;/li>
&lt;/ol>
&lt;p>Этот метод предотвращает передачу нерелевантной шумовой информации в LLM и может значительно повысить точность (Precision) ответов.&lt;/p>
&lt;div class="mermaid">graph LR
Query["Запрос"] --> VSearch["Векторный поиск (топ 20)"]
VSearch --> Reranker["Модель реранкера (Cross-Encoder)"]
Query --> Reranker
Reranker --> TopK["Высокоточные топ 3"]
TopK --> LLM["Генерация LLM"]&lt;/div>
&lt;h2 id="53-семантический-чанкинг-и-поиск-родительского-документа">5.3 Семантический чанкинг и поиск родительского документа
&lt;/h2>&lt;p>Существует метод под названием &amp;ldquo;Semantic Chunking&amp;rdquo; (Семантический чанкинг), который использует ИИ для обнаружения изменений в смысле предложений и разделяет текст на их основе, а не механически разбивает текст на фиксированное количество символов.
Кроме того, существует метод &amp;ldquo;Parent Document Retriever&amp;rdquo; (Поиск родительского документа), в котором векторизация выполняется на очень маленьких единицах (например, предложениях) для высокоточного поиска, но при передаче в LLM передается &amp;ldquo;исходный большой абзац (родительский документ)&amp;rdquo;, содержащий это предложение, что обеспечивает LLM достаточным контекстом.&lt;/p>
&lt;hr>
&lt;h1 id="6-проблемы-и-решения-при-эксплуатации-локального-rag">6. Проблемы и решения при эксплуатации локального RAG
&lt;/h1>&lt;p>При создании и эксплуатации RAG в локальной среде существуют специфические препятствия.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Нехватка VRAM (видеопамяти)&lt;/strong>:
Чтобы запустить локальную LLM с практичной скоростью (десятки токенов в секунду), вам необходимо загрузить модель в VRAM вашего GPU. Для работы модели класса 8B в формате fp16 (16-битная плавающая запятая) требуется около 16 ГБ VRAM, но с использованием технологии &lt;strong>квантования (Quantization)&lt;/strong> (сжатие до 4 или 8 бит, например, в форматах GGUF или AWQ), ее можно заставить работать достаточно быстро даже на 8 ГБ VRAM (например, на стандартном игровом ПК). Llama.cpp и Ollama поддерживают эти форматы квантования из коробки.&lt;/li>
&lt;li>&lt;strong>Ограничение контекстного окна&lt;/strong>:
Если объем полученного контекста слишком велик, он может превысить лимит ввода LLM (лимит токенов) или модель может &amp;ldquo;забыть&amp;rdquo; среднюю часть информации (феномен Lost in the middle). Важно настраивать количество извлекаемых чанков и тщательно отбирать их с помощью вышеупомянутых технологий реранжирования.&lt;/li>
&lt;li>&lt;strong>Управление свежестью данных&lt;/strong>:
Если исходные документы обновляются, соответствующие векторы документов в векторной базе данных также должны быть обновлены или удалены (операции CRUD). Поскольку ChromaDB поддерживает обновление на основе идентификаторов документов, практично управлять хеш-значениями файлов и настраивать пакетную обработку для синхронизации только изменений.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h1 id="заключение">Заключение
&lt;/h1>&lt;p>RAG (Retrieval-Augmented Generation) — это мощная парадигма, которая превращает ИИ из обычного универсального ассистента в вашего «персонального эксперта» или «эксперта, специализирующегося на ваших внутренних задачах».&lt;/p>
&lt;p>Мы увидели, что даже с высокими требованиями к конфиденциальности, не позволяющими использовать облачные сервисы, можно относительно легко создать полноценную среду «локального RAG», комбинируя элементы экосистемы с открытым исходным кодом, такие как Ollama, LangChain и ChromaDB.&lt;/p>
&lt;p>Основываясь на математическом понимании векторных пространств и продвинутых подходах, таких как разделение текста и реранжирование, рассмотренных в этой статье, попробуйте разработать собственную оригинальную систему ИИ, используя свои данные. Скорость эволюции локального ИИ поразительна, и система, которую вы построили сегодня, может быть мгновенно обновлена по производительности завтра, просто заменив ее на еще более умную легкую модель.&lt;/p>
&lt;hr>
&lt;p>&lt;em>В этом блоге мы продолжим публиковать подробные статьи о технологиях ИИ и RAG. Если у вас есть какие-либо вопросы или отзывы, пожалуйста, оставьте их в разделе комментариев.&lt;/em>&lt;/p></description></item><item><title>Подробное сравнение API ChatGPT, Gemini и Claude! Что выбрать?</title><link>http://kenji.blog/ru/p/chatgpt-gemini-claude-api-comparison/</link><pubDate>Fri, 11 Sep 2026 12:00:00 +0900</pubDate><guid>http://kenji.blog/ru/p/chatgpt-gemini-claude-api-comparison/</guid><description>&lt;img src="http://kenji.blog/p/chatgpt-gemini-claude-api-comparison/img/eyecatch.jpg" alt="Featured image of post Подробное сравнение API ChatGPT, Gemini и Claude! Что выбрать?" />&lt;h1 id="подробное-сравнение-api-chatgpt-gemini-и-claude-что-выбрать">Подробное сравнение API ChatGPT, Gemini и Claude! Что выбрать?
&lt;/h1>&lt;p>Развитие технологий ИИ поражает, особенно в области больших языковых моделей (LLM: Large Language Model), где ChatGPT (серия GPT) от OpenAI, Gemini от Google и Claude от Anthropic ведут ожесточенную борьбу за лидерство. По состоянию на 2026 год, каждая компания выпускает новые модели и функции API раз в несколько месяцев, а то и недель. Для разработчиков и ИТ-архитекторов вопрос «Какой API интегрировать в продукт?» стал важнейшим решением, определяющим успех проекта.&lt;/p>
&lt;p>В этой статье мы подробно сравним и разберем API этих трех ведущих провайдеров ИИ с точки зрения разработчика. Мы не просто перечислим их характеристики, но и углубимся в архитектуру, подробную структуру тарифов, математический анализ задержки (latency), конкретные примеры реализации на Python и Node.js, а также в новейшие методы оптимизации затрат, такие как кэширование промптов.&lt;/p>
&lt;p>Мы надеемся, что это руководство поможет вам выбрать идеальный API LLM для вашего юзкейса и создать масштабируемое, экономически эффективное ИИ-приложение.&lt;/p>
&lt;hr>
&lt;h2 id="1-философия-и-принципы-проектирования-каждого-llm-api">1. Философия и принципы проектирования каждого LLM API
&lt;/h2>&lt;p>При выборе технологии очень важно понимать философию, лежащую в основе разработки моделей и API каждой компании.&lt;/p>
&lt;h3 id="11-openai-chatgpt">1.1 OpenAI (ChatGPT)
&lt;/h3>&lt;p>Миссия OpenAI — «создание универсального искусственного интеллекта (AGI)», и компания постоянно задает стандарты в отрасли. Они предлагают разнообразные модели для разных задач, включая GPT-4o, GPT-4o-mini, а также модели серии o1, специализирующиеся на логических рассуждениях. Экосистема OpenAI является самой зрелой, с огромным количеством официальных и неофициальных библиотек и документации.&lt;/p>
&lt;h3 id="12-google-gemini">1.2 Google (Gemini)
&lt;/h3>&lt;p>Google придерживается стратегии «AI First» (ИИ в первую очередь) и делает ставку на масштабируемость, максимально используя собственную инфраструктуру (сети TPU). Главная особенность Gemini 1.5 Pro/Flash — огромное контекстное окно до 2 миллионов токенов, что позволяет за один раз обрабатывать длинные документы и многочасовые видео или аудио. Тесная интеграция с Google Cloud (Vertex AI) также делает этот вариант привлекательным для корпоративного сектора.&lt;/p>
&lt;h3 id="13-anthropic-claude">1.3 Anthropic (Claude)
&lt;/h3>&lt;p>Anthropic, основанная бывшими сотрудниками OpenAI, использует уникальный подход к безопасности под названием «Конституционный ИИ (Constitutional AI)». Модели Claude 3.5 Sonnet и Opus завоевали огромную популярность среди разработчиков благодаря своим высоким способностям к рассуждению, генерации кода, а главное — благодаря «естественному диалогу, похожему на человеческий» и «низкому уровню галлюцинаций».&lt;/p>
&lt;hr>
&lt;h2 id="2-подробное-сравнение-характеристик-семейств-моделей">2. Подробное сравнение характеристик семейств моделей
&lt;/h2>&lt;p>Сравним характеристики основных моделей по состоянию на 2026 год.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Провайдер&lt;/th>
&lt;th>Основная модель&lt;/th>
&lt;th>Максимальный контекст&lt;/th>
&lt;th>Главные преимущества&lt;/th>
&lt;th>Рекомендуемые юзкейсы&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>OpenAI&lt;/strong>&lt;/td>
&lt;td>GPT-4o&lt;/td>
&lt;td>128K&lt;/td>
&lt;td>Скорость, распознавание изображений, голосовой интерфейс&lt;/td>
&lt;td>Интерактивные приложения, задачи общего назначения&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>OpenAI&lt;/strong>&lt;/td>
&lt;td>o1-preview&lt;/td>
&lt;td>128K&lt;/td>
&lt;td>Продвинутая логика, математика, кодинг&lt;/td>
&lt;td>Генерация сложных алгоритмов, исследовательские задачи&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Google&lt;/strong>&lt;/td>
&lt;td>Gemini 1.5 Pro&lt;/td>
&lt;td>2,000K&lt;/td>
&lt;td>Обработка сверхдлинных текстов, мультимодальность (видео/аудио)&lt;/td>
&lt;td>Анализ огромных кодовых баз, суммаризация видео&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Google&lt;/strong>&lt;/td>
&lt;td>Gemini 1.5 Flash&lt;/td>
&lt;td>2,000K&lt;/td>
&lt;td>Низкая задержка, высокая пропускная способность, низкая стоимость&lt;/td>
&lt;td>Обработка в реальном времени, пакетная обработка больших объемов данных&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Anthropic&lt;/strong>&lt;/td>
&lt;td>Claude 3.5 Sonnet&lt;/td>
&lt;td>200K&lt;/td>
&lt;td>Написание кода, генерация естественного текста&lt;/td>
&lt;td>Помощь в разработке ПО, продвинутая поддержка клиентов&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Anthropic&lt;/strong>&lt;/td>
&lt;td>Claude 3.5 Haiku&lt;/td>
&lt;td>200K&lt;/td>
&lt;td>Сверхбыстрые ответы, экономичность&lt;/td>
&lt;td>Периферийный ИИ (Edge AI), чат-боты в реальном времени&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="3-глубокое-погружение-в-архитектуру-что-происходит-за-api-запросом">3. Глубокое погружение в архитектуру: что происходит за API-запросом
&lt;/h2>&lt;p>Какие процессы происходят на бэкенде при вызове LLM API? Чтобы оптимизировать производительность, необходимо понимать эту архитектуру.&lt;/p>
&lt;p>Следующая диаграмма Mermaid показывает весь путь — от отправки API-запроса клиентом до возврата токенов в виде потока (streaming).&lt;/p>
&lt;div class="mermaid">graph TD
A["Клиентское приложение"] -->|HTTP/REST или gRPC| B["API-шлюз"]
B --> C["Балансировщик нагрузки"]
C --> D["Кластер вывода (Inference Cluster)"]
D --> E["Токенизатор (BPE / SentencePiece)"]
E --> F["KV Кэш и механизм внимания (Attention Mechanism)"]
F --> G["Блоки Transformer (Прямой проход)"]
G --> H["Выходной слой (Логиты)"]
H --> I["Сэмплер (Температура, Top-p, Top-k)"]
I --> J["Детокенизатор"]
J -->|Потоковый ответ (Чанки)| A&lt;/div>
&lt;h3 id="31-алгоритмы-токенизации-tokenization">3.1 Алгоритмы токенизации (Tokenization)
&lt;/h3>&lt;p>Текст, передаваемый в API, внутренне разбивается на единицы, называемые «токенами».&lt;/p>
&lt;ul>
&lt;li>&lt;strong>OpenAI (tiktoken)&lt;/strong>: Использует Byte-Pair Encoding (BPE). Очень эффективно сжимает текст на английском языке, но для языков, не использующих латиницу (например, японского или русского), количество токенов может значительно возрастать.&lt;/li>
&lt;li>&lt;strong>Google (Gemini)&lt;/strong>: Использует SentencePiece (Unigram Language Model). Отлично работает с разными языками, обычно требуя меньше токенов для текстов на отличных от английского языках.&lt;/li>
&lt;li>&lt;strong>Anthropic (Claude)&lt;/strong>: Использует кастомную версию BPE. Улучшена мультиязычная поддержка, а начиная с версии Claude 3, эффективность токенизации для японского и других языков была существенно повышена.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="4-математический-анализ-задержки-latency-и-производительности">4. Математический анализ задержки (Latency) и производительности
&lt;/h2>&lt;p>В приложениях реального времени задержка напрямую влияет на пользовательский опыт (UX). Задержку LLM API $T_{total}$ можно математически смоделировать следующим образом:&lt;/p>
$$ T_{total} = T_{network} + T_{TTFT} + (N \times T_{TPOT}) $$
&lt;p>Где каждая переменная означает:&lt;/p>
&lt;ul>
&lt;li>$T_{network}$: Время пути туда и обратно по сети (RTT).&lt;/li>
&lt;li>$T_{TTFT}$ (Time To First Token): Время до генерации первого символа. Сильно зависит от затрат на расчет механизма внимания (Attention), который пропорционален квадрату длины промпта (количества входных токенов).&lt;/li>
&lt;li>$N$: Общее количество сгенерированных токенов.&lt;/li>
&lt;li>$T_{TPOT}$ (Time Per Output Token): Время генерации одного токена. Поскольку модели авторегрессионные, каждый следующий токен вычисляется последовательно на основе предыдущего вывода.&lt;/li>
&lt;/ul>
&lt;h3 id="41-сложность-вычисления-механизма-внутреннего-внимания-self-attention">4.1 Сложность вычисления механизма внутреннего внимания (Self-Attention)
&lt;/h3>&lt;p>Вычислительная сложность внутреннего внимания (Self-Attention) в архитектуре Transformer возрастает квадратично относительно длины входной последовательности $L$.&lt;/p>
$$ \text{Complexity} = O(L^2 \cdot d) $$
&lt;p>Где $d$ — размерность вектора встраивания (embedding). Из-за этого ограничения при увеличении промпта $T_{TTFT}$ обычно резко ухудшается.
Однако в модели Gemini 1.5 от Google используются инновационные архитектурные оптимизации, такие как «Ring Attention» и «Block-wise Compute», что позволяет генерировать первый токен за разумное время (от нескольких секунд до нескольких десятков секунд) даже при подаче текста длиной в 2 миллиона токенов.&lt;/p>
&lt;hr>
&lt;h2 id="5-структура-тарифов-и-стратегии-оптимизации-затрат">5. Структура тарифов и стратегии оптимизации затрат
&lt;/h2>&lt;p>Стоимость API в основном рассчитывается на основе количества входных и выходных токенов.&lt;/p>
$$ Cost = (Tokens_{in} \times Rate_{in}) + (Tokens_{out} \times Rate_{out}) $$
&lt;p>Но в новейших API появились новые механизмы, позволяющие радикально снизить затраты.&lt;/p>
&lt;h3 id="51-кэширование-промптов-prompt-caching">5.1 Кэширование промптов (Prompt Caching)
&lt;/h3>&lt;p>Постоянная отправка длинных системных промптов или огромных объемов документов, извлеченных через RAG, каждый раз обходится очень дорого. Чтобы решить эту проблему, компании предлагают функции кэширования.&lt;/p>
&lt;p>Anthropic (Claude) и Google (Gemini) позволяют кэшировать определенные блоки текста, снижая затраты на ввод (до 90%).&lt;/p>
&lt;p>Модель затрат при использовании кэша выглядит так:&lt;/p>
$$ Cost_{cached} = (Tokens_{cache\_write} \times Rate_{cache\_write}) + (Tokens_{cache\_read} \times Rate_{cache\_read}) + (Tokens_{out} \times Rate_{out}) $$
&lt;p>Где $Rate_{cache\_read}$ обычно составляет около 10-25% от стандартного $Rate_{in}$. Это позволяет дешево поддерживать работу чат-ботов, постоянно храня в фоне знания в виде десятков тысяч строк кодовой базы.&lt;/p>
&lt;h3 id="52-пакетное-api-batch-api">5.2 Пакетное API (Batch API)
&lt;/h3>&lt;p>Для задач, не требующих обработки в реальном времени (анализ логов, массовая классификация данных и т. д.), OpenAI и Anthropic предлагают Batch API. Это мощный механизм, позволяющий отправлять запросы группами и получать результаты в течение 24 часов со скидкой 50% (в два раза дешевле) от стандартной цены API.&lt;/p>
&lt;hr>
&lt;h2 id="6-сравнение-опыта-разработчика-dx-и-sdk">6. Сравнение опыта разработчика (DX) и SDK
&lt;/h2>&lt;p>Сравним предлагаемые компаниями SDK (Software Development Kit) с точки зрения эффективности разработки.&lt;/p>
&lt;h3 id="61-openai-api">6.1 OpenAI API
&lt;/h3>&lt;p>Используется шире всего, имеет самую быструю поддержку в сторонних библиотеках (LangChain, LlamaIndex и т.д.). Кроме того, функция Structured Outputs (Структурированный вывод) гарантирует 100% точность ответа в соответствии с заданной JSON-схемой, что делает системную интеграцию чрезвычайно простой.&lt;/p>
&lt;h3 id="62-anthropic-api-claude">6.2 Anthropic API (Claude)
&lt;/h3>&lt;p>Интерфейс SDK выглядит очень элегантно, и, по отзывам, работать с типами в TypeScript очень удобно. Структура Message API интуитивно понятна, что позволяет легко создавать мультимодальные запросы, включающие несколько изображений.&lt;/p>
&lt;h3 id="63-google-gemini-api">6.3 Google Gemini API
&lt;/h3>&lt;p>Существуют два типа доступа: через Google Cloud Vertex AI и через AI Studio (Google Gen AI SDK), что может немного запутать новичков. Тем не менее, Vertex AI SDK, предназначенный для энтерпрайз-клиентов, полностью интегрирован с системой аутентификации GCP (IAM), что позволяет создать безопасную среду разработки.&lt;/p>
&lt;hr>
&lt;h2 id="7-практика-интеграционное-тестирование-нескольких-api-на-python">7. Практика! Интеграционное тестирование нескольких API на Python
&lt;/h2>&lt;p>В этом разделе мы используем Python для написания скрипта, который одновременно отправляет асинхронные запросы к трем API: OpenAI, Anthropic и Gemini, а затем сравнивает их задержки.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">asyncio&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">time&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">os&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">openai&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">AsyncOpenAI&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">anthropic&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">AsyncAnthropic&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">google.generativeai&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="nn">genai&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Инициализация клиентов&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">openai_client&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">AsyncOpenAI&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">api_key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">environ&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;OPENAI_API_KEY&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">anthropic_client&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">AsyncAnthropic&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">api_key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">environ&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;ANTHROPIC_API_KEY&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">genai&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">configure&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">api_key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">environ&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;GEMINI_API_KEY&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;Объясните основы квантовых вычислений и их влияние на современную криптографию простым языком для начинающих.&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">async&lt;/span> &lt;span class="k">def&lt;/span> &lt;span class="nf">fetch_openai&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">start_time&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">time&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">openai_client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">chat&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">completions&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">create&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;gpt-4o&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">messages&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[{&lt;/span>&lt;span class="s2">&amp;#34;role&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;user&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">prompt&lt;/span>&lt;span class="p">}],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">max_tokens&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1024&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">elapsed&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">time&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">start_time&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s2">&amp;#34;OpenAI (GPT-4o)&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">elapsed&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">choices&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">content&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">async&lt;/span> &lt;span class="k">def&lt;/span> &lt;span class="nf">fetch_anthropic&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">start_time&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">time&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">anthropic_client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">messages&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">create&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;claude-3-5-sonnet-20240620&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">messages&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[{&lt;/span>&lt;span class="s2">&amp;#34;role&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;user&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">prompt&lt;/span>&lt;span class="p">}],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">max_tokens&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1024&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">elapsed&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">time&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">start_time&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s2">&amp;#34;Anthropic (Claude 3.5 Sonnet)&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">elapsed&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">text&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">async&lt;/span> &lt;span class="k">def&lt;/span> &lt;span class="nf">fetch_gemini&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">start_time&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">time&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">genai&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">GenerativeModel&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;gemini-1.5-pro&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Использование асинхронного метода из Gemini Python SDK&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">model&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">generate_content_async&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">prompt&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">elapsed&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">time&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">start_time&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s2">&amp;#34;Google (Gemini 1.5 Pro)&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">elapsed&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">text&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">async&lt;/span> &lt;span class="k">def&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Отправка запросов к LLM API...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Выполнение 3-х API параллельно&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">results&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">asyncio&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">gather&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fetch_openai&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fetch_anthropic&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fetch_gemini&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">provider&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">latency&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">text&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">results&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;--- &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">provider&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> ---&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Задержка: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">latency&lt;/span>&lt;span class="si">:&lt;/span>&lt;span class="s2">.2f&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> секунд&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Ответ (фрагмент): &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">[:&lt;/span>&lt;span class="mi">100&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">...&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;__main__&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">asyncio&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">main&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Запустив этот скрипт, вы сможете легко измерить, какая из моделей отвечает быстрее всего (минимизируя $T_{total}$) в реальных сетевых условиях.&lt;/p>
&lt;hr>
&lt;h2 id="8-реализация-вызова-инструментов-tool-calling--function-calling-на-nodejs">8. Реализация вызова инструментов (Tool Calling / Function Calling) на Node.js
&lt;/h2>&lt;p>Чтобы заставить LLM работать не просто как чат-бот, а как «ИИ-агент», взаимодействующий с внешними системами, необходим функционал Tool Calling (или Function Calling). Ниже приведен пример на Node.js (TypeScript), в котором API OpenAI просят вызвать API погоды.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="nx">OpenAI&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s2">&amp;#34;openai&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">openai&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">OpenAI&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">apiKey&lt;/span>: &lt;span class="kt">process.env.OPENAI_API_KEY&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">async&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">runAgent() {&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">tools&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">type&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;function&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;get_weather&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">description&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;Получает текущую погоду для указанного города.&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">parameters&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">type&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;object&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">properties&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">location&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">type&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;string&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">description&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;Название города (например: Токио, Нью-Йорк)&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">required&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;location&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="nx">openai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">chat&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">completions&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">create&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">model&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;gpt-4o&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">messages&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[{&lt;/span> &lt;span class="nx">role&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;user&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;Какая сегодня погода в Токио? Нужен ли зонт?&amp;#34;&lt;/span> &lt;span class="p">}],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">tools&lt;/span>: &lt;span class="kt">tools&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">tool_choice&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;auto&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">message&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">choices&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="nx">message&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">message&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">tool_calls&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">toolCall&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">message&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">tool_calls&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`LLM запросила вызов инструмента: имя функции = &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">toolCall&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="kd">function&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">args&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">JSON&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">parse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">toolCall&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="kd">function&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">arguments&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`Аргументы: &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">args&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">location&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Здесь реализуется вызов реального API погоды (например, OpenWeatherMap)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// const weather = await fetchWeatherFromAPI(args.location);
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Полученный результат снова передается LLM для формирования окончательного ответа
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">runAgent&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="k">catch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Claude 3.5 Sonnet и Gemini 1.5 Pro также имеют аналогичные функции Tool Calling, и хотя способы определения схем немного отличаются, базовый рабочий процесс остается тем же.&lt;/p>
&lt;hr>
&lt;h2 id="9-rag-против-long-context-window-что-использовать">9. RAG против Long Context Window: что использовать?
&lt;/h2>&lt;p>Одна из крупнейших дискуссий в архитектуре корпоративного ИИ сегодня — «Стоит ли использовать RAG (поисковую генерацию) для интеграции внешних знаний, или же лучше просто закидывать все в огромное контекстное окно (Long Context)?».&lt;/p>
&lt;h3 id="преимущества-и-недостатки-rag-retrieval-augmented-generation">Преимущества и недостатки RAG (Retrieval-Augmented Generation)
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>Преимущества&lt;/strong>: Низкая стоимость (в промпт включаются только нужные чанки), легко определить источники ответов.&lt;/li>
&lt;li>&lt;strong>Недостатки&lt;/strong>: Зависит от точности семантического поиска. Не подходит для продвинутых задач, где контекст разбросан по нескольким документам (например: «Опираясь на все протоколы совещаний за прошлый год, проанализируйте в хронологическом порядке основную причину задержки проекта А»).&lt;/li>
&lt;/ul>
&lt;h3 id="long-context-например-2-миллиона-токенов-в-gemini-15-pro">Long Context (например, 2 миллиона токенов в Gemini 1.5 Pro)
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>Преимущества&lt;/strong>: Нет упущений информации из-за поиска. Даже в тесте «Поиск иголки в стоге сена» (Needle In A Haystack: NIAH) Gemini 1.5 Pro и Claude 3.5 Sonnet извлекают нужную информацию с точностью более 99%.&lt;/li>
&lt;li>&lt;strong>Недостатки&lt;/strong>: Огромный расход токенов ведет к большим затратам, возрастает задержка ($T_{TTFT}$).&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Вывод&lt;/strong>: Лучшей практикой в 2026 году является &lt;strong>«Гибридный подход»&lt;/strong>. Для рутинных Q&amp;amp;A используется RAG с векторными базами данных, а для сложных аналитических задач или обзора всего кода применяются механизмы Long Context с кэшированием промптов.&lt;/p>
&lt;hr>
&lt;h2 id="10-сравнение-возможностей-мультимодальной-обработки">10. Сравнение возможностей мультимодальной обработки
&lt;/h2>&lt;p>ИИ-приложения следующего поколения должны уметь напрямую понимать не только текст, но и изображения, звук и видео.&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant User as "Пользователь"
participant Client as "Фронтенд-приложение"
participant API as "LLM API (Мультимодальный)"
User->>Client: Загрузить видео и текстовый промпт
Client->>API: Отправить байты видео/URI + текст
Note over API: Разбиение видео на чанки и извлечение аудио
Note over API: Модель мультимодального встраивания
API-->>Client: Вернуть текстовое резюме и таймкоды
Client-->>User: Отобразить результаты&lt;/div>
&lt;ul>
&lt;li>&lt;strong>OpenAI (GPT-4o)&lt;/strong>: Исключительно высокая точность распознавания изображений, отлично справляется с чтением рукописных чертежей и сложных графиков. Также выделяется сверхнизкой задержкой (сотни миллисекунд) для нативного голосового общения через Realtime API.&lt;/li>
&lt;li>&lt;strong>Google (Gemini 1.5 Pro)&lt;/strong>: &lt;strong>Безоговорочный лидер в анализе видео.&lt;/strong> Позволяет напрямую загружать часовой видеофайл (кадры + звук) и получать ответы на такие узконаправленные вопросы, как: «Как называется документ в руках человека, появившегося в правой части экрана на 12-й минуте 45-й секунде?».&lt;/li>
&lt;li>&lt;strong>Anthropic (Claude 3.5 Sonnet)&lt;/strong>: Способности к распознаванию изображений (Vision) находятся на том же впечатляющем уровне, что и у GPT-4o. Демонстрирует непревзойденную силу в помощи фронтенд-разработчикам: можно просто дать скриншот интерфейса со словами «Сгенерируй код React-компонента для этого экрана».&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="11-безопасность-и-комплаенс-корпоративного-уровня">11. Безопасность и комплаенс корпоративного уровня
&lt;/h2>&lt;p>Когда компании используют LLM API в производственной среде, они больше всего беспокоятся о том, «Не будут ли наши данные использоваться для обучения ИИ?» и «Соответствует ли это требованиям комплаенса?».&lt;/p>
&lt;p>Все три компании четко заявляют, что данные (промпты и ответы), передаваемые через их API, &lt;strong>не используются для обучения моделей (Zero Data Retention / No Training on Customer Data)&lt;/strong>. (※ Это не относится к бесплатным веб-версиям чат-ботов для обычных пользователей).&lt;/p>
&lt;p>Если требуются более высокие уровни безопасности:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>OpenAI&lt;/strong>: При использовании Azure OpenAI Service вы получаете доступ к системе безопасности корпоративного уровня от Microsoft, SLA и возможности подключения к закрытой сети с помощью Azure Private Link.&lt;/li>
&lt;li>&lt;strong>Google&lt;/strong>: При доступе через Google Cloud Vertex AI можно обеспечить строгую сетевую изоляцию с помощью VPC Service Controls и защиту данных клиентскими ключами шифрования (CMEK).&lt;/li>
&lt;li>&lt;strong>Anthropic&lt;/strong>: Доступ через AWS Bedrock или Google Cloud Vertex AI позволяет опираться на надежную базовую инфраструктуру безопасности облачных провайдеров.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="12-заключение-идеальное-руководство-по-выбору-для-каждого-юзкейса">12. Заключение: идеальное руководство по выбору для каждого юзкейса
&lt;/h2>&lt;p>Мы рассмотрели вопрос с разных сторон, но итоговый ответ на вопрос «Что же выбрать?» зависит от вашего юзкейса.&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Сложная разработка ПО, генерация кода и продвинутые рассуждения&lt;/strong>:
&lt;strong>👑 Победитель: Claude 3.5 Sonnet (Anthropic)&lt;/strong>
На данный момент обеспечивает лучшую производительность в понимании контекста кода, рефакторинге и написании естественного человеческого текста. Также обладает удобным API и высокой экономической эффективностью благодаря кэшированию промптов.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Анализ сверхдлинных документов и массовая обработка видео/аудио&lt;/strong>:
&lt;strong>👑 Победитель: Gemini 1.5 Pro (Google)&lt;/strong>
Контекстное окно в 2 миллиона токенов — это его уникальное преимущество. Если вам необходимо охватить картину целиком, например, проанализировать сотни страниц PDF-руководств или резюмировать многочасовые видеозаписи встреч, Gemini просто нет равных.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Универсальность, скорость выполнения и стабильный структурированный вывод (JSON)&lt;/strong>:
&lt;strong>👑 Победитель: GPT-4o / GPT-4o-mini (OpenAI)&lt;/strong>
Отлично справляется с любыми задачами и обладает наиболее широкой поддержкой сторонних инструментов. Если требуется надежный парсинг JSON с помощью Structured Outputs или сверхсложная логика с использованием модели o1, экосистема OpenAI является незаменимой.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h3 id="рекомендация-мультимодельная-маршрутизация">Рекомендация: мультимодельная маршрутизация
&lt;/h3>&lt;p>Вместо того чтобы полагаться на один API (vendor lock-in), будущим трендом становится архитектура &lt;strong>«LLM-маршрутизации (LLM Routing)»&lt;/strong>, при которой модель динамически переключается в зависимости от сложности и важности задачи.
Например, на простые вопросы пользователей можно отвечать с помощью недорогих и быстрых &lt;code>GPT-4o-mini&lt;/code> или &lt;code>Gemini 1.5 Flash&lt;/code>, а ресурсоемкие задачи поручать &lt;code>Claude 3.5 Sonnet&lt;/code>. Это обеспечивает идеальный баланс между затратами и производительностью.&lt;/p>
&lt;p>Эволюция ИИ не останавливается. Глубоко понимая сильные и слабые стороны каждого API, а также характеристики их архитектур, вы сможете создавать гибкие и масштабируемые ИИ-приложения.&lt;/p></description></item><item><title>Использование llama.cpp и введение в кастомизацию на C++</title><link>http://kenji.blog/ru/p/llama-cpp-cxx-customization/</link><pubDate>Fri, 11 Sep 2026 11:00:00 +0900</pubDate><guid>http://kenji.blog/ru/p/llama-cpp-cxx-customization/</guid><description>&lt;img src="http://kenji.blog/p/llama-cpp-cxx-customization/img/eyecatch.jpg" alt="Featured image of post Использование llama.cpp и введение в кастомизацию на C++" />&lt;p>В последние годы эволюция больших языковых моделей (LLM) поражает воображение, и сфера их применения расширяется с каждым днем. Однако для запуска моделей с миллиардами и десятками миллиардов параметров в локальной среде обычно требуются высокопроизводительные GPU с огромным объемом VRAM. &lt;strong>llama.cpp&lt;/strong> — это то, что разрушило эту &amp;ldquo;аппаратную стену&amp;rdquo; и сделало возможным практический инференс LLM на обычных ПК, Mac и даже устройствах вроде Raspberry Pi.&lt;/p>
&lt;p>В этой статье мы не ограничимся простым использованием инструмента командной строки, а подробно, специально для инженеров, разберем архитектуру базовой технологии &lt;code>ggml&lt;/code>, математическую основу Transformer и квантования, а также способы интеграции и кастомизации LLM в ваших собственных приложениях с использованием C++ API.&lt;/p>
&lt;hr>
&lt;h2 id="1-обзор-llamacpp-и-ggml">1. Обзор llama.cpp и ggml
&lt;/h2>&lt;p>&lt;code>llama.cpp&lt;/code> — это легковесный движок инференса LLM, написанный на C/C++ разработчиком Георгием Гергановым. Изначально он был создан для быстрого запуска модели LLaMA от Meta на Apple Silicon (M1/M2 Mac), но теперь поддерживает различные архитектуры и модели.&lt;/p>
&lt;p>Его главная особенность — это &lt;strong>чистая реализация на C/C++ без внешних зависимостей&lt;/strong>. Он не требует огромных экосистем вроде Python или PyTorch и может быть скомпилирован как один исполняемый файл, что делает деплой чрезвычайно простым.&lt;/p>
&lt;p>Сердцем этого &lt;code>llama.cpp&lt;/code> является библиотека тензорных вычислений &lt;strong>ggml&lt;/strong>. ggml была разработана с нуля для максимальной оптимизации матричных вычислений в машинном обучении на CPU (а также на некоторых GPU).&lt;/p>
&lt;h3 id="11-почему-llamacpp-такой-быстрый">1.1 Почему llama.cpp такой быстрый?
&lt;/h3>&lt;ol>
&lt;li>&lt;strong>Использование отображения памяти (mmap)&lt;/strong>: При загрузке весов модели в память используется системный вызов OS &lt;code>mmap&lt;/code>, что позволяет избежать полной загрузки в RAM, обеспечивая быстрый запуск и экономию памяти.&lt;/li>
&lt;li>&lt;strong>Тщательная оптимизация SIMD-инструкций&lt;/strong>: Для сверхбыстрого перемножения матриц используются специфичные для CPU наборы инструкций, такие как AVX2, AVX-512, ARM NEON и Apple AMX.&lt;/li>
&lt;li>&lt;strong>Квантование (Quantization)&lt;/strong>: Веса в формате 16-битных чисел с плавающей запятой (FP16) сжимаются в 4-битные, 5-битные и 8-битные целые числа, что устраняет узкое место пропускной способности памяти (подробнее об этом ниже).&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="2-математическая-основа-transformer-и-квантование-quantization">2. Математическая основа: Transformer и Квантование (Quantization)
&lt;/h2>&lt;p>Для глубокого понимания llama.cpp необходимо знать, какие математические формулы он вычисляет и как аппроксимирует вычисления.&lt;/p>
&lt;h3 id="21-процесс-инференса-transformer">2.1 Процесс инференса Transformer
&lt;/h3>&lt;p>Такие модели, как LLaMA, используют архитектуру авторегрессионного (Auto-regressive) декодера Transformer. Ядром генерации текста является механизм &lt;strong>Self-Attention&lt;/strong>.&lt;/p>
&lt;p>Для входной матрицы скрытых состояний $X \in \mathbb{R}^{N \times d}$, запрос (Query) $Q$, ключ (Key) $K$ и значение (Value) $V$ вычисляются как произведения с матрицами весов.&lt;/p>
$$
Q = X W_Q, \quad K = X W_K, \quad V = X W_V
$$
&lt;p>Здесь выход Attention определяется следующим образом:&lt;/p>
$$
\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V
$$
&lt;p>В цикле инференса llama.cpp узким местом становится произведение этих огромных матриц $W_Q, W_K, W_V$ и матриц весов сети прямого распространения (FFN) на вектор $X$ (на этапе генерации $N=1$, так как обрабатывается по одному токену), то есть &lt;strong>GEMV (General Matrix-Vector Multiplication)&lt;/strong>.&lt;/p>
&lt;h3 id="22-математические-основы-квантования-quantization">2.2 Математические основы квантования (Quantization)
&lt;/h3>&lt;p>При инференсе, где узким местом является пропускная способность доступа к памяти, квантование, представляющее весовые параметры небольшим количеством бит, жизненно необходимо. Давайте объясним базовый принцип блочного квантования (например, &lt;code>Q4_K&lt;/code> или &lt;code>Q4_0&lt;/code>), широко используемого в llama.cpp.&lt;/p>
&lt;p>Например, рассмотрим блок $w = [w_1, w_2, \dots, w_B]$ длины $B$ (обычно 32 или 64), который является частью матрицы весов $W$ в формате FP16. Этот блок аппроксимируется 4-битными целыми числами $q_i \in [-8, 7]$ и одним масштабным коэффициентом (scaling factor) $\Delta$ (FP16 или FP32).&lt;/p>
$$
w_i \approx \Delta \times q_i
$$
&lt;p>$\Delta$ определяется на основе максимального абсолютного значения внутри блока.&lt;/p>
$$
\Delta = \frac{\max_i |w_i|}{7}
$$
&lt;p>Если входной вектор $x$ также квантуется как $x_i \approx \Delta_x \times q_{x, i}$ при вычислении скалярного произведения $y = w \cdot x$ с использованием квантованных весов, то:&lt;/p>
$$
y = \sum_{i=1}^{B} w_i x_i \approx \Delta \Delta_x \sum_{i=1}^{B} q_i q_{x, i}
$$
&lt;p>Часть $\sum q_i q_{x, i}$ становится &lt;strong>чисто целочисленной арифметикой&lt;/strong> и может быть вычислена параллельно на очень высокой скорости с использованием SIMD-инструкций. Это и есть математический секрет того, как llama.cpp достигает невероятной скорости на CPU.&lt;/p>
&lt;hr>
&lt;h2 id="3-архитектура-и-поток-инференса">3. Архитектура и поток инференса
&lt;/h2>&lt;p>Чтобы понять внутреннее устройство llama.cpp, на следующей диаграмме Mermaid показана архитектура всей системы и потоки данных.&lt;/p>
&lt;div class="mermaid">graph TD
A["Пользовательский ввод (String)"] --> B["Токенизатор llama.cpp"]
B --> C["ID токенов (int32 массив)"]
C --> D["Буфер контекста (KV Cache)"]
D --> E["Вычислительный граф ggml"]
E --> F["Слои Transformer"]
subgraph "Движок ggml"
F --> G["Self-Attention (RoPE)"]
G --> H["Feed Forward Network"]
H --> F
end
F --> I["Логиты (Размер словаря)"]
I --> J["Сэмплер (Temperature, Top-K, Top-P)"]
J --> K["Выбранный ID токена"]
K --> L["Детокенизатор llama.cpp"]
L --> M["Выходная строка"]
K -. "Авторегрессионный цикл" .-> D&lt;/div>
&lt;p>Генерация текста — это авторегрессионный цикл: каждый раз, когда выводится один токен, он добавляется в KV Cache как следующий вход и снова проходит через вычислительный граф.&lt;/p>
&lt;hr>
&lt;h2 id="4-настройка-окружения-и-сборка">4. Настройка окружения и сборка
&lt;/h2>&lt;p>Прежде чем интегрировать llama.cpp в проект C++, давайте сначала соберем исходный код.&lt;/p>
&lt;h3 id="41-клонирование-репозитория">4.1 Клонирование репозитория
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git clone https://github.com/ggerganov/llama.cpp.git
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> llama.cpp
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="42-сборка-с-использованием-cmake">4.2 Сборка с использованием CMake
&lt;/h3>&lt;p>При интеграции в другие приложения в качестве C++ проекта использование CMake является наиболее стандартным подходом. Включение платформозависимого ускорителя (бэкенда) позволяет ускорить вычисления.&lt;/p>
&lt;p>&lt;strong>Только CPU (базовая сборка):&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">mkdir build &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nb">cd&lt;/span> build
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cmake ..
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cmake --build . --config Release -j &lt;span class="m">8&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>При использовании NVIDIA GPU (CUDA):&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">mkdir build &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nb">cd&lt;/span> build
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cmake .. -DGGML_CUDA&lt;span class="o">=&lt;/span>ON
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cmake --build . --config Release -j &lt;span class="m">8&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>При использовании Apple Silicon (Metal):&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">mkdir build &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nb">cd&lt;/span> build
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cmake .. -DGGML_METAL&lt;span class="o">=&lt;/span>ON
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cmake --build . --config Release -j &lt;span class="m">8&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>После успешной сборки в директории &lt;code>build/bin/&lt;/code> будут сгенерированы исполняемые файлы, такие как &lt;code>llama-cli&lt;/code>, и библиотека &lt;code>llama&lt;/code> (а также библиотека &lt;code>ggml&lt;/code>) для линковки через C++ API, о котором пойдет речь ниже.&lt;/p>
&lt;hr>
&lt;h2 id="5-введение-в-кастомизацию-на-c-использование-api-llamacpp">5. Введение в кастомизацию на C++: Использование API llama.cpp
&lt;/h2>&lt;p>С этого момента мы перейдем к главной теме — управлению llama.cpp из кода на C++.
Чтобы не просто использовать инструмент командной строки, а встроить LLM в собственное приложение (например, игровой движок, десктопное приложение, встроенную систему и т. д.), необходимо напрямую обращаться к C++ API.&lt;/p>
&lt;p>llama.cpp предоставляет интерфейс на языке C в основном через заголовочный файл &lt;code>llama.h&lt;/code>. При вызове из C++ также используется этот интерфейс.&lt;/p>
&lt;h3 id="51-минимально-необходимые-инклуды-и-настройки">5.1 Минимально необходимые инклуды и настройки
&lt;/h3>&lt;p>При использовании llama.cpp в собственном проекте добавьте следующие &lt;code>#include&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;#34;llama.h&amp;#34;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;vector&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;string&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;stdexcept&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Макрос для обработки ошибок
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="cp">#define LLAMA_ASSERT(x) \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp"> do { \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp"> if (!(x)) { \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp"> std::cerr &amp;lt;&amp;lt; &amp;#34;Assertion failed: &amp;#34; &amp;lt;&amp;lt; #x &amp;lt;&amp;lt; std::endl; \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp"> std::terminate(); \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp"> } \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp"> } while (0)
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="52-загрузка-модели-и-инициализация-контекста">5.2 Загрузка модели и инициализация контекста
&lt;/h3>&lt;p>Сначала загрузим файл модели в формате &lt;code>.gguf&lt;/code> и выделим контекст (область памяти и KV-кэш) для инференса.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">argc&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">char&lt;/span> &lt;span class="o">**&lt;/span> &lt;span class="n">argv&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">argc&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cerr&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Usage: &amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">argv&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34; &amp;lt;model.gguf&amp;gt;&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">string&lt;/span> &lt;span class="n">model_path&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">argv&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 1. Инициализация бэкенда (настройка окружения для CPU/GPU и т.д.)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">llama_backend_init&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 2. Получение настроек параметров модели по умолчанию
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">llama_model_params&lt;/span> &lt;span class="n">model_params&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_model_default_params&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model_params&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">n_gpu_layers&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">35&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Количество слоев, переносимых на GPU
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 3. Загрузка модели
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">llama_model&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_load_model_from_file&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model_path&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">c_str&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="n">model_params&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="k">nullptr&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cerr&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Failed to load model&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 4. Настройка параметров контекста
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">llama_context_params&lt;/span> &lt;span class="n">ctx_params&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_context_default_params&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ctx_params&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">n_ctx&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">2048&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Максимальный размер контекста (количество токенов)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">ctx_params&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">n_threads&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">8&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Количество потоков CPU, используемых для инференса
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 5. Создание контекста
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">llama_context&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">ctx&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_new_context_with_model&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ctx_params&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">ctx&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="k">nullptr&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cerr&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Failed to create context&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_free_model&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Model and context loaded successfully!&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ... дальнейшая обработка
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="53-токенизация-промпта-tokenization">5.3 Токенизация промпта (Tokenization)
&lt;/h3>&lt;p>LLM не понимают текст напрямую, а обрабатывают его как последовательность целочисленных ID (токенов). Входную строку необходимо преобразовать в токены.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">string&lt;/span> &lt;span class="n">prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#34;Q: Какая столица Японии?&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">A:&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">vector&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">llama_token&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">tokens_list&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tokens_list&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">resize&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">prompt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">length&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">4&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// Размер буфера с запасом
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Добавлять ли специальные токены (BOS: Begin of Sequence и т.д.) в начало
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">bool&lt;/span> &lt;span class="n">add_special&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Преобразование строки в массив ID токенов
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">n_tokens&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_tokenize&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">prompt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">c_str&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">prompt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">length&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tokens_list&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tokens_list&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">size&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">add_special&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">false&lt;/span> &lt;span class="c1">// parse_special
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n_tokens&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Требуется логика для перераспределения буфера и повторной попытки, если места не хватает (опущено для простоты)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cerr&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Failed to tokenize prompt&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tokens_list&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">resize&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n_tokens&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="54-цикл-инференса-и-сэмплирование">5.4 Цикл инференса и сэмплирование
&lt;/h3>&lt;p>Мы построим цикл, который передает токены в модель, получает распределение вероятностей (Logits) следующего токена и выполняет сэмплирование для определения следующего токена.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;span class="lnt">64
&lt;/span>&lt;span class="lnt">65
&lt;/span>&lt;span class="lnt">66
&lt;/span>&lt;span class="lnt">67
&lt;/span>&lt;span class="lnt">68
&lt;/span>&lt;span class="lnt">69
&lt;/span>&lt;span class="lnt">70
&lt;/span>&lt;span class="lnt">71
&lt;/span>&lt;span class="lnt">72
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Максимальное количество генерируемых токенов
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">max_gen_tokens&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">100&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Инициализация структуры для пакетной обработки (batch evaluation)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">llama_batch&lt;/span> &lt;span class="n">batch&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_batch_init&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">512&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Добавление токенов промпта в пакет
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">size_t&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">tokens_list&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">size&lt;/span>&lt;span class="p">();&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_batch_add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">batch&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">tokens_list&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="p">},&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Настройка вывода логитов (результатов предсказания) только для последнего токена промпта
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">batch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">logits&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">batch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">n_tokens&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Первоначальная оценка (скармливание промпта модели)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">llama_decode&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ctx&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">batch&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cerr&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;llama_decode() failed&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">n_cur&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">batch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">n_tokens&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Текущая длина контекста
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">n_decode&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">Output: &amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Инициализация контекста сэмплера (настройки Temperature, Top-K, Top-P и т.д.)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">llama_sampler&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">smpl&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_sampler_chain_init&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">llama_sampler_chain_default_params&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_sampler_chain_add_top_k&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">smpl&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">40&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_sampler_chain_add_top_p&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">smpl&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">0.9f&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_sampler_chain_add_temp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">smpl&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">0.7f&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_sampler_chain_add_dist&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">smpl&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1234&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// Значение Seed
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n_decode&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">max_gen_tokens&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 1. Сэмплирование: предсказание следующего токена на основе текущего контекста
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">llama_token&lt;/span> &lt;span class="n">new_token_id&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_sampler_sample&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">smpl&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ctx&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 2. Если токен - это EOS (End of Sequence), завершить цикл
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">llama_token_is_eog&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">new_token_id&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">break&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 3. Декодирование токена в строку (текст) и ее отображение
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">char&lt;/span> &lt;span class="n">buf&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">128&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">n_chars&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_token_to_piece&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">new_token_id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">buf&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">sizeof&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">buf&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n_chars&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">string&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">buf&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n_chars&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">flush&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 4. Подготовка только что сгенерированного токена как следующего пакета
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">llama_batch_clear&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">batch&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_batch_add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">batch&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">new_token_id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n_cur&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="p">},&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 5. Оценка модели (обновление KV-кэша и предсказание следующего)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">llama_decode&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ctx&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">batch&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cerr&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Failed to evaluate&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">break&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">n_cur&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">n_decode&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Очистка
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">llama_sampler_free&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">smpl&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_batch_free&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">batch&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_free&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ctx&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_free_model&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_backend_free&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Этот код реализует собственный цикл инференса с использованием базового API llama.cpp.
Группа токенов управляется с помощью структуры &lt;code>llama_batch&lt;/code>, а прямой проход (forward pass) нейронной сети выполняется с помощью &lt;code>llama_decode&lt;/code>.&lt;/p>
&lt;hr>
&lt;h2 id="6-продвинутые-примеры-кастомизации-управление-логитами-и-штрафами-на-c">6. Продвинутые примеры кастомизации: Управление логитами и штрафами на C++
&lt;/h2>&lt;p>Если вы хотите выйти за рамки простой генерации текста и принудительно задать определенный формат вывода (например, только JSON) или запретить вывод определенных слов, вы можете напрямую управлять &lt;strong>логитами (Logits)&lt;/strong> на стороне C++ перед сэмплированием.&lt;/p>
&lt;p>Вы можете получить массив необработанных оценок (значений до их преобразования в вероятности) непосредственно перед тем, как модель выведет каждый токен.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Получение массива необработанных логитов сразу после инференса и перед выполнением сэмплирования
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">float&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">logits&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_get_logits_ith&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ctx&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">batch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">n_tokens&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="n">n_vocab&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_n_vocab&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Список ID запрещенных токенов (например, 1234, 5678)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">vector&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">llama_token&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">forbidden_tokens&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="mi">1234&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">5678&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Установка вероятности появления запрещенных токенов на 0 (логит в минус бесконечность)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">llama_token&lt;/span> &lt;span class="nl">bad_tok&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">forbidden_tokens&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">logits&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">bad_tok&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">INFINITY&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Таким образом, работая напрямую с C++ API, становится возможным &lt;strong>&amp;ldquo;вмешательство на микросекундном уровне на каждом цикле инференса&amp;rdquo;&lt;/strong>, что трудно реализовать или влечет за собой большие накладные расходы при использовании LangChain или Python.&lt;/p>
&lt;hr>
&lt;h2 id="7-секреты-настройки-производительности">7. Секреты настройки производительности
&lt;/h2>&lt;p>После завершения реализации на C++, вот несколько контрольных точек для максимизации скорости в преддверии запуска в производственную среду (production).&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Оптимизация пакетной обработки (Batching):&lt;/strong> При одновременной обработке запросов от нескольких пользователей включите несколько последовательностей в &lt;code>llama_batch&lt;/code> и вызовите &lt;code>llama_decode&lt;/code> один раз (Continuous Batching). Это позволяет объединить обращения к памяти и значительно повысить пропускную способность.&lt;/li>
&lt;li>&lt;strong>Включение Flash Attention:&lt;/strong>
Установив &lt;code>ctx_params.flash_attn = true;&lt;/code> в параметрах контекста, вы можете ускорить вычисления Attention, одновременно уменьшив использование памяти. Это обязательная настройка при работе с длинными контекстами (десятки тысяч токенов).&lt;/li>
&lt;li>&lt;strong>Поддержка NUMA:&lt;/strong>
В многопроцессорных серверных средах (multi-socket) задержку доступа к памяти можно уменьшить путем правильной настройки NUMA перед вызовом &lt;code>llama_backend_init()&lt;/code>.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="8-в-заключение">8. В заключение
&lt;/h2>&lt;p>В этой статье мы подробно рассмотрели всё: от математической базы &lt;code>llama.cpp&lt;/code> и разбора его архитектуры до создания кастомного движка инференса с использованием C++ API.&lt;/p>
&lt;p>Хотя экосистема Python очень удобна для прототипирования, в производственных средах, где требуется деплой на edge-устройства, интеграция в игры или обработка в реальном времени, прямое управление через &lt;code>llama.cpp&lt;/code> на базе C/C++ демонстрирует подавляющее превосходство.&lt;/p>
&lt;p>Обязательно попробуйте написать код на C++ своими руками и ощутите радость от свободного управления LLM в вашей локальной среде.&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>Полезные ссылки&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;a class="link" href="https://github.com/ggerganov/llama.cpp" target="_blank" rel="noopener"
>llama.cpp Official Repository&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://github.com/ggerganov/ggml" target="_blank" rel="noopener"
>ggml - Tensor Library&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://arxiv.org/abs/1706.03762" target="_blank" rel="noopener"
>Attention Is All You Need (Vaswani et al., 2017)&lt;/a>&lt;/li>
&lt;/ul>
&lt;/blockquote></description></item><item><title>【2026 год: Последняя версия】Полное руководство по запуску локальных LLM в среде Windows</title><link>http://kenji.blog/ru/p/local-llm-windows-2026/</link><pubDate>Fri, 11 Sep 2026 10:00:00 +0900</pubDate><guid>http://kenji.blog/ru/p/local-llm-windows-2026/</guid><description>&lt;img src="http://kenji.blog/p/local-llm-windows-2026/img/eyecatch.jpg" alt="Featured image of post 【2026 год: Последняя версия】Полное руководство по запуску локальных LLM в среде Windows" />&lt;h1 id="1-введение-почему-именно-сейчас-локальные-llm-на-windows">1. Введение: Почему именно сейчас локальные LLM на Windows?
&lt;/h1>&lt;p>По состоянию на 2026 год эволюция генеративного ИИ и больших языковых моделей (LLM) демонстрирует серьезный сдвиг парадигмы — от гигантских облачных API-сервисов к «локальным LLM», работающим на персональных ПК или в локальной среде (on-premise). Облачные ИИ, такие как GPT-5 от OpenAI или Claude 3.5 от Anthropic, очень мощны, но компании и частные лица не могут отправлять абсолютно все свои данные в облако. С точки зрения конфиденциальности, безопасности, задержек, а также долгосрочной и устойчивой стоимости, спрос на локальные LLM сейчас высок как никогда.&lt;/p>
&lt;p>В частности, экосистема локальных LLM для среды Windows переживает поразительное развитие. Еще несколько лет назад здравый смысл подсказывал, что «разработка и запуск ИИ означает Linux», но в 2026 году Windows превратилась в очень мощную и удобную платформу для ИИ.&lt;/p>
&lt;p>В этой статье представлено полное руководство по созданию, развертыванию и оптимизации локальных LLM в среде Windows с учетом последних технологических тенденций 2026 года. Мы детально и всесторонне рассмотрим всё: от простой настройки для новичков с помощью Ollama до экстремальной оптимизации для продвинутых пользователей с использованием llama.cpp, а также глубокого понимания архитектуры, математического подхода к расчету VRAM и локального файнтюнинга.&lt;/p>
&lt;h2 id="11-технологические-тенденции-в-сфере-локальных-llm-в-2026-году">1.1 Технологические тенденции в сфере локальных LLM в 2026 году
&lt;/h2>&lt;p>Основные тенденции, формирующие текущую экосистему локальных LLM, следующие:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Полное распространение формата GGUF&lt;/strong>: Формат GGUF (GPT-Generated Unified Format), объединяющий метаданные и тензоры в один файл, стал абсолютным стандартом де-факто. Теперь достаточно скачать один файл с Hugging Face, чтобы запустить его в любой среде.&lt;/li>
&lt;li>&lt;strong>Демократизация архитектуры MoE (Mixture of Experts)&lt;/strong>: Выпущено множество небольших, но высокопроизводительных моделей MoE. Активируя при выводе (инференсе) лишь часть «экспертов», они достигают производительности, сопоставимой с гигантскими моделями, при этом снижая вычислительную нагрузку на потребительские ПК.&lt;/li>
&lt;li>&lt;strong>Высокий уровень абстракции и оптимизации движков вывода&lt;/strong>: Такие инструменты, как Ollama, LM Studio и AnythingLLM, стали настолько совершенными, что пользователям больше не нужно беспокоиться о сложных зависимостях, вроде установки драйверов CUDA. Кроме того, нативная поддержка FlashAttention 3 для Windows привела к резкому увеличению скорости инференса.&lt;/li>
&lt;li>&lt;strong>Использование NPU и развитие Windows Copilot+ PC&lt;/strong>: Технология запуска небольших LLM (SLM: Small Language Models) с низким энергопотреблением на ноутбуках без GPU с использованием встроенного NPU (Neural Processing Unit) достигла стадии практического применения.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h1 id="2-требования-к-аппаратному-обеспечению-и-подготовка-ос">2. Требования к аппаратному обеспечению и подготовка ОС
&lt;/h1>&lt;p>Чтобы локальная LLM работала с приемлемой скоростью (от 15 до 30+ токенов в секунду), самое важное — правильно выбрать аппаратное обеспечение.&lt;/p>
&lt;h2 id="21-рекомендуемая-конфигурация-оборудования">2.1 Рекомендуемая конфигурация оборудования
&lt;/h2>&lt;p>С развитием ИИ-ПК меняются и системные требования.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>ОС&lt;/strong>: Windows 11 Pro (24H2 или новее). Необходимо для полноценного использования функций WSL2, продвинутого управления памятью и работы с новейшим API DirectML.&lt;/li>
&lt;li>&lt;strong>CPU&lt;/strong>: Серия Intel Core Ultra 200 или выше, либо серия AMD Ryzen 9000 или выше. Если параллельно используется инференс на CPU, жизненно необходима высокоскоростная связь с памятью.&lt;/li>
&lt;li>&lt;strong>RAM&lt;/strong>: Минимум 32 ГБ, рекомендуется 64 ГБ или больше. Пропускная способность основной памяти (МБ/с) становится решающим узким местом при инференсе на CPU или переносе (offload) вычислений. В идеале использовать высокоскоростную память DDR5-6000 или выше.&lt;/li>
&lt;li>&lt;strong>GPU&lt;/strong>: Серия NVIDIA RTX 4000/5000. В локальных LLM наиболее важна не вычислительная мощность, а «объем VRAM».
&lt;ul>
&lt;li>&lt;strong>Начальный уровень&lt;/strong>: RTX 4060 Ti (версия 16 ГБ) — лучшее соотношение цены и качества. Идеально подходит для моделей класса 8B–14B.&lt;/li>
&lt;li>&lt;strong>Средний уровень&lt;/strong>: RTX 4070 Ti SUPER (16 ГБ) / RTX 4080 SUPER (16 ГБ)&lt;/li>
&lt;li>&lt;strong>High-end&lt;/strong>: RTX 4090 (24 ГБ) / RTX 5090 (32 ГБ) — необходимы для запуска квантованных моделей класса 30B–70B.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Накопитель&lt;/strong>: NVMe SSD PCIe Gen4 или Gen5. Резко сокращает время загрузки моделей объемом в десятки гигабайт.&lt;/li>
&lt;/ul>
&lt;h2 id="22-настройка-wsl2-windows-subsystem-for-linux-2">2.2 Настройка WSL2 (Windows Subsystem for Linux 2)
&lt;/h2>&lt;p>Хотя многие инструменты с графическим интерфейсом работают в Windows нативно, WSL2 очень удобен для разработки на Python, компиляции новейших инструментов и файнтюнинга LoRA, о котором пойдет речь ниже. В последних версиях среды Windows 11 достаточно установить драйвер NVIDIA на хосте, чтобы прозрачно использовать GPU (CUDA) из WSL2.&lt;/p>
&lt;p>Откройте PowerShell от имени администратора и выполните следующее:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># Установка WSL2 и последней версии Ubuntu&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">wsl&lt;/span> &lt;span class="p">-&lt;/span>&lt;span class="n">-install&lt;/span> &lt;span class="n">-d&lt;/span> &lt;span class="n">Ubuntu&lt;/span>&lt;span class="p">-&lt;/span>&lt;span class="mf">24.04&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"># Обновление ядра&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">wsl&lt;/span> &lt;span class="p">-&lt;/span>&lt;span class="n">-update&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>После установки выполните команду &lt;code>nvidia-smi&lt;/code> в терминале WSL2. Если GPU распознан корректно — установка прошла успешно.&lt;/p>
&lt;hr>
&lt;h1 id="3-архитектура-локальных-llm-и-механизм-вывода-инференса">3. Архитектура локальных LLM и механизм вывода (инференса)
&lt;/h1>&lt;p>Понимание внутренней структуры того, как модель генерирует текст в локальной среде, очень полезно для устранения неполадок и оптимизации.&lt;/p>
&lt;p>Следующая диаграмма Mermaid показывает типичный конвейер инференса локальной LLM.&lt;/p>
&lt;div class="mermaid">graph TD
User["Пользовательский ввод (Промпт)"] --> Tokenizer["Токенизатор (Tokenizer)"]
Tokenizer --> Embedding["Слой встраивания (Embedding)"]
subgraph "Transformer Block (x Layers)"
Embedding --> Attn["Механизм внутреннего внимания (Self-Attention)"]
Attn --> KVCache["KV-кэш (Хранение Key/Value)"]
Attn --> FFN["Сеть прямой связи (FFN)"]
end
FFN --> Logits["Вычисление логитов (Logits)"]
Logits --> Sampler["Сэмплер (Temperature, Top-K, Top-P)"]
Sampler --> OutputToken["Выходной токен"]
OutputToken --> |"Авторегрессионная генерация"| Tokenizer
OutputToken --> Decoder["Детокенизатор (Detokenizer)"]
Decoder --> FinalOutput["Итоговый сгенерированный текст"]&lt;/div>
&lt;h2 id="31-две-фазы-prefill-и-decode">3.1 Две фазы: Prefill и Decode
&lt;/h2>&lt;p>Генерация текста LLM разделена на две фазы с различными вычислительными характеристиками.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Фаза Prefill (Обработка промпта)&lt;/strong>: Фаза, в которой весь введенный промпт обрабатывается и понимается за один раз. Поскольку возможны параллельные вычисления, вычислительная мощность GPU (FLOPS) напрямую влияет на скорость. Если промпт длинный, эта фаза может занять несколько секунд.&lt;/li>
&lt;li>&lt;strong>Фаза Decode (Генерация токенов)&lt;/strong>: Фаза, в которой предсказывается по одному токену за раз, и результат передается на следующий вход (авторегрессия). Поскольку параллельные вычисления на этом этапе ограничены, пропускная способность VRAM (Memory Bandwidth) становится решающим узким местом.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h1 id="4-расчет-потребления-vram-и-математическое-понимание-размера-модели">4. Расчет потребления VRAM и математическое понимание размера модели
&lt;/h1>&lt;p>Чтобы правильно определить, «какая модель будет работать на моем ПК», необходимо понимать формулу расчета VRAM. Если из-за нехватки VRAM происходит откат (фолбэк) к системной памяти (RAM), скорость вывода падает в 10–100 раз.&lt;/p>
&lt;h2 id="41-базовый-объем-vram-на-основе-размера-параметров">4.1 Базовый объем VRAM на основе размера параметров
&lt;/h2>&lt;p>Это объем памяти, необходимый для загрузки весов модели в VRAM.
Он рассчитывается с использованием размера модели $P$ (количество параметров, единица измерения: 1 миллиард = 1B) и количества байт на 1 параметр $B$.&lt;/p>
$$
V_{base} = P \times B \quad \text{(ГБ)}
$$
&lt;p>Например, при загрузке модели с 8B (8 миллиардами) параметров в формате FP16 (полуточные числа с плавающей запятой, 16 бит = 2 байта):&lt;/p>
$$
V_{base} = 8 \times 2 = 16 \text{ ГБ}
$$
&lt;p>Другими словами, даже на GPU с 16 ГБ VRAM вы достигнете предела уже только при загрузке модели.&lt;/p>
&lt;h2 id="42-магия-квантования-quantization">4.2 Магия квантования (Quantization)
&lt;/h2>&lt;p>Вот здесь в игру вступает «квантование». Снижая точность параметров, размер модели кардинально уменьшается. В случае наиболее распространенного 4-битного квантования (например, Q4_K_M), на один параметр приходится в среднем около 0.55 байта.&lt;/p>
$$
V_{base\_4bit} = 8 \times 0.55 = 4.4 \text{ ГБ}
$$
&lt;p>Таким образом, имея 16 ГБ VRAM, вы сможете запускать модель 8B с достаточным запасом памяти.&lt;/p>
&lt;h2 id="43-расчет-kv-кэша-с-поддержкой-gqa">4.3 Расчет KV-кэша (с поддержкой GQA)
&lt;/h2>&lt;p>Во время вывода (инференса) «KV-кэш», предназначенный для сохранения прошлого контекста, потребляет VRAM. В последних моделях, таких как Llama 3, для экономии памяти применяется GQA (Grouped Query Attention).&lt;/p>
&lt;p>Потребление KV-кэша $V_{kv}$ (в гигабайтах) выражается следующей формулой:&lt;/p>
$$
V_{kv} = 2 \times b \times s \times l \times \left( \frac{h_{kv}}{h_q} \right) \times h_q \times d \times B_{kv} \div 10^9
$$
&lt;p>Упростив это, мы можем произвести вычисления с помощью количества голов Key и Value $h_{kv}$:&lt;/p>
$$
V_{kv} = 2 \times b \times s \times l \times h_{kv} \times d \times B_{kv} \div 10^9
$$
&lt;p>Где:&lt;/p>
&lt;ul>
&lt;li>$b$: Размер батча (обычно 1 для локального индивидуального использования)&lt;/li>
&lt;li>$s$: Длина последовательности (длина контекста, например: 8192)&lt;/li>
&lt;li>$l$: Количество слоев (например: 32)&lt;/li>
&lt;li>$h_{kv}$: Количество голов KV (например: 8)&lt;/li>
&lt;li>$d$: Размерность на одну голову (например: 128)&lt;/li>
&lt;li>$B_{kv}$: Количество байт KV-кэша (2 для FP16)&lt;/li>
&lt;/ul>
&lt;p>Пример расчета (Llama 3 8B, контекст 8192, кэш FP16):
$V_{kv} = 2 \times 1 \times 8192 \times 32 \times 8 \times 128 \times 2 \div 10^9 \approx 1.07 \text{ ГБ}$&lt;/p>
&lt;p>Обратите внимание, что чем больше длина контекста $s$, тем больше необходимо VRAM, и этот рост линеен.&lt;/p>
&lt;hr>
&lt;h1 id="5-практика-1-самая-быстрая-и-короткая-настройка-с-использованием-ollama">5. Практика 1: Самая быстрая и короткая настройка с использованием Ollama
&lt;/h1>&lt;p>Теперь, поняв теорию, давайте действительно запустим LLM в среде Windows.
По состоянию на 2026 год, самым удобным инструментом является «Ollama». Он предоставляет интуитивно понятный CLI в стиле Docker.&lt;/p>
&lt;h2 id="51-установка-и-запуск">5.1 Установка и запуск
&lt;/h2>&lt;ol>
&lt;li>Скачайте и запустите установщик для Windows с &lt;a class="link" href="https://ollama.com/" target="_blank" rel="noopener"
>официального сайта Ollama&lt;/a>.&lt;/li>
&lt;li>Откройте PowerShell и введите следующую команду. Здесь мы используем &lt;code>llama3:8b&lt;/code>.&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="n">ollama&lt;/span> &lt;span class="n">run&lt;/span> &lt;span class="n">llama3&lt;/span>&lt;span class="err">:&lt;/span>&lt;span class="n">8b&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>При первом запуске произойдет загрузка модели. По завершении вы сможете вести диалог прямо в терминале.&lt;/p>
&lt;h2 id="52-создание-собственного-ии-с-помощью-modelfile">5.2 Создание собственного ИИ с помощью Modelfile
&lt;/h2>&lt;p>Вы можете легко создать ИИ с определенной персоной. Создайте файл &lt;code>Modelfile&lt;/code> в любом месте.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">FROM llama3:8b
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">SYSTEM &amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Вы — выдающийся старший инженер-программист (Senior Software Engineer).
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">На вопросы пользователя всегда отвечайте логично и кратко, обязательно приводя примеры кода.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">PARAMETER temperature 0.3
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">PARAMETER num_ctx 8192
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Используйте следующие команды для сборки и запуска вашей собственной модели:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="n">ollama&lt;/span> &lt;span class="n">create&lt;/span> &lt;span class="n">SeniorDev&lt;/span> &lt;span class="o">-f&lt;/span> &lt;span class="p">./&lt;/span>&lt;span class="n">Modelfile&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ollama&lt;/span> &lt;span class="n">run&lt;/span> &lt;span class="n">SeniorDev&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="53-использование-из-внешних-приложений-ии-редакторов">5.3 Использование из внешних приложений (ИИ-редакторов)
&lt;/h2>&lt;p>Ollama открывает API-эндпоинт, совместимый с OpenAI, по адресу &lt;code>http://localhost:11434&lt;/code>.
В настройках бэкенда таких расширений для VS Code, как Cursor или Continue.dev, просто укажите этот URL и имя модели (например, &lt;code>SeniorDev&lt;/code>), чтобы получить мощного локального помощника по программированию абсолютно бесплатно.&lt;/p>
&lt;hr>
&lt;h1 id="6-практика-2-экстремальная-настройка-производительности-с-помощью-llamacpp">6. Практика 2: Экстремальная настройка производительности с помощью llama.cpp
&lt;/h1>&lt;p>Если вы хотите получить доступ к детальному управлению памятью или быстро опробовать новейшие форматы (такие как EXL2 или IQ квантование), то стоит использовать основной движок &lt;code>llama.cpp&lt;/code> напрямую.&lt;/p>
&lt;h2 id="61-процесс-сборки-llamacpp">6.1 Процесс сборки llama.cpp
&lt;/h2>&lt;p>В среде Windows лучше всего собирать из исходного кода, используя CUDA Toolkit и CMake.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="n">git&lt;/span> &lt;span class="n">clone&lt;/span> &lt;span class="n">https&lt;/span>&lt;span class="err">:&lt;/span>&lt;span class="p">//&lt;/span>&lt;span class="n">github&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">com&lt;/span>&lt;span class="p">/&lt;/span>&lt;span class="n">ggerganov&lt;/span>&lt;span class="p">/&lt;/span>&lt;span class="n">llama&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nb">cpp
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd &lt;/span>&lt;span class="n">llama&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nb">cpp
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">&lt;/span>&lt;span class="n">mkdir&lt;/span> &lt;span class="n">build&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd &lt;/span>&lt;span class="n">build&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"># Конфигурация и компиляция с поддержкой CUDA&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">cmake&lt;/span> &lt;span class="p">..&lt;/span> &lt;span class="n">-DLLAMA_CUBLAS&lt;/span>&lt;span class="p">=&lt;/span>&lt;span class="n">ON&lt;/span> &lt;span class="n">-DBUILD_SHARED_LIBS&lt;/span>&lt;span class="p">=&lt;/span>&lt;span class="n">OFF&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">cmake&lt;/span> &lt;span class="p">-&lt;/span>&lt;span class="n">-build&lt;/span> &lt;span class="p">.&lt;/span> &lt;span class="p">-&lt;/span>&lt;span class="n">-config&lt;/span> &lt;span class="n">Release&lt;/span> &lt;span class="n">-j&lt;/span> &lt;span class="mf">16&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="62-продвинутый-запуск-в-режиме-сервера">6.2 Продвинутый запуск в режиме сервера
&lt;/h2>&lt;p>Используйте собранный &lt;code>llama-server.exe&lt;/code> для хостинга модели.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="p">.\&lt;/span>&lt;span class="n">bin&lt;/span>&lt;span class="p">\&lt;/span>&lt;span class="n">Release&lt;/span>&lt;span class="p">\&lt;/span>&lt;span class="nb">llama-server&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="py">exe&lt;/span> &lt;span class="p">`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">-&lt;/span>&lt;span class="n">-model&lt;/span> &lt;span class="s2">&amp;#34;C:\models\Llama-3-8B-Instruct.Q4_K_M.gguf&amp;#34;&lt;/span> &lt;span class="p">`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">-&lt;/span>&lt;span class="n">-ctx-size&lt;/span> &lt;span class="mf">8192&lt;/span> &lt;span class="p">`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">-&lt;/span>&lt;span class="n">-n-gpu-layers&lt;/span> &lt;span class="mf">99&lt;/span> &lt;span class="p">`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">-&lt;/span>&lt;span class="n">-threads&lt;/span> &lt;span class="mf">8&lt;/span> &lt;span class="p">`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">-&lt;/span>&lt;span class="n">-flash-attn&lt;/span> &lt;span class="p">`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">-&lt;/span>&lt;span class="n">-port&lt;/span> &lt;span class="mf">8080&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;ul>
&lt;li>&lt;code>--n-gpu-layers 99&lt;/code>: Переносит на VRAM GPU все возможные слои (максимально возможное количество).&lt;/li>
&lt;li>&lt;code>--flash-attn&lt;/code>: Включает FlashAttention 3, что повышает скорость инференса и снижает потребление VRAM KV-кэшем.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h1 id="7-gui-фронтенды-lm-studio-и-создание-локального-rag">7. GUI фронтенды: LM Studio и создание локального RAG
&lt;/h1>&lt;p>Если вы не любите командную строку или хотите интуитивно выполнять RAG (Генерация с дополненной выборкой), воспользуйтесь графическими интерфейсами.&lt;/p>
&lt;h2 id="71-lm-studio">7.1 LM Studio
&lt;/h2>&lt;p>LM Studio — это отличное приложение, которое объединяет поиск моделей, их загрузку, предварительную проверку системных требований и пользовательский интерфейс для чата в одном месте. Просто нажав кнопку &amp;ldquo;Local Server&amp;rdquo; в приложении, вы запускаете API, совместимый с OpenAI.&lt;/p>
&lt;h2 id="72-архитектура-rag-с-использованием-anythingllm">7.2 Архитектура RAG с использованием AnythingLLM
&lt;/h2>&lt;p>Вот схема архитектуры среды RAG для чтения внутренних корпоративных документов или личных заметок.&lt;/p>
&lt;div class="mermaid">graph LR
Document["Документ (PDF, MD)"] --> Chunking["Разделение на чанки"]
Chunking --> EmbedModel["Модель встраивания (Embedding)"]
EmbedModel --> VectorDB["Векторная база данных"]
UserQuery["Запрос пользователя"] --> EmbedQuery["Встраивание запроса"]
EmbedQuery --> VectorDB
VectorDB --> |"Поиск по сходству"| RetrievedDocs["Извлеченные релевантные документы"]
UserQuery --> PromptBuilder["Генератор промпта"]
RetrievedDocs --> PromptBuilder
PromptBuilder --> LocalLLM["Локальная LLM"]
LocalLLM --> Answer["Итоговый ответ"]&lt;/div>
&lt;p>Используя десктопную версию AnythingLLM (для Windows), достаточно указать Ollama (как LLM и Embedding) на экране настроек и настроить использование локальной векторной БД (LanceDB), и эта архитектура будет готова за несколько минут. Так рождается приватный ИИ, который совершенно не отправляет данные наружу.&lt;/p>
&lt;hr>
&lt;h1 id="8-файнтюнинг-на-windows-в-wsl2-lora">8. Файнтюнинг на Windows в WSL2 (LoRA)
&lt;/h1>&lt;p>Если вы хотите не только локально запускать модели, но и обучать их на своих собственных данных, чтобы сделать умнее, вы можете использовать файнтюнинг с помощью LoRA (Low-Rank Adaptation). По состоянию на 2026 год, благодаря библиотеке под названием &amp;ldquo;Unsloth&amp;rdquo;, обучение 8B-модели в среде Windows WSL2 с 16 ГБ VRAM завершается всего за несколько часов.&lt;/p>
&lt;p>Выполните следующее в Ubuntu под WSL2 для настройки среды:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">conda create --name unsloth_env &lt;span class="nv">python&lt;/span>&lt;span class="o">=&lt;/span>3.11
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">conda activate unsloth_env
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pip install &lt;span class="s2">&amp;#34;unsloth[colab-new] @ git+https://github.com/unslothai/unsloth.git&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pip install --no-deps trl peft accelerate bitsandbytes
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Unsloth доводит оптимизацию ядер CUDA до предела, так что скорость обучения примерно удваивается, а потребление VRAM снижается вдвое по сравнению со стандартными библиотеками Hugging Face. Просто запустите Jupyter Notebook, загрузите свой набор данных (в формате JSONL), и вы сможете выполнить несколько эпох обучения даже на RTX 4060 Ti с VRAM от 12 ГБ до 16 ГБ.&lt;/p>
&lt;hr>
&lt;h1 id="9-устранение-проблем-с-производительностью-troubleshooting">9. Устранение проблем с производительностью (Troubleshooting)
&lt;/h1>&lt;p>Часто возникающие проблемы и способы их решения.&lt;/p>
&lt;h3 id="1-скорость-вывода-крайне-низкая-1-2-tokenss">1. Скорость вывода крайне низкая (1-2 tokens/s)
&lt;/h3>&lt;p>&lt;strong>Причина&lt;/strong>: Модель не помещается в VRAM и выгружается (offload) в системную память (RAM).
&lt;strong>Решение&lt;/strong>: Проверьте параметр «Выделенная память графического процессора» в Диспетчере задач. Если он достиг предела, уменьшите размер контекста (&lt;code>-c&lt;/code>) или используйте квантованную модель с меньшим битрейтом (например, Q4_K_M).&lt;/p>
&lt;h3 id="2-ошибка-cuda-out-of-memory">2. Ошибка &amp;ldquo;CUDA out of memory&amp;rdquo;
&lt;/h3>&lt;p>&lt;strong>Причина&lt;/strong>: VRAM полностью исчерпан. В основном возникает, когда диалог затягивается, и KV-кэш разрастается.
&lt;strong>Решение&lt;/strong>: Намеренно ограничьте значение &lt;code>num_ctx&lt;/code> в Ollama или &lt;code>-c&lt;/code> в llama.cpp, сделав его меньше.&lt;/p>
&lt;h3 id="3-странная-генерация-на-японском-или-другом-языке">3. Странная генерация на японском (или другом) языке
&lt;/h3>&lt;p>&lt;strong>Причина&lt;/strong>: Несоответствие шаблона промпта или неподдерживаемая модель.
&lt;strong>Решение&lt;/strong>: Убедитесь, что вы используете модель, содержащую слово &lt;code>Instruct&lt;/code> в названии, и что в инструменте выбран правильный шаблон, указанный автором модели (например, формат ChatML или Llama3).&lt;/p>
&lt;hr>
&lt;h1 id="10-заключение-и-перспективы-на-будущее">10. Заключение и перспективы на будущее
&lt;/h1>&lt;p>В 2026 году развертывание локальных LLM в среде Windows больше не является привилегией лишь избранных инженеров. Благодаря формату GGUF, ставшему стандартом де-факто, появлению продуманных экосистем, таких как Ollama и LM Studio, а также аппаратной оптимизации, включая FlashAttention, каждый может легко получить ИИ-среду корпоративного уровня.&lt;/p>
&lt;p>Пожалуйста, воспользуйтесь ключевыми моментами, описанными в этой статье:&lt;/p>
&lt;ol>
&lt;li>Используйте &lt;strong>математические расчеты VRAM&lt;/strong>, чтобы логически выбрать размер модели и уровень квантования, оптимальные для характеристик вашего ПК.&lt;/li>
&lt;li>Используйте &lt;strong>Ollama&lt;/strong> для максимально быстрой настройки среды и интегрируйте её с ИИ-редакторами для резкого повышения производительности.&lt;/li>
&lt;li>Раскройте предельные возможности оборудования с помощью продвинутого контроля параметров в &lt;strong>llama.cpp&lt;/strong>.&lt;/li>
&lt;li>Создайте безопасную локальную систему RAG для работы с конфиденциальными данными с помощью &lt;strong>AnythingLLM&lt;/strong>.&lt;/li>
&lt;li>Используйте &lt;strong>Unsloth (WSL2)&lt;/strong>, чтобы вырастить собственного персонализированного ИИ, обладающего вашими экспертными знаниями.&lt;/li>
&lt;/ol>
&lt;p>«Демократизация» ИИ — это больше не модное словечко, а реальная система, работающая на вашем рабочем столе Windows. Освободитесь от затрат на облачные API и рисков утечки информации, и прямо сейчас сделайте шаг в свободный и мощный мир приватного ИИ.&lt;/p></description></item></channel></rss>