AIREITER

22 plataformas, 241 comandos e por que não criei um modelo de resposta unificado

Última Atualização: 2026-07-31 06:24:47

Ao coletar dados públicos de mais de vinte plataformas, o impulso inicial quase sempre é o mesmo: desenhar um único Post, um único User e encaixar ali as respostas de todas elas. Um vídeo do bilibili, um vídeo do tiktok, uma resposta do zhihu e uma publicação do linkedin parecem, à primeira vista, apenas “um conteúdo com um autor”. Nas três primeiras integrações, essa abstração parece brilhante. Perto da vigésima, ela passa a soterrar o projeto.

No fim, não construí esse modelo unificado. Com mais de vinte plataformas e mais de duzentos comandos, a divisão aparentemente mais simples — cada plataforma cuida da própria vida — foi a que se sustentou.

Onde o modelo unificado começa a quebrar

O colapso é gradual. Na oitava plataforma, seu Post já acumulou uma dúzia de campos opcionais: algumas têm contagem de danmaku, outras não; em certas APIs, a data de publicação é um timestamp em segundos, enquanto em outras chega como uma string do tipo “há 3 dias”. Com mais de vinte plataformas, o modelo já não oferece economia alguma. Todo código consumidor precisa checar antes se aquela plataforma sequer preencheu determinado campo; a lógica dessas decisões fica mais longa do que ler a resposta bruta; e a camada unificada vira um obstáculo que você contorna para conseguir trabalhar. No lado da escrita, o custo é idêntico: cada nova integração obriga você a voltar ao modelo e enfiar campos novos em estruturas desenhadas para as plataformas antigas.

Uma plataforma, um contexto delimitado

A estrutura que funciona é o oposto: em vez de abstrair um modelo único, deixe cada plataforma cuidar do seu próprio domínio. No catálogo, cada uma é um contexto <platform>_reverse/, responsável por quatro áreas que não empresta a ninguém:

  • Validação de entrada. Só a própria plataforma sabe o formato de seus IDs e quais combinações de parâmetros são válidas.

  • Protocolo. Requisição HTTP direta ou trecho de JS local para assinatura, domínio usado, cabeçalhos: tudo isso é privado daquela plataforma.

  • Assinatura. Os mecanismos variam radicalmente entre plataformas; colocá-los em um único assinador compartilhado só cria um monstro cheio de if-else.

  • Normalização de resposta. A resposta bruta é organizada em uma estrutura pertencente à própria plataforma, não em um formato global unificado.

O quarto ponto é o mais fácil de interpretar mal. “Não ter um modelo unificado” não significa “não normalizar”. Claro que cada plataforma normaliza seus dados, mas é ela quem define o formato de destino — não um modelo compartilhado imposto a todas. A unificação só faz sentido quando se trata realmente da mesma coisa: dois endpoints da mesma plataforma que compartilham uma estrutura de publicação podem usar o mesmo objeto, pois ali existe de fato o mesmo objeto de domínio. O erro é estender essa unificação interna para atravessar plataformas.

A camada compartilhada só deve guardar o que é realmente comum

Então, o que entra na camada compartilhada? Capacidades que são de fato multiplataforma e se comportam da mesma forma em todas elas — não funcionalidades que apenas parecem semelhantes. Minha camada compartilhada tem apenas três elementos:

  1. O read-model da interface: um catálogo unificado de capacidades derivado das declarações argparse de cada plataforma. O que se unifica é como os comandos são descobertos e descritos, não o que eles retornam. O primeiro caso é genuinamente multiplataforma; o segundo pertence à plataforma. (A ideia de que “a declaração é a interface” é detalhada em the interface-as-code piece.)

  2. Transporte local em loopback: requisições autenticadas passam por um serviço local de sessão WebSocket, que trata todas as plataformas da mesma forma e não toca em nenhum campo de negócio delas.

  3. A entrada de despacho: identifica a plataforma, entrega o comando ao respectivo contexto e não faz mais nada.

