Saudações.
Apresento aos senhores um software excelente para integrar todas as contas de IA em um único ponto de contato de acesso à APIs LLM.
Links:
- Site: https://manifest.build/
- Projeto: https://github.com/mnfst/manifest
1 – O que é o Manifest
O manifest é um software, administrado pela web (navegador), para concentrar todas as suas assinaturas e APIs de IA num único sistema de roteamento – um gateway de APIs.
1.1 – Problema: Custos
O principal problema que ele resolve é custos.
Meu caso: Eu assino Claude Code e é decepcionante quando os créditos acabam pois eu desejo continuar o projeto usando o modelo OPUS no máximo mas não quero comprar a assinatura máxima. Se eu acionar sub-agentes para tarefas enormes os créditos desaparecem em minutos.
Quando isso ocorre eu tenho que ir para o OpenCode, usando a API que é MUITO MAIS CARA do que a assinatura considerando meu consumo de Mtok (mega tokens) por mês.
Pesquisando sobre uma forma de unir várias assinaturas do Claude num único sistema e apontar meu agente (Claude Code) para ele, encontrei dois softwares:
- LiteLLM – O mais completo de todos, um pouco complicado de alinhar mas sem dúvida a melhor opção para empresas;
- Manifest – O mais simples do mundo, une todos os serviços e se parece muito com o LiteLLM em funcionalidades, é mais bonito e muito amistoso, ótimo para uso individual e pequenas equipes.
Com eles várias contas podem ser aproveitas para contornar o “use-it-or-lose-it” (estude sobre non-rollover quota ou non-cumulative allowance), o que você não usa durante semanas não serve de crédito para a semana seguinte.
A única forma de economizar é gastar o máximo de cada janela.
Exemplos de uso prático:
- Se uma empresa paga o assento do Claude plano Enterprise para cada funcionário, juntar todos os assentos numa única super-conta virtual permite balancear o consumo de créditos pelas contas;
- Amigos podem se unir numa conta do manifest para alternar (cada dia um) o uso da super conta;
1.2 – Problema: RateLimit
Se a janela de créditos não for violada, isso não significa que você pode usar com paralelismo de agentes e sub-agentes, os provedores de IA implementam rate limiting impondo quantas requisições por segundo você pode utilizar na sua conta.
Isso é particularmente irritante, causa demoras na entrega, causam erros sem fallback (o agente para de trabalhar) – processos falham.
Já que sua conta está bloqueada por segundos ou minutos até que a janela de rate limit seja reiniciada, a melhor forma de resolver isso é:
- Balanceamento: cada requisição pode ser enviada para contas diferentes, para alcançar 100 req/s numa API que limita a 10 req/s você precisa de 10 contas;
- Redundância: quando uma API retornar erro de rate limit, ele utiliza outra conta até o desbloqueio da conta principal.
1.3 – Problema: Billing
Nem só de OPUS e GPT vive o agente de IA, modelos pequenos como GPT-OSS-120b são muito baratos e extremamente capazes de lidar com tarefas simples (contexto pequeno, poucas tools, prompt pontual) sem errar.
O roteamento baseado em cobrança visa usar APIs de provedores que cobram barato primeiro, e alternam entre APIs buscando sempre o menor custo por Mtok.
Se um provedor como o OpenCode Zen resolve dar um modelo gratuitamente por uma semana, melhor usar ele primeiro (custo $0), se ele der erro a requisição é repetida pelo gateway Manifest para outra API que tenha o mesmo modelo com custo mínimo ($ 0.002).
Considerar o custo cria o fallback baseado em custo.
2 – Preparativos
Vamos rodar o Manifest em Docker e preparar o ambiente.
2.1 – Rede
Crie a rede Docker “network_public“:
# Rede de containers
docker network create network_public \
-d bridge \
-o com.docker.network.bridge.name=br-net-public \
-o com.docker.network.driver.mtu=1500 \
-o com.docker.network.bridge.gateway_mode_ipv4=nat-unprotected \
--subnet 10.249.0.0/16 \
--gateway 10.249.255.254;
2.2 – Acesso HTTPs
Você precisa usar HTTPs para que o Manifest forneça uma API para seus agentes e ferramentas de IA.
Instale o Traefik e configura o DNS, vou usar o FQDN “manifest.dominio.com” nos exemplos, troque-o pelo nome que você apontou para o IP do servidor.
2.3 – Banco de dados Postgres
Vou criar o banco de dados PostgreSQL central, fora da stack do Manifest.
Vou usar a imagem pgvector (debian trixie + postgres 18 + pgvector).
O nome do serviço será “pgvector-main” e as credenciais iniciais serão exclusivas do administrador (user postgres).
# Variaveis
NAME="pgvector-main";
# Argumentos do postgres (usado somente no primeiro boot)
POSTGRES_USER="postgres";
POSTGRES_PASSWORD="tulipasql";
POSTGRES_DB="admin";
# Imagem
IMAGE="pgvector/pgvector:pg18-trixie";
docker pull $IMAGE;
# Volume
# obs: pg18 usa pasta principal interna: /var/lib/postgresql
DATADIR=/storage/$NAME;
mkdir -p $DATADIR;
chown -R 999:999 $DATADIR;
# Renovar/rodar:
docker rm -f $NAME 2>/dev/null;
docker run \
-d \
--restart always \
--name $NAME \
--hostname $NAME.intranet.br \
\
--cpus 2.0 \
--cpu-shares 1024 \
--memory 2g \
--memory-swap 2g \
--memory-reservation 1g \
--shm-size 1g \
\
--network network_public \
--ip 10.249.130.1 \
--ip6 2001:db8:10:249::130:1 \
--mac-address 02:cf:f1:30:00:01 \
\
--tmpfs /run:rw,noexec,nosuid,size=64m \
--tmpfs /tmp:rw,exec,suid,size=64m \
\
-v $DATADIR:/var/lib/postgresql \
\
-e POSTGRES_USER=$POSTGRES_USER \
-e POSTGRES_PASSWORD=$POSTGRES_PASSWORD \
-e POSTGRES_DB=$POSTGRES_DB \
-e PGDATA=/var/lib/postgresql/data/pgdata \
\
--health-cmd="pg_isready -U postgres" \
--health-interval=5s \
--health-timeout=5s \
--health-retries=10 \
\
--entrypoint "docker-entrypoint.sh" \
\
$IMAGE \
postgres \
--timezone=America/Sao_Paulo \
--log_timezone=America/Sao_Paulo \
--max_connections=150 \
--superuser_reserved_connections=3 \
--shared_buffers=384MB \
--effective_cache_size=768MB \
--work_mem=4MB \
--maintenance_work_mem=96MB \
--temp_buffers=8MB \
--wal_buffers=16MB \
--min_wal_size=512MB \
--max_wal_size=2GB \
--checkpoint_completion_target=0.9 \
--checkpoint_timeout=10min \
--wal_compression=on \
--wal_level=replica \
--random_page_cost=1.1 \
--effective_io_concurrency=200 \
--default_statistics_target=100 \
--max_worker_processes=4 \
--max_parallel_workers=2 \
--max_parallel_workers_per_gather=1 \
--max_parallel_maintenance_workers=2 \
--autovacuum=on \
--autovacuum_max_workers=2 \
--autovacuum_naptime=30s \
--autovacuum_vacuum_threshold=50 \
--autovacuum_vacuum_scale_factor=0.05 \
--autovacuum_analyze_threshold=50 \
--autovacuum_analyze_scale_factor=0.05 \
--autovacuum_vacuum_cost_delay=2ms \
--autovacuum_vacuum_cost_limit=400 \
--statement_timeout=120000 \
--idle_in_transaction_session_timeout=300000 \
--lock_timeout=30000 \
--log_destination=stderr \
--logging_collector=off \
--log_min_duration_statement=2000 \
--log_line_prefix="%t [%p] %u@%d " \
--log_checkpoints=on \
--log_connections=off \
--log_disconnections=off \
--log_lock_waits=on \
--log_temp_files=10240 \
--log_autovacuum_min_duration=1000 \
--shared_preload_libraries=pg_stat_statements \
--track_activities=on \
--track_counts=on \
--track_io_timing=on \
--track_functions=pl \
--ssl=off \
--port=5432 \
--listen_addresses="*" \
--max_wal_senders=3 \
--hot_standby=on;
2.4 – Criar banco de dados do Manifest
Primeiro, entre no container do pgvector-main direto no shell do Postgres (PSQL):
# Entrar no container e depois no psql com usuario admin 'postgres'
# Alternativa:
# docker exec -it pgvector-main bash
# psql -U postgres
docker exec -it pgvector-main bash -c 'psql -U postgres';
Uma vez no terminal PSQL, vamos criar a conta de usuário do “manifest” e o banco de dados “manifest“:
-- Criar usuario:
CREATE USER manifest
WITH PASSWORD 'tulipasql'
LOGIN;
-- Criar banco de dados
CREATE DATABASE manifest
WITH
OWNER = manifest
ENCODING = 'UTF8'
TABLESPACE = pg_default
IS_TEMPLATE = False
CONNECTION LIMIT = -1;
-- Conectar no banco:
\c manifest;
-- Adicionar extensao stats (opcional):
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
-- Conceder todos os privilégios no banco 'manifest' para o usuario 'manifest'
GRANT ALL PRIVILEGES ON DATABASE manifest TO manifest;
-- Conceder privilégios no schema público
GRANT ALL ON SCHEMA public TO manifest;
-- Garantir privilégios em tabelas futuras
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO manifest;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO manifest;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON FUNCTIONS TO manifest;
-- Verificar se o usuário foi criado
\du manifest;
-- Verificar se o banco foi criado
\l manifest;
-- Verificar privilégios no banco
\l+ manifest;
-- Sair
\q
2.5 – Instalando o Manifest
Gere uma chave secreta para a criptografia interna do Manifest:
# Gerar chave alteatoria de 16 digitos (128 bits de seguranca):
openssl rand -hex 16;
# 31fa0954c638cea5db263aad8a488357
# A chave preenche a variavel de ambiente $BETTER_AUTH_SECRET
BETTER_AUTH_SECRET="31fa0954c638cea5db263aad8a488357";
Rodando container Manifest chamado “manifest“:
# Variaveis
NAME="manifest";
FQDN="manifest.dominio.com";
IMAGE="manifestdotbuild/manifest:latest";
# Acesso ao banco de dados
# Formato: postgresql://user:senha@endereco_servidor:porta/banco_de_dados
DATABASE_URL="postgresql://manifest:tulipasql@pgvector-main:5432/manifest";
# Variaveis do manifest
PORT=2099;
BETTER_AUTH_URL="https://$FQDN";
SEED_DATA=false;
NODE_ENV=production;
MANIFEST_MODE=selfhosted;
BETTER_AUTH_SECRET="31fa0954c638cea5db263aad8a488357";
# Atualizar imagem
docker pull $IMAGE;
# Renovar/rodar:
docker container stop $NAME 2>/dev/null;
docker container rm $NAME 2>/dev/null;
docker container run \
-d \
--user root \
--restart always \
--name $NAME \
--hostname $NAME.intranet.br \
\
--cpus 4.0 \
--memory 2g \
--memory-swap 2g \
--memory-reservation 1g \
--shm-size 1g \
\
--network network_public \
--ip 10.249.91.1 \
\
-p 2099:2099 \
\
--tmpfs /run:rw,noexec,nosuid,size=512m \
--tmpfs /tmp:rw,exec,suid,mode=1777,dev,size=512m \
\
-e DATABASE_URL=$DATABASE_URL \
-e PORT=$PORT \
-e BETTER_AUTH_SECRET=$BETTER_AUTH_SECRET \
-e BETTER_AUTH_URL=$BETTER_AUTH_URL \
-e SEED_DATA=$SEED_DATA \
-e NODE_ENV=$NODE_ENV \
-e MANIFEST_MODE=$MANIFEST_MODE \
\
-e OLLAMA_HOST="http://host.docker.internal:11434" \
-e PROVIDER_TIMEOUT_MS=180000 \
-e STREAM_WARMUP_MS=15000 \
-e MANIFEST_ENCRYPTION_KEY="" \
-e EMAIL_PROVIDER="" \
-e EMAIL_API_KEY="" \
-e EMAIL_DOMAIN="" \
-e EMAIL_FROM="" \
-e MANIFEST_TELEMETRY_DISABLED=0 \
\
-e GOOGLE_CLIENT_ID="" \
-e GOOGLE_CLIENT_SECRET="" \
-e GITHUB_CLIENT_ID="" \
-e GITHUB_CLIENT_SECRET="" \
-e DISCORD_CLIENT_ID="" \
-e DISCORD_CLIENT_SECRET="" \
\
--label "traefik.enable=true" \
--label "traefik.http.routers.$NAME.rule=Host(\`$FQDN\`)" \
--label "traefik.http.routers.$NAME.entrypoints=web,websecure" \
--label "traefik.http.routers.$NAME.tls=true" \
--label "traefik.http.routers.$NAME.tls.certresolver=letsencrypt" \
--label "traefik.http.services.$NAME.loadbalancer.server.port=$PORT" \
\
$IMAGE;
# Acesso no navegador:
echo;
echo "Acesso ao Manifest:";
echo " Web.......: https://$FQDN";
echo;
Se você ja tem o banco de dados como serviço e quiser subir apenas o Manifest via Compose, arquivo YML:
# Ancoras com variaveis organizadas
x-env-postgres: &env-postgres
DATABASE_URL: "postgresql://manifest:tulipasql@pgvector-main:5432/manifest"
x-env-manifest: &env-manifest
PORT: 2099
BETTER_AUTH_SECRET: "31fa0954c638cea5db263aad8a488357"
BETTER_AUTH_URL: "https://manifest.dominio.com"
SEED_DATA: "false"
NODE_ENV: production
MANIFEST_MODE: selfhosted
OLLAMA_HOST: "http://host.docker.internal:11434"
PROVIDER_TIMEOUT_MS: 180000
STREAM_WARMUP_MS: 15000
MANIFEST_ENCRYPTION_KEY: ""
EMAIL_PROVIDER: ""
EMAIL_API_KEY: ""
EMAIL_DOMAIN: ""
EMAIL_FROM: ""
MANIFEST_TELEMETRY_DISABLED: 0
GOOGLE_CLIENT_ID: ""
GOOGLE_CLIENT_SECRET: ""
GITHUB_CLIENT_ID: ""
GITHUB_CLIENT_SECRET: ""
DISCORD_CLIENT_ID: ""
DISCORD_CLIENT_SECRET: ""
# Redes
networks:
network_public:
external: true
# Servico manifest
services:
manifest:
image: manifestdotbuild/manifest:latest
container_name: manifest
hostname: manifest.intranet.br
user: root
restart: always
deploy:
resources:
limits:
cpus: "4.0"
memory: 2g
reservations:
memory: 1g
shm_size: 1g
networks:
network_public:
ports:
- "2099:2099"
tmpfs:
- /run:rw,noexec,nosuid,size=512m
- /tmp:rw,exec,suid,mode=1777,size=512m
environment:
<<: *env-postgres
<<: *env-manifest
labels:
traefik.enable: "true"
traefik.http.routers.manifest.rule: "Host(`manifest.dominio.com`)"
traefik.http.routers.manifest.entrypoints: "web,websecure"
traefik.http.routers.manifest.tls: "true"
traefik.http.routers.manifest.tls.certresolver: "letsencrypt"
traefik.http.services.manifest.loadbalancer.server.port: "2099"
Acesse no navegador: https://manifest.dominio.com/
.
(ainda vou fazer o capitulo de configuração básica)
.
Terminamos por hoje!
Patrick Brandão, patrickbrandao@gmail.com
“Aquilo a que chamamos acaso não é,
não pode deixar de ser,
senão a causa ignorada de um efeito conhecido.“
Voltaire
