Abaixo está o plano reestruturado em **Markdown**, separado por **etapas de implementação**, e já **ajustado para o seu foco real**: extrair o máximo de TPS possível com **Qwen3.5 4B/9B** em uma **RTX 3070 8 GB** ou hardware mais fraco, sem jogar qualidade fora. O plano-base do arquivo já estava correto ao manter **llama.cpp/C++ no core**, **Rust no hot path** e **Python apenas no registry/download**, com **Axum/Tokio + SSE** e comunicação por IPC. Também fiz um ajuste importante de foco: para o seu caso, eu **não começaria tentando ser um “LM Studio completo” logo de cara**. Eu começaria como um **runtime local de altíssimo desempenho para modelos pequenos/médios**, primeiro em **texto puro**, depois abriria multi-model, features extras e UI mais rica. Isso aumenta a chance de você realmente bater TPS alto cedo. O próprio ecossistema atual já ajuda nisso: o `llama.cpp` hoje já oferece **continuous batching**, **flash attention**, **prompt caching**, tipos de **KV cache quantizado** e **speculative decoding**; e os cards da Qwen3.5 já destacam modos de serving com foco em eficiência e modo **text-only/language-model-only** para liberar memória para KV cache. ([GitHub][1]) --- # Plano de Implementação — LocalLLM Inference Engine ## 1. Objetivo do projeto Construir um backend local de inferência para LLMs com foco em: * **TPS alto** * **latência baixa** * **uso agressivamente otimizado de VRAM/RAM** * **boa qualidade prática em modelos pequenos e médios** * **arquitetura modular** * **compatibilidade com API estilo OpenAI** ### Meta prática inicial O alvo não deve ser “suportar tudo”. O alvo inicial deve ser: * rodar **Qwen3.5-4B** como perfil principal de alto desempenho * rodar **Qwen3.5-9B** como perfil balanceado * extrair o máximo da **RTX 3070 8 GB** * degradar bem para CPU/hardware mais fraco * manter o sistema preparado para expansão futura A linha **Qwen3** densa aberta originalmente trouxe variantes como **4B** e **8B**; já a linha **Qwen3.5** aberta em 2026 inclui, entre outras, **4B** e **9B**, o que bate com o foco que você quer. ([Qwen][2]) --- ## 2. Ajustes que eu faria no plano original ## 2.1. Fazer a V1 em texto puro Mesmo que alguns modelos Qwen3.5 tenham componentes multimodais, o seu objetivo é **TPS alto em inferência textual**. Então a primeira versão deve ser: * **text-only** * **single-model ativo** * **single runtime path** * sem visão * sem embeddings * sem tool-calling complexo na engine Os próprios materiais da Qwen3.5 destacam modo **text-only / language-model-only** justamente para liberar memória e permitir mais KV cache. ([Hugging Face][3]) ## 2.2. Focar em throughput real, não em generalidade cedo demais No plano-base, a arquitetura em camadas está boa. O que eu mudaria é a ordem de implementação: 1. engine local minimalista e muito rápida 2. benchmark e tuning 3. API compatível 4. registry/download 5. multi-request throughput 6. recursos avançados ## 2.3. Adotar `llama.cpp` como engine principal da V1 Para o seu caso, isso continua sendo a melhor decisão do plano original. O `llama.cpp` já é otimizado para uma variedade grande de hardware e hoje expõe exatamente os knobs que mais importam para seu objetivo: batching contínuo, flash attention, prompt cache, speculative decoding e tuning de KV cache. ([GitHub][4]) ## 2.4. Manter Rust no hot path Essa parte do plano original continua correta: **Rust + bindgen + Axum/Tokio** no hot path, e **Python só no caminho frio** de registry/Hugging Face. --- ## 3. Stack final recomendada ## 3.1. Core de inferência * **C/C++** * **llama.cpp** compilado como shared library * backend CUDA para NVIDIA ## 3.2. Hot path do servidor * **Rust** * `tokio` * `axum` * `hyper` * `bindgen` ## 3.3. Registry de modelos * **Python 3.11+** * `huggingface_hub` * processo separado * IPC local ## 3.4. UI de teste * HTML simples * SSE nativo * sem framework no começo Isso está alinhado com o documento-base enviado. --- ## 4. Diretriz de performance para a RTX 3070 A **RTX 3070** tem **8 GB de VRAM** e compute capability **8.6**. Para esse projeto, isso significa que a build CUDA do `llama.cpp` deve ser tratada como prioridade e vale compilar direcionando a arquitetura CUDA da placa. ([NVIDIA][5]) ### Build recomendada da engine * `-DGGML_CUDA=ON` * `-DCMAKE_CUDA_ARCHITECTURES=86` Isso ajuda a evitar build genérica demais para a placa. ([GitHub][6]) ### Consequência prática Com 8 GB, você não deve desenhar a V1 pensando em: * contexto gigantesco por padrão * vários modelos carregados ao mesmo tempo * multimodal cedo * concorrência alta por processo logo no início Você deve desenhar a V1 pensando em: * **1 modelo ativo por vez** * **contexto moderado** * **alto aproveitamento de GPU** * **prompt cache** * **continuous batching** * **KV cache otimizado** * **flash attention** --- # Etapas de implementação ## Etapa 0 — Definição do alvo técnico ### Objetivo Definir o recorte exato da V1 para não desperdiçar tempo com features que não aumentam TPS. ### Decisões * V1 será **texto puro** * V1 terá **um modelo carregado por vez** * V1 vai priorizar: * **Qwen3.5-4B** como modo turbo * **Qwen3.5-9B** como modo balanceado * API no estilo OpenAI desde cedo * multi-model simultâneo fica para depois ### Entregáveis * documento de metas * perfis de execução * critérios de benchmark ### Critério de pronto Você consegue responder: * qual modelo será o “modelo de desenvolvimento” * qual contexto padrão será usado * qual métrica manda no projeto: TPS, latência ou concorrência ### Minha recomendação Use como alvo inicial: * **Qwen3.5-4B** para desenvolvimento e tuning pesado * **Qwen3.5-9B** só depois que a 4B estiver extremamente bem afinada --- ## Etapa 1 — Foundation do workspace ### Objetivo Criar a base do projeto com a estrutura modular correta desde o início. ### Estrutura recomendada ```text localllm/ ├── Cargo.toml ├── crates/ │ ├── llama-sys/ │ ├── inference-core/ │ ├── inference-server/ │ ├── api-contracts/ │ └── metrics/ ├── registry/ │ ├── service.py │ └── requirements.txt ├── ui/ │ └── index.html ├── vendor/ │ └── llama.cpp/ └── models/ ``` Essa separação deriva da estrutura do plano-base, com ajuste pequeno de nomes para ficar mais clara para crescimento futuro. O documento-base já sugeria workspace Rust, crate de bindings, pool de inferência, servidor API, registry Python, UI e `vendor/llama.cpp` fixado como submódulo. ### Decisões críticas * travar o `llama.cpp` em commit fixo * não depender de versão flutuante * build reproduzível ### Critério de pronto * projeto compila vazio * submodule do `llama.cpp` fixado * layout estável --- ## Etapa 2 — Build da engine CUDA ### Objetivo Compilar o `llama.cpp` corretamente para sua GPU e validar carregamento básico. ### O que implementar * build CMake do `llama.cpp` * geração da shared library * smoke test local * script de build dev/prod ### Recomendação Ter dois modos: * **dev build** * **native optimized build** ### Critério de pronto * shared library gerada * modelo GGUF carregando * primeiro token gerado localmente ### Ajuste importante Nesta fase, não implemente API ainda. Primeiro prove que o **core gera tokens rápido**. --- ## Etapa 3 — FFI Rust com `bindgen` ### Objetivo Criar o binding Rust ↔ `llama.cpp` sem overhead no runtime. O plano-base já propõe exatamente isso com `build.rs` + `bindgen`, e essa continua sendo a decisão correta. ### O que implementar * crate `llama-sys` * `build.rs` * wrappers mínimos e seguros * tipos Rust para: * model * context * sampler * token stream ### Regras desta etapa * wrapper fino * sem abstrações demais * sem inventar engine paralela antes da hora ### Critério de pronto * Rust consegue: * abrir modelo * tokenizar prompt * chamar decode * amostrar próximo token * encerrar limpo --- ## Etapa 4 — Engine local minimalista, single-model ### Objetivo Ter um runtime local realmente rápido antes de colocar rede na frente. ### O que implementar * `InferenceEngine` * `LoadedModel` * `GenerationSession` * `SamplerConfig` * `RuntimeProfile` ### Regras * **1 modelo carregado** * **1 caminho de execução** * **sem hot-swap automático ainda** * **sem múltiplos engines** ### Perfis que eu criaria * `turbo_4b` * `balanced_9b` * `cpu_fallback` ### Critério de pronto * prompt entra * tokens saem * TPS é medido * memória é medida * warmup existe ### Otimizações obrigatórias nesta etapa * warmup de engine * reuse de buffers * evitar alocações em loop * pré-tokenização de system prompt * prompt cache quando aplicável O `llama.cpp` já suporta prompt caching e serving server-side com opções voltadas para esse tipo de fluxo. ([GitHub][1]) --- ## Etapa 5 — Perf tuning de geração pura ### Objetivo Ajustar o runtime até ele realmente ficar forte na 3070. ### O que testar * `n_gpu_layers` * `n_ctx` * `n_batch` * `ubatch` * `n_threads` * `flash attention` * tipo do KV cache * quantização do modelo O documento-base já previa expor `n_gpu_layers`, `n_ctx`, `n_threads` e `n_batch`. Hoje, além disso, o `llama.cpp` já documenta `--flash-attn`, `--batch-size`, `--ubatch-size` e tipos de KV cache quantizado. ([GitHub][1]) ### Ordem de tuning recomendada 1. acertar quantização do modelo 2. acertar `n_gpu_layers` 3. acertar `n_ctx` 4. ativar `flash attention` 5. testar KV cache mais leve 6. testar batch e ubatch 7. só depois testar concorrência ### Critério de pronto Você tem uma tabela interna com: * TPS * latência até primeiro token * VRAM usada * RAM usada * qualidade percebida para pelo menos: * Qwen3.5-4B * Qwen3.5-9B ### Minha recomendação prática Para o seu foco, eu usaria: * **4B** como perfil padrão para “TPS máximo” * **9B** como perfil “qualidade máxima viável” E faria os testes em quantizações médias antes de subir demais. Isso não é regra fixa do modelo; é estratégia de engenharia para sua GPU. --- ## Etapa 6 — API local OpenAI-compatible ### Objetivo Colocar a engine atrás de uma API limpa sem destruir a performance. O plano-base já propõe `Axum + SSE + contratos estilo OpenAI`, o que continua certo. ### Endpoints iniciais * `POST /v1/chat/completions` * `GET /v1/models` * `GET /health` ### O que deixar de fora no início * completions legadas se não forem necessárias * embeddings * files * assistants * websocket ### Por que SSE O plano-base justifica bem SSE como fluxo unidirecional simples. Isso é compatível com seu foco e mantém o streaming barato. ### Critério de pronto * request JSON entra * stream de tokens sai em SSE * formato é previsível * métrica de TPS vai na resposta final --- ## Etapa 7 — Scheduler de inferência e concorrência controlada ### Objetivo Sair do single-request puro para throughput real multi-request. O documento-base já propõe um pool com semáforo e contexts dedicados. Eu manteria a ideia, mas implementaria **depois** da engine single-model estar muito boa. ### O que implementar * fila de requests * limite de concorrência * reutilização de contexts * backpressure * cancelamento ### Regra importante **Não escale concorrência só porque dá.** Na 3070, concorrência excessiva pode reduzir TPS total por request, aumentar jitter e piorar latência. ### Estratégia correta Ter perfis: * **low-latency mode** * **high-throughput mode** ### Critério de pronto * 2 a N requests simultâneos * servidor não explode memória * total de TPS agregado aumenta ou pelo menos não degrada absurdamente ### Observação importante O `llama.cpp` server já suporta **parallel requests** e **continuous batching**, então vale estudar a arquitetura dele como referência prática antes de reinventar o scheduler inteiro. ([GitHub][1]) --- ## Etapa 8 — Continuous batching ### Objetivo Aumentar throughput agregado de verdade. ### O que implementar * agrupamento de requests compatíveis * união de prompts em janela de execução * step scheduler * contabilidade de tokens por request ### Por que isso importa Para o seu foco, **continuous batching** provavelmente entrega mais ganho real do que sair adicionando feature de produto. O `llama.cpp` já documenta serving com **continuous batching**. ([GitHub][1]) ### Critério de pronto * requests curtos simultâneos têm ganho agregado * scheduler não mata a latência de requests isolados --- ## Etapa 9 — KV cache tuning ### Objetivo Aumentar capacidade útil e throughput sem depender apenas de VRAM bruta. ### O que implementar * opção de tipo de KV cache * presets de contexto * política de truncamento inteligente * medição de impacto em qualidade O `llama.cpp` já suporta tipos de KV cache quantizado para K/V, com defaults em `f16` e alternativas mais leves. ([GitHub][1]) ### Estratégia No seu caso, isso é especialmente importante porque: * a 3070 tem só 8 GB * Qwen3.5-9B pode ficar apertado dependendo da quantização e do contexto * texto puro com KV cache otimizado pode render muito mais do que tentar “empurrar tudo” na VRAM ### Critério de pronto * presets claros: * curto * médio * longo * comparação objetiva entre qualidade e desempenho --- ## Etapa 10 — Speculative decoding / draft model ### Objetivo Buscar o próximo salto de TPS. O `llama.cpp` já documenta suporte a speculative decoding com modelo draft. A Qwen3.5 também já cita exemplos de serving com foco em MTP/eficiência. ([GitHub][1]) ### Quando fazer Só depois que: * engine base estiver estável * batching estiver bom * cache estiver afinado ### O que implementar * modelo principal * modelo draft pequeno * política de aceitação/rejeição * benchmark comparando: * TPS * latência * uso extra de VRAM/RAM ### Observação Essa etapa pode dar ganhos grandes, mas também pode complicar demais cedo. Ela é etapa de **turbo**, não de fundação. --- ## Etapa 11 — Registry de modelos ### Objetivo Adicionar busca, download e metadata sem poluir o hot path. O plano-base está correto em jogar isso para um processo Python separado com `huggingface_hub`, IPC local e atualização de metadata. ### O que implementar * `POST /registry/search` * `POST /registry/download` * `GET /registry/models` ### Regra de arquitetura Nada de Python no caminho de geração de tokens. ### Critério de pronto * busca GGUF * download resumível * checksum * atualização do índice local --- ## Etapa 12 — Observabilidade e benchmark suite ### Objetivo Transformar tuning em processo técnico e não em chute. ### O que medir * TTFT * TPS * tokens/s agregados * VRAM * RAM * filas * taxa de cancelamento * throughput por perfil ### O que implementar * endpoint de métricas * logs estruturados * benchmark CLI * benchmark replayável ### Critério de pronto Você consegue responder com dados: * qual quantização é a melhor na sua máquina * qual contexto é o limite bom * quando 9B vale a pena sobre 4B * quando speculative decoding compensa --- ## Etapa 13 — UI mínima de teste ### Objetivo Ter um frontend simples para validar streaming, sem virar foco do projeto. O plano-base já propõe uma UI HTML estática simples, servida pelo próprio servidor. Para a V1, isso é o ideal. ### O que implementar * seletor de perfil * chat simples * toggle stream * exibição de TPS * exibição de uso de memória * log de benchmark rápido ### Critério de pronto A UI só existe para testar o backend. Nada além disso. --- # Perfis recomendados para sua V1 ## Perfil A — Turbo ### Modelo Qwen3.5-4B ### Objetivo Máximo TPS com boa qualidade prática. ### Uso ideal * chat geral * coding leve a moderado * agentes rápidos * máquinas mais fracas ### Estratégia * quantização equilibrada * contexto moderado * text-only * flash attention * prompt cache * batching leve Os cards atuais da Qwen3.5-4B mostram suporte a serving moderno e reforçam o modo text-only para eficiência de memória. ([Hugging Face][7]) --- ## Perfil B — Balanceado ### Modelo Qwen3.5-9B ### Objetivo Subir qualidade sem matar completamente a velocidade. ### Uso ideal * tarefas mais difíceis * respostas mais consistentes * coding mais pesado * raciocínio melhor que 4B ### Estratégia * contexto ainda controlado * quantização mais conservadora que no 4B * tuning fino de `n_gpu_layers` * KV cache otimizado * talvez speculative decoding no futuro Os cards públicos atuais da linha Qwen3.5 incluem 9B e destacam arquitetura voltada a serving eficiente. ([Hugging Face][8]) --- # O que eu removeria da V1 ## Não colocaria agora * multimodal * vários modelos carregados ao mesmo tempo * websocket * embeddings * reranking * LoRA dinâmica * quantização on-the-fly * scheduler super complexo antes do benchmark base * UI sofisticada ## Motivo Nada disso é o principal motor de TPS para o seu alvo. --- # Ordem exata que eu seguiria ## Fase 1 Build CUDA + `llama.cpp` + primeiro token ## Fase 2 Bindings Rust + runtime local single-model ## Fase 3 Benchmark bruto de Qwen3.5-4B ## Fase 4 Tuning pesado: * `n_gpu_layers` * `n_ctx` * `n_batch` * `ubatch` * flash attention * KV cache ## Fase 5 API OpenAI-compatible com SSE ## Fase 6 Fila, scheduler e concorrência controlada ## Fase 7 Continuous batching ## Fase 8 Qwen3.5-9B balanceado ## Fase 9 Registry Python / HF Hub ## Fase 10 Speculative decoding e turbo mode --- # Conclusão técnica Seu plano-base está bom na arquitetura macro: **Rust no hot path**, **llama.cpp como core**, **Python só no registry**, **SSE/OpenAI-compatible**, **IPC local** e **workspace modular**. A principal melhoria para o seu objetivo é esta: > **transformar o projeto, na V1, de “infraestrutura genérica de inferência local” para “runtime de altíssimo desempenho para modelos textuais pequenos e médios”.** Isso significa: * começar por **Qwen3.5-4B** * tratar **Qwen3.5-9B** como segundo alvo * usar **RTX 3070** como orçamento real de VRAM * explorar cedo: * **flash attention** * **continuous batching** * **prompt caching** * **KV cache tuning** * **speculative decoding depois** Essa é a rota com maior chance de entregar o que você quer de verdade: **TPS alto sem desandar qualidade**. Base original: Ajustes de foco e recursos atuais do ecossistema: ([GitHub][1]) Posso converter isso em seguida para um **`README.md` de projeto**, já no formato de especificação técnica pronta para repositório. [1]: https://github.com/ggml-org/llama.cpp/blob/master/tools/server/README.md "llama.cpp/tools/server/README.md at master · ggml-org/llama.cpp · GitHub" [2]: https://qwenlm.github.io/blog/qwen3/?utm_source=chatgpt.com "Qwen3: Think Deeper, Act Faster" [3]: https://huggingface.co/Qwen/Qwen3.5-9B "Qwen/Qwen3.5-9B · Hugging Face" [4]: https://github.com/ggml-org/llama.cpp "GitHub - ggml-org/llama.cpp: LLM inference in C/C++ · GitHub" [5]: https://www.nvidia.com/en-us/geforce/graphics-cards/30-series/rtx-3070-3070ti/?utm_source=chatgpt.com "GeForce RTX 3070 Family" [6]: https://github.com/ggml-org/llama.cpp/blob/master/docs/build.md "llama.cpp/docs/build.md at master · ggml-org/llama.cpp · GitHub" [7]: https://huggingface.co/Qwen/Qwen3.5-4B "Qwen/Qwen3.5-4B · Hugging Face" [8]: https://huggingface.co/Qwen/models "Qwen (Qwen)"