Versão original em inglês: Running a coding model locally with Ollama.
Rodar um modelo de programação capaz na sua própria GPU exige uma instalação e um punhado de comandos. A parte melhor é o que vem depois: o modelo não é uma caixa-preta. Você consegue ler quanta memória ele usa, quão rápido ele escreve e quanto texto ele consegue segurar de uma vez, e nenhum dos seus prompts jamais sai da máquina.
Inferência local: o modelo se muda para o seu laptop
Inferência local significa rodar o modelo na sua própria máquina em vez de chamar
uma API hospedada. O Ollama é o caminho mais rápido até lá. Ele baixa modelos de
pesos abertos (open-weight), serve-os por trás de uma API HTTP local em
http://localhost:11434 e os roda na sua GPU. Pesos abertos significa que os pesos
treinados, os números que o modelo aprendeu durante o treinamento, são publicados para
você baixar e rodar, ao contrário de modelos apenas via API, como Claude ou GPT, cujos
pesos nunca saem do fornecedor.
Uma família de modelos é uma linhagem: quem treinou os pesos. Quatro valem a pena conhecer pelo nome:
- Llama (Meta)
- Qwen (Alibaba)
- Gemma (Google)
- DeepSeek (DeepSeek)
Você escolhe uma família e um tamanho, e o Ollama baixa uma build comprimida para caber na memória da sua máquina.
Instale o Ollama e o modelo
Cinco comandos levam você do zero a um modelo respondendo na sua máquina:
- Instale o Ollama com o Homebrew (no Linux ou Windows, use o instalador em ollama.com/download).
- Inicie o servidor; ele escuta em
http://localhost:11434. - Baixe (pull) um modelo de programação.
- Liste o que você baixou, com o tamanho em disco.
- Rode uma vez com um prompt para confirmar que ele responde localmente.
brew install ollama
brew services start ollama # serve em http://localhost:11434
# nomes de modelo seguem família:tamanho: deepseek-coder-v2:16b é o modelo de programação da DeepSeek
# na sua build de 16 bilhões de parâmetros (o que é um parâmetro vem com a ficha técnica)
ollama pull deepseek-coder-v2:16b
# ollama list mostra o que você baixou, e o tamanho em disco
ollama listO ollama list reporta ele em disco:
NAME ID SIZE MODIFIED
deepseek-coder-v2:16b 63fb193b3a9b 8.9 GB 2 minutes agoO ollama run escreve o código e imprime um rodapé --verbose:
# --verbose acrescenta estatísticas de tempo depois da resposta
ollama run deepseek-coder-v2:16b --verbose "Write a one-line hello world in Python."
```python
print("Hello, World!")
```
total duration: 5.994714959s
load duration: 5.659750709s
prompt eval count: 18 token(s)
prompt eval duration: 70.487ms
prompt eval rate: 255.37 tokens/s
eval count: 28 token(s)
eval duration: 262.95ms
eval rate: 106.48 tokens/sO rodapé são duas fases, leitura e depois escrita:
- O modelo não vê letras. O texto é dividido em tokens, pequenos pedaços, mais ou
menos partes de palavras. O prompt acima, mais o texto de moldura que o Ollama
adiciona ao redor dele, se divide em 18 deles: o
prompt eval count. - Antes de escrever qualquer coisa, o modelo lê todos os 18 tokens para montar o que
você pediu. Essa fase levou
70.487ms, então a prompt eval rate, 18 tokens em 70.487 ms, é255.37 tokens/s: a velocidade de leitura dele. - Só então a geração começa: a resposta de 28 tokens, escrita à eval rate de
106.48 tokens/s. Esse número é a throughput (vazão) do modelo.
Lendo a ficha técnica
O ollama show imprime a ficha técnica do modelo:
ollama show deepseek-coder-v2:16b Model
architecture deepseek2
parameters 15.7B
context length 163840
embedding length 2048
quantization Q4_0
Capabilities
completion
insert
Parameters
stop "User:"
stop "Assistant:"
License
DEEPSEEK LICENSE AGREEMENT
Version 1.0, 23 October 2023- parameters (
15.7B): quantos pesos o modelo tem.- Escrever um token significa passar pesos por contas matemáticas: quanto mais pesos participam, mais devagar cada token sai.
- Em um modelo denso (dense), todo peso participa de todo token.
- O DeepSeek-Coder-V2 é, em vez disso, um modelo mixture-of-experts (mistura de especialistas): os pesos são agrupados em pequenas sub-redes especialistas, e um roteador escolhe os poucos especialistas relevantes para cada token, do jeito que uma clínica mantém muitos especialistas no corpo clínico mas te encaminha só para os dois que o seu caso precisa.
- Apenas cerca de 2.4B dos seus 15.7B pesos trabalham em cada token, então ele escreve
como um modelo pequeno (os
106.48 tokens/sacima) enquanto carrega o conhecimento de um modelo grande. - Todos os 15.7B ainda precisam ficar na memória.
- context length (
163840): a janela, o máximo de tokens que o modelo consegue segurar de uma vez.- Prompt e saída compartilham ela: tudo o que o modelo lê e tudo o que ele escreve precisa caber dentro.
- Em ~160K, esta aqui foi feita para contexto longo.
- Essa janela é a maior alavanca sobre a memória.
- embedding length (
2048): os 2048 números que descrevem cada token.- Eles vêm de 16 attention heads (cabeças de atenção), leitoras paralelas do código.
- Cada cabeça contribui com 128 números: 16 × 128 = 2048.
- quantization (
Q4_0): a precisão com que os pesos são armazenados, o botão de tamanho-versus-qualidade.- Cada peso normalmente é um número de 16 bits, a precisão em que o modelo foi treinado e distribuído.
Q4mantém apenas 4 bits dele. Quatro bits só conseguem cair em 16 valores possíveis, então cada peso é arredondado para o mais próximo deles.- Menos bits por peso deixa o modelo cerca de 4× menor, por um pouco de precisão perdida.
O bloco Capabilities é o que o modelo sabe fazer:
completion: gerar texto.insert: fill-in-the-middle, o truque de preencher o meio do código que caracteriza um modelo de programação.- Sem
tools: function calling (chamada de função), em que o modelo responde com uma chamada estruturada para o seu código executar em vez de prosa, não está no repertório deste modelo, então a API de tools do Ollama não vai conseguir dirigi-lo. - Para ganhar uma capacidade, escolha um modelo que já a liste (
qwen2.5-coder, por exemplo, adicionatools). As capacidades são definidas pelo modelo e pelo seu template, o texto de moldura que o Ollama coloca ao redor do seu prompt antes de o modelo vê-lo. Listar uma capacidade e entregá-la são coisas diferentes; só uma sonda decide qual dos dois um flag representa.
Entendendo o embedding length
Cada uma das 16 cabeças lê um token em relação ao resto do código buscando uma relação
diferente. Pegue o total na última linha aqui:
def checkout(cart, user):
total = 0
for item in cart:
total += item.price
if user.is_member:
total *= 0.9
return totalCabeças diferentes ligam esse total a tokens anteriores diferentes ao mesmo tempo:
- uma cabeça →
total = 0, onde ele foi declarado - uma cabeça →
total += item.price, onde o preço de cada item é somado - uma cabeça →
total *= 0.9, seu valor mais recente, o desconto de membro - uma cabeça →
def checkout, marcando-o como o valor de retorno da função
Cada cabeça registra sua leitura como 128 números, um ponto em 128 dimensões, do mesmo
jeito que (x, y) é um ponto em 2 dimensões e (r, g, b) uma cor em 3. Empilhe todas
as 16 cabeças e você tem a descrição completa do token, de 2048 números:
token "total" 128 números por cabeça
head 1 [ 0.12 -0.03 0.88 … 0.05 ]
head 2 [-0.44 0.21 0.01 … -0.10 ]
head 3 [ 0.07 0.55 -0.32 … 0.19 ]
⋮
head 16 [ 0.31 -0.09 0.42 … -0.02 ]
──────────────────────────────────────────────
16 cabeças × 128 = 2048 númerosOs valores acima são ilustrativos. O Ollama reporta memória e throughput, não as ativações internas do modelo, então um dump real por cabeça precisa de um framework como o PyTorch.
Observabilidade do modelo
O ollama ps e o endpoint /api/ps te dizem como o modelo está rodando agora. O
ollama run da instalação o carregou na janela padrão, a janela que o Ollama usa
quando você não define nada, então é esse o estado em exibição:
ollama psNAME ID SIZE PROCESSOR CONTEXT UNTIL
deepseek-coder-v2:16b 63fb193b3a9b 13 GB 100% GPU 32768 4 minutes from nowPROCESSOR é a coluna que importa:
- Um modelo roda mantendo seus pesos na memória, e há dois reservatórios para isso:
- a VRAM da própria GPU, rápida para esse tipo de conta
- a RAM comum do sistema, que a CPU alcança e é bem mais lenta
100% GPUsignifica que toda camada cabe na VRAM, então a GPU faz todo o trabalho, aos106.48 tokens/sque a rodada de instalação mediu.- Quando um modelo é maior que a VRAM livre, o Ollama carrega o que cabe na GPU e joga o
resto para a RAM do sistema; o
ollama psentão mostra uma divisão como60% GPU / 40% CPU. - Essa fatia de
CPUé a parte do modelo rodando no caminho lento, e a throughput despenca, de dezenas de tokens por segundo para um só dígito.
/api/ps é o mesmo relatório por HTTP, com contagens exatas de bytes:
curl -s localhost:11434/api/ps | jq -r '
.models[] |
"\(.name)\n VRAM: \(.size_vram/1e9*10|round/10) GB of \(.size/1e9*10|round/10) GB (\(.size_vram/.size*100|round)% on GPU)\n Context: \(.context_length) tokens\n Quant: \(.details.quantization_level), \(.details.parameter_size) params"
'deepseek-coder-v2:16b
VRAM: 13.8 GB of 13.8 GB (100% on GPU)
Context: 32768 tokens
Quant: Q4_0, 15.7B paramsMesmo na janela padrão, o valor de VRAM supera os 8.9 GB que o ollama list reportou
em disco lá na instalação:
- O disco guarda apenas os pesos quantizados.
- A VRAM guarda os pesos mais um cache por token que cresce com a janela carregada.
A janela de contexto é a maior alavanca sobre a memória
Uma janela de contexto maior não é de graça:
- Para evitar reprocessar todo o contexto a cada novo token, o modelo faz cache de um pequeno pedaço de dado para cada token que já viu: o KV cache.
- Esse cache cresce com a janela, e você consegue ver ele se mexer.
O 163840 do ollama show é um teto, não um tamanho fixo:
- Você escolhe a janela real por rodada com a opção
num_ctx, até esse teto, e o modelo carrega com o valor que você escolher. - É o mesmo
deepseek-coder-v2:16bo tempo todo, só a janela muda.
O ollama run não tem uma flag de linha de comando para a janela, então o num_ctx é
definido de outro jeito:
- sessão interativa: rode o modelo, depois
/set parameter num_ctx 32768 - env do servidor:
OLLAMA_CONTEXT_LENGTH=32768 ollama serve - API:
options.num_ctxna requisição
Carregue com 32K (K = 1024 tokens), ou 32 × 1024 = 32768 tokens. Leia a pegada de
memória logo em seguida:
# num_ctx=32768 carrega com uma janela de 32K; resposta descartada, só queremos o load
curl -s localhost:11434/api/generate \
-d '{"model":"deepseek-coder-v2:16b","options":{"num_ctx":32768},"prompt":"hi"}' \
>/dev/null
curl -s localhost:11434/api/ps \
| jq -r '.models[] | "\(.context_length) ctx \(.size_vram/1e9*10|round/10) GB VRAM \(.size_vram/.size*100|round)% GPU"'32768 ctx 13.8 GB VRAM 100% GPUDepois o mesmo modelo em 128K, quatro vezes a janela:
# mesmo modelo, num_ctx=131072 recarrega com uma janela de 128K; resposta descartada, só queremos o load
curl -s localhost:11434/api/generate \
-d '{"model":"deepseek-coder-v2:16b","options":{"num_ctx":131072},"prompt":"hi"}' \
>/dev/null
curl -s localhost:11434/api/ps \
| jq -r '.models[] | "\(.context_length) ctx \(.size_vram/1e9*10|round/10) GB VRAM \(.size_vram/.size*100|round)% GPU"'131072 ctx 28.3 GB VRAM 100% GPUMesmos pesos, mesmo modelo:
- Quatro vezes a janela mais ou menos dobra a memória,
13.8 GB → 28.3 GB. - Esses
~14 GBa mais são o KV cache crescendo, a única coisa que a janela mexe. - Nesta máquina ainda coube na VRAM a 100% GPU; em uma menor, a rodada de 128K vazaria para a CPU e se arrastaria.
- A janela, não os pesos, é o que decide se um modelo cabe.
Memória é só metade do custo de uma janela grande:
- A qualidade também deriva: modelos de contexto longo são amplamente relatados como ficando menos confiáveis conforme o contexto enche, bem antes de a janela acabar.
- Um contexto grande só vale a pena pagar quando você realmente for usá-lo.
As famílias, lado a lado
Nomear famílias é uma coisa; rodá-las mostra o trade-off. Uma build de programação de
cada família na mesma máquina, mesmo prompt --verbose:
Os modelos limitam suas janelas de contexto em tetos diferentes, então a memória é medida em 8K, a única janela que os quatro suportam, para manter os números comparáveis:
| modelo | params | ctx máx | VRAM @ 8K | tokens/s | capacidades |
|---|---|---|---|---|---|
codellama:7b | 7B dense | 16K | 6.2 GB | 61.7 | completion |
qwen2.5-coder:7b | 7.6B dense | 32K | 4.9 GB | 47.9 | completion, tools, insert |
codegemma:7b | 9B dense | 8K | 7.4 GB | 40.6 | completion |
deepseek-coder-v2:16b | 15.7B MoE (~2.4B ativos) | 160K | 10.3 GB | 106.5 | completion, insert |
Maior não é mais lento, e tamanho não é o único eixo:
- O DeepSeek é o maior aqui com 15.7B e mesmo assim o mais rápido de longe, mais que o dobro de qualquer um dos três modelos densos de 7-9B, porque seu design mixture-of-experts ativa só ~2.4B de parâmetros por token.
- Entre os modelos densos, mais parâmetros custam um pouco de velocidade: o
codellamade 7B lidera com 61.7, ocodegemmade 9B fica atrás com 40.6, com oqwenno meio; toda essa diferença é menor que o salto para o modelo MoE. - A memória não acompanha a velocidade. Todos os 15.7B pesos do DeepSeek ficam na VRAM
mesmo que só ~2.4B disparem por token, então numa janela compartilhada de 8K ele é o
mais pesado (10.3 GB); entre os densos, o
qwené o mais leve (4.9 GB) apesar de ficar no meio do pelotão em parâmetros. - O teto da janela é um eixo à parte: o DeepSeek estica até 160K e o
qwenaté 32K, mas ocodegemmapara em 8K e ocodellamaem 16K, um limite real antes de você alimentar um arquivo longo. - A capacidade é, de novo, separada: só o
qwen2.5-coderlistatools; as duas buildscode*fazem sócompletion, e o DeepSeek adicionainsertmas nãotools. - Tamanho, velocidade, memória, janela e capacidade se movem de forma independente; você escolhe o canto que serve para o trabalho.
Mais um eixo: instruct versus reasoning.
- Todos os quatro modelos acima são modelos instruct, eles respondem na hora.
- Um modelo reasoning como o
deepseek-r1pensa em aberto primeiro, gastando tokens numa cadeia de raciocínio visível antes da resposta. - Ele é mais lento, e mais forte em problemas passo a passo.
- Baixe um (
ollama pull deepseek-r1:8b) e você assiste ele raciocinar antes de responder.
Um modelo local é um substituto direto para uma API na nuvem
O Ollama serve um segundo caminho de API, /v1:
- Ele fala o formato chat-completions da OpenAI, o formato de requisição e resposta que a API na nuvem da OpenAI popularizou.
- Qualquer código ou ferramenta que já chama um modelo hospedado pode apontar para o Ollama mudando uma única coisa, a URL base:
curl -s localhost:11434/v1/chat/completions \
-d '{"model":"deepseek-coder-v2:16b","messages":[{"role":"user","content":"Say hello in one short line."}]}' \
| jq '{object, model, choices: [.choices[0].message], usage}'{
"object": "chat.completion",
"model": "deepseek-coder-v2:16b",
"choices": [ { "role": "assistant", "content": " Hello!" } ],
"usage": { "prompt_tokens": 15, "completion_tokens": 3, "total_tokens": 18 }
}Mesmo formato de requisição, mesmo formato de resposta, de um servidor na sua máquina:
- A resposta traz a mesma contabilidade de
usageque uma API na nuvem retorna. - Troque
https://api.openai.com/v1porhttp://localhost:11434/v1e o código existente roda contra o modelo local.
Pi, apontado para os modelos locais
Pi, o agente de programação de
Claude Code vs Pi, sai de fábrica conhecendo
só provedores hospedados, Anthropic e Google. Um arquivo JSON,
~/.pi/agent/models.json, ensina a ele os locais. Ele adiciona o Ollama como um
provedor customizado: um nome, a URL base /v1 acima e uma entrada por modelo, com
ids exatamente como o ollama list os imprime (deepseek-coder-v2:16b,
qwen2.5-coder:7b e qwen3:8b). O apiKey é um placeholder; o Ollama o ignora, mas o
Pi exige um valor:
{
"providers": {
"ollama": {
"baseUrl": "http://localhost:11434/v1",
"api": "openai-completions",
"apiKey": "ollama",
"models": [
{ "id": "deepseek-coder-v2:16b", "contextWindow": 163840, "maxTokens": 8192 },
{ "id": "qwen2.5-coder:7b", "contextWindow": 32768, "maxTokens": 8192 },
{ "id": "qwen3:8b", "contextWindow": 40960, "maxTokens": 8192 }
]
}
}
}contextWindow e maxTokens importam: o Pi não pergunta ao Ollama os limites de um
modelo, ele acredita nesses metadados (e assume 128K quando a entrada os omite). Declare
cada janela a partir da ficha técnica do ollama show do modelo, ou o Pi vai
alegremente superalimentar um modelo de 32K. O pi --list-models ollama confirma a
conexão:
provider model context max-out thinking images
ollama deepseek-coder-v2:16b 163.8K 8.2K no no
ollama qwen2.5-coder:7b 32.8K 8.2K no no
ollama qwen3:8b 41.0K 8.2K no noUm flag de capacidade é uma alegação, não uma prova
Inferência local sem uma API externa só compensa se o modelo consegue fazer o trabalho para o qual você o contratou, e para trabalho de agente o trabalho é a chamada de ferramenta (tool call).
Três dos modelos instalados competem por ele, e seus blocos Capabilities do
ollama show discordam:
| modelo | capacidades | tools? |
|---|---|---|
deepseek-coder-v2:16b | completion, insert | não |
qwen2.5-coder:7b | completion, tools, insert | sim |
qwen3:8b | completion, tools, thinking | sim |
Se um modelo com o flag consegue de fato fazer uma chamada de ferramenta é outra
pergunta, e uma única sonda a responde: ofereça ao modelo uma única ferramenta, write,
e peça um arquivo. A mesma requisição vai para os três modelos; só o campo model muda:
curl -s localhost:11434/api/chat -d '{
"model": "deepseek-coder-v2:16b",
"stream": false,
"messages": [{"role":"user","content":"Create a file named hello.txt containing exactly the word: hello"}],
"tools": [{"type":"function","function":{"name":"write","description":"Write a file","parameters":{"type":"object","properties":{"path":{"type":"string"},"content":{"type":"string"}},"required":["path","content"]}}}]
}'A requisição tem duas partes móveis:
-
messagescarrega o pedido, um único turno de usuário: crie um arquivo chamadohello.txtcontendo exatamente a palavrahello."messages": [ { "role": "user", "content": "Create a file named hello.txt containing exactly the word: hello" } ] -
toolsentrega ao modelo a única função que ele pode chamar para dar conta disso:write, que recebe um objeto compathecontent, ambos strings, ambos obrigatórios. O blocoparametersé um JSON Schema descrevendo os argumentos da função; o modelo o lê para dar forma a uma chamada válida."tools": [ { "type": "function", "function": { "name": "write", "description": "Write a file", "parameters": { "type": "object", "properties": { "path": { "type": "string" }, "content": { "type": "string" } }, "required": ["path", "content"] } } } ]
Um modelo que merece seu flag lê o pedido, o casa com a ferramenta e responde com um
campo tool_calls estruturado carregando write e
{"path": "hello.txt", "content": "hello"}.
Uma nota sutil: um arquivo em disco não é esperado, porque o /api/chat é texto entra,
texto sai; executar a chamada é trabalho de quem chama. A sonda avalia exatamente uma
coisa: se tool_calls volta preenchido. Todo o resto é só texto.
Três modelos, três veredictos
deepseek-coder-v2: sem flag, e a ausência é imposta
O deepseek-coder-v2 não lista tools, então a expectativa é que ele nem chegue a
responder, e a sonda confirma isso na API:
{"error":"registry.ollama.ai/library/deepseek-coder-v2:16b does not support tools"}O Ollama rejeita a requisição antes de o modelo sequer ver o prompt.
qwen2.5-coder: um falso positivo
O qwen2.5-coder:7b tem o flag, então a mesma requisição passa, e falha de um jeito
mais interessante:
curl -s localhost:11434/api/chat -d '{
"model": "qwen2.5-coder:7b",
"stream": false,
"messages": [{"role":"user","content":"Create a file named hello.txt containing exactly the word: hello"}],
"tools": [{"type":"function","function":{"name":"write","description":"Write a file","parameters":{"type":"object","properties":{"path":{"type":"string"},"content":{"type":"string"}},"required":["path","content"]}}}]
}' | jq -r '"tool_calls: \(.message.tool_calls)\ncontent:\n\(.message.content)"'tool_calls: null
content:
{
"name": "write",
"arguments": {
"path": "hello.txt",
"content": "hello"
}
}A resposta, lida contra a expectativa:
tool_calls: null: o único campo que a sonda avalia está vazio; o Ollama não interpretou chamada nenhuma.contentcarrega a chamada mesmo assim: um payloadwritebem formado, com exatamente os argumentos certos, entregue como prosa.
O modelo entendeu a tarefa, escolheu a ferramenta certa e montou argumentos válidos, e
ainda assim falhou na única coisa que conta: um campo tool_calls preenchido. O parser
do Ollama procura exatamente o formato que o próprio chat template do modelo exige:
<tool_call>
{"name": "write", "arguments": {"path": "hello.txt", "content": "hello"}}
</tool_call>Mas o modelo escreveu o JSON e pulou as tags. Sem tags, sem parse: tool_calls fica
null, e a chamada cai no content como prosa. A culpa é do modelo? Vamos testar a
mesma requisição com o qwen3:8b:
curl -s localhost:11434/api/chat -d '{
"model": "qwen3:8b",
"stream": false,
"messages": [{"role":"user","content":"Create a file named hello.txt containing exactly the word: hello"}],
"tools": [{"type":"function","function":{"name":"write","description":"Write a file","parameters":{"type":"object","properties":{"path":{"type":"string"},"content":{"type":"string"}},"required":["path","content"]}}}]
}' | jq -r '"tool_calls: \(.message.tool_calls)\ncontent:\n\(.message.content)"'tool_calls: [{"id":"call_6epdfuul","function":{"index":0,"name":"write","arguments":{"path":"hello.txt","content":"hello"}}}]
content:A requisição está bem formada e o parsing do Ollama funciona; o pipeline produz uma
chamada estruturada sempre que o modelo faz a parte dele. Essa é a parte intrigante: o
mesmo prompt, delegado a dois modelos da mesma família, ambos hasteando o flag tools,
e só um entrega de verdade. A mesma requisição entra, e só os pesos diferem.
O flag do qwen2.5-coder anuncia o padrão; o modelo entrega um dialeto. Para dirigir um
agente de prateleira, isso é um falso positivo.
qwen3: o flag, entregue
O qwen3:8b (ollama pull qwen3:8b), da mesma classe de tamanho que o
qwen2.5-coder:7b e uma geração mais novo, responde à mesma sonda com uma chamada
estruturada:
{
"content": "",
"tool_calls": [{"id": "call_9lsm4tio", "function": {"index": 0, "name": "write", "arguments": {"content": "hello", "path": "hello.txt"}}}]
}A ficha técnica diz o que o chat template suporta; só uma sonda diz o que o modelo faz. Antes de construir qualquer coisa sobre um flag de capacidade, faça o modelo provar uma vez.
Ajustando a pegada de memória
Quando uma janela grande empurra o KV cache para além da sua VRAM, você tem botões no nível do servidor, distintos da quantização de pesos do próprio modelo. Inicie o Ollama com:
OLLAMA_FLASH_ATTENTION=1 OLLAMA_KV_CACHE_TYPE=q8_0 ollama serveOLLAMA_KV_CACHE_TYPE=q8_0 armazena o KV cache em 8 bits em vez de 16:
- Isso mais ou menos corta pela metade a memória dele por um custo mínimo de qualidade, então uma janela que vazava para a CPU pode voltar a caber na GPU.
- Isso quantiza o KV cache (uma alavanca de runtime), não os pesos (
Q4_0, embutidos no modelo). - Uma ressalva: esses são definidos no processo do servidor, então não se aplicam quando
o Ollama roda sob o
brew services. Pare esse e rodeollama servevocê mesmo para usá-los.
Quando rodar você mesmo vale a pena
A inferência local conquista seu lugar por razões concretas, mais ou menos nesta ordem:
- Custo em volume. Um modelo hospedado cobra por token; um local é um custo fixo de hardware e eletricidade. Passado um certo volume de chamadas, rodar o seu próprio é simplesmente mais barato.
- Latência e controle. Sem salto de rede, sem rate limits, sem cota por chamada. O modelo responde tão rápido quanto sua GPU permite, tão frequentemente quanto você quiser.
- Experimentação livre. Itere em ferramentas internas sem uma API medida no meio, sem conta por um prompt ruim, sem cota para cuidar.
- Telemetria completa. O rodapé
--verbose, oollama pse o/api/psreportam tokens, memória e a janela carregada direto do runtime, não resumidos por trás de um dashboard de fornecedor. - Localidade dos dados. Um prompt para um modelo hospedado sai da sua máquina; um local nunca sai. O raio de impacto de um vazamento encolhe para esta única máquina, o que importa mais para dados sensíveis ou regulados.
É esse o ciclo inteiro: um brew install, um ollama pull, e um modelo de programação
está escrevendo na sua própria GPU. A partir daí, o --verbose, o ollama ps e o
/api/ps reportam sua velocidade, memória e janela a cada passo, e todo prompt fica na
máquina. Nada nele é uma caixa-preta.
A nuvem ainda vence quando você precisa de qualidade de fronteira que nenhum modelo na sua GPU consegue igualar. A divisão honesta:
- vá de local para custo, controle e experimentação
- vá de um modelo de fronteira hospedado quando a tarefa exige o teto dele