Mapa Geral Mercado Livre

Mapa Geral — Mercado Livre

Mapa de referência: como a venda acontece no Mercado Livre de ponta a ponta, e onde o Bunker entra em cada etapa.


1. A jornada de venda, em 6 etapas

Tudo começa com a conta do vendedor conectada ao Bunker e termina no relacionamento pós-venda. São 6 etapas, nessa ordem:

  1. Conectar: o vendedor autoriza a conta uma única vez (OAuth). A partir daí o Mercado Livre passa a falar com o sistema.
  2. Produto & Anúncio: o que o vendedor vende e como aparece pro comprador. Existem três coisas diferentes aqui: o Catálogo do ML (a ficha oficial, igual pra todo mundo), o Meu Produto (o estoque que o vendedor tem) e o Meu Anúncio (a vitrine: preço, fotos, condições). No catálogo, vários vendedores competem pela mesma página, a buy box; num anúncio próprio, a página é só do vendedor.
  3. Vender → Pedido: o cliente compra e isso vira um pedido, com itens, valor e status (pago, cancelado, em mediação). Pode vir agrupado em um "pacote".
  4. Receber $: o dinheiro entra através do Mercado Pago, já descontando comissão e tarifas. Não cai na hora: existe um prazo de retenção até a Liberação de Pagamentos, o processo do Mercado Pago que libera o valor pro vendedor.
  5. Enviar: o produto vai pro cliente via Full, Flex ou Correios, cada um com seu prazo e rastreio. Pra envios via drop_off, xd_drop_off, cross_docking e xd_same_day, a nota fiscal (ou DC-e, pra quem não emite NF) é obrigatória: sem ela o envio fica travado no substatus invoice_pending.
  6. Pós-venda & Relacionamento: cuidar do cliente e da reputação da loja. Inclui perguntas (antes da compra), mensagens (depois da venda), reclamações/mediações, a reputação geral do vendedor e moderações por infrações.
Pra quem não é dev

drop_off / xd_drop_off / cross_docking / xd_same_day são modalidades de envio em que o vendedor leva o pacote pra uma agência ou ponto de coleta (em vez do Mercado Livre buscar). DC-e (Declaração de Conteúdo Eletrônica) é um documento alternativo à nota fiscal, pra quem não emite NF (pessoa física ou MEI não-contribuinte). invoice_pending é o status do envio que fica "travado" até a nota fiscal (ou DC-e) ser enviada. Liberação de Pagamentos é o processo do Mercado Pago que libera, pro vendedor, o dinheiro de uma venda depois de um período de retenção.

Em paralelo, duas coisas se conectam a essas etapas:

Diagrama 1 — A jornada de venda

PROMOVE EXTRATO OPC Marketing ofertas · Mercado Ads 01 Conectar OAuth · autoriza 1x 02 Produto & Anúncio catálogo · meu anúncio 03 Vender → Pedido itens · valor · status 04 Receber $ Mercado Pago · taxas 05 Enviar Full · Flex · Correios 06 Pós-venda perguntas · reputação FIN Conciliação Financeira vendas - taxas - reembolsos = repasse LEGENDA Etapa em foco (★) Etapa da jornada Apoio opcional Resultado financeiro Fluxo de dados


2. Webhooks e a camada de plataforma

Por baixo das 6 etapas existe uma camada que avisa o sistema sobre toda mudança em tempo real: os webhooks do Mercado Livre.

O padrão notify-then-fetch

A regra do ML é simples: o webhook nunca traz o dado completo, ele só avisa que "o recurso X mudou" (um id mais um tópico). Não existe "webhook completo". A partir desse aviso, o sistema precisa fazer um GET na API pra buscar o recurso atualizado.

Duas regras operacionais importantes vêm junto:

Pra quem não é dev

Webhook é um aviso automático que o Mercado Livre manda assim que algo muda do lado dele (por exemplo: "o pedido 123 mudou de status"). GET é o pedido que fazemos de volta pra API do Mercado Livre pra buscar a informação completa e atualizada (por exemplo: "me dá os detalhes do pedido 123"). API é a porta de entrada que o Mercado Livre disponibiliza pra sistemas conversarem com ele.

O pipeline: fila, worker e handler

  1. Receiver: recebe o webhook, responde 200 imediatamente (sem processar nada ainda) e salva o evento numa fila (MeliNotificationEvent).
  2. Worker (meli-webhook-worker): drena essa fila continuamente e roteia cada evento pro handler certo, de acordo com o tópico.
  3. Handler: faz o GET do recurso na API do ML pra buscar o dado completo e salva no banco (MeliOrder, MeliItem, MeliQuestion, etc).

Esse pipeline separa "receber o aviso" de "processar o aviso": o sistema não trava esperando a resposta da API do ML, e o ML não desativa o tópico por demora na resposta.

Pra quem não é dev

Receiver é a primeira parte do sistema que recebe o aviso do ML, ele só confirma "recebi" e guarda o aviso pra tratar depois. Fila é uma lista de avisos pendentes esperando a vez, em ordem. Worker é um processo que roda o tempo todo em segundo plano, pegando avisos da fila um por um. Handler é a função que sabe o que fazer com cada tipo específico de aviso. E MeliOrder, MeliItem, MeliQuestion são as "tabelas" do nosso banco, cada uma guarda uma cópia local e atualizada de uma informação do Mercado Livre (pedidos, anúncios, perguntas...).

Tópicos monitorados hoje

Categoria Tópico Status
💰 Vendas orders_v2 (vendas)
💰 Vendas payments (pagamento)
💰 Vendas shipments (envio)
💰 Vendas orders_feedback
📦 Produto/Estoque items (mudança no anúncio)
📦 Produto/Estoque stock-location (estoque)
📦 Produto/Estoque price_suggestion
📦 Produto/Estoque user_products_families
📖 Catálogo catalog_item_competition_status
📖 Catálogo catalog_suggestions
💬 Pós-venda questions (perguntas)
💬 Pós-venda messages (mensagens)
💬 Pós-venda post_purchase/claims
🧾 Fiscal invoices
🏷️ Promo/Logística public_offers
🏷️ Promo/Logística public_candidates
🏷️ Promo/Logística flex-handshakes
🏷️ Promo/Logística fbm_stock_operations

✅ já temos handler · ⬜ gap, ainda não tratamos.

O gap mais importante: invoices está ligado ao gap de nota fiscal/DC-e (Seção 1).

Detalhamento completo de cada tópico (recurso consultado, categoria de permissão, o que cada agente pode ler e escrever hoje): Capacidades por Tópico de Notificação.

Três notas sobre a tabela acima: o tópico items também cobre mudança de preço, nesse caso o handler consulta /items/$ITEM_ID/sale_price em vez de /items/$ITEM_ID; messages e post_purchase têm subtópicos extras (read e claims_actions) que já chegam junto mas não têm linha própria; e vis_leads/leads-credits ficam de fora de propósito, são exclusivos das verticais de imóveis, veículos e serviços, fora do escopo de e-commerce geral do Bunker.

Priorização dos gaps pelo priority stack do Gestor