O teste é simples: para entrar na camada compartilhada, algo precisa realmente se comportar igual em todas as plataformas. Transporte, despacho e geração das descrições de interface são iguais em todas elas. Já “um conteúdo” não se comporta da mesma forma no bilibili e no linkedin, então não entra. “Parecem iguais” é a maior armadilha da abstração: dois vídeos parecem iguais, logo surge a vontade de unificá-los. Mas semelhança superficial não é equivalência de comportamento, e tratá-la como um modelo de domínio compartilhável é a origem do colapso do modelo unificado.

A distribuição dos comandos mostra onde a abstração compensa

Ainda em dúvida sobre o modelo unificado? Basta olhar a distribuição real dos comandos. São 22 plataformas e 241 comandos, distribuídos de forma extremamente desigual:

Plataforma

Comandos

tiktok

34

bilibili

26

linkedin

18

zhihu

18

douyin

17

xiaohongshu

16

As outras 16 plataformas

1 a 13 cada

As seis principais plataformas somam 129 comandos, mais da metade do total. A outra metade se divide entre 16 plataformas de cauda longa, muitas com apenas dois ou três comandos e algumas com só um.

Essa distribuição define a economia da abstração: o custo de um modelo unificado é fixo — todo integrador precisa preencher campos, verificar valores nulos e contornar suas limitações —, enquanto seu benefício é distribuído por plataforma. Para uma plataforma de cauda longa com dois ou três comandos, o saldo da abstração é negativo, porque o adaptador necessário para encaixá-la no modelo unificado acaba sendo maior do que todo o código de negócio dela.

Não crie abstrações para uma implementação que não existe

Dessa distribuição vem outra regra: não reserve abstração para uma implementação única. Se uma plataforma tem apenas uma implementação, não crie repository, factory ou uma camada de interface porque “talvez exista outra no futuro”. Adicionar uma plataforma deve significar adicionar um contexto <platform>_reverse/, sem precisar antes mexer em uma classe-base compartilhada.

O valor de uma camada de interface está em tornar várias implementações intercambiáveis. Com uma única implementação, seu valor é zero e seu custo de manutenção é positivo. Reservar espaço para uma segunda implementação inexistente e reservar espaço para uma semelhança entre plataformas que não existe são o mesmo erro. A migração entre linguagens confirmou isso mais uma vez: algumas centenas de comandos do registro antigo ficaram explicitamente fora de um lote de migração, sem stubs nem proxies de compatibilidade, porque an empty shell costs more than a gap — ela faz a próxima pessoa acreditar que há algo ali. Uma abstração reservada causa o mesmo efeito.

Na normalização com IA, entregue também o contexto da plataforma

Essa lógica de separar por plataforma e evitar um modelo unificado continua válida ao usar um modelo para normalizar dados. Para transformar respostas brutas de mais de vinte plataformas em estruturas analisáveis, é natural recorrer a um modelo. E o erro mais fácil aqui é repetir o da camada de código: definir um esquema único, enviar o JSON bruto de cada plataforma e acrescentar “mapeie para o esquema”. Isso falha porque o modelo não sabe se o campo de visualizações do bilibili representa exatamente a mesma coisa que o campo de visualizações do tiktok. Ao forçá-lo para um esquema de mínimo denominador comum, ele ou descarta um campo importante para aquela plataforma ou o preenche apenas parcialmente certo.

O caminho correto é fornecer contexto por plataforma: diga ao modelo “isto é bilibili, estes campos significam isto e quero este formato para esta plataforma”. Normalize uma plataforma por vez e deixe a mesclagem entre plataformas para a camada de análise. O processo passa por algumas etapas, e cada uma exige uma capacidade diferente do modelo:

Etapa

Capacidade necessária

Escolha

model id

Ler a estrutura de toda a resposta bruta de uma plataforma

Contexto longo, capaz de absorver a resposta completa e as notas sobre os campos de uma vez

Kimi K3

kimi-k3

