<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>AI on kenji.blog</title><link>http://kenji.blog/pt/categories/ai/</link><description>Recent content in AI on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>pt</language><copyright>kenjinote</copyright><lastBuildDate>Fri, 11 Sep 2026 18:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/pt/categories/ai/index.xml" rel="self" type="application/rss+xml"/><item><title>Casos de Uso da API Recente e Códigos de Exemplo do Microsoft.Windows.AI</title><link>http://kenji.blog/pt/p/microsoft-windows-ai-api-guide/</link><pubDate>Fri, 11 Sep 2026 18:00:00 +0900</pubDate><guid>http://kenji.blog/pt/p/microsoft-windows-ai-api-guide/</guid><description>&lt;img src="http://kenji.blog/p/microsoft-windows-ai-api-guide/img/eyecatch.jpg" alt="Featured image of post Casos de Uso da API Recente e Códigos de Exemplo do Microsoft.Windows.AI" />&lt;h1 id="casos-de-uso-da-api-recente-e-códigos-de-exemplo-do-microsoftwindowsai-explorando-as-profundezas-do-windows-copilot-runtime">Casos de Uso da API Recente e Códigos de Exemplo do Microsoft.Windows.AI: Explorando as Profundezas do Windows Copilot Runtime
&lt;/h1>&lt;h2 id="1-introdução-a-nova-era-do-windows-com-ia-integrada-nativamente">1. Introdução: A Nova Era do Windows com IA Integrada Nativamente
&lt;/h2>&lt;p>Nos últimos anos, a evolução da tecnologia de IA tem sido notável, com uma rápida mudança de paradigma do uso de Grandes Modelos de Linguagem (LLM) na nuvem para a inferência de IA em dispositivos edge (PCs locais). O núcleo disso é o &amp;ldquo;Windows Copilot Runtime&amp;rdquo; fornecido pela Microsoft para o Windows 11 e a API &amp;ldquo;Microsoft.Windows.AI&amp;rdquo; para controlá-lo.&lt;/p>
&lt;p>O desenvolvimento de aplicativos usando APIs em nuvem (como OpenAI ou Azure OpenAI) é fácil, mas traz desafios como latência, privacidade e custos contínuos. Por outro lado, a execução de modelos de IA localmente permite obter aplicativos de latência ultrabaixa que funcionam offline sem enviar dados confidenciais para fora do dispositivo.&lt;/p>
&lt;p>Neste artigo, explicaremos detalhadamente e de forma abrangente, da arquitetura ao ajuste de desempenho, os métodos de implementação de recursos de IA local, que serão essenciais no desenvolvimento de aplicativos para Windows, juntamente com exemplos de código prático em C# e C++. Além de simplesmente chamar a API, nos aprofundaremos em detalhes técnicos avançados, como a utilização de hardware subjacente (NPU e GPU) e a integração com o DirectML.&lt;/p>
&lt;h2 id="2-windows-copilot-runtime-e-a-visão-geral-da-arquitetura">2. Windows Copilot Runtime e a Visão Geral da Arquitetura
&lt;/h2>&lt;p>O Windows Copilot Runtime é uma pilha de IA projetada para permitir que os desenvolvedores integrem facilmente modelos de IA no Windows e extraiam o melhor desempenho. Este runtime abstrai a aceleração de hardware no nível do sistema operacional e fornece uma interface unificada para os desenvolvedores.&lt;/p>
&lt;div class="mermaid">graph TD
App["Aplicativo Windows (C# / C++)"] --> API["APIs Microsoft.Windows.AI"]
App --> ORT["ONNX Runtime"]
API --> WCR["Windows Copilot Runtime (Camada do SO)"]
WCR --> SLM["Modelos Locais (Phi-Silica, etc.)"]
ORT --> DML["Provedor de Execução DirectML"]
SLM --> DML
DML --> DXCore["DXCore / DirectX 12"]
DXCore --> NPU["NPU (Unidade de Processamento Neural)"]
DXCore --> GPU["GPU (Unidade de Processamento Gráfico)"]
DXCore --> CPU["CPU"]&lt;/div>
&lt;p>Como o diagrama de arquitetura acima mostra, ao usar a API de alto nível &lt;code>Microsoft.Windows.AI&lt;/code>, o aplicativo pode acessar diretamente Pequenos Modelos de Linguagem (SLM: como o Phi-Silica) integrados ao sistema operacional. Além disso, ao usar um modelo personalizado, a aceleração de hardware pode ser explicitamente utilizada via ONNX Runtime e DirectML. Como a camada do sistema operacional otimiza a distribuição da carga de trabalho para CPU, GPU e NPU, os desenvolvedores podem construir aplicativos de IA de alto desempenho sem se preocupar profundamente com as diferenças de hardware.&lt;/p>
&lt;h2 id="3-aceleração-de-hardware-e-avaliação-matemática-da-npu">3. Aceleração de Hardware e Avaliação Matemática da NPU
&lt;/h2>&lt;p>Os mais recentes Copilot+ PCs são equipados com a NPU (Unidade de Processamento Neural), um processador especializado em processamento de IA. O desempenho da NPU é geralmente avaliado em TOPS (Tera Operations Per Second).&lt;/p>
&lt;p>Na inferência de modelos de IA, a capacidade de cálculo, especialmente de multiplicação de matrizes (GEMM: General Matrix Multiply), determina a taxa de transferência (throughput). O desempenho máximo teórico do hardware $P_{\text{peak}}$ é estimado pela seguinte fórmula:&lt;/p>
$$
P_{\text{peak}} = f \times N_{\text{cores}} \times N_{\text{MACs/core}} \times 2
$$
&lt;p>Onde:&lt;/p>
&lt;ul>
&lt;li>$f$ é a frequência de clock da NPU (Hz)&lt;/li>
&lt;li>$N_{\text{cores}}$ é o número de núcleos dentro da NPU&lt;/li>
&lt;li>$N_{\text{MACs/core}}$ é o número de unidades MAC (Multiply-Accumulate) por núcleo&lt;/li>
&lt;li>O último $2$ ocorre porque uma única operação MAC é contada como duas operações (FLOPs/OPs) de multiplicação e adição.&lt;/li>
&lt;/ul>
&lt;p>Por exemplo, para uma NPU com frequência de 1,5 GHz, 4 núcleos e cada núcleo tendo 4096 MACs:
&lt;/p>
$$
P_{\text{peak}} = 1.5 \times 10^9 \times 4 \times 4096 \times 2 \approx 49.15 \text{ TOPS}
$$
&lt;p>Isso demonstra matematicamente que atende ao requisito de 40 TOPS para os Copilot+ PCs com Windows 11.&lt;/p>
&lt;p>Além disso, a inferência (fase de decodificação) de modelos de IA, especialmente LLMs, tende a ser &lt;strong>limitada pela memória (Memory-Bound)&lt;/strong>. A largura de banda teórica $BW$ da memória do sistema é calculada da seguinte forma:&lt;/p>
$$
BW = f_{\text{mem}} \times W_{\text{bus}} \times \frac{2}{8}
$$
&lt;p>Para memória LPDDR5x-8533 ($f_{\text{mem}} = 8533 \text{ MT/s}$) e um barramento de 128 bits ($W_{\text{bus}} = 128$), a largura de banda é de aproximadamente $136 \text{ GB/s}$. Na otimização de aplicativos de IA, a forma de economizar essa largura de banda é muito importante, tornando a quantização (Quantization) de modelos, que será discutida mais adiante, essencial.&lt;/p>
&lt;h2 id="4-configuração-do-ambiente-de-desenvolvimento">4. Configuração do Ambiente de Desenvolvimento
&lt;/h2>&lt;p>Para usar a API de IA mais recente do Windows, é necessário configurar o seguinte ambiente e conjunto de ferramentas:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>SO&lt;/strong>: Windows 11 Versão 24H2 ou posterior (Altamente recomendado um dispositivo equipado com NPU que atenda aos requisitos do Copilot+ PC)&lt;/li>
&lt;li>&lt;strong>SDK&lt;/strong>: Windows App SDK (versão compatível com a extensão de IA, v1.5 ou posterior)&lt;/li>
&lt;li>&lt;strong>Ambiente de Desenvolvimento&lt;/strong>: Visual Studio 2022 (v17.10 ou posterior), carga de trabalho de desenvolvimento nativo com C++ e carga de trabalho de desenvolvimento para desktop .NET&lt;/li>
&lt;li>&lt;strong>Pacotes&lt;/strong>: Instalar &lt;code>Microsoft.Windows.AI&lt;/code> e &lt;code>Microsoft.ML.OnnxRuntime.DirectML&lt;/code> via NuGet&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;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-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- Exemplo de configuração do .csproj --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;ItemGroup&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;PackageReference&lt;/span> &lt;span class="na">Include=&lt;/span>&lt;span class="s">&amp;#34;Microsoft.Windows.AI&amp;#34;&lt;/span> &lt;span class="na">Version=&lt;/span>&lt;span class="s">&amp;#34;1.0.0-preview1&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;PackageReference&lt;/span> &lt;span class="na">Include=&lt;/span>&lt;span class="s">&amp;#34;Microsoft.WindowsAppSDK&amp;#34;&lt;/span> &lt;span class="na">Version=&lt;/span>&lt;span class="s">&amp;#34;1.5.240311000&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;PackageReference&lt;/span> &lt;span class="na">Include=&lt;/span>&lt;span class="s">&amp;#34;Microsoft.ML.OnnxRuntime.DirectML&amp;#34;&lt;/span> &lt;span class="na">Version=&lt;/span>&lt;span class="s">&amp;#34;1.17.1&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/ItemGroup&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="5-deep-dive-1-utilizando-o-modelo-de-linguagem-local-phi-silica-com-c">5. [Deep Dive 1] Utilizando o Modelo de Linguagem Local (Phi-Silica) com C#
&lt;/h2>&lt;p>O Windows Copilot Runtime inclui o &amp;ldquo;Phi-Silica&amp;rdquo;, um modelo de linguagem de pequena escala e alta eficiência desenvolvido pela Microsoft, como um componente padrão do sistema operacional. Isso permite o processamento avançado de linguagem natural (resumo de textos, geração de código, chatbots) em um ambiente offline, sem precisar baixar modelos do tamanho de GB pela rede.&lt;/p>
&lt;p>Abaixo está um exemplo de código avançado para construir uma IA de bate-papo em C# usando o namespace &lt;code>Microsoft.Windows.AI.Generative&lt;/code>. Ele suporta respostas em streaming e gera texto em tempo real sem bloquear a thread da UI.&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;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-csharp" data-lang="csharp">&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="nn">System&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="nn">System.Text&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="nn">System.Threading&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="nn">System.Threading.Tasks&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="nn">Microsoft.Windows.AI.Generative&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">namespace&lt;/span> &lt;span class="nn">WindowsAI.Sample&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="kd">public&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">LocalLanguageModelService&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="kd">private&lt;/span> &lt;span class="n">LanguageModel&lt;/span> &lt;span class="n">_languageModel&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">private&lt;/span> &lt;span class="kt">bool&lt;/span> &lt;span class="n">_isInitialized&lt;/span> &lt;span class="p">=&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>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cs">/// &amp;lt;summary&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cs">/// Inicializa o modelo de linguagem. Verifica a disponibilidade da NPU e carrega o modelo no dispositivo ideal.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cs">/// &amp;lt;/summary&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">public&lt;/span> &lt;span class="kd">async&lt;/span> &lt;span class="n">Task&lt;/span> &lt;span class="n">InitializeAsync&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="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">_isInitialized&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&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">Console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WriteLine&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Verificando os requisitos do sistema e a disponibilidade do modelo de IA local (Phi-Silica)...&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">// Verifica se o modelo está disponível no sistema (pode solicitar o download caso não seja suportado)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">var&lt;/span> &lt;span class="n">availability&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">LanguageModel&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">CheckAvailabilityAsync&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">availability&lt;/span> &lt;span class="p">!=&lt;/span> &lt;span class="n">LanguageModelAvailability&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Available&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="k">throw&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">InvalidOperationException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">$&amp;#34;O modelo de IA local não está disponível no momento. Estado: {availability}&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">// Cria a instância do modelo (neste momento, o mapeamento para o espaço de memória e a inicialização da NPU ocorrem)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">_languageModel&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">LanguageModel&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">CreateAsync&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">_isInitialized&lt;/span> &lt;span class="p">=&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>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WriteLine&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Inicialização do modelo de linguagem concluída. A aceleração de hardware via DirectML está ativa.&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="cs">/// &amp;lt;summary&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cs">/// Recebe o prompt do usuário e gera uma resposta em streaming.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cs">/// &amp;lt;/summary&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">public&lt;/span> &lt;span class="kd">async&lt;/span> &lt;span class="n">Task&lt;/span> &lt;span class="n">GenerateResponseStreamAsync&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">string&lt;/span> &lt;span class="n">prompt&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">CancellationToken&lt;/span> &lt;span class="n">cancellationToken&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="k">if&lt;/span> &lt;span class="p">(!&lt;/span>&lt;span class="n">_isInitialized&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">InitializeAsync&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">Console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WriteLine&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">$&amp;#34;\n[Entrada do Usuário]: {prompt}\n[Assistente de IA]: &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">// Configura os hiperparâmetros de geração&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">var&lt;/span> &lt;span class="n">options&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">LanguageModelOptions&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">Temperature&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">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">TopP&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">0.9f&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">MaxTokens&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">2048&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">RepetitionPenalty&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">1.1f&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">// Constrói o contexto da conversa&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">var&lt;/span> &lt;span class="n">context&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">LanguageModelContext&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">AddSystemMessage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Você é um assistente de IA avançado que funciona diretamente na NPU local do Windows. Pense logicamente passo a passo e responda de forma concisa.&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">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">AddUserMessage&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="k">try&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">// Chama a API de inferência em streaming&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">var&lt;/span> &lt;span class="n">responseStream&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">_languageModel&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">GenerateResponseStreamAsync&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">options&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">// Itera de forma assíncrona sobre os fragmentos (chunks) retornados como IAsyncEnumerable&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">await&lt;/span> &lt;span class="k">foreach&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">var&lt;/span> &lt;span class="n">chunk&lt;/span> &lt;span class="k">in&lt;/span> &lt;span class="n">responseStream&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WithCancellation&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">cancellationToken&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">// Exibe os tokens gerados (chunks) no console em tempo real&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Em um aplicativo de UI, o DispatcherQueue seria usado aqui para refletir em um TextBox, etc.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Write&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">chunk&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Text&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">Console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WriteLine&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="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">OperationCanceledException&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">Console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WriteLine&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;\n[A geração foi cancelada pelo usuário ou pelo sistema]&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="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">Exception&lt;/span> &lt;span class="n">ex&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">Console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WriteLine&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">$&amp;#34;\n[Ocorreu um erro fatal: {ex.Message}]&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;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="51-explicação-da-arquitetura-na-implementação-em-c">5.1 Explicação da Arquitetura na Implementação em C#
&lt;/h3>&lt;p>O núcleo deste código é a validação pré-execução usando &lt;code>LanguageModel.CheckAvailabilityAsync()&lt;/code> e o streaming assíncrono através do &lt;code>GenerateResponseStreamAsync&lt;/code>. Quando o Copilot Runtime operando em segundo plano no sistema operacional recebe essa chamada de API, ele invoca internamente o ONNX Runtime e seleciona o Provedor de Execução ideal (DirectML + NPU em muitos dos PCs mais recentes) dependendo da configuração do sistema.&lt;/p>
&lt;p>Os desenvolvedores podem integrar pipelines de inferência de IA de última geração em seus aplicativos com apenas algumas linhas de código em C#, sem a necessidade de se preocuparem com o formato dos tensores do modelo, a implementação do tokenizador ou a gestão da memória de cache KV.&lt;/p>
&lt;h2 id="6-deep-dive-2-inferência-de-alta-velocidade-de-modelos-personalizados-com-c-e-directml">6. [Deep Dive 2] Inferência de Alta Velocidade de Modelos Personalizados com C++ e DirectML
&lt;/h2>&lt;p>Ao lidar com domínios específicos (segmentação de imagens própria, reconhecimento de fala, modelos personalizados de detecção de objetos, etc.) que não podem ser cobertos apenas pelo modelo de linguagem padrão do sistema operacional, os desenvolvedores precisarão manipular diretamente o ONNX Runtime e o DirectML, que estão nas camadas inferiores do &lt;code>Microsoft.Windows.AI&lt;/code>.&lt;/p>
&lt;p>O uso de C++ permite otimizar a alocação de memória ao máximo e extrair o pico de desempenho da NPU/GPU. Abaixo está a implementação principal de um pipeline avançado de inicialização e inferência para executar um modelo personalizado no formato ONNX (ex: YOLOv8) em C++ usando DirectML.&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;span class="lnt">94
&lt;/span>&lt;span class="lnt">95
&lt;/span>&lt;span class="lnt">96
&lt;/span>&lt;span class="lnt">97
&lt;/span>&lt;span class="lnt">98
&lt;/span>&lt;span class="lnt">99
&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;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">#include&lt;/span> &lt;span class="cpf">&amp;lt;onnxruntime_cxx_api.h&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;dml_provider_factory.h&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="k">class&lt;/span> &lt;span class="nc">CustomVisionAIProcessor&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">private&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Env&lt;/span> &lt;span class="n">env&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Session&lt;/span> &lt;span class="n">session&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="k">nullptr&lt;/span>&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">AllocatorWithDefaultOptions&lt;/span> &lt;span class="n">allocator&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">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">CustomVisionAIProcessor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">wstring&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">modelPath&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">:&lt;/span> &lt;span class="n">env&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ORT_LOGGING_LEVEL_WARNING&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;VisionAIProcessor&amp;#34;&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>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">SessionOptions&lt;/span> &lt;span class="n">sessionOptions&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Otimização do número de threads
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">sessionOptions&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">SetIntraOpNumThreads&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="c1">// Define o nível de otimização do gráfico para o máximo
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">sessionOptions&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">SetGraphOptimizationLevel&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">GraphOptimizationLevel&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">ORT_ENABLE_ALL&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. Adição do Provedor de Execução DirectML (DML EP)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// device_id = 0 é o adaptador padrão recomendado pelo sistema (NPU ou GPU de alto desempenho)
&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="n">OrtApi&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">ortApi&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">GetApi&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">OrtDmlApi&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">dmlApi&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">nullptr&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">OrtStatus&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">status&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ortApi&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">GetExecutionProviderApi&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;DML&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ORT_API_VERSION&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">reinterpret_cast&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="o">**&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">dmlApi&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">status&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="k">nullptr&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="n">dmlApi&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">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">ThrowOnError&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">dmlApi&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">SessionOptionsAppendExecutionProvider_DML&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sessionOptions&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="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;[Info] O Provedor de Execução DirectML foi anexado com sucesso.&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="p">}&lt;/span> &lt;span class="k">else&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;[Warning] Falha ao obter a API do DirectML. Executando no modo de fallback da CPU.&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">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">status&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="k">nullptr&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">ortApi&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ReleaseStatus&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">status&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">// 2. Carregamento do modelo e criação da sessão
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">session&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Session&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">env&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">modelPath&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">sessionOptions&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="s">&amp;#34;[Info] Modelo ONNX carregado com sucesso e o gráfico de computação foi compilado.&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="p">}&lt;/span> &lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Exception&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">e&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;[Error] Falha ao carregar o modelo: &amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">what&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">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">throw&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>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">void&lt;/span> &lt;span class="nf">RunInference&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">const&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="kt">float&lt;/span>&lt;span class="o">&amp;gt;&amp;amp;&lt;/span> &lt;span class="n">imageTensor&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">const&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="kt">int64_t&lt;/span>&lt;span class="o">&amp;gt;&amp;amp;&lt;/span> &lt;span class="n">inputShape&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">// 3. Criação do buffer para o tensor de entrada
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">MemoryInfo&lt;/span> &lt;span class="n">memoryInfo&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">MemoryInfo&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">CreateCpu&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">OrtArenaAllocator&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">OrtMemTypeDefault&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Value&lt;/span> &lt;span class="n">inputTensor&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Value&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">CreateTensor&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">float&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">memoryInfo&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">const_cast&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">float&lt;/span>&lt;span class="o">*&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">imageTensor&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">imageTensor&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">inputShape&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">inputShape&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="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. Obtenção dinâmica dos nomes dos nós de entrada e saída
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">auto&lt;/span> &lt;span class="n">inputNamePtr&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">session&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">GetInputNameAllocated&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">allocator&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">auto&lt;/span> &lt;span class="n">outputNamePtr&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">session&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">GetOutputNameAllocated&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">allocator&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">const&lt;/span> &lt;span class="kt">char&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">inputNames&lt;/span>&lt;span class="p">[]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">inputNamePtr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">get&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">const&lt;/span> &lt;span class="kt">char&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">outputNames&lt;/span>&lt;span class="p">[]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">outputNamePtr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">get&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>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 5. Execução da inferência (descarregada para NPU/GPU via DirectML)
&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">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;[Info] Iniciando a execução do motor de inferência...&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">auto&lt;/span> &lt;span class="n">startTime&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">chrono&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">high_resolution_clock&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">now&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">auto&lt;/span> &lt;span class="n">outputTensors&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">session&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">RunOptions&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="k">nullptr&lt;/span>&lt;span class="p">},&lt;/span> &lt;span class="n">inputNames&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">inputTensor&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">outputNames&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="k">auto&lt;/span> &lt;span class="n">endTime&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">chrono&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">high_resolution_clock&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">now&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">auto&lt;/span> &lt;span class="n">duration&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">chrono&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">duration_cast&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">chrono&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">milliseconds&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">endTime&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">startTime&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. Recuperação e análise dos tensores de resultado
&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">outputData&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">outputTensors&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">front&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">GetTensorMutableData&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">float&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">size_t&lt;/span> &lt;span class="n">outputSize&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">outputTensors&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">front&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">GetTensorTypeAndShapeInfo&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">GetElementCount&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;[Result] Inferência concluída: &amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">duration&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">count&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34; ms&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">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;[Result] Número de elementos no tensor de saída: &amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">outputSize&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">// *Após isso, a implementação do processamento NMS (Non-Maximum Suppression) e o desenho das caixas delimitadoras (bounding boxes) devem ser feitos no tensor de saída
&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="kt">int&lt;/span> &lt;span class="nf">main&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">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Caminho do modelo ONNX a ser executado
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">CustomVisionAIProcessor&lt;/span> &lt;span class="n">processor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">L&lt;/span>&lt;span class="s">&amp;#34;models/yolov8n_quantized.onnx&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">// Dados de imagem fictícios para inferência (Tamanho do lote 1 x 3 canais x 640 x 640)
&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="kt">int64_t&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">inputShape&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">640&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">640&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="kt">float&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">dummyImage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">3&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">640&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">640&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">0.5f&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">processor&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">RunInference&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">dummyImage&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">inputShape&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="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">exception&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">e&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;O programa foi encerrado de forma anormal: &amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">what&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">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="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 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;h3 id="61-a-importância-da-gestão-de-memória-e-inferência-zero-copy-em-c">6.1 A Importância da Gestão de Memória e Inferência Zero-Copy em C++
&lt;/h3>&lt;p>A maior vantagem de usar o DirectML com C++ é a forte integração com o DirectX 12 (DX12) que isso possibilita. O código acima inclui a cópia de dados da memória da CPU padrão por uma questão didática, mas em aplicações de processamento de vídeo e motores de jogos reais, muitas vezes já existem imagens (texturas) mantidas no espaço de memória da GPU ou NPU via DX12.&lt;/p>
&lt;p>Neste caso, usando as características avançadas de vinculação da API &lt;code>OrtDmlApi&lt;/code>, você pode alcançar a &amp;ldquo;&lt;strong>Inferência Zero-Copy (Zero-Copy Inference)&lt;/strong>&amp;rdquo;, onde recursos DX12 são mapeados diretamente como tensores para o ONNX Runtime. Com isso, o custo indireto de transferência de dados do barramento PCIe (o consumo da largura de banda $BW$ descrito acima) desaparece completamente, resultando em uma melhoria drástica nas taxas de quadros para o processamento de vídeo em tempo real.&lt;/p>
&lt;h2 id="7-otimização-de-desempenho-e-melhores-práticas">7. Otimização de Desempenho e Melhores Práticas
&lt;/h2>&lt;p>Abaixo estão as estratégias de otimização essenciais para o desenvolvimento de aplicativos de IA de ponta usando a API de IA do Windows e o DirectML.&lt;/p>
&lt;h3 id="71-quantização-de-modelos-quantization-e-toolkit-olive">7.1 Quantização de Modelos (Quantization) e Toolkit Olive
&lt;/h3>&lt;p>Para extrair o verdadeiro poder da NPU, é uma condição absoluta &lt;strong>quantizar (Quantization)&lt;/strong> os pesos e ativações do modelo de IA de FP32 (ponto flutuante de precisão simples) para INT8 ou INT4. A arquitetura da NPU é especializada em operações com inteiros, o que permite atingir taxas de transferência teoricamente quatro vezes maiores e reduzir drasticamente o consumo de energia em INT8 em comparação com o FP32.&lt;/p>
&lt;p>Ao usar a cadeia de ferramentas &lt;code>Olive (ONNX Live)&lt;/code> oferecida pela Microsoft, os modelos de IA, como os do PyTorch, podem ser automaticamente otimizados para ambientes Windows. A ferramenta Olive fornece forte suporte para otimizações de atenção especializadas para modelos baseados em Transformers e compilação de gráficos por hardware.&lt;/p>
&lt;h3 id="72-trade-off-entre-processamento-em-lote-e-streaming-interativo">7.2 Trade-off Entre Processamento em Lote e Streaming Interativo
&lt;/h3>&lt;p>Nas chamadas de API, a utilização da NPU (Compute Utilization) pode ser aumentada combinando múltiplas solicitações de inferência num processamento em lote. No entanto, no caso de IUs interativas, como chatbots, não é a taxa de transferência, mas sim o tempo até que o primeiro token seja gerado (TTFT: Time To First Token) que determina a experiência do usuário (UX).
Portanto, as melhores práticas ditam projetar o tamanho de lote (batch size) como 1 em IUs interativas, priorizando a geração de streaming.&lt;/p>
&lt;h3 id="73-tarefas-em-segundo-plano-e-integração-com-o-so">7.3 Tarefas em Segundo Plano e Integração com o SO
&lt;/h3>&lt;p>A inferência de IA consome localmente grandes quantidades de energia e recursos do sistema. Ao cooperar com a &lt;code>App Lifecycle API&lt;/code> do Windows, há a necessidade de uma implementação que suspenda (Suspend) as tarefas de inferência de baixa prioridade ou restrinja o consumo de recursos quando o aplicativo for movido para o segundo plano.&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant User as "Usuário"
participant App as "Aplicativo Windows (C#)"
participant API as "Microsoft.Windows.AI"
participant OS as "Windows Copilot Runtime"
participant ORT as "ONNX Runtime (DML)"
participant NPU as "Hardware NPU"
User->>App: "Insere o prompt"
App->>API: "GenerateResponseStreamAsync()"
API->>OS: "Despacha o trabalho de inferência"
OS->>ORT: "Solicitação de execução do gráfico"
ORT->>NPU: "Execução da lista de comandos via DirectML"
NPU-->>ORT: "Cálculo concluído (Geração de 1 token)"
ORT-->>OS: "Resultado em tensor"
OS-->>API: "Texto decodificado"
API-->>App: "Fragmento IAsyncEnumerable&lt;string>"
App-->>User: "Desenho em tempo real do texto na UI"
Note over ORT,NPU: "Repete esse loop em alta velocidade até ser concluído"&lt;/div>
&lt;p>Este diagrama de sequência ilustra a beleza do processamento assíncrono, através do qual os dados fluem em streaming da camada de hardware mais profunda da NPU para a camada de apresentação do aplicativo de forma contínua, sem bloquear a thread de UI de forma alguma.&lt;/p>
&lt;h2 id="8-perspectivas-futuras-e-a-evolução-da-ia-do-windows">8. Perspectivas Futuras e a Evolução da IA do Windows
&lt;/h2>&lt;p>As APIs &lt;code>Microsoft.Windows.AI&lt;/code> e o Copilot Runtime estão passando por uma rápida evolução neste exato momento. Nas futuras atualizações para os desenvolvedores, são esperadas as seguintes mudanças de paradigma:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Integração Nativa da API Multimodal no SO&lt;/strong>: Em vez de apenas texto, a capacidade de processar simultaneamente e de forma contínua áudio, imagens e até transmissões de vídeo ao vivo, fornecendo inferência transversal de IA como um padrão ao nível do SO.&lt;/li>
&lt;li>&lt;strong>RAG (Retrieval-Augmented Generation) a Nível de Sistema&lt;/strong>: Uma IA pessoal super avançada construída através da colaboração de um modelo de IA com grupos de documentos pessoais locais e o índice do Windows Search, operando dentro de uma sandbox segura do SO e protegendo completamente a privacidade do usuário.&lt;/li>
&lt;li>&lt;strong>Dimensionamento Dinâmico de Recursos da NPU&lt;/strong>: Quando vários aplicativos de IA funcionam simultaneamente (por exemplo, cancelamento de ruído em segundo plano e geração de código em primeiro plano), o escalonador do kernel do Windows alterna os contextos de execução da NPU de forma dinâmica para garantir a Qualidade de Serviço (QoS).&lt;/li>
&lt;/ul>
&lt;h2 id="9-conclusão-o-futuro-dos-aplicativos-transformados-pela-ia-local">9. Conclusão: O Futuro dos Aplicativos Transformados pela IA Local
&lt;/h2>&lt;p>O Copilot Runtime do Windows 11 e a API &lt;code>Microsoft.Windows.AI&lt;/code> trouxeram uma arma extremamente poderosa chamada &amp;ldquo;IA Local&amp;rdquo; para todos os desenvolvedores do Windows. Não há mais a necessidade de depender completamente de APIs em nuvem. É possível eliminar a latência e oferecer uma experiência de IA de última geração que funciona totalmente offline aos usuários, protegendo rigorosamente a sua privacidade.&lt;/p>
&lt;p>Pedimos que você utilize os conhecimentos abordados neste artigo sobre a integração do modelo de linguagem padrão do sistema via C#, a avaliação matemática de desempenho e a otimização extrema de hardware usando C++ e DirectML, para criar, com as suas próprias mãos, os próximos aplicativos de Windows &amp;ldquo;Nativos de IA&amp;rdquo;. As possibilidades infinitas que a IA traz estão à sua espera, através do código que você escrever.&lt;/p>
&lt;hr>
&lt;p>&lt;em>Aviso Legal: Note que este artigo foi escrito com base nas versões de pré-lançamento da API e especificações mais recentes disponíveis em Setembro de 2026. Como as especificações da API e os requisitos de hardware podem mudar devido às atualizações do Windows, certifique-se de consultar as documentações oficiais da Microsoft Learn ao implementar.&lt;/em>&lt;/p></description></item><item><title>Adeus Python! Construindo um Motor de Inferência de IA apenas com C++</title><link>http://kenji.blog/pt/p/building-ai-inference-engine-cpp-only/</link><pubDate>Fri, 11 Sep 2026 17:00:00 +0900</pubDate><guid>http://kenji.blog/pt/p/building-ai-inference-engine-cpp-only/</guid><description>&lt;img src="http://kenji.blog/p/building-ai-inference-engine-cpp-only/img/eyecatch.jpg" alt="Featured image of post Adeus Python! Construindo um Motor de Inferência de IA apenas com C++" />&lt;h2 id="1-introdução-por-que-abandonar-o-python-e-criar-um-motor-de-inferência-de-ia-em-c">1. Introdução: Por que abandonar o Python e criar um motor de inferência de IA em C++?
&lt;/h2>&lt;p>No desenvolvimento moderno de IA, o Python é o padrão de fato. Graças a frameworks poderosos como PyTorch e TensorFlow, é possível construir, treinar e inferir redes neurais complexas com apenas algumas linhas de código. No entanto, por trás desses frameworks, linguagens de baixo nível como C++ e CUDA estão lidando com o processamento pesado de cálculos. O Python atua apenas como uma &amp;ldquo;cola&amp;rdquo; (glue).&lt;/p>
&lt;p>Então, por que se dar ao trabalho de eliminar o Python e construir um motor de inferência de IA exclusivamente em C++? Existem várias razões fortes para isso.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Desempenho extremo e baixa latência&lt;/strong>: Você pode eliminar completamente o overhead causado pelo GIL (Global Interpreter Lock) e pela tipagem dinâmica do Python. Especialmente em sistemas que exigem tempo real, atrasos de milissegundos podem ser fatal.&lt;/li>
&lt;li>&lt;strong>Facilidade de implantação&lt;/strong>: Configurar um ambiente Python (enormes bibliotecas, inferno de dependências) no ambiente do usuário final é extremamente difícil. Com o C++, basta distribuir um único binário executável (&lt;code>.exe&lt;/code> ou binário ELF) vinculado estaticamente.&lt;/li>
&lt;li>&lt;strong>Suporte a dispositivos de borda&lt;/strong>: Em ambientes com severas restrições de recursos, como smartphones, dispositivos embarcados e Raspberry Pi, não há margem para executar um runtime Python que consome vários gigabytes de memória.&lt;/li>
&lt;li>&lt;strong>Controle direto de hardware&lt;/strong>: O controle de baixo nível, como o tempo de alocação de memória, uso explícito de instruções SIMD e otimização de transferências de memória com a GPU, é possível em C++.&lt;/li>
&lt;/ol>
&lt;p>Neste artigo, inspirando-nos muito na arquitetura da biblioteca &amp;ldquo;GGML&amp;rdquo; desenvolvida por Georgi Gerganov, explicaremos profundamente o processo de construir do zero um motor de inferência para executar Modelos de Linguagem de Grande Escala (LLMs) apenas com C++, mergulhando nas profundezas técnicas.&lt;/p>
&lt;hr>
&lt;h2 id="2-visão-geral-da-arquitetura-do-motor-de-inferência">2. Visão geral da arquitetura do motor de inferência
&lt;/h2>&lt;p>O processamento de inferência de IA é, essencialmente, uma &amp;ldquo;série contínua de cálculos de matrizes gigantes&amp;rdquo;. Para executar isso de forma eficiente, o motor de inferência precisa ser composto pelos seguintes componentes.&lt;/p>
&lt;div class="mermaid">graph TD
A["Dados de Entrada (Tokens/Imagens)"] --> B["Gerenciamento de Tensores"]
B --> C["Grafo de Computação (DAG)"]
C --> D["Arena de Memória &amp; Alocador"]
C --> E["Agendador &amp; Pool de Threads"]
E --> F["Backend de CPU (AVX2/ARM NEON)"]
E --> G["Backend de GPU (CUDA/Metal)"]
F --> H["Resultados de Saída"]
G --> H&lt;/div>
&lt;ol>
&lt;li>&lt;strong>Gerenciamento de Tensores (Tensor)&lt;/strong>: Gerencia a estrutura de dados de arrays multidimensionais e o passo (Stride) de cada dimensão.&lt;/li>
&lt;li>&lt;strong>Grafo de Computação (Computation Graph)&lt;/strong>: Representa as operações de cada camada da rede neural como um Grafo Acíclico Dirigido (DAG).&lt;/li>
&lt;li>&lt;strong>Arena de Memória (Memory Arena)&lt;/strong>: Um mecanismo de gerenciamento de memória pré-alocada para evitar o overhead de alocação dinâmica de memória (&lt;code>malloc&lt;/code> ou &lt;code>new&lt;/code>).&lt;/li>
&lt;li>&lt;strong>Backend&lt;/strong>: A implementação de operações otimizadas para hardwares específicos, como CPU ou GPU (Kernels).&lt;/li>
&lt;/ol>
&lt;p>Vamos montar tudo isso usando os poderosos recursos do C++ (templates, aritmética de ponteiros, RAII, etc.).&lt;/p>
&lt;hr>
&lt;h2 id="3-os-segredos-do-gerenciamento-de-memória-arena-de-memória-e-alinhamento-simd">3. Os segredos do gerenciamento de memória: Arena de Memória e Alinhamento SIMD
&lt;/h2>&lt;p>O gerenciamento de memória em um motor de inferência é um dos fatores mais importantes que afeta diretamente o desempenho. Durante a inferência, um número colossal de tensores intermediários é gerado, especialmente ao passar por cada camada de um modelo Transformer. Se alocarmos e liberarmos isso a cada vez com o &lt;code>malloc&lt;/code> padrão, a fragmentação do heap e as trocas de contexto do SO causarão uma lentidão fatal.&lt;/p>
&lt;p>Portanto, adotamos a abordagem da &amp;ldquo;&lt;strong>Arena de Memória (Memory Arena)&lt;/strong>&amp;rdquo;. Trata-se de uma técnica em que calculamos (ou fixamos) a quantidade máxima de memória necessária no início da inferência, alocamos tudo de uma vez e separamos a memória apenas incrementando um ponteiro.&lt;/p>
&lt;h3 id="31-a-importância-do-alinhamento">3.1 A importância do alinhamento
&lt;/h3>&lt;p>As CPUs modernas suportam instruções SIMD (Single Instruction, Multiple Data). Exemplos incluem AVX2/AVX-512 da Intel/AMD e NEON da ARM. Essas instruções processam 256 bits (32 bytes) ou 512 bits (64 bytes) de dados de uma só vez, mas a memória de dados a ser processada deve estar alinhada a um limite específico de bytes (geralmente 32 ou 64 bytes).&lt;/p>
&lt;p>Abaixo, um exemplo de implementação em C++ de uma arena de memória considerando o alinhamento:&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;/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;lt;cstdint&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;cstddef&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">#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">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">struct&lt;/span> &lt;span class="nc">MemoryArena&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">size_t&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">size_t&lt;/span> &lt;span class="n">offset&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">uint8_t&lt;/span>&lt;span class="o">*&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>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">MemoryArena&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">size_t&lt;/span> &lt;span class="n">size&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="n">size&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">offset&lt;/span>&lt;span class="p">(&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">// POSIX系なら posix_memalign、Windowsなら _aligned_malloc を使用
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="cp">#ifdef _WIN32
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span> &lt;span class="n">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">static_cast&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">uint8_t&lt;/span>&lt;span class="o">*&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">_aligned_malloc&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">size&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">64&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#else
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">posix_memalign&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">reinterpret_cast&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="o">**&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="mi">64&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">size&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="k">throw&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">bad_alloc&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="cp">#endif
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&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="o">~&lt;/span>&lt;span class="n">MemoryArena&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="cp">#ifdef _WIN32
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span> &lt;span class="n">_aligned_free&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="cp">#else
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span> &lt;span class="n">free&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="cp">#endif
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&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="kt">void&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="nf">allocate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">size_t&lt;/span> &lt;span class="n">bytes&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">size_t&lt;/span> &lt;span class="n">alignment&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">64&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">size_t&lt;/span> &lt;span class="n">pad&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">alignment&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">offset&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">alignment&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">alignment&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">offset&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">pad&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">bytes&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">size&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">throw&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">runtime_error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;OOM: MemoryArena out of memory&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">offset&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="n">pad&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">void&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">ptr&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">data&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">offset&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">offset&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="n">bytes&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="n">ptr&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">void&lt;/span> &lt;span class="nf">reset&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">offset&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// メモリの解放はポインタを戻すだけ（O(1)）
&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;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Dessa forma, sempre obteremos memória através desta arena ao criar tensores. Simplesmente chamando &lt;code>reset()&lt;/code> ao final de cada etapa da inferência (como a cada geração de token), podemos reutilizar a memória instantaneamente.&lt;/p>
&lt;hr>
&lt;h2 id="4-estrutura-de-dados-do-tensor-e-a-magia-do-stride">4. Estrutura de dados do Tensor e a magia do Stride
&lt;/h2>&lt;p>O tensor é uma generalização dos conceitos de escalar, vetor e matriz. O importante na implementação é que, embora os dados reais estejam organizados na memória como um &lt;strong>array contíguo unidimensional&lt;/strong>, eles possuem o conceito de &amp;ldquo;passo&amp;rdquo; (Stride) para interpretá-los como multidimensionais.&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;/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="k">enum&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">DataType&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">FP32&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="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">INT8&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">INT4&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="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">struct&lt;/span> &lt;span class="nc">Tensor&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_dims&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">int64_t&lt;/span> &lt;span class="n">ne&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">4&lt;/span>&lt;span class="p">];&lt;/span> &lt;span class="c1">// 各次元の要素数 (Number of Elements)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">size_t&lt;/span> &lt;span class="n">nb&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">4&lt;/span>&lt;span class="p">];&lt;/span> &lt;span class="c1">// 各次元のストライド (Number of Bytes)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">DataType&lt;/span> &lt;span class="n">type&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">void&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">data&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">// 計算グラフ用
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">enum&lt;/span> &lt;span class="nc">OpType&lt;/span> &lt;span class="n">op&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">src0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">src1&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>O stride &lt;code>nb[i]&lt;/code> representa a distância em bytes na memória entre elementos adjacentes na dimensão &lt;code>i&lt;/code>.
Por exemplo, se uma matriz com elementos $M \times N$ (FP32, 4 bytes por elemento) for armazenada em Row-Major (ordem de linha principal), os strides serão os seguintes:&lt;/p>
&lt;ul>
&lt;li>&lt;code>nb[0]&lt;/code> = 4 (bytes): movimento na direção da coluna&lt;/li>
&lt;li>&lt;code>nb[1]&lt;/code> = $N \times 4$ (bytes): movimento na direção da linha&lt;/li>
&lt;/ul>
&lt;p>Ao utilizar isso, operações como &amp;ldquo;Transposição (Transpose)&amp;rdquo; e &amp;ldquo;Visão (View)&amp;rdquo; podem ser realizadas apenas trocando os valores de stride, sem envolver cópias de memória. É extremamente elegante e rápido.&lt;/p>
&lt;hr>
&lt;h2 id="5-construção-do-grafo-de-computação-dag-e-avaliação-preguiçosa">5. Construção do Grafo de Computação (DAG) e Avaliação Preguiçosa
&lt;/h2>&lt;p>Assim como no PyTorch, nosso motor de inferência também adota uma avaliação preguiçosa (Lazy Evaluation) semelhante ao &amp;ldquo;Define-by-Run&amp;rdquo;. Ou seja, no momento em que a função de operação é chamada, o cálculo não é realizado; constrói-se apenas o grafo (as dependências entre os nós).&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;/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">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="nf">tensor_add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">MemoryArena&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">arena&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">b&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">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">out&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">create_tensor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">arena&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">type&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">n_dims&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">ne&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">out&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">op&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">OpType&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">ADD&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">out&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">src0&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">out&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">src1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">b&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="n">out&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">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="nf">tensor_mul_mat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">MemoryArena&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">arena&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">b&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">// bは転置されていることが多い
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">int64_t&lt;/span> &lt;span class="n">ne&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">ne&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">ne&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&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">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">out&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">create_tensor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">arena&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">type&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ne&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">out&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">op&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">OpType&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">MUL_MAT&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">out&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">src0&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">out&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">src1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">b&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="n">out&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>O fluxo de processamento da inferência será o seguinte:&lt;/p>
&lt;div class="mermaid">graph LR
A["Definir Tensores"] --> B["Construir Grafo via Operações"]
B --> C["Ordenação Topológica"]
C --> D["Alocar Memória para Saídas"]
D --> E["Executar Nós em Ordem"]&lt;/div>
&lt;p>Ao avaliar o grafo (forward pass), utilizamos a ordenação topológica para executar o processamento em ordem a partir dos nós sem dependências. Se for apenas para inferência, não há necessidade de reter gradientes para retropropagação (backpropagation), o que torna o gerenciamento de memória extremamente simples.&lt;/p>
&lt;hr>
&lt;h2 id="6-o-núcleo-da-matemática-e-otimização-produto-de-matrizes-gemm">6. O núcleo da matemática e otimização: Produto de Matrizes (GEMM)
&lt;/h2>&lt;p>Mais de 90% da quantidade de cálculos de inferência de IA é gasta em multiplicação de matrizes (GEMM: General Matrix Multiply). O mecanismo de atenção (Attention), núcleo do modelo Transformer, bem como as redes feed-forward (FFN), resumem-se, em última análise, a produtos gigantes de matrizes.&lt;/p>
&lt;p>O produto $C = A B$ (tamanho $M \times N$) de duas matrizes $A$ (tamanho $M \times K$) e $B$ (tamanho $K \times N$) pode ser expresso através da seguinte fórmula:&lt;/p>
$$
C_{i,j} = \sum_{k=0}^{K-1} A_{i,k} \cdot B_{k,j}
$$
&lt;p>Se isso for implementado com um loop triplo ingênuo, os erros de cache (cache misses) ocorrerão com frequência, e não haverá nenhum desempenho.&lt;/p>
&lt;h3 id="61-bloqueio-de-cache-cache-blocking-e-otimização-simd-na-cpu">6.1 Bloqueio de Cache (Cache Blocking) e Otimização SIMD na CPU
&lt;/h3>&lt;p>A estratégia básica para acelerar o GEMM na CPU é a seguinte:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Loop Tiling (Bloqueio de Cache)&lt;/strong>: A matriz é dividida em blocos menores que cabem no cache L1/L2 e calculados.&lt;/li>
&lt;li>&lt;strong>Empacotamento de Dados (Data Packing)&lt;/strong>: Os dados são reorganizados internamente para que o padrão de acesso à memória seja contíguo.&lt;/li>
&lt;li>&lt;strong>Aproveitamento de SIMD&lt;/strong>: Utilizamos instruções FMA (Fused Multiply-Add) como &lt;code>_mm512_fmadd_ps&lt;/code> no AVX-512 para executar várias operações de multiplicar-adicionar em um único ciclo de clock.&lt;/li>
&lt;/ol>
&lt;p>Aqui está um exemplo simplificado do produto escalar (Dot Product) de vetores utilizando C++ e SIMD Intrinsics:&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;/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;lt;immintrin.h&amp;gt;&lt;/span>&lt;span class="cp"> &lt;/span>&lt;span class="c1">// AVX命令用
&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">// AVX2を用いたFP32の高速内積
&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="nf">dot_product_avx2&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="kt">float&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="kt">float&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">n&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">__m256&lt;/span> &lt;span class="n">sum256&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">_mm256_setzero_ps&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">i&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="c1">// 8要素ずつ一度に処理（256ビット = 32バイト = 8 * 4バイト）
&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">i&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">8&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="mi">8&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">__m256&lt;/span> &lt;span class="n">va&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">_mm256_loadu_ps&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">__m256&lt;/span> &lt;span class="n">vb&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">_mm256_loadu_ps&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">b&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// FMA命令: sum256 = va * vb + sum256
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">sum256&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">_mm256_fmadd_ps&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">va&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">vb&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">sum256&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">// SIMDレジスタ内の値を水平加算
&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="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">8&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">_mm256_storeu_ps&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">sum256&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">float&lt;/span> &lt;span class="n">dot&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">result&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">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">+&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">4&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">5&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">6&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">7&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">i&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="o">++&lt;/span>&lt;span class="n">i&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">dot&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&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="k">return&lt;/span> &lt;span class="n">dot&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>Apenas com esse pequeno esforço, podemos obter uma melhoria de velocidade de várias a dezenas de vezes em comparação com uma implementação ingênua.&lt;/p>
&lt;hr>
&lt;h2 id="7-ultrapassando-a-barreira-do-hardware-integração-dos-backends-cuda-e-metal">7. Ultrapassando a barreira do hardware: Integração dos backends CUDA e Metal
&lt;/h2>&lt;p>Embora uma implementação puramente em C++ já funcione consideravelmente bem na CPU, o poder de computação paralela das GPUs é indispensável para executar modelos gigantes como LLMs a velocidades práticas (ex.: gerar 20 tokens ou mais por segundo). Por isso, introduziremos uma camada de abstração de backend em nosso motor.&lt;/p>
&lt;h3 id="71-abstração-de-backend">7.1 Abstração de Backend
&lt;/h3>&lt;p>Usando o polimorfismo do C++, permitiremos a troca do executor das operações (Executor).&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;/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="k">class&lt;/span> &lt;span class="nc">Backend&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">virtual&lt;/span> &lt;span class="o">~&lt;/span>&lt;span class="n">Backend&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">default&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">virtual&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">alloc_buffer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">t&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>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">virtual&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">free_buffer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">t&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>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">virtual&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">copy_to_device&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">t&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>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">virtual&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">copy_to_host&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">t&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>&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">virtual&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">compute_add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">src0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">src1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">dst&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>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">virtual&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">compute_mul_mat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">src0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">src1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">dst&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>&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;h3 id="72-implementação-do-backend-nvidia-cuda">7.2 Implementação do Backend NVIDIA CUDA
&lt;/h3>&lt;p>Para utilizar as GPUs da NVIDIA, implementaremos o backend usando a extensão CUDA C++. Embora seja possível escrever nossos próprios kernels, a melhor abordagem em relação à multiplicação de matrizes é aproveitar a &amp;ldquo;cuBLAS&amp;rdquo;, a biblioteca de ponta fornecida pela NVIDIA.&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;/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;lt;cublas_v2.h&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;cuda_runtime.h&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="k">class&lt;/span> &lt;span class="nc">CUDABackend&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="k">public&lt;/span> &lt;span class="n">Backend&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">private&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cublasHandle_t&lt;/span> &lt;span class="n">handle&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">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">CUDABackend&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">cublasCreate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">handle&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="o">~&lt;/span>&lt;span class="n">CUDABackend&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">cublasDestroy&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">handle&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">void&lt;/span> &lt;span class="nf">compute_mul_mat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">src0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">src1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">dst&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">override&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// CUDAではデフォルトがColumn-Majorのため、パラメータに注意が必要
&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">float&lt;/span> &lt;span class="n">alpha&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mf">1.0f&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">const&lt;/span> &lt;span class="kt">float&lt;/span> &lt;span class="n">beta&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mf">0.0f&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="kt">int&lt;/span> &lt;span class="n">m&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">src0&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">ne&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="kt">int&lt;/span> &lt;span class="n">k&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">src0&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">ne&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="kt">int&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">src1&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">ne&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">];&lt;/span> &lt;span class="c1">// src1は転置されている前提
&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">cublasSgemm&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">handle&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">CUBLAS_OP_T&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">CUBLAS_OP_N&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">m&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">k&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">alpha&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="k">const&lt;/span> &lt;span class="kt">float&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">src0&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">k&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="k">const&lt;/span> &lt;span class="kt">float&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">src1&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">k&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">beta&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="kt">float&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">dst&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cudaDeviceSynchronize&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;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Como a transferência de dados (&lt;code>cudaMemcpy&lt;/code>) entre a memória CUDA e a memória host (CPU) é muito pesada, é importante projetar para manter ao máximo todos os pesos (tensores de peso) e tensores intermediários na VRAM durante a inferência.&lt;/p>
&lt;h3 id="73-backend-apple-silicon-metal">7.3 Backend Apple Silicon (Metal)
&lt;/h3>&lt;p>Nos últimos anos, os chips M1/M2/M3 do Mac (Apple Silicon) têm se mostrado excelentes como máquinas de inferência de IA. A razão para isso está na &amp;ldquo;memória unificada&amp;rdquo; (Unified Memory). Como a CPU e a GPU compartilham a mesma área de memória, elimina-se totalmente a necessidade de transferências de memória onerosas entre host e dispositivo através do barramento PCIe, como acontece com CUDA.&lt;/p>
&lt;p>Para chamar o Metal a partir do C++, usamos Objective-C++ (arquivos &lt;code>.mm&lt;/code>) como ponte ou utilizamos a biblioteca &lt;code>metal-cpp&lt;/code>.
Escrevemos o kernel utilizando o Compute Shader do Metal (escrito em arquivos &lt;code>.metal&lt;/code> de forma semelhante ao C++).&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;/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">// Metalシェーダ (kernel.metal)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;metal_stdlib&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 class="k">using&lt;/span> &lt;span class="k">namespace&lt;/span> &lt;span class="n">metal&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">kernel&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">mul_mat_kernel&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&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="kt">float&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">A&lt;/span> &lt;span class="p">[[&lt;/span>&lt;span class="n">buffer&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="n">device&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="kt">float&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">B&lt;/span> &lt;span class="na">[[buffer(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">device&lt;/span> &lt;span class="kt">float&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">C&lt;/span> &lt;span class="na">[[buffer(2)]]&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">constant&lt;/span> &lt;span class="n">uint3&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">dims&lt;/span> &lt;span class="na">[[buffer(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">uint2&lt;/span> &lt;span class="n">gid&lt;/span> &lt;span class="na">[[thread_position_in_grid]]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&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">uint&lt;/span> &lt;span class="n">m&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dims&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">uint&lt;/span> &lt;span class="n">k&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dims&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">y&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">uint&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dims&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">z&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">uint&lt;/span> &lt;span class="n">row&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">gid&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">y&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">uint&lt;/span> &lt;span class="n">col&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">gid&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&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">row&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">m&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="n">col&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">n&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="kt">float&lt;/span> &lt;span class="n">sum&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mf">0.0&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="p">(&lt;/span>&lt;span class="n">uint&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">k&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="o">++&lt;/span>&lt;span class="n">i&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">sum&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="n">A&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">row&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">k&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">B&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">col&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="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">C&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">row&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">col&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sum&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;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Em ambientes Apple Silicon, uma biblioteca de otimização chamada MPS (Metal Performance Shaders) voltada para o produto de matrizes também é fornecida, e sua utilização na prática pode alcançar velocidades de inferência surpreendentes.&lt;/p>
&lt;hr>
&lt;h2 id="8-processamento-específico-para-modelos-transformer-attention-e-cache-kv">8. Processamento específico para modelos Transformer: Attention e Cache KV
&lt;/h2>&lt;p>Os LLMs de ponta, como LLaMA 2/3 e GPT, são baseados na arquitetura Transformer. Para implementar isso em C++, é indispensável construir o &amp;ldquo;Scaled Dot-Product Attention&amp;rdquo;, expresso pela seguinte fórmula:&lt;/p>
$$
\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V
$$
&lt;p>Além disso, na geração de tokens autorregressiva (Autoregressive), é necessário manter os resultados de cálculo (Key e Value) de tokens passados. A isso damos o nome de &amp;ldquo;&lt;strong>Cache KV (Key-Value Cache)&lt;/strong>&amp;rdquo;.&lt;/p>
&lt;div class="mermaid">graph TD
T["Token Atual"] --> Q["Consulta (Query)"]
T --> K["Chave (Key)"]
T --> V["Valor (Value)"]
K --> KCache["Adicionar ao Cache KV"]
V --> VCache["Adicionar ao Cache KV"]
Q --> Dot1["Q * K_Cache^T"]
KCache --> Dot1
Dot1 --> Scale["Escala (1/sqrt(d))"]
Scale --> Softmax["Softmax"]
Softmax --> Dot2["Saída Softmax * V_Cache"]
VCache --> Dot2
Dot2 --> Out["Vetor de Contexto"]&lt;/div>
&lt;p>A alocação de memória para o Cache KV também deve operar como um buffer circular (ring buffer), garantindo antecipadamente na arena um espaço de memória equivalente ao comprimento máximo de contexto (por exemplo, 4096 ou 8192 tokens). Isso evita a necessidade de realocar memória a cada etapa de geração.&lt;/p>
&lt;p>Além disso, para a Codificação Posicional (Positional Encoding), implementaremos o &amp;ldquo;RoPE (Rotary Position Embedding)&amp;rdquo;, que tem se tornado a norma nos últimos anos. Esta técnica embute a informação de posição como um vetor de rotação num espaço complexo, e a otimização de chamadas de funções &lt;code>sin&lt;/code> e &lt;code>cos&lt;/code> em C++ (por exemplo, uso de tabelas de consulta / look-up tables) é a chave para a performance.&lt;/p>
&lt;hr>
&lt;h2 id="9-otimização-extrema-por-meio-da-quantização-quantization-de-modelos">9. Otimização extrema por meio da Quantização (Quantization) de modelos
&lt;/h2>&lt;p>Se carregarmos um modelo de grande escala (por exemplo, o modelo LLaMA com 7 bilhões de parâmetros) em FP32 (ponto flutuante de 32 bits), ele consumirá cerca de 28 GB de memória (VRAM) apenas para os pesos. Ao incluir o cache KV e os buffers de inferência, ultrapassará facilmente os 30 GB, tornando-se inexequível em GPUs de uso comum para consumidores.&lt;/p>
&lt;p>Aí entra a necessidade indispensável da &amp;ldquo;&lt;strong>Quantização (Quantization)&lt;/strong>&amp;rdquo;. Esse é, inclusive, o verdadeiro ponto forte do formato GGML.&lt;/p>
&lt;p>A quantização é a técnica de reduzir intencionalmente a precisão dos pesos.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>FP16 (16 bits)&lt;/strong>: Tamanho reduzido pela metade. Quase nenhuma degradação de precisão.&lt;/li>
&lt;li>&lt;strong>INT8 (8 bits)&lt;/strong>: 1/4 do tamanho. Leve degradação.&lt;/li>
&lt;li>&lt;strong>INT4 (4 bits)&lt;/strong>: 1/8 do tamanho. Utilizando bloqueios e fatores de escala próprios, a inferência prática torna-se possível.&lt;/li>
&lt;/ul>
&lt;p>No lado do motor de inferência, lemos os pesos comprimidos em INT4 (ou INT8) da memória e, &lt;strong>imediatamente após carregá-los nos registradores da CPU ou GPU, expandimos (Desquantização/Dequantize) para FP16 ou FP32 para realizar os cálculos&lt;/strong>.&lt;/p>
&lt;p>Surpreendentemente, mesmo que o volume de cálculos aumente, reduzir a quantidade de dados lidos da memória torna o processo mais rápido. Isso ocorre porque o gargalo nas tarefas de inferência no hardware moderno não é o &amp;ldquo;Poder de Computação (Compute Bound)&amp;rdquo;, mas sim a &amp;ldquo;&lt;strong>Largura de Banda da Memória (Memory Bandwidth Bound)&lt;/strong>&amp;rdquo;. Com um motor implementado em C++ utilizando quantização INT4, é possível executar LLMs locais suavemente até mesmo em um MacBook Air com 8 GB de RAM unificada.&lt;/p>
&lt;hr>
&lt;h2 id="10-ajuste-de-desempenho-performance-tuning-arquitetura-numa-e-pool-de-threads">10. Ajuste de desempenho (Performance Tuning): Arquitetura NUMA e Pool de Threads
&lt;/h2>&lt;p>Ao realizar inferência utilizando CPU, a implementação de multi-threading é obrigatória. No entanto, não podemos dizer que é o ideal simplesmente inicializar um grande número de &lt;code>std::thread&lt;/code>.&lt;/p>
&lt;p>Em servidores multi-soquete modernos ou CPUs de ponta como o Ryzen Threadripper, adota-se a arquitetura &lt;strong>NUMA (Non-Uniform Memory Access)&lt;/strong>. O acesso de um núcleo de CPU para a memória fisicamente mais próxima (memória local) é rápido, mas o acesso à memória vinculada a outro processador será extremamente lento.&lt;/p>
&lt;p>Em um motor de inferência C++ avançado, fazemos pleno uso das seguintes técnicas:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Fixação de Threads (Thread Pinning)&lt;/strong>: Fixa cada thread a um núcleo de CPU específico (definindo Afinidade/Affinity) para evitar a invalidação de cache causada por trocas de contexto.&lt;/li>
&lt;li>&lt;strong>Alocação ciente de NUMA (NUMA-aware allocation)&lt;/strong>: Garante a alocação de memória no mesmo nó NUMA que a thread que está processando os dados.&lt;/li>
&lt;li>&lt;strong>Pool de threads do tipo Work-Stealing&lt;/strong>: Divide cada nó do grafo de computação em pequenas tarefas e implementa um agendador eficiente onde threads ociosas capturam (roubam) tarefas automaticamente para execução.&lt;/li>
&lt;/ol>
&lt;p>Fazendo uso dessas técnicas, é possível manter a taxa de uso da CPU muito próxima de 100%, atingindo um rendimento (throughput) que se aproxima do valor teórico.&lt;/p>
&lt;hr>
&lt;h2 id="11-conclusão-a-alegria-de-conduzir-a-ia-com-os-músculos-do-c">11. Conclusão: A alegria de conduzir a IA com os &amp;ldquo;músculos&amp;rdquo; do C++
&lt;/h2>&lt;p>É certo que o Python é conveniente. Não há linguagem que se iguale à sua produtividade na fase de pesquisa, desenvolvimento e prototipagem. Contudo, no instante em que passamos para a fase de &amp;ldquo;executar o modelo concluído no mundo real, de forma eficiente e em todos os dispositivos&amp;rdquo;, chega o momento de usar o C++.&lt;/p>
&lt;p>Manipular a sequência de bytes na memória de forma direta, forçar os registradores ao limite com instruções SIMD e lutar contra a largura de banda da VRAM da GPU para construir um motor de inferência que vai gerando textos (tokens) em japonês natural (ou qualquer outro idioma) sequencialmente no console&amp;hellip; A sensação de realização ao ver isso acontecer proporciona uma &amp;ldquo;alegria genuína de engenheiro&amp;rdquo;, a qual nunca se obterá simplesmente chamando &lt;code>model.generate()&lt;/code> em um framework Python.&lt;/p>
&lt;p>Embora a tecnologia de IA tenda a se tornar uma &amp;ldquo;Caixa Preta (Black Box)&amp;rdquo;, escrever tudo à mão em C++, desde as operações de tensores até a alocação de memória, permite a você entender profundamente o verdadeiro mecanismo de como um LLM &amp;ldquo;pensa&amp;rdquo;.&lt;/p>
&lt;p>Se você tem conhecimentos básicos de C++ e possui um forte interesse nas atuais tecnologias de IA, experimente o desafio de desenvolver seu próprio motor de inferência. Os códigos-fonte do GGML ou llama.cpp servirão, sem dúvida, como os melhores livros didáticos vivos disponíveis.&lt;/p>
&lt;p>&lt;strong>Vamos lá, jogue fora os pesados runtimes do Python e faça a IA de ponta rodar com os músculos do C++!&lt;/strong>&lt;/p></description></item><item><title>Como Fazer o Ajuste Fino Mais Rápido do TinyLLaMA em um Ambiente On-Premises</title><link>http://kenji.blog/pt/p/tinyllama-on-premises-fast-tuning-guide/</link><pubDate>Fri, 11 Sep 2026 16:00:00 +0900</pubDate><guid>http://kenji.blog/pt/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 Como Fazer o Ajuste Fino Mais Rápido do TinyLLaMA em um Ambiente On-Premises" />&lt;h2 id="1-introdução-por-que-tinyllama-e-on-premises-agora">1. Introdução: Por que TinyLLaMA e On-Premises agora?
&lt;/h2>&lt;p>A evolução dos grandes modelos de linguagem (LLM) está avançando a uma velocidade incrível, mas, consequentemente, o número de parâmetros dos modelos continua a inchar para a escala de centenas de bilhões. Embora modelos supergigantes como GPT-4 e Claude 3 possuam um desempenho inigualável, o custo computacional para inferência e treinamento, bem como as preocupações de segurança e privacidade de dados ao usar APIs externas, tornaram-se grandes obstáculos para as empresas. Especialmente em operações que lidam com dados corporativos altamente confidenciais e informações pessoais, enviar dados para uma API de LLM pública na nuvem frequentemente não é permitido do ponto de vista da conformidade (como GDPR e LGPD).&lt;/p>
&lt;p>É aí que os &lt;strong>Pequenos Modelos de Linguagem (SLM: Small Language Models)&lt;/strong> e as &lt;strong>operações locais em ambientes on-premises&lt;/strong> estão ganhando destaque. Entre eles, o &amp;ldquo;&lt;strong>TinyLLaMA&lt;/strong>&amp;rdquo; tem um tamanho compacto de apenas 1.1B (1.1 bilhão) de parâmetros, mas foi pré-treinado com um enorme conjunto de dados de cerca de 3 trilhões de tokens, demonstrando um desempenho impressionante em comparação com modelos da mesma classe.&lt;/p>
&lt;p>Neste artigo, forneceremos um guia completo para realizar o fine-tuning (ajuste fino) deste TinyLLaMA de forma &amp;ldquo;mais rápida e altamente eficiente&amp;rdquo; para tarefas específicas da sua empresa em um ambiente on-premises (servidores locais ou estações de trabalho). Explicaremos de forma abrangente desde os fundamentos matemáticos e as mais recentes tecnologias de otimização até códigos práticos de implementação em PyTorch.&lt;/p>
&lt;hr>
&lt;h2 id="2-arquitetura-e-características-do-tinyllama">2. Arquitetura e Características do TinyLLaMA
&lt;/h2>&lt;p>O TinyLLaMA segue a arquitetura LLaMA (Large Language Model Meta AI) desenvolvida pela Meta. Embora mantenha o número de parâmetros em 1.1B, ele utiliza a mesma pilha de tecnologia do LLaMA 2, o que o caracteriza por uma compatibilidade extremamente alta com o ecossistema.&lt;/p>
&lt;h3 id="principais-componentes-da-arquitetura">Principais Componentes da Arquitetura
&lt;/h3>&lt;ol>
&lt;li>&lt;strong>RMSNorm (Root Mean Square Normalization):&lt;/strong>
Um método de normalização que omite a subtração da média dos cálculos do LayerNorm tradicional, melhorando a eficiência computacional. Ele aumenta a taxa de transferência enquanto mantém a estabilidade do treinamento.&lt;/li>
&lt;li>&lt;strong>Função de Ativação SwiGLU:&lt;/strong>
Na Feed Forward Network (FFN), o SwiGLU é adotado em vez do ReLU ou GELU tradicionais. Matematicamente, isso é expresso da seguinte forma:
$$ \text{SwiGLU}(x, W, V) = \text{Swish}(xW) \otimes (xV) $$
Aqui, $\otimes$ representa o produto elemento a elemento (Produto de Hadamard) e a função Swish é $\text{Swish}(z) = z \cdot \sigma(\beta z)$. Isso melhora significativamente a capacidade de representação.&lt;/li>
&lt;li>&lt;strong>RoPE (Rotary Position Embedding):&lt;/strong>
Um método que combina as vantagens da codificação de posição absoluta e da codificação de posição relativa. Possui alta capacidade de generalização mesmo quando o comprimento da sequência é estendido.&lt;/li>
&lt;li>&lt;strong>Grouped Query Attention (GQA):&lt;/strong>
Uma abordagem intermediária entre a Multi-Head Attention (MHA) e a Multi-Query Attention (MQA), que economiza largura de banda de memória e melhora drasticamente a velocidade de inferência agrupando as cabeças de chaves e valores.&lt;/li>
&lt;/ol>
&lt;p>O diagrama Mermaid a seguir mostra o fluxo de dados geral e a estrutura dos blocos Transformer do TinyLLaMA.&lt;/p>
&lt;div class="mermaid">graph TD
A["Texto de Entrada"] --> B["Tokenizador (BPE)"]
B --> C["Camada de Embedding"]
C --> D["Blocos Transformer (x22 Camadas para TinyLLaMA)"]
D --> E["RMSNorm (Final)"]
E --> F["Projeção Linear (Tamanho do Vocabulário)"]
F --> G["Probabilidades de Saída (Softmax)"]
subgraph "Anatomia do Bloco Transformer"
D1["Estado Oculto de Entrada"] --> D2["RMSNorm"]
D2 --> D3["Grouped Query Attention (GQA)"]
D3 --> D4["Adição Residual"]
D4 --> D5["RMSNorm"]
D5 --> D6["SwiGLU FFN"]
D6 --> D7["Adição Residual"]
D7 --> D8["Saída para a Próxima Camada"]
D1 -.-> D4
D4 -.-> D7
end&lt;/div>
&lt;hr>
&lt;h2 id="3-um-avanço-no-fine-tuning-lora-e-qlora">3. Um Avanço no Fine-Tuning: LoRA e QLoRA
&lt;/h2>&lt;p>Realizar um fine-tuning com todos os parâmetros em um ambiente on-premises, mesmo para um modelo de 1.1B, consome dezenas de GBs de VRAM (memória de vídeo) para manter os estados do otimizador e gradientes. Para treinar eficientemente com recursos limitados, o método &lt;strong>PEFT (Parameter-Efficient Fine-Tuning)&lt;/strong> chamado &amp;ldquo;&lt;strong>LoRA&lt;/strong>&amp;rdquo; e sua extensão quantizada &amp;ldquo;&lt;strong>QLoRA&lt;/strong>&amp;rdquo; são essenciais.&lt;/p>
&lt;h3 id="31-contexto-matemático-do-lora-low-rank-adaptation">3.1 Contexto Matemático do LoRA (Low-Rank Adaptation)
&lt;/h3>&lt;p>LoRA é uma técnica que fixa (congela) as matrizes de pesos pré-treinadas e aproxima a atualização desses pesos ($\Delta W$) como o produto de duas matrizes pequenas de baixo posto.&lt;/p>
&lt;p>Suponha que os pesos pré-treinados sejam $W_0 \in \mathbb{R}^{d \times k}$. No fine-tuning completo, o próprio $W_0$ é atualizado para $W_0 + \Delta W$, mas no LoRA a matriz de atualização $\Delta W$ é decomposta da seguinte forma:&lt;/p>
$$ \Delta W = B \times A $$
&lt;p>Aqui, $B \in \mathbb{R}^{d \times r}$, $A \in \mathbb{R}^{r \times k}$, e $r$ é um hiperparâmetro chamado posto (Rank), que é um valor muito pequeno que satisfaz $r \ll \min(d, k)$ (geralmente 8, 16, 32, etc.).&lt;/p>
&lt;p>O cálculo na passagem para frente (forward pass) é o seguinte:&lt;/p>
$$ h = W_0 x + \Delta W x = W_0 x + B A x $$
&lt;p>No estado inicial, a matriz $A$ é inicializada aleatoriamente com uma distribuição normal (distribuição Gaussiana), e a matriz $B$ é inicializada com uma matriz zero. Isso garante que $\Delta W$ seja zero no início do treinamento, permitindo iniciar o treinamento preservando totalmente a saída do modelo base.&lt;/p>
&lt;div class="mermaid">graph LR
X["Vetor de Entrada x"] --> W0["Peso Pré-treinado Congelado (W_0)"]
X --> A["Matriz LoRA Treinável A (r x k)"]
A --> B["Matriz LoRA Treinável B (d x r)"]
W0 --> Add["Adição de Vetores"]
B --> Add
Add --> Y["Vetor de Saída h"]&lt;/div>
&lt;h3 id="32-a-inovação-do-qlora-quantized-lora">3.2 A Inovação do QLoRA (Quantized LoRA)
&lt;/h3>&lt;p>QLoRA impulsiona ainda mais a abordagem do LoRA, quantizando o modelo base $W_0$ em precisão de 4 bits (NormalFloat 4, NF4) e carregando-o na memória. Isso reduz drasticamente o consumo de VRAM.&lt;/p>
&lt;p>O QLoRA incorpora 3 tecnologias cruciais:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Quantização de 4 bits NormalFloat (NF4):&lt;/strong> Um tipo de dados teoricamente ideal otimizado para pesos que seguem uma distribuição normal.&lt;/li>
&lt;li>&lt;strong>Double Quantization (Quantização Dupla):&lt;/strong> Economiza ainda mais memória quantizando a própria constante de quantização (fator de escala).&lt;/li>
&lt;li>&lt;strong>Paged Optimizers:&lt;/strong> Um mecanismo que utiliza o recurso de memória unificada da NVIDIA para descarregar temporariamente o status do otimizador para a RAM da CPU quando a VRAM se esgota.&lt;/li>
&lt;/ol>
&lt;p>Como resultado, o fine-tuning que normalmente requeriria 16GB a 24GB de VRAM pode ser executado facilmente até mesmo em GPUs de nível consumidor (como RTX 3060 de 12GB e RTX 4070).&lt;/p>
&lt;hr>
&lt;h2 id="4-requisitos-de-hardware-e-configuração-em-um-ambiente-on-premises">4. Requisitos de Hardware e Configuração em um Ambiente On-Premises
&lt;/h2>&lt;p>Os requisitos de hardware para o fine-tuning do TinyLLaMA (1.1B) com QLoRA podem ser mantidos muito baixos.&lt;/p>
&lt;h3 id="especificações-de-hardware-recomendadas">Especificações de Hardware Recomendadas
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>GPU:&lt;/strong> NVIDIA RTX 3060 (12GB), RTX 3090/4090 (24GB), ou NVIDIA A10G/A100, etc. Funcionará com pelo menos 8GB de VRAM, mas recomenda-se 12GB ou mais para aumentar o tamanho do batch.&lt;/li>
&lt;li>&lt;strong>CPU:&lt;/strong> Uma CPU moderna com 8 núcleos ou mais (Intel Core i7/i9, AMD Ryzen 7/9).&lt;/li>
&lt;li>&lt;strong>RAM:&lt;/strong> 32GB ou mais (importante como destino de backup da VRAM ao usar Paged Optimizers).&lt;/li>
&lt;li>&lt;strong>Armazenamento:&lt;/strong> NVMe SSD (para acelerar a leitura de conjuntos de dados e o salvamento de modelos).&lt;/li>
&lt;/ul>
&lt;h3 id="configuração-do-ambiente-de-software">Configuração do Ambiente de Software
&lt;/h3>&lt;p>O procedimento de configuração a seguir assume um ambiente Ubuntu 22.04 LTS. Usa-se o Python 3.10 ou superior.&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"># Criação e ativação do ambiente virtual&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"># Instalação do PyTorch (para 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"># Instalação das bibliotecas relacionadas aos transformers&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-técnicas-de-otimização-para-o-ajuste-fino-mais-rápido">5. Técnicas de Otimização para o Ajuste Fino Mais Rápido
&lt;/h2>&lt;p>Para concluir o fine-tuning de forma &amp;ldquo;mais rápida&amp;rdquo;, além de simplesmente executar o script, é necessário combinar as seguintes técnicas de otimização.&lt;/p>
&lt;h3 id="51-flash-attention-2">5.1 Flash Attention 2
&lt;/h3>&lt;p>O mecanismo padrão de Attention tem uma complexidade computacional de tempo e espaço de $O(N^2)$ para um comprimento de sequência $N$. O Flash Attention 2 otimiza o acesso à memória entre a SRAM da GPU e a HBM (High Bandwidth Memory), eliminando o gargalo de IO sem reduzir a quantidade de cálculos, o que aumenta a velocidade de treinamento em várias vezes e reduz drasticamente o consumo de memória.&lt;/p>
&lt;h3 id="52-gradient-checkpointing-ponto-de-verificação-de-gradiente">5.2 Gradient Checkpointing (Ponto de Verificação de Gradiente)
&lt;/h3>&lt;p>Em vez de salvar todas as ativações intermediárias calculadas na passagem para frente (forward pass) na VRAM, apenas uma parte é salva e recalculada quando necessária na passagem para trás (backward pass). Embora o tempo de computação aumente em cerca de 20%, ele reduz o consumo de memória dramaticamente, permitindo a definição de um tamanho de batch maior e melhorando a taxa de transferência geral.&lt;/p>
&lt;h3 id="53-mixed-precision-training-treinamento-de-precisão-mista-e-bfloat16">5.3 Mixed Precision Training (Treinamento de Precisão Mista) e Bfloat16
&lt;/h3>&lt;p>Para maximizar a utilização dos Tensor Cores da GPU, os cálculos durante o treinamento são realizados em &lt;code>bfloat16&lt;/code> (Brain Floating Point). Comparado ao &lt;code>float16&lt;/code>, o comprimento de bits da parte expoente é o mesmo que no &lt;code>float32&lt;/code>, portanto o risco de overflow e underflow é extremamente baixo, resultando em um treinamento mais estável.&lt;/p>
&lt;hr>
&lt;h2 id="6-prática-código-de-fine-tuning-qlora-do-tinyllama">6. Prática: Código de Fine-Tuning QLoRA do TinyLLaMA
&lt;/h2>&lt;p>Agora, explicaremos o script PyTorch para o fine-tuning mais rápido, incorporando todas as otimizações acima. Aqui, utilizaremos o &lt;code>SFTTrainer&lt;/code> da biblioteca &lt;code>trl&lt;/code> (Transformer Reinforcement Learning) da Hugging Face.&lt;/p>
&lt;h3 id="61-preparação-do-conjunto-de-dados-e-carregamento-do-modelo">6.1 Preparação do Conjunto de Dados e Carregamento do Modelo
&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. Especificar modelo e tokenizador&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. Configuração de quantização de 4 bits para 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"># O cálculo é feito em 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. Carregamento do modelo (Habilitar 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;Carregando o modelo...&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"># A chave para o treinamento mais rápido&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. Carregamento do tokenizador&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"># Definido como right para evitar bugs durante o treinamento 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-aplicação-do-adaptador-lora-e-formatação-do-conjunto-de-dados">6.2 Aplicação do Adaptador LoRA e Formatação do Conjunto de Dados
&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. Preparação para o treinamento k-bit e ativação do gradient checkpointing&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. Configuração do 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"># Posto&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"># Fator de escala&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"># O desempenho melhora se todas as camadas Lineares forem os alvos&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"># Exemplo de saída: 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. Carregamento do conjunto de dados (Aqui, usamos um dataset de instruções em japonês como exemplo)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Na prática, você carregará arquivos JSONL privados em seu ambiente on-premises&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"> Formata a string para corresponder ao formato ChatML ou ao template de prompt
&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-execução-do-treinamento">6.3 Execução do Treinamento
&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. Definição dos argumentos de treinamento&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"># Aumente se houver VRAM suficiente&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"># Tamanho de batch efetivo = 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"># Economia de VRAM através do 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"># Mixed precision training (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 passos para teste. Em produção, especifique pelo número de épocas&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. Iniciar o treinamento com o 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"># Ajuste conforme o comprimento de entrada esperado&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;Iniciando o treinamento...&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. Salvar o adaptador 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;Treinamento completo e modelo salvo.&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-avaliação-de-desempenho-e-solução-de-problemas">7. Avaliação de Desempenho e Solução de Problemas
&lt;/h2>&lt;p>Ao realizar o treinamento em um ambiente on-premises, aqui estão alguns problemas frequentes e suas soluções.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Ocorre OOM (Out Of Memory):&lt;/strong>
&lt;ul>
&lt;li>Reduza &lt;code>per_device_train_batch_size&lt;/code> para &lt;code>1&lt;/code>.&lt;/li>
&lt;li>Aumente &lt;code>gradient_accumulation_steps&lt;/code> para manter o tamanho de batch efetivo.&lt;/li>
&lt;li>Reduza &lt;code>max_seq_length&lt;/code> de &lt;code>2048&lt;/code> para &lt;code>1024&lt;/code> ou &lt;code>512&lt;/code>.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>A perda (Loss) não diminui ou diverge:&lt;/strong>
&lt;ul>
&lt;li>A taxa de aprendizado (&lt;code>learning_rate&lt;/code>) pode estar muito alta. Tente reduzi-la de &lt;code>2e-4&lt;/code> para algo em torno de &lt;code>5e-5&lt;/code>.&lt;/li>
&lt;li>Se você estiver usando Float16 em vez de Bfloat16, pode estar ocorrendo underflow dos gradientes. Verifique se &lt;code>bf16=True&lt;/code> está ativado.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Strings estranhas são geradas durante a inferência:&lt;/strong>
&lt;ul>
&lt;li>Verifique se &lt;code>padding_side=&amp;quot;right&amp;quot;&lt;/code> está configurado corretamente. Além disso, você precisa verificar se o formato do conjunto de dados (tokens especiais como &lt;code>&amp;lt;|im_start|&amp;gt;&lt;/code>) é consistente com os usados durante o pré-treinamento do modelo base.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="8-implantação-do-modelo-após-o-ajuste-fino-deployment">8. Implantação do Modelo Após o Ajuste Fino (Deployment)
&lt;/h2>&lt;p>Quando o fine-tuning estiver completo, o que será salvo não é o &amp;ldquo;modelo base inteiro&amp;rdquo;, mas apenas um &amp;ldquo;&lt;strong>Adaptador LoRA (pesos de diferença)&lt;/strong>&amp;rdquo; de alguns MB a algumas dezenas de MB. Para realizar a inferência em alta velocidade, você precisa fundir (integrar) esses pesos LoRA de volta ao modelo base original e exportá-lo como um modelo único.&lt;/p>
&lt;h3 id="script-de-fusão-do-modelo">Script de Fusão do Modelo
&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"># Carrega o modelo e o adaptador em 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"># Funde os pesos e salva&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="configuração-de-um-servidor-de-inferência-ultrarrápido-com-vllm">Configuração de um Servidor de Inferência Ultrarrápido com vLLM
&lt;/h3>&lt;p>Na implantação em um ambiente on-premises, para maximizar a velocidade de inferência (Tokens por segundo), é fortemente recomendado o uso do &lt;strong>vLLM&lt;/strong> ou &lt;strong>TGI (Text Generation Inference)&lt;/strong>, em vez do &lt;code>pipeline&lt;/code> padrão da Hugging Face. O vLLM usa a tecnologia PagedAttention para evitar a fragmentação da memória da GPU, melhorando drasticamente a capacidade de lidar com requisições concorrentes.&lt;/p>
&lt;p>O diagrama Mermaid a seguir mostra o pipeline desde o treinamento até a implantação do servidor de inferência.&lt;/p>
&lt;div class="mermaid">graph TD
A["Dados Privados Brutos"] --> B["Pré-processamento e Formatação (JSONL)"]
B --> C["Fine-Tuning QLoRA (SFTTrainer)"]
C --> D["Pesos do Adaptador LoRA (.safetensors)"]
D --> E["Fuselagem com o Base TinyLLaMA 1.1B"]
E --> F["Modelo Fundido"]
F --> G["Implantação via Servidor vLLM"]
G --> H["Endpoint da API / UI (ex: Chatbot)"]&lt;/div>
&lt;p>Iniciar um servidor de API usando o vLLM pode ser concluído com o seguinte comando.&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>Com isso, um endpoint compatível com a API da OpenAI será configurado em seu ambiente on-premises, permitindo que você utilize a IA local de forma segura e rápida.&lt;/p>
&lt;hr>
&lt;h2 id="9-conclusão">9. Conclusão
&lt;/h2>&lt;p>Neste artigo, explicamos um método para realizar o fine-tuning do &amp;ldquo;TinyLLaMA&amp;rdquo; — um modelo de alto desempenho, apesar de ser leve com apenas 1.1B parâmetros — em um ambiente on-premises de maneira rápida e com baixo uso de memória.&lt;/p>
&lt;ul>
&lt;li>O &lt;strong>LoRA / QLoRA&lt;/strong> possibilita o fine-tuning completo de LLMs, mesmo em GPUs de nível consumidor.&lt;/li>
&lt;li>O uso intenso de &lt;strong>Flash Attention 2&lt;/strong> e &lt;strong>Gradient Checkpointing&lt;/strong> otimiza o tempo de treinamento e o consumo de VRAM ao máximo.&lt;/li>
&lt;li>O deployment utilizando o &lt;strong>vLLM&lt;/strong> atinge uma alta taxa de transferência, mesmo em ambientes de produção.&lt;/li>
&lt;/ul>
&lt;p>A operação de um LLM local on-premises não apenas protege a confidencialidade dos dados, mas também se torna uma arma poderosa para construir IAs especializadas em domínios específicos (como jurídico, médico, regulamentos internos, etc.) a um baixo custo. Utilize este guia como referência para cultivar o TinyLLaMA exclusivo da sua própria empresa.&lt;/p></description></item><item><title>Como os Programadores Devem Sobreviver na Era da IA?</title><link>http://kenji.blog/pt/p/how-programmers-survive-in-ai-era/</link><pubDate>Fri, 11 Sep 2026 15:00:00 +0900</pubDate><guid>http://kenji.blog/pt/p/how-programmers-survive-in-ai-era/</guid><description>&lt;img src="http://kenji.blog/p/how-programmers-survive-in-ai-era/img/eyecatch.jpg" alt="Featured image of post Como os Programadores Devem Sobreviver na Era da IA?" />&lt;h1 id="como-os-programadores-devem-sobreviver-na-era-da-ia-o-fim-da-codificação-e-o-alvorecer-da-nova-engenharia">Como os Programadores Devem Sobreviver na Era da IA? O Fim da Codificação e o Alvorecer da Nova Engenharia
&lt;/h1>&lt;p>Em 2026, o cenário de desenvolvimento de software está passando por um período de convulsão sem precedentes. Até poucos anos atrás, o conceito de &amp;ldquo;IA que escreve código&amp;rdquo; não passava de uma &amp;ldquo;ferramenta auxiliar&amp;rdquo; para programadores, limitada a coisas como a geração de código boilerplate (código padronizado) e autocompletar funções. No entanto, com a evolução surpreendente dos Grandes Modelos de Linguagem (LLMs), a situação virou de cabeça para baixo. A IA moderna não é mais apenas uma &amp;ldquo;máquina de escrever inteligente&amp;rdquo;. Se você lhe der um documento de requisitos, ela se transforma em um &amp;ldquo;engenheiro júnior autônomo&amp;rdquo;, capaz de construir de forma instantânea e autônoma todo o sistema, desde a lógica de front-end até o back-end, design de esquema de banco de dados e até a construção de pipelines CI/CD.&lt;/p>
&lt;p>Nesta era, como nós, &amp;ldquo;programadores&amp;rdquo; e &amp;ldquo;engenheiros de software&amp;rdquo;, devemos sobreviver? À medida que o valor econômico do próprio ato de &amp;ldquo;escrever código&amp;rdquo; se deflaciona rapidamente, os &amp;ldquo;codificadores&amp;rdquo; que apenas conhecem a sintaxe (gramática) de uma linguagem de programação específica e estão familiarizados com a API de um framework específico estão rapidamente sendo eliminados do mercado.&lt;/p>
&lt;p>Neste artigo, examinaremos as estratégias de sobrevivência dos programadores na era da IA ​​com grandes detalhes, a partir de perspectivas técnicas, matemáticas e filosóficas. Esta não é apenas uma teoria de carreira, mas uma redefinição da própria disciplina da engenharia de software.&lt;/p>
&lt;hr>
&lt;h2 id="1-a-história-da-abstração-abstraction-e-a-redefinição-da-programação">1. A História da Abstração (Abstraction) e a Redefinição da &amp;ldquo;Programação&amp;rdquo;
&lt;/h2>&lt;p>Olhando para a história da engenharia de software, podemos ver que sempre foi uma história de &amp;ldquo;Abstração (Abstraction)&amp;rdquo;. Sempre construímos camadas para descrever sistemas cada vez mais complexos em uma linguagem mais próxima dos humanos.&lt;/p>
&lt;p>Os primeiros cientistas da computação usavam cartões perfurados para operar diretamente interruptores de hardware físico e davam instruções aos computadores em linguagem de máquina (sequências de 0 e 1). Mais tarde, surgiu a linguagem Assembly, permitindo que os humanos operassem o hardware com mnemônicos fáceis de entender. Conforme o tempo avançou, linguagens de alto nível, como C e Fortran, surgiram e conseguiram encapsular detalhes complexos de hardware, como gerenciamento de memória e registradores de CPU. Com as linguagens modernas subsequentes, como Java, Python, Ruby e TypeScript, os programadores puderam se concentrar mais no &amp;ldquo;o que queremos que o computador faça (What)&amp;rdquo; em vez de &amp;ldquo;como fazer o computador funcionar (How)&amp;rdquo;.&lt;/p>
&lt;p>O advento da IA ​​(LLM) é a maior e mais recente mudança de paradigma nesta história de abstração. Se a evolução das linguagens de programação foi a &amp;ldquo;ocultação do hardware&amp;rdquo;, a evolução dos LLMs é a &amp;ldquo;ocultação da sintaxe (gramática)&amp;rdquo;.&lt;/p>
&lt;div class="mermaid">graph TD
A["Código de Máquina / Cartões Perfurados (Anos 1940)"] --> B["Linguagem Assembly (Anos 1950)"]
B --> C["Linguagens de Alto Nível Compiladas (Anos 1970)"]
C --> D["Linguagens Gerenciadas / de Script (Anos 1990)"]
D --> E["Linguagem Natural via LLMs (Anos 2020)"]
E --> F["Agentes de IA Autônomos (2026-)"]
style E fill:#f9f,stroke:#333,stroke-width:2px
style F fill:#fbb,stroke:#333,stroke-width:2px&lt;/div>
&lt;p>Acabou a era em que os desenvolvedores se preocupavam com vazamentos de memória manipulando ponteiros ou escrevendo centenas de linhas de código padronizado para análise de JSON. Usar a linguagem natural (português ou inglês), a linguagem de maior nível de abstração para a humanidade, para definir sistemas se tornou o padrão para &amp;ldquo;programação&amp;rdquo; em 2026.&lt;/p>
&lt;hr>
&lt;h2 id="2-o-modelo-matemático-da-produtividade-surfando-na-onda-do-crescimento-exponencial">2. O Modelo Matemático da Produtividade: Surfando na Onda do Crescimento Exponencial
&lt;/h2>&lt;p>Vamos avaliar quantitativamente as melhorias de produtividade trazidas pela IA usando um modelo matemático.
A produtividade individual $P_{traditional}$ no desenvolvimento de software tradicional poderia ser modelada como uma combinação linear do nível de habilidade individual $S$, experiência de domínio $E$ e eficiência da ferramenta $T$.&lt;/p>
$$ P_{traditional} = c_1 \cdot S + c_2 \cdot E + c_3 \cdot T $$
&lt;p>No entanto, no desenvolvimento moderno usando IA, a capacidade da IA $A(t)$ atua como uma &amp;ldquo;poderosa alavancagem (Multiplier)&amp;rdquo; que amplifica as capacidades humanas. Como as capacidades da IA ​​crescem exponencialmente ao longo do tempo $t$ (versão IA da Lei de Moore), a produtividade $P_{AI}(t)$ na era da IA ​​pode ser expressa pela seguinte equação.&lt;/p>
$$ P_{AI}(t) = \alpha \cdot S_{core} \cdot e^{\beta \cdot A(t)} $$
&lt;p>Aqui, cada variável significa o seguinte:&lt;/p>
&lt;ul>
&lt;li>$\alpha$: Coeficiente base de produtividade humana&lt;/li>
&lt;li>$S_{core}$: &amp;ldquo;Habilidades humanas essenciais&amp;rdquo; que não são substituídas pela IA (design de arquitetura, compreensão de requisitos de negócios, julgamento ético, etc.)&lt;/li>
&lt;li>$A(t)$: Capacidade absoluta do modelo de IA no tempo $t$ (contagem de parâmetros, janela de contexto, capacidade de raciocínio)&lt;/li>
&lt;li>$\beta$: Coeficiente indicando a eficácia com que você pode extrair as ferramentas de IA (qualidade da engenharia de prompt e sofisticação do fluxo de trabalho colaborativo com a IA)&lt;/li>
&lt;/ul>
&lt;p>Um insight importante derivado desta fórmula é que, &lt;strong>em um mundo onde $A(t)$ cresce exponencialmente, habilidades tradicionais como mera velocidade de digitação e memorização de uma linguagem específica têm muito pouco impacto na produtividade geral.&lt;/strong> Em vez disso, o coeficiente $\beta$ para surfar no crescimento exponencial da IA e a área não coberta pela IA, $S_{core}$, tornam-se os fatores dominantes que determinam o valor de mercado do engenheiro.&lt;/p>
&lt;hr>
&lt;h2 id="3-a-probabilidade-de-automação-de-tarefas-probability-of-automation">3. A Probabilidade de Automação de Tarefas (Probability of Automation)
&lt;/h2>&lt;p>Então, quais tarefas serão automatizadas e quais ficarão nas mãos de humanos?
A probabilidade $P_{auto}(T)$ de que uma determinada tarefa $T$ seja totalmente automatizada pela IA pode ser formulada da seguinte forma.&lt;/p>
$$ P_{auto}(T) = 1 - \exp\left(-\lambda \cdot \frac{\text{Predictability}(T)}{\text{Complexity}(T) \times \text{Context Dependency}(T)}\right) $$
&lt;ul>
&lt;li>$\text{Predictability}(T)$: Previsibilidade da tarefa (quanta padronização existe nos dados anteriores)&lt;/li>
&lt;li>$\text{Complexity}(T)$: Complexidade da tarefa&lt;/li>
&lt;li>$\text{Context Dependency}(T)$: Força do &amp;ldquo;contexto implícito (conhecimento específico de domínio e relacionamentos humanos)&amp;rdquo; do qual a tarefa depende&lt;/li>
&lt;li>$\lambda$: Taxa de avanço tecnológico da IA&lt;/li>
&lt;/ul>
&lt;p>Tarefas com alta previsibilidade e baixa dependência de contexto, como escrever o processamento de roteamento para uma API ou criar uma tela CRUD simples, se tornam $P_{auto} \approx 1$ e serão quase completamente automatizadas. Por outro lado, tarefas altamente dependentes do contexto, como &amp;ldquo;como integrar sistemas legados existentes com novos microsserviços de forma segura&amp;rdquo; ou &amp;ldquo;como projetar um fluxo de autenticação que satisfaça os requisitos do departamento jurídico sem comprometer a experiência do usuário&amp;rdquo;, são difíceis de automatizar.&lt;/p>
&lt;hr>
&lt;h2 id="4-retorno-da-sintaxe-gramática-à-arquitetura-estrutura">4. Retorno da Sintaxe (Gramática) à Arquitetura (Estrutura)
&lt;/h2>&lt;p>Separar claramente o que a IA faz bem e o que os humanos fazem bem é um requisito absoluto para a sobrevivência.&lt;/p>
&lt;div class="mermaid">graph LR
Sub1["Domínios de Excelência da IA"]
Sub2["Domínios de Excelência dos Humanos"]
A["Geração de Código a partir de Especificações"] --> Sub1
B["Sintaxe, Erros e Correção de Bugs"] --> Sub1
C["Boilerplate / Geração de Testes"] --> Sub1
D["Análise de Logs e Correspondência de Padrões"] --> Sub1
E["Design de Arquitetura de Sistemas"] --> Sub2
F["Resolução de Requisitos Ambíguos"] --> Sub2
G["Negociação entre Equipes"] --> Sub2
H["Julgamento Ético / Responsabilidade"] --> Sub2&lt;/div>
&lt;p>A IA supera os humanos na &amp;ldquo;otimização local&amp;rdquo;. Nenhuma mente humana pode superar a velocidade e a precisão da IA para escrever uma função, classe ou módulo único. No entanto, a IA é extremamente vulnerável a &amp;ldquo;otimizações globais&amp;rdquo; ou &amp;ldquo;contexto ausente (Missing Context)&amp;rdquo;.&lt;/p>
&lt;p>Os programadores do futuro devem mudar seus papéis de &amp;ldquo;trabalhadores que escrevem código&amp;rdquo; para &amp;ldquo;arquitetos que orquestram os inúmeros componentes gerados pela IA&amp;rdquo;. Observar o sistema como um todo, onde traçar limites de microsserviços, como resolver o trade-off entre disponibilidade e consistência no teorema CAP de acordo com o contexto de negócios, e como controlar a dívida técnica. Estas são tarefas intelectuais avançadas que apenas humanos, que compreendem o quadro geral e os objetivos de negócios, podem realizar.&lt;/p>
&lt;hr>
&lt;h2 id="5-a-engenharia-de-requisitos-é-a-verdadeira-engenharia-de-prompt">5. A Engenharia de Requisitos é a &amp;ldquo;Verdadeira Engenharia de Prompt&amp;rdquo;
&lt;/h2>&lt;p>O termo &amp;ldquo;Engenharia de Prompt&amp;rdquo;, que ouvimos com frequência recentemente, é muitas vezes mal compreendido como &amp;ldquo;um truque para enganar a IA e obter a saída desejada&amp;rdquo;. No entanto, a essência da engenharia de prompt no desenvolvimento de software é inegavelmente a &lt;strong>&amp;ldquo;Engenharia de Requisitos Avançada (Requirements Engineering)&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;p>Para instruir a IA em linguagem natural e fazê-la produzir o software pretendido, os seguintes elementos devem ser verbalizados de forma rigorosa:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Propósito (Why)&lt;/strong>: Por que essa funcionalidade é necessária? Qual é o valor para o negócio?&lt;/li>
&lt;li>&lt;strong>Restrições (Constraints)&lt;/strong>: Requisitos de desempenho (latência, rendimento), requisitos de segurança, restrições de custo.&lt;/li>
&lt;li>&lt;strong>Casos Extremos (Edge Cases)&lt;/strong>: Processamento de fallback (alternativo) quando o usuário faz uma entrada inesperada.&lt;/li>
&lt;li>&lt;strong>Interfaces (Interfaces)&lt;/strong>: Especificações de integração com sistemas existentes.&lt;/li>
&lt;/ol>
&lt;p>A partir de instruções (prompts) ambíguas, apenas sistemas ambíguos e frágeis nascerão. A capacidade de entrevistar profundamente sobre &amp;ldquo;o que o cliente realmente queria&amp;rdquo;, organizar requisitos contraditórios e criar especificações (prompts) lógicas e perfeitas. Essa é a habilidade de codificação mais forte na era da IA. Os programadores passarão menos tempo no editor de código e mais tempo de frente para o Notion ou arquivos Markdown, descrevendo detalhadamente como o sistema deve ser em texto.&lt;/p>
&lt;hr>
&lt;h2 id="6-a-vantagem-esmagadora-do-conhecimento-de-domínio-domain-knowledge">6. A Vantagem Esmagadora do Conhecimento de Domínio (Domain Knowledge)
&lt;/h2>&lt;p>Como a IA aprende a partir do código de código aberto e da documentação pública do mundo todo, ela é bem versada em tecnologias e algoritmos web gerais. No entanto, há dados que a IA não pode acessar. Estas são as &amp;ldquo;regras de negócios específicas da sua empresa&amp;rdquo; e o &amp;ldquo;conhecimento de domínio profundamente enraizado em um setor específico (médico, financeiro, manufatura, etc.)&amp;rdquo;.&lt;/p>
&lt;p>Por exemplo, suponha que você esteja desenvolvendo um sistema de prontuário eletrônico em uma startup médica. A IA sabe &amp;ldquo;como construir uma interface de tabela com React&amp;rdquo; e a &amp;ldquo;estrutura de dados geral do HL7 FHIR&amp;rdquo;. No entanto, ela não aprendeu o conhecimento tácito, como &amp;ldquo;em qual departamento específico do Hospital A, em que ordem os médicos visualizam os dados dos pacientes e que tipo de interface de usuário pode minimizar o risco de erros médicos&amp;rdquo;.&lt;/p>
&lt;p>Em um mundo onde a tecnologia em si está se tornando uma commodity (produto padronizado), o verdadeiro valor de um engenheiro está na interseção entre a &amp;ldquo;tecnologia&amp;rdquo; e o &amp;ldquo;domínio de negócios&amp;rdquo;. Não se trata mais apenas de competir com a proficiência técnica, mas o talento que tem conhecimento especializado profundo em um domínio específico, como médico, financeiro, logística ou entretenimento, e que pode resolver problemas naquele domínio usando a poderosa ferramenta da IA, irá liderar o mercado no futuro.&lt;/p>
&lt;hr>
&lt;h2 id="7-o-problema-do-bonde-no-desenvolvimento-de-software-quem-assume-a-responsabilidade">7. O &amp;ldquo;Problema do Bonde&amp;rdquo; no Desenvolvimento de Software: Quem assume a responsabilidade?
&lt;/h2>&lt;p>À medida que nossa dependência da IA aumenta, enfrentamos problemas filosóficos e éticos sérios. É a questão da &amp;ldquo;onde reside a responsabilidade&amp;rdquo; na engenharia de software.&lt;/p>
&lt;p>Se o código gerado autonomamente pela IA causar um bug grave no ambiente de produção, resultando em centenas de milhões de dólares de perdas para a empresa, ou causar o mau funcionamento de um sistema médico com risco de morte, quem assumirá a responsabilidade por isso? A empresa que desenvolveu o modelo de IA? Ou o engenheiro que inseriu o prompt? Não se pode &amp;ldquo;demitir&amp;rdquo; ou &amp;ldquo;prender&amp;rdquo; a IA.&lt;/p>
&lt;p>Não importa o quanto a tecnologia avance, o papel do &amp;ldquo;humano&amp;rdquo; como sujeito de assumir a &amp;ldquo;Responsabilidade (Accountability)&amp;rdquo; legal e ética pelo impacto que o sistema tem na sociedade não desaparecerá. Em vez disso, quanto mais o processo de geração de código se tornar uma caixa-preta, maior será a responsabilidade dos humanos como &amp;ldquo;Aprovadores finais (Approver)&amp;rdquo; e &amp;ldquo;Supervisores (Supervisor)&amp;rdquo; do sistema.&lt;/p>
&lt;p>Auditar se a arquitetura e o código propostos pela IA atendem aos padrões de segurança, se não têm problemas éticos (se há viés incluído) e se cumprem com o compliance, e dar a aprovação final. Esse ato de &amp;ldquo;assumir a responsabilidade&amp;rdquo; em si se tornará uma parte importante do trabalho de um engenheiro.&lt;/p>
&lt;hr>
&lt;h2 id="8-o-pareamento-de-programação-com-a-ia-e-o-gerenciamento-da-carga-cognitiva-cognitive-load">8. O Pareamento de Programação com a IA e o Gerenciamento da Carga Cognitiva (Cognitive Load)
&lt;/h2>&lt;p>Ao trabalhar com a IA, a natureza da nossa &amp;ldquo;Carga Cognitiva (Cognitive Load)&amp;rdquo; também muda. A carga cognitiva ao escrever código do zero e a carga cognitiva ao ler e analisar centenas de linhas de código desconhecido gerado pela IA são completamente diferentes.&lt;/p>
&lt;p>De acordo com a teoria da carga cognitiva em psicologia, ao processar informações complexas que não correspondem aos esquemas existentes (a estrutura de conhecimento no cérebro), a memória de trabalho humana se esgota rapidamente. O código gerado pela IA às vezes contém otimizações sofisticadas que os humanos não conseguiriam imaginar, e às vezes contém &amp;ldquo;Alucinações (Hallucinations)&amp;rdquo; que ignoram o contexto.&lt;/p>
&lt;p>Para evitar isso, é necessário sistematizar o processo de revisão para a IA.&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant H as "Engenheiro Humano (Arquiteto)"
participant A as "Agente de IA"
participant S as "CI/CD &amp; Testes"
H->>A: "Definir requisitos rígidos e restrições"
A->>H: "Propor Arquitetura e Código Inicial"
Note over H,A: Fase de Revisão: Alta Carga Cognitiva
H->>A: "Criticar escolhas de design, pedir refatoração"
A->>S: "Gerar Código Final e Fazer Push"
S-->>H: "Resultados de Testes Automatizados e Análise Estática"
H->>H: "Aprovação Final e Assunção de Responsabilidade"&lt;/div>
&lt;p>Os humanos precisam maximizar não as habilidades de &amp;ldquo;escrever&amp;rdquo;, mas as habilidades de &amp;ldquo;ler rapidamente e detectar falhas lógicas instantaneamente (Code Reading &amp;amp; Auditing)&amp;rdquo;. A importância do Desenvolvimento Orientado a Testes (TDD) está aumentando ainda mais na era da IA. A abordagem padrão se tornará aquela em que um humano ou outra IA escreve um código de teste rigoroso antes de deixar a IA escrever o código, e fazer a IA modificar o código até que passe nesses testes.&lt;/p>
&lt;hr>
&lt;h2 id="9-estratégia-específica-de-sobrevivência-o-que-você-deve-aprender-a-partir-de-amanhã">9. Estratégia Específica de Sobrevivência: O que você deve aprender a partir de amanhã
&lt;/h2>&lt;p>Com base na análise até este ponto, propomos um plano de ação concreto para que os programadores sobrevivam à era da IA.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Reaprender minuciosamente as &amp;ldquo;bases&amp;rdquo; da tecnologia&lt;/strong>: Você pode deixar o uso de frameworks para a IA. No entanto, é absolutamente necessário um entendimento profundo de como os sistemas operacionais funcionam, protocolos de rede (TCP/IP, HTTP/3), estrutura interna de bancos de dados (B-Tree, níveis de isolamento de transações), estruturas de dados e algoritmos. Uma base sólida de ciência da computação é essencial para julgar se o resultado da IA ​​está correto.&lt;/li>
&lt;li>&lt;strong>Dominar a Arquitetura de Nuvem e os Sistemas Distribuídos&lt;/strong>: Em vez de códigos individuais, concentre-se em como combinar recursos de nuvem como AWS, GCP e Azure para criar sistemas escaláveis. Entenda conceitos de IaC (Infrastructure as Code) como Terraform, e cultive a capacidade de projetar todo o sistema como código.&lt;/li>
&lt;li>&lt;strong>Tornar-se um especialista em domínio de negócios&lt;/strong>: Estude profundamente o modelo de negócios da indústria à qual pertence, os regulamentos legais e a psicologia comportamental do usuário. Vá além dos limites de um engenheiro e tenha uma perspectiva mais próxima de um Gerente de Produto (PM).&lt;/li>
&lt;li>&lt;strong>Polir as habilidades de comunicação e facilitação&lt;/strong>: O processo de resolução da &amp;ldquo;ambiguidade&amp;rdquo; que existe entre humanos para alcançar um consenso não pode ser substituído pela IA. As soft skills (habilidades interpessoais) para se comunicar com as partes interessadas (stakeholders) e descobrir desafios genuínos se tornarão a habilidade de maior valor.&lt;/li>
&lt;li>&lt;strong>Utilizar a IA ao máximo como um &amp;ldquo;colega&amp;rdquo;&lt;/strong>: Não tenha medo da evolução das ferramentas de IA, use-as como sua arma mais poderosa. Use LLMs modernos ou agentes de codificação baseados em IA diariamente e acumule &amp;ldquo;conhecimento tácito&amp;rdquo; de onde a IA falha e como criar prompts para obter o melhor desempenho.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="conclusão-não-tenha-medo-surfe-na-onda">Conclusão: Não tenha medo, surfe na onda
&lt;/h2>&lt;p>A automação da programação pela IA não significa a &amp;ldquo;morte&amp;rdquo; da profissão de programador. Em vez disso, é uma &lt;strong>&amp;ldquo;Renascença (Renascimento Cultural)&amp;rdquo;&lt;/strong> que nos liberta do &amp;ldquo;trabalho não essencial&amp;rdquo; no desenvolvimento de software, como corrigir erros de digitação, solucionar problemas de construção de ambiente ou escrever códigos boilerplate entediantes.&lt;/p>
&lt;p>Historicamente, seja com o advento do tear mecânico ou do software de planilha (Excel), espalhou-se o pessimismo de que os empregos desapareceriam. Mas a realidade é que o aumento drástico na produtividade criou uma nova demanda e levou a trabalhos de nível superior. A mesma coisa vai acontecer no mundo do software. À medida que se torna possível &amp;ldquo;construir sistemas de forma barata&amp;rdquo;, o software permeará em todas as áreas que não foram transformadas em TI devido à falta de custo-benefício até o momento, e os problemas que os engenheiros devem resolver (What) se expandirão infinitamente.&lt;/p>
&lt;p>A nós programadores, está sendo dada a chance de evoluirmos de artesãos que escrevem código para &amp;ldquo;maestros de uma orquestra&amp;rdquo; que comandam a poderosa inteligência que é a IA. Em vez de ficar morrendo de medo das ondas de tecnologia e permanecer na costa, vamos surfar nessa onda cedo e zarpar para uma jornada de criar sistemas ainda maiores e mais valiosos. A era da IA é a era em que a &amp;ldquo;Engenharia&amp;rdquo; em seu verdadeiro sentido irá começar.&lt;/p></description></item><item><title>Guia de Desenvolvimento de Modelos de IA em Pequena Escala (como TinyLLaMA) com C++</title><link>http://kenji.blog/pt/p/cpp-small-ai-model-tinyllama-dev-guide/</link><pubDate>Fri, 11 Sep 2026 14:00:00 +0900</pubDate><guid>http://kenji.blog/pt/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 Guia de Desenvolvimento de Modelos de IA em Pequena Escala (como TinyLLaMA) com C++" />&lt;h1 id="guia-de-desenvolvimento-de-modelos-de-ia-em-pequena-escala-como-tinyllama-com-c">Guia de Desenvolvimento de Modelos de IA em Pequena Escala (como TinyLLaMA) com C++
&lt;/h1>&lt;p>Recentemente, o interesse na execução de Grandes Modelos de Linguagem (LLMs) em ambientes locais aumentou rapidamente. Em particular, modelos de pequena escala como o TinyLLaMA (1.1B parâmetros) são capazes de inferir a uma velocidade prática mesmo em dispositivos de borda com recursos limitados ou notebooks comuns (incluindo ambientes Windows). Enquanto o desenvolvimento usando Python e PyTorch é a tendência principal, a combinação de C++ e &amp;ldquo;ggml&amp;rdquo;, uma biblioteca de tensores baseada em C, tornou-se o padrão de fato quando se busca o máximo de desempenho e eficiência de memória.&lt;/p>
&lt;p>Neste artigo, explicaremos o procedimento de desenvolvimento muito detalhado para construir um motor de inferência do zero (ou compreender profundamente a estrutura interna do &lt;code>llama.cpp&lt;/code> existente) para carregar o TinyLLaMA e gerar texto usando C++.&lt;/p>
&lt;hr>
&lt;h2 id="1-por-que-c-e-ggml">1. Por que C++ e ggml?
&lt;/h2>&lt;p>Na fase de treinamento da IA, o Python, com sua flexibilidade e ecossistema rico, é esmagadoramente vantajoso. No entanto, nas fases de implantação e &amp;ldquo;Inferência&amp;rdquo;, o C++ torna-se uma escolha poderosa pelas seguintes razões:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Redução de Sobrecarga&lt;/strong>: Pode eliminar completamente a sobrecarga do Global Interpreter Lock (GIL) e do tempo de execução do Python.&lt;/li>
&lt;li>&lt;strong>Eficiência de Memória e Alocação de Arena&lt;/strong>: Como a alocação e liberação de memória podem ser controladas manualmente, é possível evitar picos imprevisíveis causados pela coleta de lixo.&lt;/li>
&lt;li>&lt;strong>Acesso Direto ao Hardware&lt;/strong>: É possível chamar diretamente funções intrínsecas (Intrinsics) SIMD, como AVX-512, AVX2 e ARM NEON, para maximizar o poder de computação da CPU.&lt;/li>
&lt;li>&lt;strong>Eliminação de Dependências&lt;/strong>: O ggml é uma biblioteca C/C++ sem dependências (Zero dependencies) que pode ser facilmente compilada em um ambiente MSVC no Windows, desde que haja um compilador.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="2-visão-geral-da-arquitetura">2. Visão Geral da Arquitetura
&lt;/h2>&lt;p>O fluxo completo do pipeline de inferência é mostrado no diagrama Mermaid abaixo. É um processo em série que começa com o texto de entrada do usuário até a geração final do próximo token.&lt;/p>
&lt;div class="mermaid">graph TD
A["Texto de Entrada do Usuário"] --> B["Tokenizador BPE"]
B --> C["Matriz de IDs de Tokens"]
C --> D["Busca na Camada de Embedding"]
D --> E["Blocos Transformer"]
E --> F["RMSNorm"]
F --> G["Camada Head LM"]
G --> H["Matriz de Logits"]
H --> I["Módulo Amostrador"]
I --> J["Próximo ID de Token"]
J --> K["Detokenizador"]
K --> L["Pedaço de Texto de Saída"]
J -.-> |"Adicionar ao Contexto"| C&lt;/div>
&lt;p>Por ser um modelo autorregressivo, o token de saída é adicionado novamente ao contexto e circula como entrada para a previsão do próximo token (parte tracejada do diagrama).&lt;/p>
&lt;hr>
&lt;h2 id="3-formato-do-modelo-e-mapeamento-de-memória-mmap">3. Formato do Modelo e Mapeamento de Memória (mmap)
&lt;/h2>&lt;p>O maior obstáculo ao lidar com os pesos de uma rede neural gigantesca é a E/S de disco e o consumo de memória. Na implementação em C++, isso é resolvido com o &lt;strong>mapeamento de memória (mmap)&lt;/strong>.&lt;/p>
&lt;h3 id="31-mecanismo-de-mapeamento-de-memória-e-implementação-no-windows">3.1 Mecanismo de Mapeamento de Memória e Implementação no Windows
&lt;/h3>&lt;p>O mmap permite mapear o conteúdo de um arquivo diretamente para o espaço de memória virtual do processo.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Zero-copy&lt;/strong>: Os dados são lidos diretamente do disco para o cache de página do kernel, sem causar cópias extras para o espaço do usuário.&lt;/li>
&lt;li>&lt;strong>Carregamento sob Demanda (Page Fault)&lt;/strong>: No exato momento em que a CPU acessa aquele endereço de memória, ocorre uma falha de página, e apenas o pedaço necessário (geralmente 4KB) é carregado na memória física.&lt;/li>
&lt;/ul>
&lt;p>No ambiente Windows, usa-se as APIs Win32 &lt;code>CreateFileMapping&lt;/code> e &lt;code>MapViewOfFile&lt;/code> em vez do &lt;code>mmap&lt;/code> POSIX.&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant OS["Sistema Operacional Windows"]
participant RAM["Memória Física"]
participant App["Aplicativo C++"]
App->>OS: "CreateFileMapping / MapViewOfFile"
OS-->>App: "Ponteiro de Endereço de Memória Virtual"
App->>App: "Ler Dados do Tensor no Ponteiro"
OS->>RAM: "Page Fault / Carregar página do Disco"
RAM-->>App: "Dados prontos para Computação SIMD"&lt;/div>
&lt;h3 id="32-estrutura-binária-do-formato-gguf">3.2 Estrutura Binária do Formato GGUF
&lt;/h3>&lt;p>O &lt;strong>GGUF (GPT-Generated Unified Format)&lt;/strong>, convertido a partir de formatos como &lt;code>.safetensors&lt;/code> do Hugging Face, é o formato definitivo para inferência. Ele possui o seguinte layout binário estrito:&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>: Número da versão do formato.&lt;/li>
&lt;li>&lt;strong>Tensor Count &amp;amp; Metadata Count&lt;/strong>: Número de tensores e número de pares chave-valor de metadados.&lt;/li>
&lt;li>&lt;strong>Metadata (Key-Value Pairs)&lt;/strong>: Chaves com prefixo de comprimento de string e valores tipados.&lt;/li>
&lt;li>&lt;strong>Tensor Info&lt;/strong>: Nome de cada tensor, número de dimensões, tipo de dados (FP16, Q4_K, etc.) e a posição de deslocamento no arquivo.&lt;/li>
&lt;li>&lt;strong>Padding&lt;/strong>: Preenchimento inserido para garantir que os dados do tensor sejam alinhados a um limite específico (geralmente 32 bytes ou 64 bytes). Isso é essencial para acessos rápidos à memória em instruções SIMD (especialmente AVX).&lt;/li>
&lt;li>&lt;strong>Tensor Data&lt;/strong>: A matriz real de dados de peso alinhados.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="4-base-matemática-do-tinyllama-e-algoritmo-c">4. Base Matemática do TinyLLaMA e Algoritmo C++
&lt;/h2>&lt;p>O TinyLLaMA incorpora várias inovações arquitetônicas avançadas para maior eficiência. Explicaremos as representações matemáticas para implementá-las corretamente em C++.&lt;/p>
&lt;h3 id="41-rmsnorm-root-mean-square-normalization">4.1 RMSNorm (Root Mean Square Normalization)
&lt;/h3>&lt;p>Reduz o custo de cálculo ao omitir a centralização da média do LayerNorm e realizar apenas o dimensionamento da variância.&lt;/p>
$$ \text{RMSNorm}(x) = \frac{x}{\sqrt{\frac{1}{d}\sum_{i=1}^{d} x_i^2 + \epsilon}} \odot \gamma $$
&lt;p>$d$ é o número de dimensões, e $\gamma$ é o tensor de escalonamento aprendido.
Ao implementar em C++, ele é otimizado primeiramente calculando rapidamente a soma dos quadrados da matriz com &lt;code>_mm256_fmadd_ps&lt;/code> do AVX2, e multiplicando pela raiz quadrada inversa (como a instrução &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>Esta é uma técnica para aplicar informações de posição de tokens como uma rotação no espaço tensorial. Pode ser vista como uma rotação no plano complexo, e aplica a seguinte rotação a pares de dimensões adjacentes $(x_1, x_2)$ do vetor $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>Aqui, $m$ é o índice absoluto de posição do token e $\theta$ é a frequência fundamental pré-calculada. No ggml, a execução paralela é feita simplesmente adicionando o operador &lt;code>ggml_rope&lt;/code> durante a construção do grafo de inferência.&lt;/p>
&lt;h3 id="43-grouped-query-attention-gqa">4.3 Grouped-Query Attention (GQA)
&lt;/h3>&lt;p>Na Multi-Head Attention (MHA) comum, há o mesmo número de cabeças para Query, Key e Value. No entanto, o TinyLLaMA adota o &lt;strong>Grouped-Query Attention (GQA)&lt;/strong> para reduzir drasticamente o consumo de largura de banda de memória e cache KV.&lt;/p>
$$ \text{Attention}(Q, K, V) = \text{softmax}\left(\frac{Q K^T}{\sqrt{d_k}}\right) V $$
&lt;p>No GQA, várias cabeças de Query compartilham uma única cabeça de Key/Value. Na implementação C++, antes de executar o produto de matrizes &lt;code>ggml_mul_mat&lt;/code>, é necessária uma operação para transmitir (broadcast) os tensores KV para corresponder ao número de Queries.&lt;/p>
&lt;h3 id="44-função-de-ativação-swiglu">4.4 Função de Ativação SwiGLU
&lt;/h3>&lt;p>Na camada Feed-Forward Network (FFN), SwiGLU é usado em vez de GELU.&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>No grafo computacional, isso é expresso pela combinação do operador &lt;code>ggml_silu&lt;/code> com &lt;code>ggml_mul&lt;/code>.&lt;/p>
&lt;hr>
&lt;h2 id="5-construção-de-grafo-computacional-e-gerenciamento-de-memória-com-ggml">5. Construção de Grafo Computacional e Gerenciamento de Memória com ggml
&lt;/h2>&lt;p>O ggml adota uma abordagem &amp;ldquo;Define-and-Run&amp;rdquo;, onde um grafo computacional estático para inferência é construído e então avaliado posteriormente.&lt;/p>
&lt;h3 id="51-ggml_context-e-alocador-de-arena">5.1 ggml_context e Alocador de Arena
&lt;/h3>&lt;p>A característica mais singular do ggml é a &amp;ldquo;alocação de arena&amp;rdquo;, que evita qualquer alocação dinâmica de memória (&lt;code>malloc&lt;/code> ou &lt;code>new&lt;/code>) dentro do loop de inferência.
Uma região contígua e gigante de memória (arena) é alocada no momento da inicialização, e toda vez que &lt;code>ggml_new_tensor&lt;/code> é chamado, o ponteiro dessa região é incrementado. Quando uma etapa de inferência é concluída, simplesmente redefinir o ponteiro de alocação para a posição inicial conclui instantaneamente a alocação de memória para a próxima etapa de inferência.&lt;/p>
&lt;h3 id="52-exemplo-concreto-de-construção-de-grafo">5.2 Exemplo Concreto de Construção de Grafo
&lt;/h3>&lt;p>Para cada etapa de inferência, o seguinte grafo computacional é montado na memória.&lt;/p>
&lt;div class="mermaid">graph TD
A["ID de Entrada de Tokens"] --> B["Busca de Embedding"]
B --> C["ggml_rms_norm"]
C --> D["Projeções Q / K / V"]
D --> E["Posicional ggml_rope"]
E --> F["Armazenar em Cache KV"]
E --> G["Carregar Cache KV"]
G --> H["Self Attention"]
H --> I["Escalar &amp; Softmax"]
I --> J["Saída da Atenção"]
J --> K["Projeção de Saída"]
K --> L["Adicionar Residual"]&lt;/div>
&lt;hr>
&lt;h2 id="6-quantização-quantization-e-otimização-para-windows--simd">6. Quantização (Quantization) e Otimização para Windows / SIMD
&lt;/h2>&lt;p>Lidar com o TinyLLaMA (1.1B) em FP16 requer cerca de 2.2GB de memória, mas por meio da quantização de 4 bits (como Q4_K), pode ser comprimido drasticamente para cerca de 600MB.&lt;/p>
&lt;h3 id="61-arquitetura-de-quantização-em-bloco">6.1 Arquitetura de Quantização em Bloco
&lt;/h3>&lt;p>O ggml não quantiza o tensor inteiro de maneira uniforme, mas o faz em unidades de &amp;ldquo;blocos&amp;rdquo;.
No formato &lt;code>Q4_0&lt;/code>, 32 valores FP16 são agrupados em um bloco.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Fator de escala&lt;/strong>: 1 valor FP16 (2 bytes)&lt;/li>
&lt;li>&lt;strong>Dados quantizados&lt;/strong>: 32 valores de 4 bits (16 bytes)
Isso minimiza a influência de outliers locais.&lt;/li>
&lt;/ul>
&lt;h3 id="62-aceleração-de-produto-escalar-com-avx2">6.2 Aceleração de Produto Escalar com AVX2
&lt;/h3>&lt;p>Ao compilar para a CPU x86 mais recente no ambiente Windows, sinalizadores de compilador como &lt;code>/arch:AVX2&lt;/code> são utilizados, e o processamento SIMD é realizado no fluxo abaixo.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Carregar&lt;/strong>: Dados quantizados de 4 bits são carregados da memória em registradores AVX de 256 bits.&lt;/li>
&lt;li>&lt;strong>Expansão e Desempacotamento&lt;/strong>: Os valores de 4 bits são expandidos para Int8 ou Int16 com máscaras de bits e operações de deslocamento.&lt;/li>
&lt;li>&lt;strong>Desquantização&lt;/strong>: O fator de escala é multiplicado para converter em ponto flutuante.&lt;/li>
&lt;li>&lt;strong>Operação FMA&lt;/strong>: Executa o cálculo paralelo de multiplicar e somar usando o valor de ativação e &lt;code>_mm256_fmadd_ps&lt;/code> (Fused Multiply-Add).&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="7-detalhes-de-implementação-do-cache-kv">7. Detalhes de Implementação do Cache KV
&lt;/h2>&lt;p>Na geração autorregressiva, o &amp;ldquo;cache KV&amp;rdquo; é um recurso indispensável para pular o cálculo de Key e Value dos tokens anteriores.&lt;/p>
&lt;p>Os pontos da implementação em C++ são os seguintes:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Alocação Prévia do Tensor&lt;/strong>: Um tensor gigante para o tamanho máximo do contexto (ex: 2048 tokens) é inicializado para o cache KV (recomenda-se FP16).&lt;/li>
&lt;li>&lt;strong>Cópia com Deslocamento&lt;/strong>: Quando o cálculo para a posição $N$ do token for realizado, os vetores K e V obtidos nessa etapa são armazenados na $N$-ésima linha do tensor de cache KV usando &lt;code>ggml_cpy&lt;/code> ou similares.&lt;/li>
&lt;li>&lt;strong>Criação de Visualização na Atenção&lt;/strong>: Ao calcular a atenção, uma &amp;ldquo;visualização&amp;rdquo; apontando apenas para a parte dos tokens de 0 a $N$ é criada e passada para a multiplicação de matrizes.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="8-tokenizador-bpe-e-decodificação">8. Tokenizador BPE e Decodificação
&lt;/h2>&lt;p>Trata as strings de entrada como sequências de bytes UTF-8 e as combina com um vocabulário pré-definido. Em C++, algoritmos utilizando &lt;strong>Árvore Trie (árvore de prefixo)&lt;/strong> ou filas de prioridade são implementados para acelerar a busca no vocabulário.&lt;/p>
&lt;p>A partir dos logits emitidos pelo LM Head, as probabilidades são escalonadas usando o parâmetro Temperature, os candidatos são reduzidos por meio da extração Top-K ou Top-P (Nucleus Sampling), e o próximo token final é determinado por meio de números aleatórios.&lt;/p>
&lt;hr>
&lt;h2 id="9-configuração-do-projeto-c-ambiente-windows--powershell">9. Configuração do Projeto C++ (Ambiente 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"># Otimização e definição do sinalizador AVX2 para 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>Exemplo de comando de build no 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-resumo">10. Resumo
&lt;/h2>&lt;p>Implementar o motor de inferência de um modelo de IA em pequena escala como o TinyLLaMA do zero usando C++ e ggml é uma excelente oportunidade para descobrir a caixa preta do deep learning e aprender a beleza do controle de hardware de baixo nível. Vamos abrir o futuro da IA de borda (Edge AI) aproveitando a essência da programação de sistemas, como o carregamento de zero cópia usando mapeamento de memória, otimização SIMD e construção de cache KV.&lt;/p></description></item><item><title>【Guia de Implementação RAG】Como fazer a IA local ler seus próprios documentos</title><link>http://kenji.blog/pt/p/rag-local-ai-implementation-guide/</link><pubDate>Fri, 11 Sep 2026 13:00:00 +0900</pubDate><guid>http://kenji.blog/pt/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 【Guia de Implementação RAG】Como fazer a IA local ler seus próprios documentos" />&lt;h1 id="introdução">Introdução
&lt;/h1>&lt;p>Nos últimos anos, a evolução dos Grandes Modelos de Linguagem (LLM) tem sido notável, com muitas IAs, lideradas pelo ChatGPT e Claude, permeando nossas vidas e trabalhos. No entanto, os LLMs em geral têm uma fraqueza clara. É o fato de que eles conhecem apenas &amp;ldquo;informações públicas no momento do treinamento&amp;rdquo;. Eles naturalmente não podem responder a perguntas sobre &amp;ldquo;documentos privados&amp;rdquo;, como regulamentos internos, notas pessoais ou materiais de projetos não publicados. Tentar forçá-los a responder aumenta o risco de gerar mentiras plausíveis que diferem dos fatos (alucinações).&lt;/p>
&lt;p>Portanto, a arquitetura de tecnologia que está se espalhando explosivamente pelo mundo agora é a &lt;strong>RAG (Retrieval-Augmented Generation: Geração Aumentada por Recuperação)&lt;/strong>. Ao usar a RAG, é possível fornecer dinamicamente conhecimento próprio ao LLM a partir de um banco de dados externo e fazer com que ele gere respostas precisas e fundamentadas com base nisso.&lt;/p>
&lt;p>Além disso, ao lidar com informações confidenciais na área corporativa ou pessoal, o envio de dados para APIs baseadas em nuvem, como a da OpenAI, muitas vezes não é permitido pelas políticas de segurança. Portanto, o que se exige é a construção de uma &amp;ldquo;RAG local&amp;rdquo; combinada com &lt;strong>IA local&lt;/strong> (LLMs que funcionam e se completam no seu próprio PC ou servidor on-premise).&lt;/p>
&lt;p>Neste artigo, explicaremos detalhadamente, desde a teoria básica da RAG até o método específico de implementação da RAG local usando Python, o contexto matemático (o mecanismo da pesquisa vetorial) e técnicas avançadas para colocar o sistema em produção.&lt;/p>
&lt;hr>
&lt;h1 id="1-arquitetura-geral-da-rag">1. Arquitetura Geral da RAG
&lt;/h1>&lt;p>A RAG não é um modelo de IA único, mas uma arquitetura de sistema na qual vários componentes trabalham juntos. Ela é dividida basicamente em duas partes: a &amp;ldquo;Fase de Ingestão (captura de dados)&amp;rdquo; e a &amp;ldquo;Fase de Recuperação e Geração (pesquisa e geração)&amp;rdquo;.&lt;/p>
&lt;p>O diagrama Mermaid abaixo mostra a visão geral do sistema RAG.&lt;/p>
&lt;div class="mermaid">graph TD
subgraph "Fase de Ingestão (Preparação prévia)"
Doc["Documentos Próprios (PDF, TXT, etc.)"] --> Loader["Carregador de Documentos"]
Loader --> Splitter["Divisão de Texto (Chunking)"]
Splitter --> EmbedModel1["Modelo de Incorporação (Embedding)"]
EmbedModel1 --> VectorDB["Banco de Dados Vetorial"]
end
subgraph "Fase de Inferência (Durante a consulta do usuário)"
User["Pergunta do Usuário (Consulta)"] --> EmbedModel2["Modelo de Incorporação (Embedding)"]
EmbedModel2 --> QueryVector["Vetor da Consulta"]
QueryVector --> Search["Pesquisa de Similaridade (Pesquisa Vetorial)"]
VectorDB --> Search
Search --> Context["Extração de Chunks Relevantes (Contexto)"]
User --> PromptBuilder["Construção do Prompt"]
Context --> PromptBuilder
PromptBuilder --> LocalLLM["LLM Local"]
LocalLLM --> Answer["Geração da Resposta Final"]
end&lt;/div>
&lt;h2 id="fase-de-ingestão-preparação-prévia">Fase de Ingestão (Preparação prévia)
&lt;/h2>&lt;ol>
&lt;li>&lt;strong>Leitura de documentos&lt;/strong>: Carrega dados não estruturados, como arquivos PDF, Word e texto.&lt;/li>
&lt;li>&lt;strong>Chunking (Divisão de texto)&lt;/strong>: Divide textos longos em blocos significativos (chunks) para caber no limite de entrada do LLM (janela de contexto) e para aumentar a precisão da pesquisa.&lt;/li>
&lt;li>&lt;strong>Embedding (Vetorização)&lt;/strong>: Insere os chunks divididos em um Modelo de Incorporação (Embedding Model) e os converte em matrizes numéricas (vetores) de centenas a milhares de dimensões.&lt;/li>
&lt;li>&lt;strong>Armazenamento no banco de dados&lt;/strong>: Salva os vetores convertidos associados aos dados de texto originais em um banco de dados vetorial (Vector DB).&lt;/li>
&lt;/ol>
&lt;h2 id="fase-de-inferência-em-tempo-de-execução">Fase de Inferência (Em tempo de execução)
&lt;/h2>&lt;ol>
&lt;li>&lt;strong>Vetorização da consulta&lt;/strong>: Vetoriza a frase da pergunta do usuário usando o mesmo modelo de incorporação da preparação prévia.&lt;/li>
&lt;li>&lt;strong>Pesquisa de similaridade&lt;/strong>: Calcula a similaridade entre o vetor da consulta e os vetores de documentos no banco de dados, recuperando os principais chunks de texto semanticamente próximos (altamente relevantes).&lt;/li>
&lt;li>&lt;strong>Construção do prompt&lt;/strong>: Combina os textos relevantes obtidos como &amp;ldquo;contexto (conhecimento prévio)&amp;rdquo; com a frase da pergunta do usuário para criar o prompt de entrada para o LLM.&lt;/li>
&lt;li>&lt;strong>Geração da resposta&lt;/strong>: O LLM recebe o prompt estendido e gera uma resposta com base nas informações de contexto fornecidas.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h1 id="2-entendimento-profundo-da-pesquisa-vetorial-e-incorporações-embeddings">2. Entendimento Profundo da Pesquisa Vetorial e Incorporações (Embeddings)
&lt;/h1>&lt;p>O núcleo da RAG é a &amp;ldquo;pesquisa vetorial (pesquisa semântica)&amp;rdquo;. Enquanto a pesquisa tradicional por palavras-chave (como o BM25) é baseada na correspondência exata e frequência de palavras, a pesquisa vetorial é baseada na &amp;ldquo;similaridade de significado&amp;rdquo;. Por exemplo, como &amp;ldquo;cão&amp;rdquo; e &amp;ldquo;filhote&amp;rdquo; ou &amp;ldquo;PC&amp;rdquo; e &amp;ldquo;computador&amp;rdquo;, mesmo palavras diferentes aparecerão na pesquisa se seus significados forem próximos.&lt;/p>
&lt;h2 id="o-que-é-um-modelo-de-incorporação-embedding-model">O que é um Modelo de Incorporação (Embedding Model)?
&lt;/h2>&lt;p>Um modelo de incorporação é uma rede neural que recebe texto em linguagem natural como entrada e gera um vetor denso (Dense Vector) de comprimento fixo como saída. Modelos comuns (como &lt;code>text-embedding-3-small&lt;/code> ou o código aberto &lt;code>multilingual-e5-large&lt;/code>) mapeiam o texto para vetores de números reais de 384 ou 1024 dimensões.&lt;/p>
&lt;p>Neste espaço multidimensional (espaço latente), eles são treinados de forma que frases com significados semelhantes fiquem mais próximas na distância dentro do espaço de coordenadas.&lt;/p>
&lt;h2 id="contexto-matemático-do-cálculo-de-similaridade-similaridade-de-cosseno">Contexto Matemático do Cálculo de Similaridade: Similaridade de Cosseno
&lt;/h2>&lt;p>Quando um banco de dados vetorial pesquisa documentos relevantes, a métrica de distância mais comumente usada é a &lt;strong>Similaridade de Cosseno (Cosine Similarity)&lt;/strong>. Diferente da distância euclidiana (distância espacial absoluta), a similaridade de cosseno foca no &amp;ldquo;ângulo formado entre dois vetores&amp;rdquo;. Como é menos afetada pelo comprimento da frase (norma do vetor), é muito adequada para calcular a similaridade de textos.&lt;/p>
&lt;p>Expresso matematicamente, a similaridade de cosseno entre os vetores $\mathbf{A}$ e $\mathbf{B}$ é a seguinte:&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}$ representa o produto escalar (Dot Product).&lt;/li>
&lt;li>$\|\mathbf{A}\|$ representa a norma L2 (comprimento) do vetor $\mathbf{A}$.&lt;/li>
&lt;li>$n$ é o número de dimensões dos vetores.&lt;/li>
&lt;/ul>
&lt;p>A similaridade de cosseno varia de -1 a 1.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Próximo a 1&lt;/strong>: A direção dos dois vetores é quase a mesma (os significados são muito semelhantes).&lt;/li>
&lt;li>&lt;strong>Próximo a 0&lt;/strong>: Os dois vetores são ortogonais (não relacionados).&lt;/li>
&lt;li>&lt;strong>Próximo a -1&lt;/strong>: Os dois vetores apontam em direções opostas (significados opostos).&lt;/li>
&lt;/ul>
&lt;p>Os bancos de dados vetoriais recentes (Chroma, FAISS, Qdrant, etc.) adotam um algoritmo de Pesquisa Aproximada de Vizinhos Mais Próximos (ANN) chamado HNSW (Hierarchical Navigable Small World), sendo otimizados para pesquisar documentos com alta similaridade de cosseno em milissegundos, mesmo entre milhões de dados vetoriais.&lt;/p>
&lt;hr>
&lt;h1 id="3-pilha-de-tecnologia-para-construir-a-rag-local">3. Pilha de Tecnologia para Construir a RAG Local
&lt;/h1>&lt;p>Para construir uma RAG totalmente local, sem depender da nuvem, utilizamos o ecossistema de código aberto. A pilha de tecnologia recomendada é apresentada abaixo.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Modelo de Linguagem (LLM)&lt;/strong>
&lt;ul>
&lt;li>Ferramenta: &lt;code>Ollama&lt;/code> ou &lt;code>Llama.cpp&lt;/code>&lt;/li>
&lt;li>Modelo: Modelos abertos leves e de alto desempenho, como &lt;code>Llama-3-8B-Instruct&lt;/code>, &lt;code>Gemma-2-9B-It&lt;/code>, &lt;code>Qwen2-7B-Instruct&lt;/code>. Para tarefas em japonês, modelos ajustados como o &lt;code>Llama-3-ELYZA-JP-8B&lt;/code> são adequados.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Modelo de Incorporação (Embedding)&lt;/strong>
&lt;ul>
&lt;li>Modelo: &lt;code>intfloat/multilingual-e5-large&lt;/code> ou &lt;code>BAAI/bge-m3&lt;/code>. Ao executar localmente, geralmente é feito o download pelo Hugging Face e a execução usando o Sentence-Transformers.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Banco de Dados Vetorial (Vector DB)&lt;/strong>
&lt;ul>
&lt;li>&lt;code>ChromaDB&lt;/code>: Baseado em Python e extremamente fácil de configurar. Ideal para desenvolvimento local.&lt;/li>
&lt;li>&lt;code>FAISS&lt;/code>: Uma biblioteca rápida de pesquisa vetorial desenvolvida pela Meta.&lt;/li>
&lt;li>&lt;code>Qdrant&lt;/code> / &lt;code>Milvus&lt;/code>: Mais adequados para ambientes de produção e maior escala.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Estrutura de Orquestração&lt;/strong>
&lt;ul>
&lt;li>&lt;code>LangChain&lt;/code>: O padrão de fato para conectar componentes (Chains).&lt;/li>
&lt;li>&lt;code>LlamaIndex&lt;/code>: Um framework de conexão de dados voltado especificamente para a RAG.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>Desta vez, implementaremos usando a combinação mais fácil de introduzir: &lt;strong>LangChain + ChromaDB + Ollama + HuggingFaceEmbeddings&lt;/strong>.&lt;/p>
&lt;hr>
&lt;h1 id="4-tutorial-de-implementação-construção-completa-da-rag-local-com-python">4. Tutorial de Implementação: Construção Completa da RAG Local com Python
&lt;/h1>&lt;p>A partir daqui, vamos construir a RAG local escrevendo código Python real. Antes de começar, instale o Ollama no seu PC e inicie-o em segundo plano. Além disso, baixe o modelo no Ollama (ex: &lt;code>ollama run llama3&lt;/code>).&lt;/p>
&lt;h2 id="passo-1-instalação-das-bibliotecas-necessárias">Passo 1: Instalação das Bibliotecas Necessárias
&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="passo-2-visão-geral-do-código-de-implementação">Passo 2: Visão Geral do Código de Implementação
&lt;/h2>&lt;p>Abaixo está o script Python completo para ler um arquivo PDF, vetorizá-lo e permitir perguntas e respostas usando o LLM local.&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. Leitura do documento&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;Carregando documento...&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"># Especifique o caminho do PDF que deseja ler&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. Divisão de chunks (Text Splitting)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Dividimos em tamanhos moderados para não quebrar o significado do texto&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"># Número máximo de caracteres por chunk&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"># Número de caracteres sobrepostos entre os chunks (evita a desconexão do contexto)&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;Dividido em &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"> chunks.&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. Inicialização do modelo de incorporação (Local HuggingFace Model)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Usando um modelo multilíngue forte&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;Carregando o modelo de incorporação...&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"># Use &amp;#39;cuda&amp;#39; ou &amp;#39;mps&amp;#39; se tiver uma 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. Construção do banco de dados vetorial (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;Construindo o banco de dados vetorial...&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"># Criação do recuperador (Retriever). Configurado para buscar os 3 principais documentos relevantes&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. Inicialização do LLM Local (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;Conectando ao LLM local...&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"># Certifique-se de obter o modelo antecipadamente usando &amp;#39;ollama pull llama3&amp;#39; etc.&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. Definição do template de prompt&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;Você é um excelente assistente com amplo conhecimento das regras e informações internas da empresa.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">Responda à pergunta do usuário em detalhes usando APENAS o seguinte contexto (informação prévia).
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">Se a resposta não puder ser encontrada no contexto, não tente adivinhar e responda honestamente &amp;#34;Não consigo encontrar a resposta nas informações fornecidas&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">【Contexto】
&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">【Pergunta】
&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">【Resposta】:
&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. Construção da Chain da 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"># Configuração para retornar os documentos fonte&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. Execução da pergunta&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;Por favor, explique as condições para o pagamento das despesas de transporte no trabalho remoto.&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">Pergunta: &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;【Resposta】&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;【Fontes de informação consultadas】&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;- Página &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;Desconhecida&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="explicação-dos-pontos-chave-do-código">Explicação dos Pontos-Chave do Código
&lt;/h2>&lt;ol>
&lt;li>&lt;strong>RecursiveCharacterTextSplitter&lt;/strong>:
É o divisor mais recomendado para o processamento de linguagem natural. Ele tenta dividir o texto na ordem de parágrafos (&lt;code>\n\n&lt;/code>), quebras de linha (&lt;code>\n&lt;/code>) e pontos finais (&lt;code>.&lt;/code>), visando manter a coerência do significado tanto quanto possível e garantir que caiba no &lt;code>chunk_size&lt;/code> especificado. Ao definir um &lt;code>chunk_overlap&lt;/code>, previne-se que os limites de contexto sejam cortados e que informações se percam.&lt;/li>
&lt;li>&lt;strong>HuggingFaceEmbeddings&lt;/strong>:
&lt;code>intfloat/multilingual-e5-large&lt;/code> é um modelo de incorporação de código aberto multilíngue muito poderoso. Ele permite vetorizar textos offline na memória local, sem o uso de APIs na nuvem (como o &lt;code>text-embedding-ada-002&lt;/code> da OpenAI).&lt;/li>
&lt;li>&lt;strong>ChromaDB&lt;/strong>:
Como roda em memória ou no armazenamento local (baseado em SQLite), não é necessário iniciar servidores de banco de dados complexos. Ao especificar o &lt;code>persist_directory&lt;/code>, você pode pular o processo de vetorização em execuções subsequentes e carregar o BD direto do disco.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h1 id="5-técnicas-avançadas-da-rag-advanced-rag-techniques">5. Técnicas Avançadas da RAG (Advanced RAG Techniques)
&lt;/h1>&lt;p>O sistema RAG básico (Naive RAG) construído no tutorial acima já funciona, mas se for exigida alta precisão nas respostas em um ambiente de produção, será necessário implementar técnicas avançadas como as descritas a seguir.&lt;/p>
&lt;h2 id="51-pesquisa-híbrida-hybrid-search">5.1 Pesquisa Híbrida (Hybrid Search)
&lt;/h2>&lt;p>A pesquisa vetorial é excelente para captar o &amp;ldquo;significado&amp;rdquo;, mas pode não lidar bem com pesquisas rigorosas por palavras-chave, como &amp;ldquo;nomes próprios específicos&amp;rdquo;, &amp;ldquo;números de modelo de produto&amp;rdquo; e &amp;ldquo;IDs de funcionários&amp;rdquo;.
Portanto, a realização da &lt;strong>pesquisa semântica&lt;/strong> baseada em vetores em paralelo com a &lt;strong>pesquisa por palavras-chave&lt;/strong> (usando algoritmos como o BM25) e a integração de ambos os resultados (usando técnicas como o Reciprocal Rank Fusion, RRF) podem reduzir drasticamente o número de omissões de pesquisa.&lt;/p>
&lt;h2 id="52-reclassificação-re-ranking">5.2 Reclassificação (Re-ranking)
&lt;/h2>&lt;p>A pesquisa vetorial é rápida, mas nem sempre avalia a relevância exata de um contexto no seu sentido literal. O pipeline geral para melhorar a precisão da pesquisa é o seguinte:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Recuperação Inicial (First-stage Retrieval)&lt;/strong>: Recupera entre 20 a 30 chunks relevantes do banco de dados vetorial, com alcance mais amplo e superficial.&lt;/li>
&lt;li>&lt;strong>Reavaliação (Re-ranking)&lt;/strong>: Utiliza-se um modelo de machine learning diferente e mais pesado chamado Cross-Encoder (por exemplo: &lt;code>bge-reranker&lt;/code>) para introduzir pares compostos da consulta do usuário e dos chunks recuperados, recalculando a pontuação de relevância semântica.&lt;/li>
&lt;li>&lt;strong>Seleção&lt;/strong>: Somente os 3 a 5 principais resultados com as maiores pontuações são repassados ao prompt do LLM como o contexto final.&lt;/li>
&lt;/ol>
&lt;p>Esse método impede que informações de ruído não relacionadas passem para o LLM, o que pode melhorar significativamente a precisão (Precision) da resposta.&lt;/p>
&lt;div class="mermaid">graph LR
Query["Consulta"] --> VSearch["Pesquisa Vetorial (Top 20)"]
VSearch --> Reranker["Modelo Reclassificador (Cross-Encoder)"]
Query --> Reranker
Reranker --> TopK["Top 3 de Alta Precisão"]
TopK --> LLM["Geração do LLM"]&lt;/div>
&lt;h2 id="53-chunking-semântico-e-pesquisa-do-documento-pai">5.3 Chunking Semântico e Pesquisa do Documento Pai
&lt;/h2>&lt;p>Em vez de dividir o texto mecanicamente por um número fixo de caracteres, existe uma técnica chamada &amp;ldquo;Semantic Chunking&amp;rdquo;, onde a IA detecta as mudanças de significado do texto para dividi-lo.
Além disso, com a técnica de &amp;ldquo;Pesquisa do Documento Pai (Parent Document Retriever)&amp;rdquo;, a vetorização é feita em unidades muito pequenas (como frases isoladas) para a pesquisa, buscando alta precisão. Mas ao passar para o LLM, o parágrafo original inteiro (&amp;ldquo;documento pai&amp;rdquo;) que contém essa frase é fornecido, dando ao LLM um contexto suficiente para compreensão.&lt;/p>
&lt;hr>
&lt;h1 id="6-desafios-e-soluções-na-operação-da-rag-local">6. Desafios e Soluções na Operação da RAG Local
&lt;/h1>&lt;p>Existem obstáculos específicos ao construir e operar uma RAG em um ambiente local.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Esgotamento da VRAM (Memória de Vídeo)&lt;/strong>:
Para executar LLMs locais a velocidades práticas (dezenas de tokens por segundo), é necessário carregar o modelo na VRAM da GPU. Um modelo de classe 8B requer aproximadamente 16GB de VRAM para rodar em fp16 (ponto flutuante de 16 bits), mas ao usar tecnologias de &lt;strong>Quantização (Quantization)&lt;/strong> (formatos GGUF ou AWQ, que comprimem os modelos para 4 ou 8 bits), é possível executá-los com rapidez satisfatória com apenas 8GB de VRAM (como em um PC gamer comum). O Llama.cpp e o Ollama já suportam nativamente esses formatos de quantização.&lt;/li>
&lt;li>&lt;strong>Limite da Janela de Contexto&lt;/strong>:
Se a quantidade de contexto recuperado for muito grande, o LLM pode exceder seu limite máximo de entrada (limite de tokens) ou o modelo pode &amp;ldquo;esquecer&amp;rdquo; informações do meio (o fenômeno &amp;ldquo;Lost in the middle&amp;rdquo;). O ajuste do número de chunks extraídos e a seleção cuidadosa usando as tecnologias de reclassificação mencionadas acima são fundamentais.&lt;/li>
&lt;li>&lt;strong>Gestão da Atualidade dos Dados&lt;/strong>:
Se o documento fonte for atualizado, o vetor do documento correspondente no banco de dados vetorial também precisará ser atualizado ou excluído (operações CRUD). Como o ChromaDB suporta atualizações baseadas em IDs de documentos, a gestão dos valores hash de arquivos para sincronizar as diferenças através de processamento em lote é a melhor prática.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h1 id="conclusão">Conclusão
&lt;/h1>&lt;p>A RAG (Geração Aumentada por Recuperação) é um poderoso paradigma que evolui a IA de um simples assistente geral para um &amp;ldquo;especialista dedicado&amp;rdquo; ou um &amp;ldquo;especialista em operações internas&amp;rdquo;.&lt;/p>
&lt;p>Mesmo com requisitos de alta confidencialidade, nos quais serviços em nuvem não podem ser usados, foi possível perceber que um ambiente RAG totalmente local pode ser construído com relativa facilidade, combinando o ecossistema de código aberto, como o Ollama, o LangChain e o ChromaDB.&lt;/p>
&lt;p>Com base no entendimento matemático do espaço vetorial, do chunking de texto e das abordagens avançadas, como a reclassificação, explicadas neste artigo, não deixe de tentar desenvolver o seu próprio sistema original de IA usando seus dados. A velocidade de evolução da IA local é espantosa; os sistemas construídos hoje podem ter seu desempenho atualizado instantaneamente apenas trocando os modelos por modelos menores e mais inteligentes lançados amanhã.&lt;/p>
&lt;hr>
&lt;p>&lt;em>Neste blog, continuaremos postando artigos aprofundados sobre tecnologias de IA e RAG no futuro. Se você tiver dúvidas ou feedback, sinta-se à vontade para compartilhá-los na seção de comentários.&lt;/em>&lt;/p></description></item><item><title>ChatGPT・Gemini・Claude API: Comparação Completa e Qual Escolher?</title><link>http://kenji.blog/pt/p/chatgpt-gemini-claude-api-comparison/</link><pubDate>Fri, 11 Sep 2026 12:00:00 +0900</pubDate><guid>http://kenji.blog/pt/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 ChatGPT・Gemini・Claude API: Comparação Completa e Qual Escolher?" />&lt;h1 id="chatgptgeminiclaude-api-comparação-completa-e-qual-escolher">ChatGPT・Gemini・Claude API: Comparação Completa e Qual Escolher?
&lt;/h1>&lt;p>A evolução da tecnologia de IA é notável, especialmente no campo dos Grandes Modelos de Linguagem (LLM: Large Language Model), onde o ChatGPT (série GPT) da OpenAI, o Gemini do Google e o Claude da Anthropic estão travando uma intensa batalha tripartida pela supremacia. A partir de 2026, cada empresa tem lançado novos modelos e funcionalidades de API em questão de meses, ou até mesmo semanas, e para desenvolvedores e arquitetos de TI corporativos, a questão de &amp;ldquo;qual API integrar ao produto&amp;rdquo; tornou-se uma decisão crítica que dita o sucesso de um projeto.&lt;/p>
&lt;p>Neste artigo, não apenas listaremos as especificações, mas compararemos e explicaremos detalhadamente as APIs desses três grandes provedores de IA sob a perspectiva do desenvolvedor, cobrindo o design da arquitetura, estrutura detalhada de preços, análise matemática da latência (atraso), exemplos práticos de implementação em Python e Node.js, e até os mais recentes métodos de otimização de custos, como o cache de prompt.&lt;/p>
&lt;p>Nosso objetivo é que este seja um guia completo para ajudar os leitores a selecionar a API de LLM ideal para seus casos de uso e construir aplicativos de IA escaláveis e econômicos.&lt;/p>
&lt;hr>
&lt;h2 id="1-filosofia-e-conceito-de-design-de-cada-api-de-llm">1. Filosofia e Conceito de Design de Cada API de LLM
&lt;/h2>&lt;p>Na escolha de tecnologia, é muito importante primeiro entender qual é a filosofia que cada empresa utiliza para construir seus modelos e APIs.&lt;/p>
&lt;h3 id="11-openai-chatgpt">1.1 OpenAI (ChatGPT)
&lt;/h3>&lt;p>A OpenAI tem como missão a &amp;ldquo;realização da Inteligência Artificial Geral (AGI)&amp;rdquo; e está sempre liderando o padrão de fato da indústria. Ela oferece uma ampla variedade de modelos adaptados aos casos de uso, como GPT-4o e GPT-4o-mini, além do modelo o1 especializado em raciocínio. Seu ecossistema é o mais maduro, com a documentação e bibliotecas mais ricas, sejam elas oficiais ou não oficiais.&lt;/p>
&lt;h3 id="12-google-gemini">1.2 Google (Gemini)
&lt;/h3>&lt;p>O Google defende a abordagem &amp;ldquo;AI First&amp;rdquo; e utiliza a escalabilidade de sua infraestrutura (rede TPU) ao máximo como sua principal arma. A maior característica do Gemini 1.5 Pro/Flash é sua enorme janela de contexto de até 2 milhões de tokens, permitindo que processe documentos extensos e horas de vídeo e áudio de uma só vez. Sua forte integração com o Google Cloud (Vertex AI) também é muito atraente para empresas.&lt;/p>
&lt;h3 id="13-anthropic-claude">1.3 Anthropic (Claude)
&lt;/h3>&lt;p>A Anthropic é uma empresa fundada por ex-membros da OpenAI e adota uma abordagem de segurança exclusiva chamada &amp;ldquo;Constitutional AI&amp;rdquo; (IA Constitucional). O Claude 3.5 Sonnet e o Opus conquistaram o apoio entusiástico de muitos desenvolvedores por suas altas capacidades de raciocínio, habilidades de geração de código e, acima de tudo, seus &amp;ldquo;diálogos naturais semelhantes aos humanos&amp;rdquo; e &amp;ldquo;baixa taxa de alucinação&amp;rdquo;.&lt;/p>
&lt;hr>
&lt;h2 id="2-comparação-detalhada-de-especificações-das-famílias-de-modelos">2. Comparação Detalhada de Especificações das Famílias de Modelos
&lt;/h2>&lt;p>Aqui está uma comparação das especificações dos principais modelos até 2026.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Provedor&lt;/th>
&lt;th>Modelo Principal&lt;/th>
&lt;th>Contexto Máximo&lt;/th>
&lt;th>Principais Pontos Fortes&lt;/th>
&lt;th>Casos de Uso Recomendados&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>Velocidade, reconhecimento visual, suporte a áudio&lt;/td>
&lt;td>Aplicativos interativos, tarefas gerais&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>Raciocínio lógico avançado, matemática, codificação&lt;/td>
&lt;td>Geração de algoritmos complexos, pesquisa&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>Processamento de textos ultralongos, multimodal (vídeo, áudio)&lt;/td>
&lt;td>Análise de grandes bases de código, resumo de vídeos&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>Baixa latência, alta taxa de transferência, custo extremamente baixo&lt;/td>
&lt;td>Processamento em tempo real, processamento em lote de dados em massa&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>Habilidades de codificação, geração de texto natural&lt;/td>
&lt;td>Suporte ao desenvolvimento de software, suporte avançado ao cliente&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>Resposta ultrarrápida, custo-benefício&lt;/td>
&lt;td>Edge AI, chatbots em tempo real&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="3-aprofundamento-na-arquitetura-os-bastidores-das-requisições-de-api">3. Aprofundamento na Arquitetura: Os Bastidores das Requisições de API
&lt;/h2>&lt;p>Quando uma API de LLM é chamada, que tipo de processamento ocorre no back-end? Para otimizar o desempenho, é necessário entender essa arquitetura.&lt;/p>
&lt;p>O diagrama Mermaid abaixo mostra a visão geral desde o momento em que a requisição da API é enviada pelo cliente até o momento em que os tokens são retornados em fluxo contínuo (streaming).&lt;/p>
&lt;div class="mermaid">graph TD
A["Aplicação Cliente"] -->|HTTP/REST ou gRPC| B["Gateway de API"]
B --> C["Balanceador de Carga"]
C --> D["Cluster de Inferência"]
D --> E["Tokenizador (BPE / SentencePiece)"]
E --> F["Cache KV &amp; Mecanismo de Atenção"]
F --> G["Blocos Transformer (Passagem Direta)"]
G --> H["Camada de Saída (Logits)"]
H --> I["Amostrador (Temperature, Top-p, Top-k)"]
I --> J["Destokenizador"]
J -->|Resposta em Streaming (Chunk)| A&lt;/div>
&lt;h3 id="31-algoritmo-de-tokenização-tokenization">3.1 Algoritmo de Tokenização (Tokenization)
&lt;/h3>&lt;p>O texto de entrada na API é dividido internamente em unidades chamadas &amp;ldquo;tokens&amp;rdquo;.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>OpenAI (tiktoken)&lt;/strong>: Adota o Byte-Pair Encoding (BPE). Ele é compactado com extrema eficiência, especialmente em inglês, mas o número de tokens tende a aumentar em idiomas não alfabéticos, como o japonês.&lt;/li>
&lt;li>&lt;strong>Google (Gemini)&lt;/strong>: Adota o SentencePiece (Unigram Language Model). Ele lida bem com múltiplos idiomas e tende a conseguir representar textos em japonês com um número relativamente pequeno de tokens.&lt;/li>
&lt;li>&lt;strong>Anthropic (Claude)&lt;/strong>: Usa uma versão customizada do BPE. O suporte multilíngue foi fortalecido e, a partir do Claude 3, a eficiência dos tokens em japonês também melhorou significativamente.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="4-análise-matemática-de-latência-e-desempenho">4. Análise Matemática de Latência e Desempenho
&lt;/h2>&lt;p>Em aplicações de tempo real, a latência está diretamente ligada à experiência do usuário (UX). A latência da API do LLM, $T_{total}$, pode ser matematicamente modelada da seguinte forma:&lt;/p>
$$ T_{total} = T_{network} + T_{TTFT} + (N \times T_{TPOT}) $$
&lt;p>Aqui, cada variável tem o seguinte significado:&lt;/p>
&lt;ul>
&lt;li>$T_{network}$: Tempo de ida e volta da rede (RTT).&lt;/li>
&lt;li>$T_{TTFT}$ (Time To First Token): O tempo até que o primeiro caractere seja gerado. Ele depende fortemente do custo do cálculo da atenção, que é proporcional ao quadrado do comprimento do prompt (número de tokens de entrada).&lt;/li>
&lt;li>$N$: O número total de tokens gerados na saída.&lt;/li>
&lt;li>$T_{TPOT}$ (Time Per Output Token): Tempo de geração por token. Por se tratar de um modelo autorregressivo, o cálculo é feito em série dependendo da saída anterior.&lt;/li>
&lt;/ul>
&lt;h3 id="41-complexidade-computacional-do-mecanismo-de-autoatenção">4.1 Complexidade Computacional do Mecanismo de Autoatenção
&lt;/h3>&lt;p>A complexidade computacional da autoatenção (Self-Attention) na arquitetura Transformer aumenta de forma quadrática em relação ao comprimento da sequência de entrada $L$.&lt;/p>
$$ \text{Complexity} = O(L^2 \cdot d) $$
&lt;p>Onde $d$ é o número de dimensões do vetor de incorporação (embedding). Devido a essa restrição, o $T_{TTFT}$ normalmente se degrada muito rapidamente à medida que o prompt se torna mais longo.
No entanto, o Gemini 1.5 do Google adota arquiteturas inovadoras de otimização como &amp;ldquo;Ring Attention&amp;rdquo; e &amp;ldquo;Block-wise Compute&amp;rdquo;, conseguindo gerar o primeiro token em um tempo realista (de alguns segundos a dezenas de segundos), mesmo ao inserir textos longos de até 2 milhões de tokens.&lt;/p>
&lt;hr>
&lt;h2 id="5-estrutura-de-preços-e-estratégias-de-otimização-de-custos">5. Estrutura de Preços e Estratégias de Otimização de Custos
&lt;/h2>&lt;p>O custo das APIs é basicamente calculado com base no número de tokens de entrada e no número de tokens de saída.&lt;/p>
$$ Cost = (Tokens_{in} \times Rate_{in}) + (Tokens_{out} \times Rate_{out}) $$
&lt;p>No entanto, as APIs mais recentes introduziram novos mecanismos para reduzir os custos drasticamente.&lt;/p>
&lt;h3 id="51-cache-de-prompt-prompt-caching">5.1 Cache de Prompt (Prompt Caching)
&lt;/h3>&lt;p>O envio de um prompt de sistema extremamente longo ou de uma grande quantidade de documentos pesquisados via RAG a cada vez custa muito dinheiro. Para lidar com isso, cada empresa oferece um recurso de cache.&lt;/p>
&lt;p>Na Anthropic (Claude) e no Google (Gemini), ao fazer o cache de blocos específicos de texto, você pode reduzir significativamente (em até 90%) o custo de entrada.&lt;/p>
&lt;p>O modelo de custo ao utilizar o cache seria o seguinte:&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>Aqui, $Rate_{cache\_read}$ é definido para algo em torno de 10% a 25% da $Rate_{in}$ normal. Isso possibilita a operação de chatbots a baixo custo, mantendo sempre em memória bases de código de dezenas de milhares de linhas como conhecimento contextual.&lt;/p>
&lt;h3 id="52-api-em-lote-batch-api">5.2 API em Lote (Batch API)
&lt;/h3>&lt;p>Para tarefas que não exigem resultados em tempo real (como análise de logs, classificação de grandes volumes de dados, etc.), a OpenAI e a Anthropic oferecem APIs em lote. Ao enviar requisições de forma agrupada e receber os resultados em até 24 horas, é possível obter um poderoso mecanismo pela metade do preço (50% de desconto) da tarifa regular da API.&lt;/p>
&lt;hr>
&lt;h2 id="6-experiência-do-desenvolvedor-dx-e-comparação-de-sdks">6. Experiência do Desenvolvedor (DX) e Comparação de SDKs
&lt;/h2>&lt;p>Do ponto de vista da eficiência do desenvolvimento, comparamos os SDKs (Software Development Kits) fornecidos por cada empresa.&lt;/p>
&lt;h3 id="61-openai-api">6.1 OpenAI API
&lt;/h3>&lt;p>É o mais amplamente utilizado e possui o suporte mais rápido para bibliotecas de terceiros (LangChain, LlamaIndex, etc.). Além disso, a funcionalidade de Structured Outputs (Saídas Estruturadas) garante o retorno de respostas que aderem com 100% de precisão aos esquemas JSON, tornando a integração de sistemas muito fácil.&lt;/p>
&lt;h3 id="62-anthropic-api-claude">6.2 Anthropic API (Claude)
&lt;/h3>&lt;p>As interfaces do seu SDK são muito bem trabalhadas, e sua definição de tipos em TypeScript tem a reputação de ser extremamente fácil de manusear. A estrutura da API de Mensagens, em particular, é muito intuitiva, tornando requisições multimodais com várias imagens simples de descrever.&lt;/p>
&lt;h3 id="63-google-gemini-api">6.3 Google Gemini API
&lt;/h3>&lt;p>Existem dois tipos de acesso: um via Google Cloud Vertex AI e outro via AI Studio (Google Gen AI SDK), o que pode ser um pouco confuso para iniciantes. No entanto, o Vertex AI SDK voltado para corporações é totalmente integrado com o IAM (Sistema de Autenticação e Autorização) do GCP, permitindo a construção de ambientes de desenvolvimento seguros.&lt;/p>
&lt;hr>
&lt;h2 id="7-prática-implementação-de-teste-de-integração-simultânea-com-python">7. Prática! Implementação de Teste de Integração Simultânea com Python
&lt;/h2>&lt;p>Aqui, usaremos o Python para implementar um script que envia solicitações assíncronas simultaneamente às três APIs (OpenAI, Anthropic e Gemini) e compara as latências.&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"># Inicialização dos clientes&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;Por favor, explique aos iniciantes os fundamentos dos computadores quânticos e seu impacto nas tecnologias atuais de criptografia.&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"># Uso do método assíncrono do SDK Python do Gemini&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;Enviando requisições para as APIs de 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>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Executar as 3 APIs em paralelo&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;Latency: &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"> segundos&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;Response (Excerpt): &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>Ao executar este script, você pode facilmente medir qual modelo responde mais rapidamente (minimizando $T_{total}$) em um ambiente de rede real.&lt;/p>
&lt;hr>
&lt;h2 id="8-implementação-de-tool-calling-chamada-de-função-em-nodejs">8. Implementação de Tool Calling (Chamada de Função) em Node.js
&lt;/h2>&lt;p>Para que o LLM funcione não apenas como um chatbot, mas como um &amp;ldquo;Agente de IA&amp;rdquo; que interage com sistemas externos, o Tool Calling (ou Function Calling) é essencial. Abaixo está um exemplo em Node.js (TypeScript) instruindo a API da OpenAI a chamar uma API de clima.&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;Obtém o clima atual da cidade especificada.&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;Nome da cidade (ex: Tóquio, Nova York)&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;Como está o clima hoje em Tóquio? Preciso de um guarda-chuva?&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">`O LLM solicitou a chamada de uma ferramenta: Nome da função = &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">`Argumentos: &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">// Aqui seria implementada a chamada real da API de clima (ex: 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">// Depois, o resultado é passado de volta ao LLM para gerar a resposta final
&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>O Claude 3.5 Sonnet e o Gemini 1.5 Pro possuem capacidades equivalentes de Tool Calling, e embora existam pequenas diferenças na definição do esquema, o fluxo básico é comum a todos.&lt;/p>
&lt;hr>
&lt;h2 id="9-rag-vs-janela-de-contexto-longa-qual-adotar">9. RAG vs Janela de Contexto Longa: Qual Adotar?
&lt;/h2>&lt;p>Um dos maiores debates atuais em arquiteturas de IA empresariais é se devemos &amp;ldquo;usar o RAG (Geração Aumentada por Recuperação) para trazer conhecimento externo, ou se devemos simplesmente jogar tudo em uma enorme janela de contexto (Contexto Longo)&amp;rdquo;.&lt;/p>
&lt;h3 id="vantagens-e-desafios-do-rag-retrieval-augmented-generation">Vantagens e Desafios do RAG (Retrieval-Augmented Generation)
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>Vantagens&lt;/strong>: Baixo custo (já que apenas os chunks necessários são colocados no prompt), e é mais fácil identificar a fundamentação (fonte) da resposta.&lt;/li>
&lt;li>&lt;strong>Desafios&lt;/strong>: Uma vez que depende da precisão da busca semântica, ele não é adequado para tarefas de raciocínio complexo onde o contexto se espalha por vários documentos (ex: &amp;ldquo;A partir das atas de todas as reuniões do ano passado, analise cronologicamente a causa raiz dos atrasos no Projeto A&amp;rdquo;).&lt;/li>
&lt;/ul>
&lt;h3 id="contexto-longo-ex-2-milhões-de-tokens-do-gemini-15-pro">Contexto Longo (ex: 2 milhões de tokens do Gemini 1.5 Pro)
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>Vantagens&lt;/strong>: Nenhuma informação é perdida por conta da pesquisa. Mesmo no teste de &amp;ldquo;Achar a agulha num palheiro (Needle In A Haystack: NIAH)&amp;rdquo;, o Gemini 1.5 Pro e o Claude 3.5 Sonnet podem extrair informações com mais de 99% de precisão.&lt;/li>
&lt;li>&lt;strong>Desafios&lt;/strong>: O consumo de tokens torna-se enorme e eleva os custos, e há um aumento na latência ($T_{TTFT}$).&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Conclusão&lt;/strong>: A melhor prática em 2026 é uma &lt;strong>&amp;ldquo;Abordagem Híbrida&amp;rdquo;&lt;/strong>. A tendência principal de design é usar o RAG com bancos de dados vetoriais para Q&amp;amp;A rotineiros e usar um Contexto Longo em combinação com o cache de prompt para tarefas especializadas que exigem análises complexas ou a revisão de todo o código.&lt;/p>
&lt;hr>
&lt;h2 id="10-comparação-de-capacidades-de-processamento-multimodal">10. Comparação de Capacidades de Processamento Multimodal
&lt;/h2>&lt;p>Em aplicativos de IA de próxima geração, a capacidade de entender diretamente imagens, áudio e vídeo em vez de apenas texto é cada vez mais necessária.&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant User as "Usuário"
participant Client as "App Frontend"
participant API as "LLM API (Multimodal)"
User->>Client: Enviar Vídeo &amp; Prompt de Texto
Client->>API: Enviar Bytes/URI de Vídeo + Texto
Note over API: Fragmentação do vídeo &amp; Separação do áudio
Note over API: Modelo de Incorporação Multimodal
API-->>Client: Retornar Resumo de Texto &amp; Timestamps
Client-->>User: Mostrar Insights&lt;/div>
&lt;ul>
&lt;li>&lt;strong>OpenAI (GPT-4o)&lt;/strong>: Precisão de reconhecimento de imagem extremamente alta, superando na leitura de desenhos manuscritos e gráficos complexos. A interação por voz nativa usando a Realtime API com latência ultrabaixa (centenas de milissegundos) também é poderosa.&lt;/li>
&lt;li>&lt;strong>Google (Gemini 1.5 Pro)&lt;/strong>: &lt;strong>Apresenta superioridade esmagadora na análise de vídeos.&lt;/strong> É capaz de processar 1 hora de vídeo (quadros + áudio) de uma só vez e responder a perguntas pontuais como &amp;ldquo;Qual o título do documento segurado pela pessoa no canto direito da tela aos 12 minutos e 45 segundos?&amp;rdquo;.&lt;/li>
&lt;li>&lt;strong>Anthropic (Claude 3.5 Sonnet)&lt;/strong>: A capacidade de reconhecimento visual (Vision) está no mesmo nível do GPT-4o e é altamente capaz. Ele é incomparável em suporte ao desenvolvimento de frontend, lidando com pedidos como &amp;ldquo;Gere o código de componentes React a partir desta captura de tela da UI&amp;rdquo;.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="11-segurança-e-conformidade-de-nível-empresarial">11. Segurança e Conformidade de Nível Empresarial
&lt;/h2>&lt;p>A maior preocupação quando empresas usam APIs de LLM em ambientes de produção é &amp;ldquo;nossos dados serão usados para treinar a IA?&amp;rdquo; e &amp;ldquo;isso atende aos requisitos de conformidade?&amp;rdquo;.&lt;/p>
&lt;p>As 3 empresas declararam explicitamente que os dados enviados através de suas APIs (prompts e respostas) &lt;strong>não são usados para treinar os modelos (Zero Data Retention / No Training on Customer Data)&lt;/strong> (※ A interface de chat da Web gratuita para consumidores é diferente).&lt;/p>
&lt;p>Se for exigido um nível mais elevado de segurança:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>OpenAI&lt;/strong>: Usando o Azure OpenAI Service, pode-se obter a segurança de nível empresarial da Microsoft, SLAs e conexão privada via Azure Private Link.&lt;/li>
&lt;li>&lt;strong>Google&lt;/strong>: Usando o Google Cloud Vertex AI, é possível contar com o isolamento rígido de rede pelo VPC Service Controls e a proteção de dados através do CMEK (Chaves de Criptografia Gerenciadas pelo Cliente).&lt;/li>
&lt;li>&lt;strong>Anthropic&lt;/strong>: Ao utilizar as plataformas AWS Bedrock ou Google Cloud Vertex AI, as empresas podem se beneficiar da infraestrutura de segurança robusta desses provedores em nuvem.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="12-conclusão-guia-de-escolha-definitivo-por-caso-de-uso">12. Conclusão: Guia de Escolha Definitivo por Caso de Uso
&lt;/h2>&lt;p>Comparamos extensamente sob vários ângulos, mas, no final, a resposta para &amp;ldquo;Qual devo escolher?&amp;rdquo; depende do caso de uso.&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Desenvolvimento de Software Complexo, Geração de Código, Raciocínio Avançado&lt;/strong>:
&lt;strong>👑 Vencedor: Claude 3.5 Sonnet (Anthropic)&lt;/strong>
Atualmente oferece a melhor performance em entender o contexto de códigos, refatoração e elaboração de textos mais humanos e naturais. A facilidade de uso da API e o custo-benefício por causa do cache de prompt também são notáveis.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Análise de Documentos Ultralongos, Processamento em Lote de Vídeo/Áudio&lt;/strong>:
&lt;strong>👑 Vencedor: Gemini 1.5 Pro (Google)&lt;/strong>
A janela de contexto de 2 milhões de tokens é uma arma incomparável. Para tarefas que precisam compreender a visão geral dos dados, como a análise de manuais em PDF de centenas de páginas e o resumo de gravações de reuniões longas, o Gemini é imbatível.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Versatilidade, Velocidade de Execução, Saídas Estruturadas Estáveis (JSON)&lt;/strong>:
&lt;strong>👑 Vencedor: GPT-4o / GPT-4o-mini (OpenAI)&lt;/strong>
Lida bem com qualquer tarefa perfeitamente e oferece o suporte mais rico para ferramentas de terceiros. Se a sua empresa necessita de parsing de JSON confiável por meio das Structured Outputs (Saídas Estruturadas) ou raciocínio lógico muito avançado usando o modelo o1, o ecossistema da OpenAI é indispensável.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h3 id="recomendação-de-roteamento-multimodelo">Recomendação de Roteamento Multimodelo
&lt;/h3>&lt;p>A tendência futura de arquitetura é não depender (e sofrer vendor lock-in) de uma única API, mas adotar o &lt;strong>&amp;ldquo;Roteamento de LLM&amp;rdquo; (LLM Routing)&lt;/strong> para alternar de forma dinâmica o modelo dependendo da dificuldade e importância da tarefa.
Por exemplo, responder perguntas simples dos usuários usando as opções mais baratas e rápidas, como o &lt;code>GPT-4o-mini&lt;/code> ou o &lt;code>Gemini 1.5 Flash&lt;/code>, e recorrer ao &lt;code>Claude 3.5 Sonnet&lt;/code> apenas quando for determinado que um processamento complexo é necessário, para se obter o equilíbrio perfeito entre custo e desempenho.&lt;/p>
&lt;p>A evolução da IA é incessante. Entenda profundamente as forças e fraquezas de cada API e as características de sua arquitetura, e construa aplicações de IA flexíveis e escaláveis.&lt;/p></description></item><item><title>Como usar o llama.cpp e uma introdução à customização em C++</title><link>http://kenji.blog/pt/p/llama-cpp-cxx-customization/</link><pubDate>Fri, 11 Sep 2026 11:00:00 +0900</pubDate><guid>http://kenji.blog/pt/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 Como usar o llama.cpp e uma introdução à customização em C++" />&lt;p>Nos últimos anos, a evolução dos Grandes Modelos de Linguagem (LLM) tem sido impressionante, e sua área de aplicação se expande dia após dia. No entanto, para executar modelos com bilhões ou dezenas de bilhões de parâmetros em um ambiente local, normalmente é necessária uma GPU de ponta com uma quantidade enorme de VRAM. O &lt;strong>llama.cpp&lt;/strong> é o que quebrou essa &amp;ldquo;barreira de hardware&amp;rdquo; e tornou possível a inferência prática de LLM em PCs comuns, Macs e até dispositivos como o Raspberry Pi.&lt;/p>
&lt;p>Neste artigo, explicaremos de forma extremamente detalhada para engenheiros, indo além do simples uso de ferramentas de linha de comando, para cobrir a arquitetura de sua tecnologia base &lt;code>ggml&lt;/code>, a base matemática do Transformer e a quantização, e até mesmo métodos para incorporar e customizar LLMs em suas próprias aplicações usando a API C++.&lt;/p>
&lt;hr>
&lt;h2 id="1-visão-geral-do-llamacpp-e-ggml">1. Visão geral do llama.cpp e ggml
&lt;/h2>&lt;p>O &lt;code>llama.cpp&lt;/code> é um motor de inferência de LLM leve escrito em C/C++, desenvolvido por Georgi Gerganov. Inicialmente criado com o propósito de rodar o modelo LLaMA da Meta rapidamente no Apple Silicon (Macs M1/M2), agora ele suporta várias arquiteturas e modelos.&lt;/p>
&lt;p>Sua principal característica é ser uma &lt;strong>implementação pura em C/C++ sem dependências externas&lt;/strong>. Ele não requer grandes ecossistemas como Python ou PyTorch e pode ser compilado como um único arquivo executável, o que torna o deploy extremamente fácil.&lt;/p>
&lt;p>No coração deste &lt;code>llama.cpp&lt;/code> está a biblioteca de operações tensoriais &lt;strong>ggml&lt;/strong>. A ggml foi projetada do zero para otimizar operações de matriz em aprendizado de máquina no CPU (e algumas GPUs) ao limite absoluto.&lt;/p>
&lt;h3 id="11-por-que-o-llamacpp-é-tão-rápido">1.1 Por que o llama.cpp é tão rápido?
&lt;/h3>&lt;ol>
&lt;li>&lt;strong>Utilização de mapeamento de memória (mmap)&lt;/strong>: Ao carregar os pesos do modelo na memória, ele usa o &lt;code>mmap&lt;/code> do SO, evitando um carregamento completo na RAM, alcançando assim uma inicialização rápida e economia de memória.&lt;/li>
&lt;li>&lt;strong>Otimização exaustiva de instruções SIMD&lt;/strong>: Ele utiliza conjuntos de instruções específicos do CPU, como AVX2, AVX-512, ARM NEON e Apple AMX para acelerar enormemente a multiplicação de matrizes.&lt;/li>
&lt;li>&lt;strong>Quantização (Quantization)&lt;/strong>: Ele comprime pesos de ponto flutuante de 16 bits (FP16) em inteiros de 4 bits, 5 bits ou 8 bits, eliminando o gargalo na largura de banda da memória (mais detalhes abaixo).&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="2-base-matemática-transformer-e-quantização-quantization">2. Base Matemática: Transformer e Quantização (Quantization)
&lt;/h2>&lt;p>Para entender profundamente o llama.cpp, você precisa saber quais fórmulas matemáticas ele está calculando e como ele aproxima esses cálculos.&lt;/p>
&lt;h3 id="21-o-processo-de-inferência-do-transformer">2.1 O Processo de Inferência do Transformer
&lt;/h3>&lt;p>Modelos como o LLaMA adotam uma arquitetura de decodificador Transformer auto-regressiva (Auto-regressive). O núcleo da geração de texto é o mecanismo de &lt;strong>Self-Attention&lt;/strong>.&lt;/p>
&lt;p>Para uma matriz de estado oculto de entrada $X \in \mathbb{R}^{N \times d}$, as queries $Q$, keys $K$ e values $V$ são calculadas por produtos com matrizes de pesos.&lt;/p>
$$
Q = X W_Q, \quad K = X W_K, \quad V = X W_V
$$
&lt;p>Aqui, a saída da Attention é definida como a seguir.&lt;/p>
$$
\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V
$$
&lt;p>No loop de inferência do llama.cpp, o gargalo é o produto dessas enormes matrizes $W_Q, W_K, W_V$ ou das matrizes de pesos da rede feed-forward (FFN) com o vetor $X$ (já que processa um token por vez na fase de geração, $N=1$), isto é, &lt;strong>GEMV (General Matrix-Vector Multiplication)&lt;/strong>.&lt;/p>
&lt;h3 id="22-base-matemática-da-quantização-quantization">2.2 Base Matemática da Quantização (Quantization)
&lt;/h3>&lt;p>Na inferência, onde a largura de banda de acesso à memória se torna um gargalo, a quantização, que expressa parâmetros de peso em um número menor de bits, é essencial. Explicaremos o princípio básico da quantização em blocos amplamente utilizada no llama.cpp (por exemplo, &lt;code>Q4_K&lt;/code> e &lt;code>Q4_0&lt;/code>).&lt;/p>
&lt;p>Por exemplo, considere um bloco $w = [w_1, w_2, \dots, w_B]$ de comprimento $B$ (geralmente 32 ou 64) que é parte da matriz de pesos FP16 $W$. Nós aproximamos este bloco para um inteiro de 4 bits $q_i \in [-8, 7]$ e um único fator de escala $\Delta$ (FP16 ou FP32).&lt;/p>
$$
w_i \approx \Delta \times q_i
$$
&lt;p>O $\Delta$ é determinado com base no valor absoluto máximo dentro do bloco.&lt;/p>
$$
\Delta = \frac{\max_i |w_i|}{7}
$$
&lt;p>Ao calcular o produto escalar $y = w \cdot x$ usando os pesos quantizados, se o vetor de entrada $x$ for similarmente quantizado de modo que $x_i \approx \Delta_x \times q_{x, i}$, então&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>Esta parte de $\sum q_i q_{x, i}$ se torna uma &lt;strong>operação inteira pura&lt;/strong>, a qual pode ser calculada em paralelo em altíssima velocidade usando instruções SIMD. Este é o truque matemático de como o llama.cpp alcança sua velocidade incrível em CPUs.&lt;/p>
&lt;hr>
&lt;h2 id="3-arquitetura-e-fluxo-de-inferência">3. Arquitetura e Fluxo de Inferência
&lt;/h2>&lt;p>Para entender o funcionamento interno do llama.cpp, o diagrama Mermaid a seguir mostra a arquitetura de todo o sistema e o fluxo de dados.&lt;/p>
&lt;div class="mermaid">graph TD
A["Entrada do Usuário (String)"] --> B["Tokenizer llama.cpp"]
B --> C["IDs de Tokens (array int32)"]
C --> D["Buffer de Contexto (KV Cache)"]
D --> E["Grafo de Computação ggml"]
E --> F["Camadas do Transformer"]
subgraph "Motor ggml"
F --> G["Self-Attention (RoPE)"]
G --> H["Rede Feed Forward"]
H --> F
end
F --> I["Logits (Tamanho do Vocabulário)"]
I --> J["Sampler (Temperature, Top-K, Top-P)"]
J --> K["ID do Token Selecionado"]
K --> L["Detokenizer llama.cpp"]
L --> M["String de Saída"]
K -. "Loop auto-regressivo" .-> D&lt;/div>
&lt;p>A geração de texto é um loop auto-regressivo onde cada vez que um token é emitido, ele é adicionado ao KV Cache como a próxima entrada e passa novamente pelo grafo de computação.&lt;/p>
&lt;hr>
&lt;h2 id="4-configuração-do-ambiente-e-métodos-de-build">4. Configuração do Ambiente e Métodos de Build
&lt;/h2>&lt;p>Antes de incorporar o llama.cpp ao seu projeto em C++, vamos primeiro tentar compilar o código-fonte.&lt;/p>
&lt;h3 id="41-clonando-o-repositório">4.1 Clonando o Repositório
&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-compilando-usando-cmake">4.2 Compilando Usando CMake
&lt;/h3>&lt;p>Para integrá-lo em outros aplicativos como um projeto C++, usar o CMake é a abordagem mais padrão. Ativar o acelerador (backend) para cada plataforma pode aumentar a velocidade de computação.&lt;/p>
&lt;p>&lt;strong>Apenas CPU (Build básico):&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>Ao usar 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>Ao usar 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>Uma vez que a compilação for bem-sucedida, arquivos executáveis como o &lt;code>llama-cli&lt;/code>, bem como a biblioteca &lt;code>llama&lt;/code> (e a biblioteca &lt;code>ggml&lt;/code>) para vinculação com a API C++ descrita mais adiante, serão gerados no diretório &lt;code>build/bin/&lt;/code>.&lt;/p>
&lt;hr>
&lt;h2 id="5-introdução-à-customização-em-c-usando-a-api-do-llamacpp">5. Introdução à Customização em C++: Usando a API do llama.cpp
&lt;/h2>&lt;p>A partir daqui, discutiremos o tema principal: controlar o llama.cpp a partir de código C++.
Para não apenas usar a ferramenta de linha de comando, mas também incorporar o LLM em sua própria aplicação (como motores de jogos, aplicativos de desktop, sistemas embarcados, etc.), você precisa chamar a API C++ diretamente.&lt;/p>
&lt;p>O llama.cpp fornece uma interface de linguagem C principalmente através de um arquivo de cabeçalho chamado &lt;code>llama.h&lt;/code>. Quando você o chama do C++, você também utilizará esta interface.&lt;/p>
&lt;h3 id="51-includes-e-configurações-mínimas-necessárias">5.1 Includes e Configurações Mínimas Necessárias
&lt;/h3>&lt;p>Ao usar o llama.cpp em seu próprio projeto, você incluirá o seguinte.&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">// Macro para tratamento de erros
&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-carregando-o-modelo-e-inicializando-o-contexto">5.2 Carregando o Modelo e Inicializando o Contexto
&lt;/h3>&lt;p>Primeiro, carregamos um arquivo de modelo no formato &lt;code>.gguf&lt;/code> e alocamos um contexto (espaço de memória e KV cache) para inferência.&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. Inicialização do backend (configuração do ambiente como CPU/GPU, etc.)
&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. Obter configurações padrão de parâmetros do modelo
&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">// Número de camadas a descarregar para a 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. Carregando o modelo
&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. Configuração dos parâmetros de contexto
&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">// Tamanho máximo de contexto (em tokens)
&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">// Número de threads da CPU para inferência
&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. Criação do contexto
&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">// ... Processamento subsequente
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="53-tokenização-do-prompt-tokenization">5.3 Tokenização do Prompt (Tokenization)
&lt;/h3>&lt;p>Um LLM não entende textos diretamente, mas sim os processa como uma sequência de IDs inteiros (tokens). A string de entrada deve ser convertida em tokens.&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: Qual é a capital do Japão?&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">// Tamanho de buffer com margem
&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">// Adicionar um token especial (como BOS: Begin of Sequence) ao início
&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">// Converter a string para um array de IDs de tokens
&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">// Se o buffer for insuficiente, é necessário realocar e tentar novamente (omitido por simplificação)
&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-loop-de-inferência-e-amostragem-sampling">5.4 Loop de Inferência e Amostragem (Sampling)
&lt;/h3>&lt;p>Construímos um loop onde os tokens são inseridos no modelo, obtemos a distribuição de probabilidade (Logits) do próximo token e realizamos amostragem a partir dela para determinar o próximo token.&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">// Número máximo de tokens a gerar
&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">// Inicializar a estrutura para avaliação em lote (batch)
&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">// Adicionar tokens de prompt ao batch
&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">// Configurar para produzir logits (resultados previstos) apenas para o último token do prompt
&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">// Avaliação inicial (alimentando o modelo com o prompt)
&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">// Tamanho atual do contexto
&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">// Inicialização do contexto do sampler (configurações como Temperature, Top-K, Top-P, etc.)
&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">// Valor da semente (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. Amostragem: Prever o próximo token com base no contexto atual
&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. Se o token for EOS (End of Sequence), encerrar o loop
&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. Decodificar o token para uma string (texto) e exibir
&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. Preparar o token recém-gerado para o próximo batch
&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. Avaliação do modelo (atualiza a KV cache e prevê a seguir)
&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">// Cleanup (Limpeza)
&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>Este código implementa um loop de inferência customizado usando a API básica do llama.cpp.
Ele usa a estrutura &lt;code>llama_batch&lt;/code> para gerenciar grupos de tokens e executa um forward pass (propagação para frente) na rede neural com &lt;code>llama_decode&lt;/code>.&lt;/p>
&lt;hr>
&lt;h2 id="6-exemplo-de-customização-avançada-manipulação-de-logits-e-controle-de-penalidade-com-c">6. Exemplo de Customização Avançada: Manipulação de Logits e Controle de Penalidade com C++
&lt;/h2>&lt;p>Se além da simples geração de texto você deseja forçar a saída de um formato específico (por exemplo, apenas JSON) ou quer controlar para evitar a geração de palavras proibidas específicas, você manipula diretamente os &lt;strong>Logits&lt;/strong> antes da amostragem no lado do C++.&lt;/p>
&lt;p>Você pode obter o array de pontuações brutas (valores antes de serem convertidos para probabilidades) de cada token imediatamente antes que o modelo o emita.&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">// Logo após a inferência e antes de realizar a amostragem, obter o array bruto de logits
&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">// Lista de IDs de tokens proibidos (por exemplo, 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">// Define a probabilidade de um token proibido como 0 (o Logit vai para menos infinito)
&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>Dessa forma, usando a API C++ diretamente, uma &lt;strong>&amp;ldquo;intervenção em nível de microssegundo por ciclo de inferência&amp;rdquo;&lt;/strong> se torna possível, a qual pode ser muito difícil ou acarretar uma alta sobrecarga se feita via LangChain ou Python.&lt;/p>
&lt;hr>
&lt;h2 id="7-segredos-para-ajuste-de-desempenho">7. Segredos para Ajuste de Desempenho
&lt;/h2>&lt;p>Após concluir sua implementação em C++, apresentaremos alguns pontos de verificação para aumentar a velocidade ao limite para uma operação no mundo real.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Otimização de processamento em lote (Batching):&lt;/strong> Ao processar requisições de vários usuários simultaneamente, adicione múltiplas sequências no &lt;code>llama_batch&lt;/code> e chame &lt;code>llama_decode&lt;/code> de uma só vez (Continuous Batching). Isso permite compartilhar os acessos de memória, melhorando dramaticamente o throughput.&lt;/li>
&lt;li>&lt;strong>Ativando a Flash Attention:&lt;/strong>
Configurando &lt;code>ctx_params.flash_attn = true;&lt;/code> nos parâmetros de contexto, você pode acelerar os cálculos da Attention enquanto reduz o uso de memória. Esta é uma configuração obrigatória quando lidando com contextos longos (dezenas de milhares de tokens).&lt;/li>
&lt;li>&lt;strong>Suporte NUMA:&lt;/strong>
Em um ambiente de servidor multi-socket, você pode reduzir a latência de acesso à memória configurando corretamente as configurações NUMA antes do &lt;code>llama_backend_init()&lt;/code>.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="8-conclusão">8. Conclusão
&lt;/h2>&lt;p>Neste artigo, explicamos em detalhe desde a base matemática do &lt;code>llama.cpp&lt;/code> até a explicação de sua arquitetura, e como construir um motor de inferência customizado fazendo total uso da API em C++.&lt;/p>
&lt;p>O ecossistema Python é extremamente útil para prototipagem, mas em ambientes de produção que exigem implantações em dispositivos de borda, integração em jogos e processamento em tempo real, o controle direto do &lt;code>llama.cpp&lt;/code> baseado em C/C++ mostra um poder esmagador.&lt;/p>
&lt;p>Nós encorajamos fortemente que você tente escrever seu próprio código em C++ e experiencie a alegria de controlar e operar os LLMs livremente em seu ambiente local.&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>Lista de Links de Referência&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>【Atualizado em 2026】Guia Completo para Executar LLM Local em Ambiente Windows</title><link>http://kenji.blog/pt/p/local-llm-windows-2026/</link><pubDate>Fri, 11 Sep 2026 10:00:00 +0900</pubDate><guid>http://kenji.blog/pt/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 【Atualizado em 2026】Guia Completo para Executar LLM Local em Ambiente Windows" />&lt;h1 id="1-introdução-por-que-llm-local-no-windows-agora">1. Introdução: Por que LLM Local no Windows agora?
&lt;/h1>&lt;p>Em 2026, a evolução da IA generativa e dos Grandes Modelos de Linguagem (LLM) está mostrando uma grande mudança de paradigma de serviços gigantescos de API na nuvem para &amp;ldquo;LLMs Locais&amp;rdquo; rodando em PCs pessoais ou ambientes locais (on-premises). IAs na nuvem como o GPT-5 da OpenAI e o Claude 3.5 da Anthropic são extremamente poderosas, mas nem todas as empresas ou indivíduos podem enviar todos os seus dados para a nuvem. Do ponto de vista de privacidade, segurança, latência e custos sustentáveis a longo prazo, a demanda por LLMs locais explodiu como nunca antes.&lt;/p>
&lt;p>Particularmente no ambiente Windows, a evolução do ecossistema de LLM local tem sido notável. Até poucos anos atrás, &amp;ldquo;Desenvolvimento e execução de IA significa Linux&amp;rdquo; era o senso comum, mas em 2026, o Windows se transformou em uma plataforma de IA extremamente poderosa e acessível.&lt;/p>
&lt;p>Neste artigo, com base nas últimas tendências tecnológicas de 2026, forneceremos um guia completo para construir, operar e otimizar LLMs locais em ambientes Windows. Explicaremos detalhadamente, com um volume esmagador, desde configurações simples com o Ollama para iniciantes, passando por otimizações extremas usando o llama.cpp para usuários avançados, até abordagens matemáticas para cálculo de VRAM, compreensão profunda da arquitetura e fine-tuning local.&lt;/p>
&lt;h2 id="11-tendências-tecnológicas-de-llm-local-em-2026">1.1 Tendências Tecnológicas de LLM Local em 2026
&lt;/h2>&lt;p>As principais tendências que moldam o atual ecossistema de LLM local são as seguintes:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Adoção total do formato GGUF&lt;/strong>: O GGUF (GPT-Generated Unified Format), que integra metadados e tensores em um único arquivo, tornou-se completamente o padrão de fato. Isso permite a execução em qualquer ambiente apenas baixando um único arquivo do Hugging Face.&lt;/li>
&lt;li>&lt;strong>Democratização da arquitetura MoE (Mixture of Experts)&lt;/strong>: Vários modelos MoE pequenos, porém de alto desempenho, foram lançados. Ao ativar apenas alguns especialistas durante a inferência, eles alcançam um desempenho comparável ao de modelos gigantes, mantendo a carga computacional baixa em PCs de consumo.&lt;/li>
&lt;li>&lt;strong>Abstração avançada e otimização de motores de inferência&lt;/strong>: Ferramentas como Ollama, LM Studio e AnythingLLM foram refinadas, eliminando a necessidade de os usuários se preocuparem com dependências complexas, como a instalação de drivers CUDA. Além disso, o suporte nativo do FlashAttention 3 para Windows melhorou drasticamente a velocidade de inferência.&lt;/li>
&lt;li>&lt;strong>Utilização da NPU e a ascensão dos PCs Windows Copilot+&lt;/strong>: Mesmo em laptops sem GPU, a tecnologia para executar pequenos LLMs (SLMs: Small Language Models) com baixo consumo de energia usando a NPU (Neural Processing Unit) integrada entrou em fase prática.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h1 id="2-requisitos-de-hardware-e-preparação-do-so">2. Requisitos de Hardware e Preparação do SO
&lt;/h1>&lt;p>Para que um LLM local opere em uma velocidade prática (15 a 30+ tokens por segundo), a escolha do hardware é o fator mais importante.&lt;/p>
&lt;h2 id="21-configuração-de-hardware-recomendada">2.1 Configuração de Hardware Recomendada
&lt;/h2>&lt;p>Com a evolução dos AI PCs, as especificações exigidas também estão mudando.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>SO&lt;/strong>: Windows 11 Pro (24H2 ou posterior). Essencial para utilizar as funções completas do WSL2, gerenciamento avançado de memória e as APIs mais recentes do DirectML.&lt;/li>
&lt;li>&lt;strong>CPU&lt;/strong>: Série Intel Core Ultra 200 ou superior, ou série AMD Ryzen 9000 ou superior. Quando usado em conjunto com inferência de CPU, a comunicação de memória de alta largura de banda é indispensável.&lt;/li>
&lt;li>&lt;strong>RAM&lt;/strong>: Mínimo de 32GB, recomendado 64GB ou mais. A largura de banda da memória principal (MB/s) torna-se o gargalo decisivo durante a inferência na CPU ou offloading. Memória de alta velocidade, como DDR5-6000 ou superior, é o ideal.&lt;/li>
&lt;li>&lt;strong>GPU&lt;/strong>: Séries NVIDIA RTX 4000/5000. O que mais importa para LLMs locais não é o desempenho de processamento, mas sim a &amp;ldquo;Capacidade de VRAM&amp;rdquo;.
&lt;ul>
&lt;li>&lt;strong>Entrada&lt;/strong>: RTX 4060 Ti (versão de 16GB) - Melhor custo-benefício. Ideal para modelos na classe de 8B a 14B.&lt;/li>
&lt;li>&lt;strong>Intermediário&lt;/strong>: RTX 4070 Ti SUPER (16GB) / RTX 4080 SUPER (16GB)&lt;/li>
&lt;li>&lt;strong>High-end&lt;/strong>: RTX 4090 (24GB) / RTX 5090 (32GB) - Necessário para rodar modelos quantizados na classe de 30B a 70B.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Armazenamento&lt;/strong>: NVMe SSD PCIe Gen4 ou Gen5. Reduz drasticamente o tempo de carregamento de dezenas de GB de modelos.&lt;/li>
&lt;/ul>
&lt;h2 id="22-configuração-do-wsl2-windows-subsystem-for-linux-2">2.2 Configuração do WSL2 (Windows Subsystem for Linux 2)
&lt;/h2>&lt;p>Embora muitas ferramentas GUI funcionem nativamente no Windows, o WSL2 é extremamente útil para desenvolvimento com Python, compilação de ferramentas mais recentes e para o fine-tuning LoRA mencionado posteriormente. Nos ambientes Windows 11 mais recentes, você pode usar GPUs (CUDA) de forma transparente no WSL2 apenas instalando o driver da NVIDIA no host.&lt;/p>
&lt;p>Abra o PowerShell com privilégios de administrador e execute o seguinte:&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"># Instalação do WSL2 e do Ubuntu mais recente&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"># Atualização do kernel&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>Após a instalação, execute &lt;code>nvidia-smi&lt;/code> no terminal do WSL2; se a GPU for reconhecida corretamente, a configuração foi bem-sucedida.&lt;/p>
&lt;hr>
&lt;h1 id="3-arquitetura-de-llm-local-e-mecanismo-de-inferência">3. Arquitetura de LLM Local e Mecanismo de Inferência
&lt;/h1>&lt;p>Compreender a estrutura interna de como o modelo gera texto em um ambiente local é extremamente útil para solução de problemas e otimização.&lt;/p>
&lt;p>O diagrama Mermaid abaixo mostra um pipeline típico de inferência de LLM local.&lt;/p>
&lt;div class="mermaid">graph TD
User["Entrada do usuário (Prompt)"] --> Tokenizer["Tokenizador (Tokenizer)"]
Tokenizer --> Embedding["Camada de Embedding (Embedding)"]
subgraph "Transformer Block (x Layers)"
Embedding --> Attn["Autoatenção (Self-Attention)"]
Attn --> KVCache["Cache KV (Retenção Key/Value)"]
Attn --> FFN["Rede Feed-Forward (FFN)"]
end
FFN --> Logits["Cálculo de Logits (Logits)"]
Logits --> Sampler["Amostrador (Temperature, Top-K, Top-P)"]
Sampler --> OutputToken["Token de saída"]
OutputToken --> |"Geração autorregressiva"| Tokenizer
OutputToken --> Decoder["Detokenizador (Detokenizer)"]
Decoder --> FinalOutput["Texto de saída final"]&lt;/div>
&lt;h2 id="31-duas-fases-prefill-e-decode">3.1 Duas Fases: Prefill e Decode
&lt;/h2>&lt;p>A geração de texto do LLM é dividida em duas fases com características computacionais diferentes.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Fase Prefill (Processamento de Prompt)&lt;/strong>: A fase onde todo o prompt inserido é processado e compreendido de uma só vez. Devido à possibilidade de cálculo paralelo, a capacidade de cálculo da GPU (FLOPS) está diretamente ligada à velocidade. Se o prompt for longo, esta fase pode levar alguns segundos.&lt;/li>
&lt;li>&lt;strong>Fase Decode (Geração de Tokens)&lt;/strong>: A fase onde se prevê um token de cada vez, que é repassado como próxima entrada (autorregressivo). Nesta fase, o cálculo paralelo é limitado, portanto a largura de banda de memória VRAM da GPU (Memory Bandwidth) se torna um gargalo decisivo.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h1 id="4-cálculo-do-consumo-de-vram-e-compreensão-matemática-do-tamanho-do-modelo">4. Cálculo do Consumo de VRAM e Compreensão Matemática do Tamanho do Modelo
&lt;/h1>&lt;p>Para julgar corretamente &amp;ldquo;quais modelos rodam no meu PC?&amp;rdquo;, é necessário entender a fórmula de cálculo da VRAM. Se a falta de VRAM causar fallback para a memória do sistema (RAM), a velocidade de inferência cairá de 10x a 100x.&lt;/p>
&lt;h2 id="41-vram-base-com-base-no-tamanho-dos-parâmetros">4.1 VRAM Base com Base no Tamanho dos Parâmetros
&lt;/h2>&lt;p>Esta é a quantidade de memória necessária para carregar os pesos (weights) do modelo para a VRAM.
É calculada usando o tamanho do modelo $P$ (número de parâmetros, unidade: 1 bilhão = 1B) e os bytes por parâmetro $B$.&lt;/p>
$$
V_{base} = P \times B \quad \text{(GB)}
$$
&lt;p>Por exemplo, ao carregar um modelo de 8B (8 bilhões) de parâmetros em FP16 (ponto flutuante de meia precisão, 16 bits = 2 bytes):&lt;/p>
$$
V_{base} = 8 \times 2 = 16 \text{ GB}
$$
&lt;p>Em outras palavras, mesmo uma GPU com 16GB de VRAM estará no seu limite apenas carregando o modelo.&lt;/p>
&lt;h2 id="42-a-magia-da-quantização-quantization">4.2 A Magia da Quantização (Quantization)
&lt;/h2>&lt;p>Aí entra a &amp;ldquo;quantização&amp;rdquo;. Ao reduzir a precisão dos parâmetros, o tamanho do modelo é reduzido drasticamente. Na quantização de 4 bits mais comum (ex: Q4_K_M), a média é cerca de 0,55 bytes por parâmetro.&lt;/p>
$$
V_{base\_4bit} = 8 \times 0.55 = 4.4 \text{ GB}
$$
&lt;p>Com isso, se você tiver 16GB de VRAM, terá bastante margem para rodar um modelo 8B de forma muito tranquila.&lt;/p>
&lt;h2 id="43-cálculo-do-cache-kv-versão-compatível-com-gqa">4.3 Cálculo do Cache KV (Versão Compatível com GQA)
&lt;/h2>&lt;p>Durante a inferência, o &amp;ldquo;Cache KV&amp;rdquo;, usado para manter o contexto passado, consome VRAM. Nos modelos mais recentes, como o Llama 3, o GQA (Grouped Query Attention) é adotado para economizar memória.&lt;/p>
&lt;p>O consumo do Cache KV $V_{kv}$ (em gigabytes) é expresso pela seguinte fórmula:&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>Simplificando, podemos calcular usando o número de cabeças de chave/valor (key/value heads) $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>Onde:&lt;/p>
&lt;ul>
&lt;li>$b$: Tamanho do batch (geralmente 1 para uso local pessoal)&lt;/li>
&lt;li>$s$: Comprimento da sequência (comprimento do contexto, ex: 8192)&lt;/li>
&lt;li>$l$: Número de camadas (ex: 32)&lt;/li>
&lt;li>$h_{kv}$: Número de cabeças KV (ex: 8)&lt;/li>
&lt;li>$d$: Dimensões por cabeça (ex: 128)&lt;/li>
&lt;li>$B_{kv}$: Bytes do Cache KV (2 para FP16)&lt;/li>
&lt;/ul>
&lt;p>Exemplo de cálculo (Llama 3 8B, Contexto 8192, Cache FP16):
$V_{kv} = 2 \times 1 \times 8192 \times 32 \times 8 \times 128 \times 2 \div 10^9 \approx 1.07 \text{ GB}$&lt;/p>
&lt;p>Note que quanto mais longo for o comprimento do contexto $s$, mais a VRAM necessária aumentará linearmente.&lt;/p>
&lt;hr>
&lt;h1 id="5-prática-1-configuração-rápida-e-curta-com-ollama">5. Prática 1: Configuração Rápida e Curta com Ollama
&lt;/h1>&lt;p>Agora que entendemos a teoria, vamos rodar um LLM num ambiente Windows na prática.
A partir de 2026, a ferramenta mais amigável ao usuário é o &amp;ldquo;Ollama&amp;rdquo;. Ele fornece uma CLI intuitiva semelhante à do Docker.&lt;/p>
&lt;h2 id="51-instalação-e-execução">5.1 Instalação e Execução
&lt;/h2>&lt;ol>
&lt;li>Baixe e execute o instalador para Windows no &lt;a class="link" href="https://ollama.com/" target="_blank" rel="noopener"
>Site Oficial do Ollama&lt;/a>.&lt;/li>
&lt;li>Abra o PowerShell e insira o seguinte comando. Aqui usaremos o &lt;code>llama3:8b&lt;/code>, que possui suporte para japonês.&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>Na primeira execução, o modelo será baixado. Ao concluir, você poderá conversar diretamente no terminal.&lt;/p>
&lt;h2 id="52-criação-de-uma-ia-customizada-via-modelfile">5.2 Criação de uma IA Customizada via Modelfile
&lt;/h2>&lt;p>Você pode criar facilmente uma IA com uma persona específica. Crie um &lt;code>Modelfile&lt;/code> em qualquer local.&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">Você é um excelente engenheiro de software sênior.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Sempre responda às perguntas dos usuários com exemplos de código, de forma lógica e concisa.
&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>Execute os comandos a seguir para compilar e executar o seu próprio modelo customizado.&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-utilização-de-aplicações-externas-editores-de-ia">5.3 Utilização de Aplicações Externas (Editores de IA)
&lt;/h2>&lt;p>O Ollama expõe um endpoint de API compatível com OpenAI em &lt;code>http://localhost:11434&lt;/code>.
Ao especificar esta URL nas configurações de backend de extensões do VS Code, como Cursor ou Continue.dev, e designar o nome do modelo como &lt;code>SeniorDev&lt;/code>, você tem um assistente de codificação local poderoso e gratuito.&lt;/p>
&lt;hr>
&lt;h1 id="6-prática-2-afinação-extrema-de-desempenho-com-llamacpp">6. Prática 2: Afinação Extrema de Desempenho com llama.cpp
&lt;/h1>&lt;p>Se você quiser ter uma gestão fina de memória ou testar antecipadamente novos formatos (como EXL2, quantização IQ), interaja diretamente com o mecanismo central &lt;code>llama.cpp&lt;/code>.&lt;/p>
&lt;h2 id="61-procedimento-de-compilação-do-llamacpp">6.1 Procedimento de Compilação do llama.cpp
&lt;/h2>&lt;p>No ambiente Windows, a melhor forma é compilá-lo a partir do código-fonte usando CUDA Toolkit e 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"># Configurar e compilar com suporte a 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-execução-avançada-no-modo-servidor">6.2 Execução Avançada no Modo Servidor
&lt;/h2>&lt;p>Use o &lt;code>llama-server.exe&lt;/code> compilado para hospedar o modelo.&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>: Descarrega (offload) todas as camadas possíveis para a VRAM da GPU.&lt;/li>
&lt;li>&lt;code>--flash-attn&lt;/code>: Habilita o FlashAttention 3, alcançando uma melhoria de velocidade de inferência e redução de consumo de VRAM para o Cache KV.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h1 id="7-front-end-gui-lm-studio-e-construção-de-rag-local">7. Front-end GUI: LM Studio e Construção de RAG Local
&lt;/h1>&lt;p>Se você não gosta de linhas de comando ou quer usar a RAG (Geração Aumentada por Recuperação) intuitivamente, utilize uma GUI.&lt;/p>
&lt;h2 id="71-lm-studio">7.1 LM Studio
&lt;/h2>&lt;p>O LM Studio é um aplicativo maravilhoso que centraliza a busca de modelos, o download, as verificações de pré-requisitos do sistema e a interface de chat. Ao simplesmente apertar o botão &amp;ldquo;Local Server&amp;rdquo; no app, uma API compatível com OpenAI é iniciada.&lt;/p>
&lt;h2 id="72-arquitetura-de-rag-usando-o-anythingllm">7.2 Arquitetura de RAG usando o AnythingLLM
&lt;/h2>&lt;p>Este é o diagrama de arquitetura do ambiente RAG para carregar documentos internos e notas pessoais.&lt;/p>
&lt;div class="mermaid">graph LR
Document["Documentos (PDF, MD)"] --> Chunking["Divisão em Chunks"]
Chunking --> EmbedModel["Modelo de Embedding"]
EmbedModel --> VectorDB["Banco de Dados Vetorial"]
UserQuery["Pergunta do usuário"] --> EmbedQuery["Embedding da Pergunta"]
EmbedQuery --> VectorDB
VectorDB --> |"Busca de similaridade"| RetrievedDocs["Extração de Documentos Relevantes"]
UserQuery --> PromptBuilder["Geração do Prompt"]
RetrievedDocs --> PromptBuilder
PromptBuilder --> LocalLLM["LLM Local"]
LocalLLM --> Answer["Resposta Final"]&lt;/div>
&lt;p>Com a versão desktop do AnythingLLM (Windows), você só precisa designar o Ollama (para LLM e Embedding) e configurar o VectorDB local (LanceDB) nas opções, e essa arquitetura estará concluída em minutos. É o nascimento de uma IA Privada que nunca envia os dados a terceiros.&lt;/p>
&lt;hr>
&lt;h1 id="8-fine-tuning-no-wsl2-do-windows-lora">8. Fine-tuning no WSL2 do Windows (LoRA)
&lt;/h1>&lt;p>Além de rodar os modelos localmente, se você deseja que eles fiquem mais inteligentes com seus próprios dados, o fine-tuning através do LoRA (Low-Rank Adaptation) é possível. Em 2026, com uma biblioteca chamada &amp;ldquo;Unsloth&amp;rdquo;, o treinamento de um modelo 8B é finalizado em algumas horas, mesmo com 16GB de VRAM, no ambiente Windows WSL2.&lt;/p>
&lt;p>Crie o ambiente executando o seguinte dentro do Ubuntu do 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>O Unsloth otimiza ao extremo os kernels do CUDA. Se comparado a uma biblioteca padronizada do Hugging Face, sua velocidade de treinamento é duas vezes maior e o consumo de VRAM é reduzido pela metade. Bastará iniciar um Jupyter Notebook e carregar o seu dataset (formato JSONL) para que ocorram as épocas de treinamento em GPUs com VRAM entre 12GB a 16GB, como a RTX 4060 Ti, por exemplo.&lt;/p>
&lt;hr>
&lt;h1 id="9-solução-de-problemas-de-desempenho">9. Solução de Problemas de Desempenho
&lt;/h1>&lt;p>Abaixo, encontre os problemas comumente encontrados e suas soluções.&lt;/p>
&lt;h3 id="1-a-velocidade-de-inferência-é-extremamente-lenta-12-tokenss">1. A velocidade de inferência é extremamente lenta (1~2 tokens/s)
&lt;/h3>&lt;p>&lt;strong>Causa&lt;/strong>: O modelo extrapolou a capacidade da VRAM e o processamento offload recaiu à memória do sistema (RAM).
&lt;strong>Solução&lt;/strong>: Inspecione o espaço de &amp;ldquo;Memória da GPU Dedicada&amp;rdquo; no Gerenciador de Tarefas do Windows. Se já atingiu o limite, reduza o tamanho do contexto (&lt;code>-c&lt;/code>) ou utilize um modelo quantizado com menor número de bits (como o Q4_K_M).&lt;/p>
&lt;h3 id="2-erro-cuda-out-of-memory">2. Erro &amp;ldquo;CUDA out of memory&amp;rdquo;
&lt;/h3>&lt;p>&lt;strong>Causa&lt;/strong>: A VRAM foi completamente esgotada. Geralmente ocorre quando a conversa se estende, inchando o tamanho do Cache KV.
&lt;strong>Solução&lt;/strong>: Restrinja intencionalmente o valor de &lt;code>num_ctx&lt;/code> no Ollama ou o parâmetro &lt;code>-c&lt;/code> do llama.cpp para um número mais baixo.&lt;/p>
&lt;h3 id="3-a-geração-em-japonês-está-estranha">3. A geração em japonês está estranha
&lt;/h3>&lt;p>&lt;strong>Causa&lt;/strong>: Incompatibilidade no template de prompt ou o modelo não possui suporte.
&lt;strong>Solução&lt;/strong>: Utilize modelos que incluam &lt;code>Instruct&lt;/code> no nome, e confirme na ferramenta se o template exigido pelos criadores do modelo, como os formatos ChatML ou Llama3, encontra-se devidamente selecionado.&lt;/p>
&lt;hr>
&lt;h1 id="10-resumo-e-perspectivas-futuras">10. Resumo e Perspectivas Futuras
&lt;/h1>&lt;p>Em 2026, criar um ambiente local de LLM no Windows não é mais privilégio de apenas alguns engenheiros. Devido à padronização do formato GGUF, à ascensão de ecossistemas refinados como Ollama e LM Studio, e a otimizações de hardware como FlashAttention, qualquer pessoa pode obter um ambiente de IA corporativo com extrema facilidade.&lt;/p>
&lt;p>Aproveite ao máximo os seguintes pontos detalhados neste artigo:&lt;/p>
&lt;ol>
&lt;li>Fazer uso do &lt;strong>cálculo matemático de VRAM&lt;/strong> para logicamente selecionar o nível de quantização e o tamanho do modelo ideais para as especificações do seu computador.&lt;/li>
&lt;li>Usar o &lt;strong>Ollama&lt;/strong> para um ambiente o mais rápido de construir e integrá-lo a um editor de IA, com o objetivo de aumentar drasticamente a produtividade.&lt;/li>
&lt;li>Usar os parâmetros de controle avançado no &lt;strong>llama.cpp&lt;/strong> para extrair as margens de desempenho do hardware.&lt;/li>
&lt;li>Construir através do &lt;strong>AnythingLLM&lt;/strong> um ambiente e sistema RAG Local seguro o suficiente para manipular os dados da sua empresa.&lt;/li>
&lt;li>Fazer uso do &lt;strong>Unsloth (WSL2)&lt;/strong> para criar a sua própria customizada IA, baseada no seu nível e especialidade de conhecimento.&lt;/li>
&lt;/ol>
&lt;p>A &amp;ldquo;democratização&amp;rdquo; da IA não se resume a um mero jargão tecnológico. Trata-se do real cenário em que os próprios sistemas são executados na tela de desktop de seu Windows. Livre-se dos riscos de vazamento de dados e custos nas APIs em nuvem, e comece hoje mesmo sua imersão em um mundo onde a IA Privada é poderosa e completamente sua.&lt;/p></description></item></channel></rss>