Associado
A comissão de uma indicação depende de um aperto de mão fora do sistema, e um lado lucra negando que aconteceu.
- fastapi
- postgresql
- pgvector
- claude
- sqlalchemy
O problema e as restrições
O Associado existe para formalizar algo que associações de classe já fazem informalmente e de forma inconsistente: um associado pergunta por aí por um encanador, um contador, um fotógrafo, alguém do grupo indica outro associado específico, os dois se conectam, um negócio acontece e, em tese, a associação era devedora de uma pequena comissão por ter feito a apresentação em primeiro lugar. Na prática, essa última parte quase nunca acontece, porque ninguém acompanha quem indicou quem, se deu em algo, ou quanto valeu.
A primeira restrição é que o único fato do qual o modelo de negócio inteiro depende (o negócio de fato fechou, e por quanto) acontece inteiramente fora do software, numa conversa e num aperto de mão entre duas pessoas. A plataforma pode perguntar sobre isso, mas não pode observar diretamente, e está perguntando a duas pessoas sem incentivo simétrico para responder com honestidade: quem teria que pagar uma comissão tem todo motivo para dizer menos que a verdade, enquanto quem não pagaria nada não tem motivo particular para corrigir.
A segunda restrição é a forma do próprio pedido. Associado não digita nome de categoria. Digita “quem conserta um vazamento”, e um sistema que só entende um termo exato de catálogo como “encanador” vai falhar em silêncio no momento em que o vocabulário de alguém não bater com o que foi digitado na lista de categorias. Mas um sistema que responde só por similaridade semântica vaga produz um resultado que ninguém consegue auditar, e a reclamação mais previsível que essa plataforma jamais vai receber é um prestador perguntando por que ele, especificamente, não foi o indicado. O que quer que resolva um pedido precisa ser flexível o bastante para entender linguagem real e preciso o bastante para se explicar numa frase.
A terceira restrição é lidar com dinheiro num sistema cujo mecanismo central é um agente de IA lendo mensagem de WhatsApp sem estrutura. Texto de um associado é exatamente o tipo de entrada que consegue manobrar um modelo de linguagem, de propósito ou não, e a única parte desse sistema que absolutamente não pode ser manobrada é o que é cobrado de quem. A arquitetura construída precisava tornar estruturalmente impossível qualquer coisa que o modelo toque decidir diretamente uma comissão, uma garantia que nenhuma política sozinha entregaria.
A quarta restrição foi evitar um erro específico já vivido de verdade. Uma plataforma irmã (um CRM de WhatsApp já em produção) resolve um problema de forma parecida com uma ferramenta visual de workflow orquestrando dezenas de passos de integração, e essa ferramenta tem um custo real e contínuo: credencial que não transfere entre ambientes, ordem de execução que depende de onde uma caixa está posicionada no canvas em vez de qualquer coisa versionada, e um arquivo de workflow salvo que diverge do que de fato roda. O volume esperado deste projeto é algumas centenas de solicitações por mês para uma associação, bem aquém da escala de SaaS multi-tenant que justificou a complexidade daquele outro sistema. Trazer a mesma arquitetura para cá significaria pagar custo operacional que o tamanho real deste projeto não pede.
A última restrição foi o tempo em si. Uma indicação que nunca expira vira uma comissão devida sobre uma relação comercial da qual a associação parou de fato de fazer parte no momento em que a apresentação virou um arranjo contínuo entre duas pessoas. Um encanador indicado uma vez que passa a fazer a manutenção mensal de um cliente por anos é um resultado real que essa plataforma precisa não interpretar como trinta e seis eventos separados merecedores de comissão.
Arquitetura
Um associado manda uma mensagem de WhatsApp descrevendo o que precisa. Um agente de IA (Claude, usando tool use em vez de geração livre) lê e tenta resolver para uma categoria no catálogo de serviços, primeiro por correspondência exata e trigram contra termos e sinônimos conhecidos, depois, só se isso não resolver com confiança, por busca semântica por vetor sobre os embeddings das categorias. Se nenhum dos dois caminhos resolve de forma limpa, o agente faz uma pergunta de esclarecimento em vez de chutar: uma solicitação não resolvida é registrada como lacuna no catálogo em vez de descartada em silêncio, o que eventualmente é o que diz à associação onde falta um sinônimo ou uma categoria inteira.
Depois que uma categoria resolve, o mesmo agente consulta prestadores que oferecem aquele serviço (filtrado, estruturalmente, só para quem ligou o consentimento de indicação) e devolve uma lista curta ao associado, avisando cada prestador indicado de que foi indicado. Tudo até aqui é trabalho do agente: entender linguagem, casar intenção com um item de catálogo, formatar uma resposta útil. Nada disso toca dinheiro.
Dinheiro vive inteiramente num caminho separado e determinístico ao qual o agente não tem acesso. Dias depois de uma indicação sair, um worker de agendamento (rodando como parte do mesmo processo, sobre uma tabela de banco comum em vez de uma fila de tarefas) manda um acompanhamento ao solicitante e ao prestador, de forma independente, perguntando se o negócio fechou e por quanto. Só quando as duas respostas descrevem o mesmo negócio, dentro de uma pequena tolerância para arredondamento, o backend gera uma comissão automaticamente. Divergência de valor, silêncio de um lado só, ou qualquer ambiguidade não é resolvida com mais automação: vira um item na fila do painel administrativo para uma pessoa olhar, na teoria de que uma cobrança gerada indevidamente custa mais confiança à associação do que uma perdida custa dinheiro.
Essa comissão, uma vez gerada, é limitada no tempo nas duas pontas. Uma solicitação que passa 30 dias sem um fechamento confirmado simplesmente expira. Não existe indicação aberta para sempre. E se o mesmo solicitante e prestador fecham outro negócio na mesma categoria dentro de 90 dias do último, o sistema ainda registra a nova indicação e ainda avisa todo mundo, mas grava o negócio resultante como isento em vez de gerador de comissão, visivelmente, como um registro de valor zero e explicitamente isento, em vez de uma entrada que simplesmente não existe. As duas janelas são valores de configuração em vez de constante fixada na lógica, e as duas são deliberadamente números de primeiro chute que o desenho espera revisar assim que existir dado real de conversão para revisar com ele.
Arquitetura
Decisões
DECISION 01/04 · Casar um pedido em texto livre
A primeira reclamação previsível desse sistema é um prestador perguntando por que ele não foi indicado; casar por categoria primeiro mantém toda indicação explicável numa frase, e dá à regra de comissão uma chave estável para se ligar. Deixar o modelo escolher prestadores direto colocaria a lista inteira de associados no contexto dele e tornaria o resultado inauditável
Custo aceitoExistem dois caminhos de código para responder a mesma pergunta, com limiares de confiança que começam como um chute informado em vez de um resultado medido, e um provedor externo de embeddings entra na stack só para cobrir o vocabulário que a taxonomia sozinha não cobre
DECISION 02/04 · Confirmar um negócio fechado
O lado que paga a comissão é também o único lado com incentivo para subdeclarar o valor ou negar que o negócio aconteceu: um relato de um lado só transforma o cálculo de comissão em autodeclaração de quem menos tem motivo para ser preciso sobre isso
Custo aceitoDobra as mensagens enviadas por indicação, o que aumenta tanto o incômodo percebido quanto o risco de conta junto ao provedor de WhatsApp, e silêncio sem resposta, o resultado mais comum na prática, vira trabalho humano recorrente em vez de um fechamento automatizado
DECISION 03/04 · Por quanto tempo uma indicação vale
Sem janela, uma indicação de janeiro deveria em tese comissão sobre um negócio fechado em novembro, e uma relação recorrente (um contador contratado todo mês) deveria comissão todo mês a partir de uma única apresentação original, as duas são relações comerciais que a associação parou de fato de intermediar
Custo aceitoA associação perde comissão de negócios que fecham devagar de verdade (obra grande, ciclo longo de orçamento), e a regra de antiduplicidade pode ser explorada simplesmente esperando 90 dias entre contratações, os dois aceitos porque a alternativa, cobrar para sempre, causa muito mais atrito com os associados do que a receita que protegeria
DECISION 04/04 · Um serviço só
Essa plataforma irmã funciona, mas o custo operacional dela se concentra em lugares específicos e recorrentes: um workflow visual de 99 nós cujas credenciais não transferem entre ambientes e cuja ordem de execução real depende da posição do nó no canvas em vez de qualquer coisa versionada. No volume esperado deste projeto (centenas de solicitações por mês, lógica majoritariamente determinística), esse custo não tem benefício correspondente
Custo aceitoNão existe editor visual para ajustar o fluxo sem deploy: qualquer mudança de comportamento é commit e rebuild. O worker de agendamento roda no mesmo processo da API, então um pico de tráfego HTTP pode atrasar uma mensagem de acompanhamento; separar isso é a primeira mudança a fazer se o volume crescer o suficiente para importar
Invariantes
Um associado sem consentimento de indicação ativo nunca aparece num resultado de busca, em nenhuma circunstância
Garantido por
a consulta que resolve uma categoria para prestadores candidatos filtra por consentimento_indicacao = true antes de qualquer outra coisa rodarO agente de IA nunca escreve na tabela de comissões nem muda o status de um negócio
Garantido por
nenhuma tool exposta ao modelo tem acesso de escrita a essas tabelas; toda escrita em dinheiro ou status de negócio acontece exclusivamente em código determinístico de backend que o modelo não alcançaUm valor monetário nunca é representado como float
Garantido por
todo valor é um BIGINT em centavos, só em BRL, uma restrição no nível do schema em vez de uma convenção de código que alguém precisa lembrarA mesma mensagem de WhatsApp recebida nunca é processada duas vezes, mesmo se o provedor entregar mais de uma vez
Garantido por
toda mensagem recebida é gravada com o ID de mensagem do próprio provedor sob índice UNIQUE; conflito na inserção é tratado como duplicata e descartadoO identificador estável de uma categoria nunca muda depois de criado
Garantido por
categorias_servico.slug é imutável por convenção e pelo fato do histórico de comissão e dos relatórios usarem ele como chave; só o rótulo de exibição é editável
Risco identificado
Risco identificado · mitigado no desenho
Este projeto ainda não tem incidente real porque nunca rodou em produção. Este é um risco que o desenho já contempla.
- Sintoma
- Se a comissão fosse gerada pela palavra de um lado só, o lado que paga tem incentivo direto para subdeclarar o valor do negócio ou negar que ele fechou, transformando o cálculo de comissão em autodeclaração de quem menos tem motivo para ser honesto sobre isso
- Causa raiz
- O fato de um negócio ter acontecido vive inteiramente fora do sistema, numa conversa de WhatsApp e num aperto de mão entre duas pessoas; o único sinal da plataforma é o que qualquer um dos lados escolher digitar de volta, dias depois, quando perguntado
- Correção
- Perguntar aos dois lados de forma independente, e gerar comissão automaticamente só quando a resposta do solicitante e do prestador concordam sobre o mesmo negócio, com tolerância de 20% no valor, para arredondamento comum não virar trabalho manual. Qualquer coisa fora isso, inclusive silêncio de um dos lados, vira caso pendente na fila para um humano resolver
- Prevenção
- O desenho assume rodar conservador por padrão: uma comissão perdida custa menos à associação do que uma cobrança indevida custaria, e toda divergência é tratada como um dado para revisar em vez de um limiar para ajustar em silêncio até as reclamações pararem
Resultados
Stack completa
Backend
- FastAPI (Python 3.12)
- SQLAlchemy 2 + Alembic
- Jinja2 + HTMX (painel admin)
Dados
- PostgreSQL 16 + pgvector
Infra
- Evolution API (WhatsApp self-hosted)
- EasyPanel
IA
- Claude (Anthropic, tool use)
- provedor externo de embeddings
Tem interesse em um projeto como esse? Entre em contato.
Entrar em contato