Definir a fronteira da normalização: quais campos são realmente multiplataforma e quais são específicos da plataforma

Raciocínio forte, resistente à unificação excessiva

Claude Opus 5

claude-opus-5

Extrair campos em massa por plataforma, mapeando item a item

Baixo custo, centenas a milhares de chamadas com alta concorrência

Claude Sonnet 5

claude-sonnet-5

Explicar por que campos com o mesmo nome em duas plataformas não correspondem entre si

Raciocínio intermediário, capaz de explicar as diferenças a partir dos campos

GPT-5.6 Sol

gpt-5.6-sol

A segunda etapa é a única em que trocar de modelo altera visivelmente o resultado. O que ela testa é se você vai admitir que dois campos não são, na prática, a mesma coisa. É o mesmo princípio de the counter-evidence section in algorithm-family identification: um modelo fraco segue sua sugestão de “unificar”; um modelo forte aponta onde está a fronteira.

O verdadeiro problema é o custo de trocar de modelo

As quatro faixas vêm de três fornecedores, três SDKs, três esquemas de autenticação e três formatos de erro. Reescrever o cliente três vezes para alternar modelos entre as etapas não vale a pena. Por isso, a maioria das pessoas usa uma única faixa do início ao fim — muitas vezes uma que falha justamente na etapa de definir a fronteira — e acaba criando um esquema que desmorona outra vez ao chegar a mais de vinte plataformas.

AIReiter simplifica essa camada: uma chave, uma interface compatível com OpenAI e as quatro faixas por trás dela. Para trocar, basta alterar o campo model no corpo da requisição.

# Set the normalization boundary: the reasoning tier
curl https://aireiter.com/api/v1/chat/completions \
  -H "Authorization: Bearer $AIREITER_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-opus-5",
    "messages": [{"role": "user", "content": "<one platform response sample + have it mark which fields are platform-specific>"}]
  }'

# Extract fields per platform in bulk: change the model field, leave the rest
#   "model": "claude-sonnet-5"
# Field-difference attribution:
#   "model": "gpt-5.6-sol"

Se você já usa o SDK da OpenAI, aponte base_url para https://aireiter.com/api/v1 e não mude mais nada. No SDK da Anthropic, use POST /api/v1/messages com a mesma chave.

Em preço, os modelos Claude saem com 30% de desconto sobre a tabela, os modelos GPT pela metade, e o Kimi K3 pode ser chamado com a mesma chave. O desconto incide justamente no principal centro de custo: a extração em massa de campos por plataforma é a etapa com mais chamadas, envolvendo mais de vinte plataformas e centenas a milhares de registros em cada uma, uma chamada por registro, executada no Sonnet mais barato com mais 30% de desconto. Ler uma resposta longa inteira no Kimi K3 consome algumas centenas de milhares de tokens por entrada, outro volume relevante de custo. Já a faixa de raciocínio para definir fronteiras exige poucas chamadas, então quase não pesa.

Conclusão

Na coleta multiplataforma, a reação inicial de abstrair um único Post/User unificado funciona muito bem em escala pequena, mas inevitavelmente desmorona com mais de vinte plataformas: há um custo fixo, um benefício por plataforma e uma distribuição de comandos extremamente concentrada na cauda longa. A divisão que se sustenta é um contexto delimitado por plataforma, cada qual responsável por validar entradas, lidar com protocolo, assinatura e normalização de respostas. A camada compartilhada deve conter apenas o que se comporta da mesma forma em todas elas — transporte, despacho e geração de descrições de interface —, não um modelo de domínio baseado em semelhança superficial. E não se deve reservar abstração para uma implementação única ou uma característica comum que não existe. Com modelos, a regra é a mesma: normalize com contexto específico da plataforma, não com um esquema unificado, e deixe a mesclagem entre plataformas apenas para a camada de análise. The full four-stage workflow detalha melhor a divisão em quatro faixas; conectadas por uma interface única, elas deixam de ter o custo de troca como motivo para não usá-las.