Como o lemon-code escolhe o modelo de cada tarefa com uma soma de fatores que você consegue ler, sem chamar outro LLM para decidir
English summary: How lemon-code scores ready HTTP providers with readable local factors, while a separate configurable JEV layer handles another routing path.
Publicado: 26 de outubro de 2026
Tempo de leitura: 9 min
Série: lemon-code em 30 dias, parte 4 de 13
"Por que ele mandou essa migração de banco para o modelo mais barato?" Eu me fiz essa pergunta numa terça à noite, olhando um diff errado. Não havia resposta. O roteador tinha decidido, e a decisão não deixava rastro.
Em uma rota para provider HTTP, o lemon-code pode registrar um breakdown neste formato. Os valores abaixo são ilustrativos:
anthropic score 71.5 (order +50.0, risk +20.0, history +1.5)
Este post explica o score base de providers HTTP: sinais da tarefa, fatores somados após readiness e um breakdown dos itens que pesaram. O formato abaixo não representa decisões de runtime CLI nem todas as camadas do dispatch.
Pense numa banca de seleção que avalia candidatos com uma ficha de pontos. Cada critério soma ou subtrai. No final você não sabe só quem ganhou, sabe por quanto e por quê. Se a escolha parecer errada, você olha a ficha e encontra o critério com peso torto.
A ficha descreve o score base de providers HTTP, que é local e determinístico. O lemon-code também tem um caminho JEV separado, configurável em modos shadow, advisory e enforce. Ele usa inferência local via Laya quando disponível e pode consultar o Vercel Gateway quando a chave está configurada; o resultado depende do modo escolhido.
Um exemplo didático do score base cabe em poucas linhas. Os pesos ilustram o formato e não reproduzem valores default de produção. O segredo é guardar cada parcela da soma:
// roteador aditivo mínimo (ilustrativo, sem dependências)
type Factor = { label: string; value: number };
type Task = { risk: number; vision: boolean };
type Provider = "anthropic" | "openai" | "google";
function scoreProvider(name: Provider, task: Task, position: number, historyBonus = 0) {
const factors: Factor[] = [{ label: "order", value: 50 - position * 0.5 }];
if (task.risk >= 7) factors.push({ label: "risk", value: name === "anthropic" ? 20 : -4 });
if (task.vision) factors.push({ label: "vision", value: name === "google" ? 22 : -18 });
if (historyBonus !== 0) factors.push({ label: "history", value: historyBonus });
const score = factors.reduce((s, f) => s + f.value, 0);
return { name, score, factors };
}
const ranked = [
scoreProvider("anthropic", { risk: 8, vision: false }, 0, 1.5),
scoreProvider("openai", { risk: 8, vision: false }, 1),
scoreProvider("google", { risk: 8, vision: false }, 2),
]
.sort((a, b) => b.score - a.score);
const describeFactor = (f: Factor) => f.label + " " + (f.value >= 0 ? "+" : "") + f.value.toFixed(1);
console.log(ranked.map((r) =>
r.name + " score " + r.score.toFixed(1) + " (" + r.factors.map(describeFactor).join(", ") + ")"
).join(" | "));
// anthropic score 71.5 (order +50.0, risk +20.0, history +1.5)
// openai score 45.5 (order +49.5, risk -4.0) | google score 45.0 (order +49.0, risk -4.0)
Rode com bun router.ts. A tarefa de risco 8 manda o modelo forte para o topo, e você sabe exatamente qual parcela fez isso.
Produtos gratuitos e pagos para transformar ideias em uma base que você consegue executar.
13 produtos disponíveisContinue explorando tópicos similares
Eu pagava API para tarefas que o Claude Code ou o Codex, já logados na minha máquina, resolveriam. A regra CLI-first do lemon-code cabe em quatro linhas de precedência.
O lemon-code tem seis ADRs com o número 0049, todas criadas no mesmo dia. Esse erro ensina mais sobre memória de projeto com vários agentes do que qualquer ADR bem numerada.
A telemetria do meu agente vem desligada, aponta para localhost e não exporta nenhum prompt. Mesmo assim ela responde quanto custou cada turno e por que um modelo foi escolhido.
Checklist prático para validar segurança, custos, avaliações e observabilidade antes de colocar seu app com IA em produção.
Templates testados em produção, usados por desenvolvedores. Economize semanas de setup no seu próximo projeto.
Antes de olhar provider, o lemon-code pontua a tarefa em cinco dimensões, todas calculadas localmente:
| Dimensão | Sinais usados |
|---|---|
| Complexidade | intenção, tamanho do prompt, arquivos e blocos de código |
| Risco | palavras como auth, payment, database, migration, production, secret |
| Contexto | tamanho, arquivos referenciados com @, code fences |
| Multimodal | anexos de imagem |
| Sensibilidade a custo | pedidos explícitos como "barato" ou "rápido" e carga local da máquina |
O risco é o mais simples e o mais útil. Uma lista de doze palavras (auth, billing, migration, deploy, delete, token, secret e outras) já separa "renomeia essa variável" de "altera a migração de pagamentos".
A complexidade cai em três faixas documentadas: 1 a 3 para leitura e perguntas curtas, 4 a 7 para refatoração localizada e debugging comum, 8 a 10 para mudanças em vários arquivos e decisões de arquitetura.
Com a tarefa pontuada, cada provider pronto recebe uma lista de fatores. O código real tem a mesma forma do exemplo mínimo: um fator order que preserva a preferência configurada, um fator risk que favorece modelos fortes quando a tarefa é crítica, um fator vision quando há imagem, e outros para contexto, custo e estratégia.
A regra de ouro: readiness vem antes do score base. Um provider indisponível não entra na soma e pode gerar um diagnóstico com dica de configuração.
O SQLite local guarda, por provider, sucessos, erros, cancelamentos, latência média, custo estimado e taxa de fallback. Esses números viram um fator history. Um provider que falhou nas últimas tentativas perde alguns pontos; um que resolve rápido ganha.
O cuidado aqui é não deixar o histórico dominar. Ele ajusta a ordem, sem apagar o efeito do risco. Se o histórico pesasse mais que o risco, um modelo barato que acerta tarefas fáceis acabaria recebendo as difíceis.
Um breakdown com quinze fatores ninguém lê. Para rotas de provider HTTP, o formatador filtra fatores com valor absoluto abaixo de 0,05, ordena pelo impacto e mostra os cinco maiores; o roteador guarda esse resultado em scoreBreakdown. Rotas para runtime CLI não usam esse mesmo breakdown.
A mesma informação vai para o histórico de decisões e, se você ativar, para o OpenTelemetry como lemon.dispatch.task.score. O post 8 da série trata dessa parte.
Roteamento explicável é uma soma que guarda as parcelas. Parece simples demais, e esse é o ponto: quando a escolha parecer errada, você abre a ficha, acha o fator torto e corrige com um teste.
Na quarta, o assunto é o que acontece depois da escolha: guardrails, modos de permissão e aprovações.
Se o seu time está montando um orquestrador de modelos e precisa explicar cada decisão para auditoria ou custo, é um dos temas em que ajudo na LEMON.DEV.