Os 7 tópicos ⬜ encaixam no priority stack do Gestor (#1 Reputação > #2 Dinheiro > #3 Crescer, ver Seção 9):

Tópico ⬜ O que destrava Prioridade
orders_feedback Feedback de venda confirmada #1
flex-handshakes Handoff de transportadora Flex (rastreio/atraso) #1
invoices NF gerada pelo Faturador ML (gap fiscal/DC-e, Seção 1) #2
fbm_stock_operations Operações de estoque Full (ruptura) #2
price_suggestion Sugestão de preço do ML (/suggestions/items/.../details) #2 / #3
user_products_families Mudança em famílias/variações do produto #3
catalog_suggestions Sugestões de produto pro catálogo (Brand Central) #3

Diagrama 2 - O pipeline de webhooks

WEBHOOK GET /recurso completo ENFILEIRA DRENA ROTEIA SALVA ALIMENTA EXT Mercado Livre dispara webhook · expõe API EVT Recebido responde 200 (~500ms) Q Na fila MeliNotificationEvent WRK Processando worker drena · roteia API Buscando recurso handler · GET recurso DB Persistido MeliOrder · MeliItem PRÓX 6 Agentes de IA consomem os dados já persistidos (próxima seção) LEGENDA Estado crítico (★) Estado do pipeline Sistema externo Próxima seção Notify → Fetch


3. Arquitetura dos 6 agentes de IA

O Bunker tem 6 agentes de IA, cada um especialista em uma parte da jornada. O Gestor é o diretor: orquestra os outros 5 e tem como prioridade #1 cuidar da reputação da conta.

Diagrama 3 — Os 6 agentes e quem orquestra quem

GESTOR Gestor diretor · orquestra os 5 agentes prioridade #1: Reputação & Buy Box ANU Anúncios título · attrs · tags conversão · health score CRI Criativo imagens · hero + 7 slots ≈80% do CTR ANL Analista preço · listing type ganha buy box / relevância ADS Ads PPC · campanhas stub · read-only (ADR-08) SAC SAC perguntas · mensagens resolve em 1 toque LEGENDA Gestor (orquestrador, ★) Agente especialista Hierarquia (orquestra) Stub / aguardando setup

Onde cada agente atua no ML × o que consome no banco

Agente Onde atua no Mercado Livre O que consome no banco
Gestor Toda a jornada (coordena)
Conta / Reputação
Competição (buy box)
MeliSellerMetrics / Profile
MeliOrder (margem real)
MeliCatalogCompetition
MeliStockLocation 🆕 (ruptura)
+ saída dos outros 5 agentes
Analista Anúncio (preço, listing type)
Buy box / Competição
Vender (sales velocity)
MeliCatalogCompetition (price_to_win)
MeliItem (price/listingType)
MeliStockLocation 🆕
MeliOrder (velocity)
MeliSellerMetrics
Anúncios Anúncio (título, attributes, tags)
Catálogo (ficha técnica)
MeliItem (title/attrs/healthScore)
MeliCatalogProduct 🆕 (ficha)
MeliVariation 🆕
Criativo Anúncio (imagens / pictures)
Catálogo (imagens de referência)
MeliItem (pictures, category)
MeliCatalogProduct 🆕 (ref)
Ads Mercado Ads (Product/Brand)
Buy box (só anuncia GANHANDO)
Vender (conversão)
MeliCatalogCompetition (gate)
MeliItem
MeliOrder (conversão)
SAC Pós-venda (perguntas, mensagens)
Reclamações / Mediações
Pedido · Envio (status)
MeliQuestion / Message / Claim
MeliItem (info p/ responder)
MeliOrder
MeliShipment
Pra quem não é dev

  • 🆕 = schema que ainda não existe no banco, precisa ser criado.
  • Buy box = a "vitrine" do Mercado Livre quando vários vendedores vendem o mesmo produto. Só 1 aparece em destaque (ganha a venda e a visibilidade), os outros ficam escondidos.
  • "Mais Relevante" = um critério de ordenação da busca do ML, não é só preço, envolve reputação, conversão etc.
  • Listing type = o "plano" do anúncio no ML (ex: clássico, premium), afeta taxa e exposição.
  • Health score = uma nota que o ML dá pro anúncio com base na qualidade do cadastro (título, fotos, ficha técnica completa).
  • CTR = taxa de cliques, quantas pessoas clicam no anúncio em relação a quantas veem.
  • Sales velocity = a velocidade de vendas de um produto, quantas unidades por dia/semana.
  • ACOS / ROAS = métricas de anúncio pago: ACOS é quanto você gasta em ads pra cada R$1 de venda, ROAS é o inverso, quanto você fatura pra cada R$1 investido em ads.
  • ADR-08 / read-only = uma decisão técnica registrada que hoje trava o agente Ads em modo leitura: ele lê os dados dos anúncios pagos, mas ainda não pode alterar campanhas sozinho.
  • price_to_win = o preço que, segundo o ML, faria o vendedor ganhar a buy box agora.


4. Como o SAC decide, gatilho a gatilho

O SAC cuida do atendimento: perguntas, mensagens e reclamações. A missão é resolver em 1 toque (uma resposta só resolve o problema) sem nunca quebrar o compliance do Mercado Livre. Por trás disso existe um fluxo bem definido, do webhook que avisa "chegou uma dúvida" até a resposta (ou a escalada pra um humano).

O gatilho: do webhook ao evento

Existem 3 áreas que disparam o SAC, e todas têm a mesma natureza: alguém tem uma dúvida sobre o produto.

O caminho até virar um evento de decisão segue o pipeline da seção 2: o Receiver responde 200 e guarda o aviso na fila, o Worker drena a fila e roteia pro handler certo, e o Handler salva o registro no banco (MeliQuestion, MeliMessage ou MeliClaim).

Aqui entra o passo que faz a diferença: o sistema resolve o produto. A pergunta, mensagem ou claim chega com um itemId, mas isso sozinho não é suficiente pra responder. O sistema busca o MeliItem correspondente e monta o productContext, com título, atributos e condições do anúncio. É esse contexto que alimenta a resposta; sem ele, o SAC não sabe do que está falando.

Com o productContext em mãos, o sistema emite o evento CUSTOMER_QUESTION, que vira o gatilho do fluxo de decisão.

Pra quem não é dev

productContext é um "resumo" do anúncio (título, atributos, condições) montado na hora a partir do MeliItem, pra dar à IA a informação que ela precisa pra responder. Evento (CUSTOMER_QUESTION) é um sinal interno que diz "chegou uma dúvida, aqui está o contexto, decida o que fazer". MeliQuestion, MeliMessage e MeliClaim são as tabelas que guardam, respectivamente, perguntas, mensagens e reclamações vindas do Mercado Livre.

Status atual: os 3 gatilhos estão ligados ponta a ponta (PRs BUN-122/123/129/131). questions é o único que pode responder sozinho (quando confiante e compliant); messages e claims sempre passam por humano.

O fluxo de decisão

Com o evento CUSTOMER_QUESTION em mãos, o SAC segue 4 perguntas antes de agir:

  1. A LLM gera uma resposta: { answer, confident, detectedPattern }.
  2. A resposta veio válida? (a LLM respondeu e o JSON bateu com o schema esperado)
  3. A resposta passa no compliance? (sem palavrão, sem URL, sem CPF/CNPJ/e-mail)
  4. A IA está confiante na resposta?

Dependendo de onde a resposta passa ou falha nesses 4 pontos, o sistema cai em um dos 3 sinais abaixo.

Pra quem não é dev

LLM é o modelo de IA que gera a resposta, o "cérebro" do agente. JSON/schema é o formato estruturado que a resposta da LLM precisa seguir pro sistema conseguir ler os campos answer, confident e detectedPattern; se vier fora do formato, a resposta é descartada (llm_parse_failed). Compliance aqui são as regras do Mercado Livre pra mensagens entre vendedor e comprador: nada de palavrão, link ou dado pessoal (CPF, CNPJ, e-mail), chamados de PII. needsHuman é o estado que tira a decisão da IA e coloca na fila de um humano; draftAnswer é o rascunho que a IA deixa pronto pra esse humano só revisar. applied é o estado que indica que a IA já respondeu sozinha. Gate de tier (R7 / Tier 3) é uma trava de aprovação por nível de risco: ações de alto impacto (como reembolso) não são executadas pela IA, o fluxo para e espera aprovação humana.

Os sinais

O princípio por trás dos sinais: quanto maior o impacto da ação, mais "vermelho" ela fica. Responder uma pergunta é leve (verde); reembolsar ou cancelar um pedido sobe pro gate de aprovação, e a IA nunca executa sozinha.

Essa escala de sinais vale pro gatilho de pergunta (CUSTOMER_QUESTION). Os outros 2 gatilhos têm regra própria, mais restritiva, e nunca chegam ao verde:

A inbox de aprovação

Os rascunhos amarelos (SAC_QUESTION_DRAFT e SAC_POST_SALE_DRAFT) caem numa inbox de aprovação (ExecuteSacDraftUseCase): o humano revisa, edita se quiser, e decide SEND ou DISMISS. SEND dispara o envio real ao Mercado Livre primeiro (answerQuestion ou sendMessage) e só depois grava audit; DISMISS só marca como rejeitado, nada é enviado ao ML. Claims não passam por essa inbox: não existe "enviar texto" pra um claim, o humano age direto no Mercado Livre pelos endpoints de claim.

Handoff: corrigindo a causa

Se o detectedPattern revela que a dúvida vem de um problema no próprio anúncio (ex: muita gente pergunta a medida do produto), o SAC roteia esse sinal pro Anúncios ou pro Criativo, em paralelo e de forma opcional. Anúncios pode adicionar o atributo que falta; Criativo pode gerar um infográfico explicando a medida. A resposta ao cliente resolve o sintoma; o handoff corrige a causa.

Diagrama 4 — O fluxo completo do SAC

4a · O gatilho completo

Perguntas do produto pré-venda · webhook questions Mensagens pós-venda pós-compra · webhook messages Reclamação / dúvida em claim pós-venda · webhook post_purchase RCV Receiver responde 200 (~500ms) salva MeliNotificationEvent WRK meli-webhook-worker drena a fila → roteia por tópico topics: questions · messages · post_purchase HDL Handler salva no banco MeliQuestion · MeliMessage · MeliClaim registro salvo conforme o tipo de evento CTX Resolve produto itemId → MeliItem → productContext título · atributos · condições do anúncio EVT Emite evento CUSTOMER_QUESTION { question, itemId, productContext } Segue para o fluxo de decisão → Diagrama 4b · evento CUSTOMER_QUESTION NOTA Por que «Resolve produto» existe (o ponto-chave) O SAC cobre mais de uma área, mas todas são "dúvida sobre o produto": antes de responder, precisa saber de qual produto se trata. itemId (pergunta/mensagem/claim) → join MeliItem → productContext: título, atributos, condições — é isso que alimenta a resposta. Status: os 3 gatilhos ligados ponta a ponta (PRs BUN-122/123/129/131) · só «questions» pode responder sozinho, ver Diagrama 4d. LEGENDA Gatilho (entrada) Etapa do pipeline Ponto-chave (★) Convergência (merge) Nota / status

4b · O fluxo de decisão

↑ vem do Diagrama 4a · chega com productContext em mãos sim sim sim não bloqueado não Evento CUSTOMER_QUESTION chega chega com productContext (montado no Diagrama 4a) LLM gera resposta { answer, confident, detectedPattern } A resposta da LLM é válida? respondeu + JSON/schema ok? Passa no compliance? palavrão · URL/www · CPF CNPJ · e-mail A IA está confiante na resposta? confidence acima do limiar? Responde a pergunta no ML meli.answerQuestion(itemId, questionId, texto) applied = true respondeu sozinho no Mercado Livre needsHuman llm_parse_failed needsHuman compliance_block needsHuman low_confidence + rascunho LEGENDA Início / fim do fluxo Etapa do fluxo Decisão (sim/não) Ação principal (★) Sinal verde · applied (responde sozinho) Sinal amarelo · needsHuman + rascunho Sinal vermelho · needsHuman (bloqueado/erro)

4c · Regras em paralelo (handoff, compliance, responsabilidades)

↳ regras que valem em paralelo ao fluxo de decisão (Diagrama 4b) HANDOFF Handoff (paralelo, opcional) Se detectedPattern revela um problema no anúncio, o SAC roteia em paralelo pra «Anúncios» ou «Criativo», sem travar a resposta. Exemplo: muita gente pergunta a medida → Anúncios adiciona o atributo faltante, Criativo gera um infográfico. Corrige a causa, não só o sintoma. COMPLIANCE Compliance · o que aciona o bloqueio palavrão (lista PT-BR) · qualquer URL/www · PII: CPF, CNPJ, e-mail → bloqueado vira rascunho pra um humano revisar e enviar. ESCOPO Responsabilidades do Agente SAC • resolver em 1 mensagem (one-touch), sempre que possível • manter compliance com as regras do ML (sem link, avaliação, promoção ou PII) • detectar padrões e rotear pra quem corrige o anúncio (Anúncios / Criativo) • escalar pra humano tudo que for crítico ou incerto INBOX Inbox de aprovação · human-in-the-loop Rascunhos (SAC_QUESTION_DRAFT, SAC_POST_SALE_DRAFT) caem na inbox via ExecuteSacDraftUseCase; humano revisa e decide. SEND → envia ao ML primeiro (answerQuestion / sendMessage), depois grava audit. DISMISS → marca rejected, nada enviado. Claim não passa por aqui: sem botão «enviar texto», humano resolve direto no ML pelos endpoints de claim (BUN-122). LEGENDA Regra / referência em paralelo (nota) Sinal vermelho · aciona compliance_block (ver 4b)

4d · Os sinais

↳ resultado final do fluxo de decisão (Diagrama 4b) · o que cada sinal significa na prática VERDE · responde sozinho estado: applied = true QUANDO: IA confiante e passou no compliance → responde sozinha no ML. Resolução 1-toque, baixo risco, sem humano. AMARELO · humano revisa estado: needsHuman · low_confidence QUANDO: resposta plausível e dentro do compliance, mas a IA não tem certeza. → gera draftAnswer; humano aprova antes de enviar. VERMELHO · aprovação obrigatória estado: needsHuman (nunca sozinho) QUANDO (qualquer um): compliance_block (palavrão · URL · PII) · falha técnica: llm_parse_failed / meli_answer_failed · ação crítica (reembolso, cancelar) → gate de aprovação (R7/Tier 3); humano decide. PRINCÍPIO Princípio dos sinais Quanto maior o impacto da ação, mais «vermelho» ela fica: responder uma pergunta é leve (verde). Reembolsar ou cancelar sobe pro gate de aprovação (R7 / Tier 3); a IA nunca executa essas ações sozinha. POR GATILHO Message e claim nunca chegam ao verde Essa escala (verde/amarelo/vermelho) vale pro gatilho de pergunta (CUSTOMER_QUESTION). Os outros 2 são mais restritivos. POST_SALE_MESSAGE: sempre amarelo, vira SAC_POST_SALE_DRAFT na inbox (D1) — nunca envia sozinho ao Mercado Livre. CLAIM_OPENED: sempre vermelho (TIER_3), IA só sugere; humano resolve direto no ML (BUN-122), sem inbox — ver Diagrama 4c. LEGENDA Verde · applied (responde sozinho) Amarelo · needsHuman + rascunho (low_confidence) Vermelho · needsHuman, bloqueia (gate R7/Tier 3) Sinal = estado final do fluxo (Diagrama 4b) Princípio / nota

Detalhe das 3 ramificações do sacNode (o que o código faz hoje em cada ramo, lado a lado): SAC — As 3 Ramificações do sacNode.


5. Como o Analista decide preço e buy box, por tier de autorização

O Analista cuida da precificação: monitora os itens, compara com a concorrência e ajusta o preço pra ganhar a buy box (o destaque "Mais Relevante" do Mercado Livre), sem nunca vender abaixo do custo. A diferença em relação ao SAC é que aqui a IA pode agir sozinha em mais situações, e tudo é organizado por tiers de autorização (1, 2 e 3), de acordo com o tamanho da mudança de preço.

O gatilho: do monitor ao evento PRICE_CHANGE

Existem 2 fontes que disparam uma reavaliação de preço:

As duas fontes convergem pra "Detecta alerta", que identifica o diff de preço/posição no item monitorado, e emite o evento PRICE_CHANGE, com { itemId, diffPct, newPrice, competitor }.

Pra decidir o que fazer, o fluxo lê o MeliItem (preço, listing type, custo, de onde sai a margem) e o price_to_win / MeliCatalogCompetition (o preço necessário pra ganhar a buy box e o status da disputa).

Status atual: as 2 fontes já estão implementadas e mergeadas (PRs #76 + #77). O price-monitor.job roda de hora em hora, varre o catálogo ativo do seller e emite PRICE_CHANGE quando o item está disputando a buy box (competing/listed/sharing_first_place) e o price_to_win difere do preço atual; item já winning nunca dispara, porque já é dono da buy box. O webhook catalog_item_competition_status emite só quando o seller perdeu a posição (status ≠ winning) e há price_to_win disponível. As duas fontes chamam getPriceToWin e gravam um snapshot na tabela MeliCatalogCompetition, que já existe e está populada.

Pra quem não é dev

Buy box (também chamada de "Comprar" ou "Mais Relevante") é o destaque que o Mercado Livre dá ao melhor vendedor de um anúncio com vários sellers; ganhar a buy box significa aparecer em destaque pra maioria dos compradores. price_to_win é o preço calculado como o necessário pra ganhar essa disputa. MeliCatalogCompetition é a tabela que guarda o status dessa disputa de preço (snapshot atualizado a cada gatilho). Listing type é o tipo de anúncio (clássico, premium etc.), que entra no cálculo do algoritmo "Mais Relevante".

O fluxo de decisão

Com o evento PRICE_CHANGE em mãos, o Analista segue esta sequência:

  1. O evento PRICE_CHANGE chega com { itemId, diffPct, newPrice, competitor }.
  2. O sistema busca o contexto de preço: getItem + getItemPricesContext (price_to_win e dados de concorrência).
  3. A LLM recomenda: { recommendation, newPrice, reason, marginFloorRespected }.
  4. A margem foi respeitada? (marginFloorRespected)
  5. Se sim, classifica a mudança por magnitude (|diffPct|) em 3 faixas de tier.

Margem primeiro, magnitude depois: se a margem não foi respeitada, não importa o tamanho da mudança, o sistema força o TIER_3 (aprovação obrigatória), sempre. Só quando a margem está ok é que o |diffPct| decide entre os 3 tiers.

Pra quem não é dev

marginFloorRespected indica se o preço sugerido respeita o piso de margem (não vende abaixo do custo). diffPct é a diferença percentual entre o preço atual e o preço recomendado. TIER_1/2/3 são os níveis de autorização: quanto maior o tier, maior o risco e mais aprovação humana é exigida. createPendingApproval é a função que cria um pedido de aprovação pendente pra um humano revisar.

Os sinais

A diferença-chave em relação ao SAC: aqui o amarelo (TIER_2) já executa e só notifica depois; somente o vermelho (TIER_3) espera aprovação antes de agir. A trava de margem é o "sempre-vermelho" do Analista, equivalente ao compliance_block do SAC.

Regras do Analista

Como ele decide o preço (do prompt): o "Mais Relevante" do Mercado Livre não é só preço, ele pesa o preço final (item + frete), o listing type (clássico/premium), a reputação do vendedor e o uso do Envio Full. O Analista mira o price_to_win pra ganhar a buy box, sem furar o piso de margem.

A trava de margem (regra-mãe): se a IA diz que marginFloorRespected = false, o sistema força TIER_3 automaticamente, não importa o tamanho do |diffPct|. Essa regra protege contra vender abaixo do custo, e tem prioridade sobre a classificação por magnitude.

Responsabilidades: achar o preço que ganha a buy box ou melhora a posição; respeitar o piso de margem; agir sozinho em mudanças pequenas; pedir aprovação em mudanças grandes ou fora da margem; registrar tudo em audit log.

Diagrama 5 — O fluxo completo do Analista

5a · O gatilho completo

Monitor de preços (CRON) price-monitor · detecta mudança de preço/posição Concorrente mudou a buy box webhook catalog_item_competition_status ALR Detecta alerta calcula o diff de preço/posição no item monitorado EVT Emite evento PRICE_CHANGE { itemId, diffPct, newPrice, competitor } Segue para o fluxo de decisão → Diagrama 5b · evento PRICE_CHANGE NOTA Estado hoje (implementado) price-monitor.job já mergeado (PR #76): roda de hora em hora, varre o catálogo ativo do seller e emite PRICE_CHANGE. Webhook catalog_item_competition_status já mergeado (PR #77): schema MeliCatalogCompetition existe e está populado. LEGENDA Gatilho (entrada) Etapa do pipeline Evento PRICE_CHANGE (★) Convergência (merge) Nota / status

5b · O fluxo de decisão

↑ vem do Diagrama 5a · chega com o evento PRICE_CHANGE sim sim sim não não não Evento PRICE_CHANGE chega { itemId, diffPct, newPrice, competitor } Busca contexto de preço getItem + getItemPricesContext LLM recomenda { recommendation, newPrice, reason, marginFloorRespected } Margem respeitada? (marginFloorRespected) trava de margem → sempre TIER_3 |diffPct| ≤ 10%? (mudança não é grande) mudança grande (>10%) |diffPct| < 5%? (mudança é pequena) entre 5% e 10% → TIER_2 TIER_1 · atualiza preço sozinho updatePrice(itemId, newPrice) · sem notificar applied = true audit: PRICE_CHANGE_AUTO TIER_3 · approval pendente margem falhou OU |diffPct| > 10% createPendingApproval humano decide; IA não executa TIER_2 · notify (executa) updatePrice + audit PRICE_CHANGE_NOTIFY LEGENDA Início / fim do fluxo Etapa do fluxo Decisão (sim/não) Ação principal (★) Verde · TIER_1 (auto, sozinho) Amarelo · TIER_2 (executa + notifica) Vermelho · TIER_3 (approval, IA não executa)

5c · Regras em paralelo (como decide o preço, trava de margem, responsabilidades)

↳ regras que valem em paralelo ao fluxo de decisão (Diagrama 5b) PREÇO Como o Analista decide o preço "Mais Relevante" não é só preço: pesa preço final (item + frete), listing type (clássico/premium), reputação e Envio Full. O Analista mira o price_to_win pra ganhar a buy box, sem furar o piso de margem. MARGEM Trava de margem · regra-mãe (sempre TIER_3) Se marginFloorRespected = false, o sistema força TIER_3 automaticamente, não importa o |diffPct|; prioridade sobre a magnitude. ESCOPO Responsabilidades do Agente Analista • achar o preço que ganha a buy box ou melhora a posição (price_to_win) • respeitar o piso de margem · nunca vender abaixo do custo • agir sozinho (TIER_1) ou notificar (TIER_2) em mudanças pequenas/médias • pedir aprovação (TIER_3) em mudanças grandes ou fora da margem • registrar tudo em audit log LEGENDA Regra / referência em paralelo (nota) Vermelho · aciona TIER_3 (trava de margem, ver 5b)

5d · Os sinais, por tier

↳ resultado final do fluxo de decisão (Diagrama 5b) · o que cada tier significa na prática TIER_1 · Verde (auto) estado: |diffPct| < 5% e margem respeitada Atualiza o preço sozinho no ML (updatePrice). Mudança pequena, baixo risco; não notifica ninguém. Audit: PRICE_CHANGE_AUTO. TIER_2 · Amarelo (notify) estado: |diffPct| entre 5% e 10%, margem ok Atualiza o preço e notifica um humano. Executa, mas avisa; humano acompanha e pode reagir. Audit: PRICE_CHANGE_NOTIFY. TIER_3 · Vermelho (aprovação) estado: margem falhou OU |diffPct| > 10% Não executa: cria approval pendente. Humano decide antes de qualquer ação. Falhas técnicas (busca/LLM/parse/update) caem aqui. PRINCÍPIO Diferença-chave vs. o SAC Aqui o amarelo (TIER_2) já executa e só notifica depois; só o vermelho (TIER_3) espera aprovação antes de agir. A trava de margem é o «sempre-vermelho» do Analista, equivalente ao compliance_block do SAC (Diagrama 5c). LEGENDA Verde · TIER_1 (auto, sem aviso) Amarelo · TIER_2 (executa + notifica) Vermelho · TIER_3 (approval pendente) Sinal = resultado do fluxo de decisão (Diagrama 5b) Princípio / nota

Detalhe do analistaNode e do desfecho implementado por TIER (o que o código faz hoje, com nomes de função e audit): Analista — analistaNode e Desfecho por TIER.


6. Como o Ads otimiza campanhas por ROAS/ACOS, sem escrever no Mercado Livre

O Ads (Product Ads) cuida das campanhas de publicidade do vendedor: lê as métricas reais que o Mercado Livre expõe, analisa o desempenho de cada campanha e recomenda ajustes pra melhorar o retorno. A lógica de decisão é o "harvest flywheel", ele olha pra ROAS e pra impression_share e decide a próxima ação a partir disso. A diferença mais importante em relação aos outros agentes é que hoje ele nasce read-only: a API de escrita do Mercado Livre pra anúncios não está disponível, então tudo que ele faz é ler, analisar e recomendar, quem aplica é sempre o humano.

O gatilho: do CRON ads-sync ao evento ADS_REVIEW

Não existe webhook de ads. O gatilho é um CRON (ads-sync) que roda diariamente, recalcula às 10h GMT-3, olhando uma janela rolling de 90 dias com granularidade diária.

O primeiro passo é um pré-requisito com risco: descobrir o advertiser_id via GET /advertising/advertisers. Esse endpoint exige reputação amarela ou melhor, pelo menos 15 dias de conta e vendas mínimas, sem fatura vencida, se algum desses requisitos não for atendido, o Mercado Livre retorna 404.

Com o advertiser_id em mãos, o pipeline segue: busca campanhas, anúncios e métricas (campaigns/search + ads/search), salva no banco em 3 tabelas novas (MeliAdsCampaign, MeliAdsAd, MeliAdsMetricsDaily), junta esses dados com MeliItem e MeliCatalogCompetition (buy_box, preço, custo) e, por fim, emite o evento ADS_REVIEW, um por anunciante, todo dia.

Pra quem não é dev

advertiser_id é o identificador da conta de anúncios dentro do Mercado Ads, diferente do ID do vendedor. ROAS (Return on Ad Spend) é quanto retorno em vendas cada real investido em anúncio gerou. ACOS (Advertising Cost of Sale) é o inverso: quanto da venda foi gasto em anúncio. impression_share é a fatia das vezes que seu anúncio podia aparecer e de fato apareceu. CRON é uma tarefa que roda automaticamente em horários fixos, aqui, todo dia às 10h.

O fluxo de decisão

Com o evento ADS_REVIEW em mãos:

  1. O evento ADS_REVIEW chega (cron diário, por anunciante).
  2. O sistema lê campanhas, anúncios e métricas dos últimos 90 dias do banco.
  3. A LLM analisa: roas vs roas_target, acos vs acos_benchmark e sov (share of voice).
  4. Decide a AÇÃO, usando a matriz de decisão do flywheel (ver Diagrama 6c).
  5. Classifica o IMPACTO dessa ação, o que define o sinal.

A matriz de decisão (o "flywheel")

A matriz é o coração do agente: ela traduz métricas reais do Mercado Livre em ações.

Pra alimentar essa matriz, o agente lê (verbatim) acos, roas, sov, cpc, ctr, cvr, cost, clicks, prints, impression_share, top_impression_share, lost_impression_share_by_budget/_by_ad_rank, acos_benchmark, recommended, buy_box_winner e as vendas direct/indirect/organic. E o que ele controlaria, se pudesse escrever: o modo (Automático ou Personalizado), a strategy (PROFITABILITY, INCREASE ou VISIBILITY), o budget diário, o roas_target (de 1x a 35x) e o status (active/paused).

Pra quem não é dev

lost_impression_share é a fatia de exibições perdidas, dividida entre "por orçamento" (o budget acabou) e "por ranking" (o anúncio não é bom o suficiente, mesmo com budget sobrando). recommended é um sinalizador que o próprio Mercado Livre calcula, indicando que aquele item tem potencial pra anúncio. strategy é a estratégia de otimização da campanha: priorizar lucro (PROFITABILITY), vendas (INCREASE) ou alcance (VISIBILITY).

Os sinais

A página de documentação "product-ads-leitura-e-escritura" não tem conteúdo público, a escrita não está documentada, e os endpoints legados foram desativados em 26/02/2026. Por isso o agente nasce read-only: ele recomenda, quem executa é sempre o humano.

Diagrama 6 — O fluxo completo do Ads

6a · O gatilho completo

CRON ads-sync (sem webhook) recálculo 10h GMT-3 · janela rolling 90 dias · granularidade DAILY REQ Descobre advertiser_id GET /advertising/advertisers requer reputação amarela+ · 15d · vendas mín · senão 404 API Busca campanhas + anúncios GET campaigns/search +metrics (date_from/to, DAILY) GET ads/search DB NOVO Salva no banco MeliAdsCampaign · MeliAdsAd MeliAdsMetricsDaily JOIN Junta com competição MeliItem + MeliCatalogCompetition (buy_box, preço, custo) EVT Emite evento ADS_REVIEW por anunciante, diário (evento) Segue para o fluxo de decisão → Diagrama 6b · evento ADS_REVIEW (por anunciante, diário) NOTA Endpoints de leitura (api-version: 2) · verbatim GET /advertising/{site}/advertisers/{adv}/product_ads/campaigns/search?metrics=...&aggregation_type=DAILY GET .../campaigns/{id} (detalhe + impression_share, acos_benchmark) GET .../advertisers/{adv}/product_ads/ads/search · GET /advertising/{site}/product_ads/ads/{item_id} filtros: status, item_id, buy_box_winner, recommended, campaign_id LEGENDA Gatilho (entrada) Etapa do pipeline Risco de 404 (pré-requisito) Ponto-chave (★) Nota / endpoints

6b · O fluxo de decisão

↑ vem do Diagrama 6a · chega o evento ADS_REVIEW (por anunciante, diário) Evento ADS_REVIEW chega cron diário, por anunciante Lê dados (90d) campanhas, anúncios e métricas (90 dias) do banco LLM analisa roas vs roas_target · acos vs acos_benchmark · sov Decide a AÇÃO matriz do flywheel · o coração do agente (→ 6c) Classifica IMPACTO define o sinal: verde, amarelo ou vermelho (↓) LEITURA / DASHBOARD Mostra ROAS, ACOS, SOV, impression_share e lost_share dos itens monitorados → risco zero · automático, sempre AJUSTE PEQUENO budget ±10-20% no alvo · ativa item recommended (idle → active) → recomenda; humano confirma e aplica ESCRITA DE IMPACTO budget grande · pausar campanha mudar strategy ou roas_target → aprovação (tier 3) · bloqueado hoje LEGENDA Início do fluxo Etapa do fluxo Decisão central (matriz → 6c) Verde · automático (sempre) Amarelo · recomenda, humano aplica Vermelho · aprovação (bloqueado hoje)

6c · Contexto em paralelo (matriz de decisão, métricas, config)

↳ contexto que alimenta o passo «Decide a AÇÃO» (Diagrama 6b) FLYWHEEL Matriz de decisão (o «flywheel») • lost_impression_share_by_BUDGET alto → orçamento baixo, há demanda → sobe budget • lost_impression_share_by_AD_RANK alto → ranking baixo → handoff pro Anúncios/Analista • roas < roas_target → otimiza/pausa anúncio fraco · roas ≫ target (folga) → escala budget • recommended=true E status=idle → ativa publicidade nesse item • status=hold (sem estoque/pausado) → avisa estoque/anúncio · acos vs acos_benchmark = régua MÉTRICAS Métricas que lê (verbatim) acos · roas · sov (share of voice) · cpc · ctr · cvr · cost · clicks · prints impression_share · top_impression_share · lost_impression_share_by_budget / _by_ad_rank acos_benchmark · recommended · buy_box_winner · vendas direct/indirect/organic CONFIG Config / estratégia (o que controlaria, se pudesse escrever) modo: Automático (ML escolhe) vs Personalizado strategy: PROFITABILITY · INCREASE · VISIBILITY budget (média diária) · roas_target (1x-35x) · status active/paused LEGENDA Contexto que o passo «Decide a AÇÃO» consulta (nota)

6d · Os sinais

↳ resultado final do fluxo de decisão (Diagrama 6b) · o que cada sinal significa na prática VERDE · leitura/dashboard estado: sempre disponível (read-only) Mostra ROAS, ACOS, SOV, impression_share, lost_share Risco zero; roda automaticamente todo dia. Não recomenda nem aplica nada, só informa. AMARELO · ajuste pequeno estado: recomendação pronta (humano aplica) budget ±10-20% no alvo · ativar recommended idle Mudança pequena, baixo risco financeiro. Humano confirma e aplica manualmente no ML. VERMELHO · escrita de impacto estado: aprovação (tier 3) · bloqueado hoje budget grande · pausar · mudar strategy/roas_target Alto impacto financeiro → vai pra aprovação. + write API indisponível hoje → bloqueado também DIFERENÇA Diferença-chave vs. os outros agentes Mesmo o verde aqui é só leitura: nenhum sinal do Ads resulta em escrita autônoma no ML. Amarelo e vermelho dependem do humano pra agir; o vermelho hoje tem bloqueio duplo: alto impacto + write API indisponível. LEGENDA Verde · leitura/dashboard, sempre automático Amarelo · recomenda, humano aplica Vermelho · aprovação (tier 3) + bloqueado (write API) Sinal = resultado do fluxo de decisão (Diagrama 6b) Princípio / nota


7. Como o Anúncios otimiza título, atributos e imagens, em 2 passadas com o Criativo

O Anúncios cuida da ficha do produto: título, atributos e imagens, otimizando pra conversão, que é o #1 fator de ranking no Mercado Livre (não keyword stuffing). Ele trabalha em 2 passadas: na primeira, propõe mudanças (título, atributos, quantos slots de imagem precisam ser preenchidos); aciona o Criativo pra gerar as imagens que faltam; e na segunda passada, aplica tudo de fato no anúncio. O "freio" desse agente é o title-validator, que garante que o título final respeita o limite de caracteres da categoria (em geral 60) e não usa palavras proibidas.

O gatilho: 2 fontes convergem em LOW_SEO

Existem 2 caminhos que disparam uma otimização de anúncio:

O primeiro é o CRON seo-audit, a fonte principal: ele varre os anúncios, calcula o seoScore de cada um (via scoreListing) e, se o score for baixo, emite o evento LOW_SEO. Hoje esse job ainda é um stub.

O segundo é um handoff do SAC: quando o agente de atendimento detecta uma dúvida recorrente sobre o mesmo assunto (detectedPattern, routeTo: anuncios), normalmente porque o anúncio está com algum atributo faltando, ele aciona o Anúncios.

Em ambos os casos, o evento final é o mesmo: LOW_SEO { itemId, score, issues }. Pra otimizar, o agente lê o MeliItem (título, atributos, imagens, healthScore/performance) e o MeliCatalogProduct, a ficha oficial do catálogo, que diz quais atributos o Mercado Livre espera pra aquele tipo de produto. É contra essa ficha que ele preenche atributos faltantes e melhora o título.

O fluxo de decisão: 2 passadas, com o Criativo no meio

  1. O evento LOW_SEO chega (por item); o agente busca o item e a ficha do catálogo (getItem).
  2. 1ª passada (runAudit): a LLM analisa e propõe recommendedTitle, recommendedAttributes e classifica imageIssues (0-7 problemas de imagem) e o seoScore. Tudo fica marcado como applied: false, é só uma proposta.
  3. Se houver problemas de imagem (imageIssues > 0), o agente aciona o Criativo, que gera as imagens necessárias e marca imageIssuesResolved = true.
  4. 2ª passada (applyUpdate): o agente reentra no fluxo, o título passa pelo title-validator (ver Diagrama 7c) e, se aprovado, chama updateListing(title, attributes, pictureUrls), marcando applied: true (LISTING_UPDATE_AUTO).
  5. O anúncio sai atualizado: novo título, atributos preenchidos e fotos completas, tudo registrado em audit log.
Pra quem não é dev

seoScore é uma nota que o sistema calcula pra cada anúncio, indicando o quão bem otimizado ele está pra busca e conversão. 2 passadas significa que o agente roda o fluxo duas vezes: na primeira só planeja (propõe), na segunda executa de fato (aplica), o que dá tempo pro Criativo gerar as imagens entre uma passada e outra. title-validator é uma checagem automática que revisa o título antes dele ser publicado.

O freio: title-validator e as regras do ML

O title-validator é a "guarda" do título: ele valida se o título respeita o limite de caracteres da categoria (em geral 60) e se não contém nenhuma palavra proibida. Se a LLM insistir numa palavra proibida, o sistema tenta de novo uma vez (retry); se mesmo assim falhar, ele recua e mantém o título atual (fallback_to_current_title, com warning). É o equivalente, pro Anúncios, ao que o compliance é pro SAC.

Por trás da proposta da LLM existe um conjunto de regras do Mercado Livre, vindas direto do prompt: conversão é o fator #1 de ranking, não keyword; o título tem um limite de caracteres que varia por categoria (em geral 60), com as palavras-chave na frente e sem enrolação; como o Mercado Livre não tem "backend keywords" (campo invisível de busca), o agente usa atributos estruturados e tags pra isso, e usa subtítulo + atributos no lugar de bullet points; preenche os atributos faltantes contra a ficha do catálogo; e cuida dos 7 slots de imagem possíveis, delegando a geração ao Criativo.

Pra quem não é dev

Palavra proibida aqui é qualquer termo que o Mercado Livre não permite em títulos de anúncio (por exemplo, termos de outras marcas, promessas exageradas etc.). Backend keywords é um recurso que existe em outros marketplaces (um campo de busca invisível pro comprador), mas não no Mercado Livre, por isso a estratégia muda pra atributos e tags visíveis.

Os sinais

O ponto-chave: o Anúncios não age sozinho de ponta a ponta. Ele propõe na 1ª passada, o Criativo entra no meio pra preencher as imagens, e só então ele aplica na 2ª passada. Mexer em anúncio é considerado leve (verde, automático); o freio real é o title-validator, que vira vermelho só se uma palavra proibida travar o processo.

Diagrama 7 — O fluxo completo do Anúncios

7a · O gatilho completo

seo-audit (CRON) — principal varre anúncios · calcula seoScore (scoreListing); hoje é stub Handoff do SAC detectedPattern, routeTo: anuncios → atributo faltando EVT Emite evento LOW_SEO { itemId, score, issues } Segue para o fluxo de decisão → Diagrama 7b · evento LOW_SEO NOTA Dados que lê pra otimizar + estado hoje MeliItem (title, attributes, pictures, healthScore/performance) + MeliCatalogProduct (a ficha oficial: atributos que o catálogo espera). É contra essa ficha que preenche atributos faltantes e melhora o título → contexto carregado antes da 1ª passada (Diagrama 7b). Estado hoje (honesto): seo-audit (CRON) ainda é stub; estrutura pronta, falta o job varrer os anúncios e calcular o seoScore de verdade. LEGENDA Gatilho (entrada) Evento LOW_SEO (★) Convergência (merge) Nota / status

7b · O fluxo de decisão (2 passadas)

↑ vem do Diagrama 7a · evento LOW_SEO { itemId, score, issues } LOW_SEO chega getItem: busca o item + MeliCatalogProduct (ficha do catálogo) LLM 1ª passada · runAudit recommendedTitle + recommendedAttributes imageIssues (0-7) + seoScore · applied:false EXT Aciona o Criativo imageIssues > 0 → Criativo gera imagens (Diagrama 8) → retorna imageIssuesResolved=true 2ª passada · applyUpdate título → title-validator (→ Diagrama 7c) updateListing(title, attrs, pictureUrls) → applied:true Anúncio atualizado novo título + atributos completos + fotos → audit log ↻ Entre os passos 3 e 4, o Criativo gera as imagens (Diagrama 8) e devolve imageIssuesResolved = true; o agente reentra pra 2ª passada. LEGENDA Etapa do fluxo (1ª/2ª passada) Decisão da LLM (1ª passada) Handoff → Criativo (Diagrama 8) Resultado / saída do fluxo

7c · Contexto em paralelo (title-validator e regras do ML)

↳ contexto que alimenta o «title-validator» e a 2ª passada (Diagrama 7b) VALIDADOR title-validator: a guarda do título Valida: título com no máximo 60 caracteres e sem nenhuma palavra proibida. Se a LLM insistir numa palavra proibida, o sistema tenta de novo 1x (retry). Se falhar de novo, recua: mantém o título atual (fallback_to_current_title, com warning). É o equivalente, pro Anúncios, ao que o compliance é pro SAC. PROMPT Regras do prompt (o que orienta a LLM) Conversão é o fator #1 de ranking (não keyword stuffing); título ≤60 chars, keywords na frente, sem enrolação. ML não tem backend keywords: usa atributos estruturados + tags, e subtítulo + atributos no lugar de bullet points. Preenche atributos faltantes contra o MeliCatalogProduct; cuida dos 7 slots de imagem, delegando a geração ao Criativo. LEGENDA Contexto que o title-validator / 2ª passada consulta (nota)

7d · Os sinais

↳ resultado final do fluxo de decisão (Diagrama 7b) · o que cada sinal significa na prática VERDE · aplica auto 2ª passada: título válido + imagens prontas updateListing roda automático (LISTING_UPDATE_AUTO), sem aprovação. Baixo impacto; tudo no audit log. AMARELO · em progresso applied:false (1ª passada) ou fallback (a) proposta pronta, esperando o Criativo gerar as imagens, ou (b) title-validator caiu no fallback. VERMELHO · bloqueio title-validator bloqueou, ou falha técnica meli_fetch, llm_call, parse, meli_update → noop ou erro. Rewrite alto impacto deveria pedir DIFERENÇA Diferença-chave vs. os outros agentes O Anúncios não age sozinho de ponta a ponta: propõe (1ª passada) → Criativo entra no meio → aplica (2ª passada). Mexer em anúncio é leve (verde, automático); o freio real é o title-validator, vermelho só se palavra proibida travar. LEGENDA Verde · aplica automático (LISTING_UPDATE_AUTO) Amarelo · em progresso/degradado, aplica parcial Vermelho · bloqueio ou falha, sem gate de aprovação Sinal = resultado do fluxo de decisão (Diagrama 7b) Princípio / nota


8. Como o Criativo gera as imagens do anúncio

O Criativo cuida das imagens do anúncio, hero (slot 1) mais até 7 slots adicionais. A imagem principal sozinha responde por cerca de 80% do CTR, é a peça mais importante do anúncio depois do título. Hoje o agente está com o fluxo inteiro ligado (briefing por LLM, upload, audit, loop guard), mas a geração da imagem em si ainda é um placeholder, o provider real (DALL-E, Stable Diffusion ou Replicate) está deferido pela ADR-09.

O gatilho: 2 fontes decidem quantos slots gerar

Existem 2 caminhos que acionam o Criativo:

O primeiro é o próprio Anúncios, na 1ª passada do fluxo dele (ver Parte 7): quando ele detecta imageIssues > 0, o Criativo lê state.outputs.anuncios.imageIssues pra saber quantos slots de imagem precisam ser gerados.

O segundo é um evento direto, IMAGE_ISSUE, com payload { itemId, slot, reason }, que pede pra corrigir um slot específico.

A função inferSlotsNeeded decide quais slots gerar a partir disso: se veio um IMAGE_ISSUE, gera só aquele slot; se veio do LOW_SEO, gera de 1 até imageIssues (no máximo 7); e se imageIssues = 0, cai num fallback que gera os slots 1, 2 e 3.

Pra gerar as imagens, o agente usa como contexto o item e o que o Anúncios apontou (imageIssues), além do sellerId e threadId, que servem de chave no storage. No fim, a saída volta pro Anúncios fechar a 2ª passada dele com as pictureUrls.

Pra quem não é dev

Slot é cada posição de imagem do anúncio (o limite varia por categoria, geralmente até 12, ou até 10 em produtos com variações, o agente trabalha com 7). Hero é a imagem principal, a primeira que o comprador vê. CTR (Click-Through Rate) é a taxa de cliques, quantas pessoas que viram o anúncio clicaram nele. Briefing é a instrução que a LLM gera descrevendo o que cada imagem deveria mostrar.

O fluxo: briefing real, imagem placeholder, upload, retorno

  1. O gatilho chega (LOW_SEO ou IMAGE_ISSUE), os slots já estão definidos.
  2. A LLM gera o briefing (criativoTaskPrompt{ briefings[] }), descrevendo o que cada imagem deveria conter. Essa parte já roda de verdade (prova o prompt); se falhar, o sistema usa um briefing default.
  3. O agente gera a imagem, mas hoje isso é um placeholder (stub da ADR-09): o mesmo PNG genérico é devolvido pra qualquer slot. O provider real de geração de imagem ainda está por plugar.
  4. As imagens (placeholders, por enquanto) sobem pro storage R2, com a chave criativo/{seller}/{thread}/slot-N.png, gerando as generatedUrls[].
  5. O agente retorna { generatedUrls, briefings, imageIssuesResolved: true } pro Anúncios, com um registro de auditoria IMAGE_UPLOAD. É aqui que entra o loop guard (ver Diagrama 8c): o Anúncios usa esse retorno pra fazer a 2ª passada dele.
Pra quem não é dev

Placeholder é um conteúdo genérico que ocupa o lugar do conteúdo final, até que ele seja implementado de verdade, aqui, uma imagem padrão no lugar da imagem real do produto. Stub é uma versão simplificada de uma função, que existe só pra não quebrar o fluxo, mas ainda não faz o trabalho completo. R2 é o serviço de armazenamento de arquivos (similar a um "HD na nuvem") onde as imagens geradas ficam guardadas.

O contexto: o loop guard e a estratégia de 7 slots

O loop guard é a decisão de design mais importante do agente: imageIssuesResolved é sempre true, mesmo quando o upload falha (nesse caso, generatedUrls vem vazio). O motivo é garantir que a 2ª passada do Anúncios sempre termine, sem travar a orquestração. O trade-off é que o anúncio pode fechar sem imagem nova, mas o erro fica registrado no audit/log, sem bloquear o fluxo.

A estratégia de 7 slots, que vem do prompt, prioriza a imagem principal (hero, slot 1), responsável por ~80% do CTR: fundo branco, produto ocupando ~85% do quadro. O slot 2 é tipicamente lifestyle (produto em uso), o slot 3 um infográfico (specs, dimensões), e assim por diante (o limite de slots varia por categoria, geralmente até 12, ou até 10 em produtos com variações). A doutrina aqui é clara: um hero excelente vale mais que 10 imagens medianas.

Os sinais

Estado honesto: o Criativo está ligado de ponta a ponta (briefing por LLM, upload, audit, loop guard), só a geração de imagem é placeholder. Quando o provider real (DALL-E, Stable Diffusion ou Replicate) for plugado, só o passo 3 muda, o resto do fluxo já está pronto.

Diagrama 8 — O fluxo completo do Criativo

8a · O gatilho completo

Handoff do Anúncios (1ª passada) state.outputs.anuncios.imageIssues → quantos slots gerar Evento IMAGE_ISSUE { itemId, slot, reason } → corrige um slot específico CALC Decide quais slots gerar inferSlotsNeeded: IMAGE_ISSUE → 1 slot · LOW_SEO → 1..imageIssues (máx 7) imageIssues = 0 → fallback gera slots 1, 2 e 3 Segue para o fluxo de geração → Diagrama 8b · slots definidos NOTA Contexto que lê + onde a saída volta Usa como contexto: o item + o que o Anúncios apontou (imageIssues), além de sellerId e threadId (chave no storage). A saída volta pro Anúncios fechar a 2ª passada dele, com as pictureUrls geradas (Diagrama 8b → 7b). Estado hoje: briefing e upload já rodam de verdade; só a geração da imagem em si é placeholder (ver Diagrama 8c). LEGENDA Gatilho (entrada) inferSlotsNeeded (★) Convergência (merge) Nota / status

8b · O fluxo de geração

↑ vem do Diagrama 8a · slots definidos (inferSlotsNeeded) Gatilho chega LOW_SEO ou IMAGE_ISSUE · slots já definidos (Diagrama 8a) LLM LLM gera briefing criativoTaskPrompt → briefings[] (1 por slot) roda de verdade; se falhar, usa briefing default STUB Gera imagem PLACEHOLDER · stub ADR-09 mesmo PNG genérico pra qualquer slot provider real: a plugar Upload pro R2 key: criativo/{seller}/ {thread}/slot-N.png → generatedUrls[] Retorna pro Anúncios {generatedUrls, briefings, imageIssuesResolved:true} + audit IMAGE_UPLOAD LEGENDA Etapa do fluxo LLM gera o briefing (roda de verdade) Placeholder / stub (provider a plugar) Retorna pro Anúncios (Diagrama 7b)

8c · Contexto em paralelo (loop guard e estratégia de 7 slots)

↳ contexto que molda os passos 2-5 do fluxo de geração (Diagrama 8b) LOOP GUARD O loop guard: imageIssuesResolved sempre true imageIssuesResolved é SEMPRE true, mesmo quando o upload falha (generatedUrls vem vazio). Motivo: garantir que a 2ª passada do Anúncios sempre termine, sem travar a orquestração. Trade-off: o anúncio pode fechar sem imagem nova, mas o erro fica no audit/log, sem bloquear o fluxo. 7 SLOTS A estratégia de 7 slots Slot 1 (hero): ~80% do CTR, fundo branco, produto ocupando ~85% do quadro. Slot 2: lifestyle (produto em uso). Slot 3: infográfico (specs, dimensões). ML permite até 12 slots no total. Doutrina: um hero excelente vale mais que 10 imagens medianas. LEGENDA Contexto que molda os passos 2-5 do fluxo de geração (nota)

8d · Os sinais

↳ resultado do retorno pro Anúncios (Diagrama 8b) · o que cada sinal significa na prática VERDE · entrega as imagens briefing ok + upload ok Devolve generatedUrls + imageIssuesResolved:true; Anúncios aplica. Ressalva: imagem é placeholder. AMARELO · degradado briefing default ou placeholder genérico (a) LLM do briefing falhou → segue com briefings default, sem travar, ou (b) placeholder no lugar da peça final. VERMELHO · falha upload falhou, ou provider real deferido Upload falhou → generatedUrls vazio, mas loop libera mesmo assim. Provider real deferido (ADR-09/STORY-024). PONTO-CHAVE Ponto-chave O Criativo está ligado de ponta a ponta (briefing LLM, upload, audit, loop guard); só a geração de imagem é placeholder. Quando o provider real (DALL-E, Stable Diffusion ou Replicate) for plugado, só o passo 3 (Diagrama 8b) muda. LEGENDA Verde · entrega as imagens, Anúncios aplica Amarelo · degradado (default/placeholder), segue Vermelho · upload falhou ou provider deferido (gap) Sinal = resultado do retorno (Diagrama 8b) Princípio / nota


9. Como o Gestor orquestra os outros 5

O Gestor não age no Mercado Livre diretamente, ele distribui o trabalho entre os outros 5 agentes e faz o "standup" do time. A prioridade #1 dele é sempre a Reputação na conta.

O gatilho: 2 entradas, 2 modos

Existem 2 caminhos que acionam o Gestor:

O primeiro é o daily-standup, um CRON que dispara todo dia e gera o evento STANDUP_TRIGGER, isso aciona o modo STANDUP (relatório do time).

O segundo é o evento PATTERN_DETECTED, que vem do SAC (sac.detectedPattern): um padrão foi identificado (por exemplo, o SAC percebeu uma dúvida recorrente dos compradores), e isso aciona o modo ROUTE.

O Gestor é o único agente que "olha pra dentro": ele lê os outputs dos outros 5 agentes (sac.detectedPattern, anuncios.imageIssues, etc.) e os traces de execução (agente, tokens, custo, duração de cada chamada). Se nenhum dos 2 gatilhos disparar, ele não faz nada (noop).

Pra quem não é dev

Standup é uma reunião curta e diária, emprestada do método ágil, em que o time reporta o que foi feito, o que vai ser feito e o que está travando. Trace é o registro técnico de uma execução (quanto custou, quanto tempo levou, qual agente rodou). Padrão (pattern), aqui, é um comportamento recorrente detectado nas conversas com clientes, por exemplo, várias pessoas perguntando a mesma coisa.

O fluxo: os 2 modos

No modo ROUTE, que distribui o trabalho, o SAC detecta um padrão (PATTERN_DETECTED), o Gestor (LLM) decide quem deve corrigir aquilo, define um routeTo (anuncios ou criativo), e o especialista escolhido corrige a causa, não o sintoma. Por exemplo: se muita gente pergunta a medida de um produto, o Gestor manda pro Anúncios, que adiciona o atributo faltante no anúncio, em vez de o SAC ficar respondendo a mesma pergunta repetidamente.

No modo STANDUP, que é o relatório do time, o STANDUP_TRIGGER dispara diariamente, o Gestor lê os últimos 10 traces (agente, tokens, custo, duração), uma LLM gera { summary, insights[] }, e esse resumo do dia é entregue ao humano. Esse modo é puramente de observabilidade: o Gestor vê custo, tokens, agentes falhando e padrões, mas não age sozinho, ele reporta.

A doutrina: por que ele é o diretor

O Gestor segue um priority stack inegociável: #1 é a Reputação na conta (cancelamentos, claims, atraso no handling, se isso está em risco, tudo para); #2 é resolver problemas que sangram dinheiro (ruptura de estoque, preço fora da margem); #3 é crescer (otimizar anúncios, escalar). Sempre nessa ordem.

Por trás disso está o flywheel, a tese central do Bunker: anúncios melhores geram mais conversão, que melhora o ranking, que reduz o custo de aquisição, que aumenta o lucro, que financia anúncios ainda melhores, fechando o ciclo. O papel do Gestor é fazer os 5 agentes trabalharem como um time (não como 6 bots soltos) girando esse ciclo.

Os sinais

Estado honesto: hoje o gestorNode faz 2 coisas no código, ROUTE (decide entre anúncios e criativo) e STANDUP (resumo dos traces). O priority stack e a regra "reputação = tudo para" são a doutrina do prompt, a inteligência de priorização ainda não é um gate executável no código.

Diagrama 9 — O fluxo completo do Gestor

9a · O gatilho completo

CRON diário (daily-standup) STANDUP_TRIGGER → modo STANDUP PATTERN_DETECTED (do SAC) pergunta recorrente sem solução → modo ROUTE DEC Gestor identifica o modo checkpoint decide mode: 'standup' | 'route' | 'noop', conforme o evento recebido evento sem match conhecido → noop, nada é alterado Segue para o Diagrama 9b → fluxo ROUTE ou fluxo STANDUP, conforme o modo NOTA Contexto que lê + onde a saída volta É o único agente que olha pra dentro: lê os outputs dos outros 5 agentes + traces de execução (agente, tokens, custo, duração). No modo ROUTE, decide qual especialista corrige a causa raiz; no modo STANDUP, gera o resumo do dia pro humano. Estado hoje: gestorNode no código só executa ROUTE e STANDUP; a priority stack é doutrina do prompt (ver Diagrama 9c). LEGENDA Gatilho (entrada) Gestor identifica o modo (★) Convergência (merge) Nota / status

9b · O fluxo dos 2 modos

↑ vem do Diagrama 9a · 2 modos do Gestor, disparados por gatilhos diferentes MODO ROUTE distribui o trabalho pro especialista que resolve a causa MODO STANDUP gera o resumo do dia, observa e relata pro humano EVT SAC detecta padrão PATTERN_DETECTED · pergunta recorrente sem solução (ex: dúvida sobre medida que o ML rejeita no anúncio) LLM Gestor (LLM) decide quem corrige routeTo: anuncios | criativo, escolhe o especialista que ataca a causa raiz, não o sintoma reportado pelo SAC OK Especialista corrige a causa ex: Anúncios adiciona o atributo de medida faltante no anúncio, resolvendo a dúvida na origem (não responde só o ticket) CRON STANDUP_TRIGGER (diário) CRON daily-standup dispara o modo STANDUP uma vez por dia, sem depender de evento externo LLM LLM gera summary + insights[] Lê os últimos 10 traces de execução: agente, tokens, custo e duração de cada chamada → produz { summary, insights[] } OK Resumo entregue ao humano Observabilidade: aponta custo subindo, agente falhando, padrão recorrente sem solução. Gestor reporta, não age. LEGENDA Etapa do fluxo Decisão do Gestor (LLM) 2 modos independentes, disparados por gatilhos diferentes (Diagrama 9a)

9c · A doutrina (priority stack e flywheel)

↳ contexto que explica POR QUE o Gestor decide assim (molda o passo "decide quem corrige" em 9b) PRIORITY STACK Priority stack (inegociável) #1 Reputação ML (cancelamentos, claims, atraso no handling) → se em risco, TUDO PARA. #2 Problemas que sangram dinheiro (ruptura de estoque, preço fora da margem). #3 Crescer (otimizar anúncios, escalar). Sempre nessa ordem: reputação > dinheiro > crescimento. FLYWHEEL O flywheel (a tese) Anúncios melhores → + conversão → + ranking → ↓ custo de aquisição → + lucro → financia anúncios melhores. O ciclo se realimenta: cada melhoria gera caixa pra financiar a próxima melhoria. Papel do Gestor: fazer os 5 agentes trabalharem como um time girando esse ciclo, não 6 bots soltos. ESTADO HOJE Estado hoje: doutrina vs. código No código, gestorNode hoje só executa os modos ROUTE e STANDUP (Diagrama 9b). A priority stack e a regra "reputação = tudo para" são doutrina do prompt: orientam a LLM, mas ainda não são um gate executável no orquestrador. Ver os sinais reais no Diagrama 9d. LEGENDA Contexto que explica as prioridades do Gestor (não é um agente)

9d · Os sinais

↳ resultado dos 2 modos (Diagrama 9b) · verde é o normal, vermelho é a regra #1 do priority stack (9c) VERDE · coordena ok routeTo decidido ou standup gerado Distribui (routeTo) ou relata (summary + insights[]); baixo risco, não executa ação no ML. Pode ser automático. AMARELO · sinaliza no standup, algo merece atenção Custo/tokens subindo, um agente falhando, ou padrão recorrente sem solução. Sinaliza; humano decide a prioridade. VERMELHO · tudo para regra #1 do priority stack, ou erro técnico Reputação ML ameaçada → "tudo para", prioridade máxima, ação imediata. + falhas llm_call/parse/wrong_kind → noop/erro. ESTADO HOJE Estado honesto O gestorNode hoje só executa ROUTE (decide anuncios/criativo) e STANDUP (resumo dos traces) no código. Priority stack e "reputação = tudo para" são doutrina do prompt: orientam a LLM, ainda não são um gate executável (Diagrama 9c). LEGENDA Verde · coordenação ok, pode ser automático Amarelo · sinaliza pro humano, não bloqueia Vermelho · reputação em risco ou falha técnica Sinal = resultado dos 2 modos (Diagrama 9b) Estado / nota