Menuabrir
EstávelAtualizado em 14 de mai. de 2026, 00:06

Este módulo depende de

1
  • crmcontact_id em price_inquiry, objection, appointment; contexto do cliente

Módulos que dependem deste

4
  • instagramAgente consulta brand_voice, products, forbidden_topics via tools brain
  • councilcross_module.read_products, read_knowledge, read_brain_config, read_objections
  • agentsread_brain_config, read_products, read_objections, read_knowledge via tools
  • intelligenceLê objections, price_inquiries, appointments para refresh L1 (churn indicators)

Business (Catálogo + Conhecimento + Brain Config)

Status: 🟢 Estável Code: backend/app/modules/business UI: frontend/src/app/(admin)/[slug]/admin/business Última revisão deste doc: 2026-05-13 por Felipe + Claude Dependências fortes: crm (contact_id em todas as tabelas relacionadas a pessoa), council + agents (consomem tudo via tools)


1. Identidade

O que faz (uma frase)

Define o catálogo de produtos, base de conhecimento operacional, objeções/cotações/agendamentos por contato e configuração global do Cérebro do tenant (brand_voice, tópicos proibidos, horário de atendimento) — fonte de verdade que Conselho e Agentes consultam pra responder com consistência.

Por que existe (negócio)

Sem Business, agente IA inventa preço, mistura produtos, fala fora do tom da marca, não sabe horário de atendimento. Com Business:

  • Catálogo único: Hari sempre cita o preço atualizado de "Mentoria Premium R$ 2.997 mensal" — nunca chuta.
  • Brand voice cravada: "Tom direto, sem floreio, sem emoji" — todo agente respeita.
  • Forbidden topics: "Não fale sobre política/religião/concorrentes" — proteção legal/reputacional.
  • Knowledge base: FAQ "Como cancelo minha assinatura?" → agente responde sem inventar.
  • Histórico de objeções: "Já disse 3x que preço é alto" → agente Hari ajusta abordagem.
  • Cotações (price_inquiry): "Cliente X perguntou preço de Y, ainda não converteu" → segmentação automática + retomada.

Por que existe (técnico)

Antes do Business, esses dados ficavam espalhados:

  • Catálogo numa planilha externa (sem API).
  • Brand voice no docs interno (não consumível por código).
  • Cotações em Notion (manual).

Como módulo dedicado:

  • Tabelas dedicadas com FK lógica para crm_contact.
  • Endpoints CRUD acessíveis por UI + API + tools de IA.
  • Schema versionado (Pydantic in/out garante contrato).
  • Multi-tenant nativo (slugs unique per tenant).
  • TenantBrainConfig: única linha por tenant — pull único pra montar system prompt.

Status atual

  • Estável em prod desde Sprint 1.B+1.C (alembic 0095+0096, 2026-05-13). Ver memória [[brain-observatory-sprint-1bc]].
  • 8 tools no Conselho consumindo dados business.

Próxima mudança planejada: Embeddings em knowledge_entry (pgvector) pra busca semântica; upload de PDFs em agent_resource com extração automática.


2. Cases de uso reais

Situação: Lead pergunta "qual o valor da Mentoria Premium?".

Fluxo:

  1. Brain de Hari chama tool read_products(is_active=true).
  2. Tool retorna lista de business_product com preços.
  3. Brain encontra "Mentoria Premium" → cita "R$ 2.997 mensal, com 2 sessões 1:1 + acesso à comunidade".
  4. Se tenant tem description_md rico, Brain incorpora na resposta.

Impacto: Sem inventar preço. Se o preço muda, basta editar business_product.price_brl e todo agente passa a citar o novo.

Case 2: Conselheiro Commercial vê histórico de cotações antes de propor ação

Situação: Felipe pergunta no Conselho "como vai a conversão de cotações?"

Fluxo:

  1. Conselheiro Commercial chama read_contact_price_inquiries por contato.
  2. Tool retorna list de price_inquiry com status (asked / converted / declined / ghosted) e summary_by_status.
  3. Brain monta análise: "85 cotações no último mês, 12% converted, 23% ghosted (estes precisam followup)..."
  4. Propõe ação via propose_action(intent=send_message, target_agent_slug=clara, target_contact=...) pra fazer followup das ghosted.

Impacto: Felipe aprova um botão e cliente recebe nudge personalizado.

Case 3: Agente Hari evita objeção já registrada

Situação: Lead tem objeção "preço alto" registrada no product_objection (severity=high, ainda unresolved).

Fluxo:

  1. Brain chama read_contact_objections(contact_id, unresolved_only=true).
  2. Detecta a objeção.
  3. Brain ajusta resposta: "Entendo que valor era preocupação. Tenho duas opções: parcelar em 12x ou começar com plano básico R$ 197...".

Impacto: Conversão não morre por agente "robô" repetindo o pitch que já não funcionou.

Case 4: Brain Config define tom da marca cross-agente

Situação: Felipe quer que TODOS os agentes (Hari, Clara, Aurora, etc.) parem de usar emoji.

Fluxo:

  1. Felipe edita em /admin/business/brain-config: brand_voice_md = "Tom profissional, sem emoji, sem floreio".
  2. PUT /api/business/brain-config upsert em tenant_brain_config.
  3. Todo agente que próximo turn chamar read_brain_config recebe o novo brand_voice.
  4. System prompt do brain inclui: "## Voz da marca\nTom profissional, sem emoji, sem floreio".

Impacto: Mudança imediata em todos os agentes sem deploy.


3. Oportunidades de negócio

  • Catalog-as-a-Service standalone: vender "Mandir Catalog" como API REST + UI pra empresas com produtos (e-commerce, infoprodutor) que não têm CRM próprio.
  • Knowledge base integrada com FAQ público: transformar knowledge_entry em FAQ pública (subdomain ajuda.<tenant>.com.br) com search nativo. Eliminar dependência de Zendesk/HelpScout.
  • Marketplace de products templates: "pacote curso EAD" (knowledge + products + price tiers + objection responses) por vertical.
  • Compliance LGPD pre-built: forbidden_topics_md com templates legais (saúde, financeiro, infantil) — venda como "compliance starter pack".
  • AI training feedback loop: registrar objeções extraídas automaticamente de conversa → treinar agentes → fechamento maior.
  • Resource library com S3: upload PDFs/docs pra agent_resource.media_storage_path → OCR + embeddings → agente envia anexo certo na hora certa.

Riscos comerciais:

  • Catálogo desatualizado vira passivo (preço errado é dor pública). Mitigação: webhook de alteração + audit log.
  • Knowledge base sem governança vira lixo. Mitigação: priority + last_updated.

4. Arquitetura interna

Sem Celery tasks. Sem adapters externos. Apenas modelos + service + routes.

Arquivos do módulo

ArquivoPropósito
models.py7 tabelas + constantes (PRODUCT_KINDS, PRICE_INQUIRY_STATUSES, TENANT_CAPABILITIES, OBJECTION_CATEGORIES)
routes.py23 endpoints REST
schemas.pyPydantic in/out + aliases backward compat (QuoteIn/QuoteOut = PriceInquiryIn/Out)
__init__.pyexporta router

5. Tabelas + relacionamentos

7 tabelas, prefixo varia (business_*, price_*, product_*, knowledge_*, agent_*, appointment, tenant_*).

business_product

Catálogo único do tenant.

Coluna chaveTipoNotas
tenant_idUUIDidx
slugVARCHARUNIQUE per tenant
nameVARCHAR"Mentoria Premium"
description_mdTEXTMarkdown
kindVARCHARdigital / physical / service / subscription / package / course / other
price_brlDECIMAL
currencyVARCHARdefault BRL
billing_cycleVARCHARmensal / anual / uma_vez
metadata / tagsJSONB
is_activeBOOLSoft-delete via toggle

Índices: (tenant, slug) UNIQUE, (tenant, is_active).

price_inquiry

Registro "X perguntou preço de Y" (alias QuoteIn/QuoteOut backward compat).

Coluna chaveTipoNotas
contact_idUUIDFK lógica → crm_contact
product_idUUIDFK lógica → business_product
deal_idUUIDOpcional → crm_deal
quoted_price_brlDECIMAL
channelVARCHARwhatsapp (default) / email / web / phone
statusVARCHARasked / converted / declined / ghosted
sent_at / decided_atTIMESTAMP(tz)
notesTEXT

Índices: (tenant, contact_id), (tenant, product_id, status). Lifecycle: asked → {converted | declined | ghosted}. Imutável após decisão.

product_objection

Objeções/reclamações.

Coluna chaveTipoNotas
contact_idUUIDFK lógica
product_idUUIDNullable (pode ser objeção genérica)
quote_idUUIDOpcional → price_inquiry
categoryVARCHARprice / timing / trust / feature_missing / competitor / logistics / personal / other
textTEXT
severityVARCHARlow / medium / high
sourceVARCHARmanual (default) / extracted / inferred
resolved_at / resolution_md

Índices: (tenant, contact_id), (tenant, category).

knowledge_entry

Wiki/FAQ/manuais internos.

Coluna chaveTipoNotas
slugVARCHARUNIQUE per tenant
title / content_md
kindVARCHARarticle (default) / faq / procedure / template
source_kind / source_refnotion / gdrive / confluence / manual (URL/ID externo opcional)
tagsJSONB array
audienceVARCHARall (default) / internal / coach
languageVARCHARdefault pt-BR
token_countINTEstimativa simples (word count)
priorityINT0-100, default 50, ord DESC
is_activeBOOL

Índices: (tenant, slug) UNIQUE, (tenant, kind, is_active). Busca: ILIKE em title + content_md (sem embeddings hoje).

agent_resource

Anexos que agentes podem enviar (PDFs, links, modelos).

Coluna chaveTipoNotas
slugVARCHARUNIQUE per tenant
name / description
kindVARCHARpdf / link / template / video / other
urlTEXTOpcional
media_storage_pathTEXTS3/blob opcional
tagsJSONB
when_to_send_mdTEXTInstruções pra IA
usage_countINTContador

appointment

Agendamentos: aulas, consultas, terapias, eventos.

Coluna chaveTipoNotas
contact_idUUIDFK lógica
kindVARCHARclass (default) / consultation / therapy / event / other
titleOpcional
scheduled_atTIMESTAMP(tz)
duration_minutesINT
statusVARCHARscheduled (default) / completed / no_show / cancelled
no_show_reasonQuando aplicável
notes / metadata

Índices: (tenant, contact_id, scheduled_at), (tenant, status, scheduled_at).

tenant_brain_config

Config global do "Cérebro" do tenant. Única linha por tenant.

Coluna chaveTipoNotas
tenant_idUUIDUNIQUE
verticalVARCHARDeprecated — usar capabilities
capabilitiesJSONB arraysells_physical_products / sells_digital_products / offers_scheduled_service / offers_subscription / offers_hospitality / hosts_events
brand_voice_mdTEXT"Tom direto, sem emoji"
forbidden_topics_mdTEXT"Não fale sobre política, religião, concorrentes"
business_hoursJSONB{"mon": "09:00-17:00", "tue": null, ...}
llm_budget_daily_usdDECIMALOpcional cap diário
default_council_modelVARCHAROverride global do modelo
auto_apply_patternsBOOLdefault false
modules_enabledJSONB arrayOverride de quais módulos do Suite estão visíveis

Upsert-only: PUT /api/business/brain-config (cria se não existe, atualiza se sim).

Relacionamentos cross-módulo

DireçãoOutro móduloComoPor quê
↗ Lêcrm_contactcontact_id em price_inquiry/objection/appointmentContexto do cliente
↗ Lêcrm_dealdeal_id opcional em price_inquiryVínculo com pipeline
↘ Escreve via toolscouncil8 tools (read_knowledge, read_products, etc.)Conselho consulta
↘ Escreve via toolsagentsbrain_config + products + objectionsSystem prompt + ajuste de resposta
↗ Lêintelligence (cross_module)objections + price_inquiries + appointmentsRefresh L1 (churn risk, no_show_rate)

6. API / Endpoints (23)

Prefixo /api/business. Todos exigem require_tenant_admin.

Products (5)

MétodoRotaO que faz
GET/productsLista (filtros: is_active, kind, limit)
POST/productsCria → 201
PATCH/products/{id}Update parcial
DELETE/products/{id}Soft (toggle is_active) ou hard

Price Inquiries (3)

MétodoRotaO que faz
GET/price-inquiriesLista (filtros: contact_id, product_id, status, limit)
POST/price-inquiriesCria → 201
POST/price-inquiries/{id}/decideTransição: converted / declined / ghosted (seta decided_at)

Objections (2)

MétodoRotaO que faz
GET/objectionsLista (filtros: contact_id, category, unresolved, limit)
POST/objectionsCria

Knowledge (4)

MétodoRotaO que faz
GET/knowledgeLista (filtros: kind, is_active, limit; ord priority DESC)
POST/knowledgeCria (calcula token_count)
PATCH/knowledge/{id}Update (recalcula token_count se content mudar)
DELETE/knowledge/{id}Hard delete

Resources (2)

MétodoRotaO que faz
GET/resourcesLista (filtros: kind, is_active)
POST/resourcesCria

Appointments (3)

MétodoRotaO que faz
GET/appointmentsLista (filtros: contact_id, status, limit)
POST/appointmentsCria → 201
PATCH/appointments/{id}Update (status, no_show_reason, notes, scheduled_at)

Brain Config (1)

MétodoRotaO que faz
GET/brain-configRead (null se não existe)
PUT/brain-configUpsert

Agregados por Contact (3)

MétodoRotaO que faz
GET/price-inquiries-by-contact?contact_id=UUIDcount + summary_by_status + lista
GET/objections-by-contact?contact_id=UUIDcount + objections
GET/appointments-by-contact?contact_id=UUIDcount + summary_by_status + no_show patterns

7. Tools que outros módulos consomem

Conselho usa 8 tools read-only (em council/cross_module.py):

ToolFunção internaO que retorna
read_knowledge(kind, tags, audience, limit, cap=20)cross_module.read_knowledgeEntries ordenadas por priority DESC
search_knowledge(query, limit, cap=20)cross_module.search_knowledgeILIKE em title + content_md, excerpt 800 chars
read_products(is_active=true, kind, limit, cap=100)cross_module.read_productsCatálogo completo com preços
read_contact_price_inquiries(contact_id, limit, cap=100)cross_module.read_contact_price_inquiriesCotações + summary_by_status
read_contact_objections(contact_id, unresolved_only=false, limit, cap=100)cross_module.read_contact_objectionsObjeções com filtro de resolução
read_contact_appointments(contact_id, window_days, status, limit, cap=100)cross_module.read_contact_appointmentsAgenda com análise no-show
read_brain_config()cross_module.read_brain_config{found, brand_voice_md, forbidden_topics_md, business_hours, default_council_model, auto_apply_patterns, modules_enabled}
read_agent_resources()TBDLista de PDFs/links/templates

Agentes usam principalmente: read_brain_config (no system prompt), read_products (citar preço), read_contact_objections (ajustar abordagem), read_contact_price_inquiries (lembrar cotações abertas).


8. Search/RAG

  • knowledge_entry: busca textual simples (ILIKE). SEM embeddings hoje.
  • Futuro: pgvector pra busca semântica em KnowledgeEntry; embedding no create/patch.
  • FTS: não ativo. ILIKE suficiente para corpus pequeno (<10k docs).

9. Configuração

Env vars

Nenhuma específica do módulo. Usa env globais (MANDIR_CRYPTO_MASTER_KEY para futuras features cifradas).

Kill switches

  • Por entidade: is_active em product / knowledge / resource (toggle).
  • Catálogo deletado: soft-delete via is_active=false (preserva histórico em price_inquiries vinculadas).

10. Operações

Como adicionar produto novo

UI: /admin/business/products → "+ Novo produto" → form (slug, name, kind, price_brl, billing_cycle, description_md). API: POST /api/business/products body com mesmos campos.

Como atualizar brand voice

UI: /admin/business/brain-config → form com textarea brand_voice_md, forbidden_topics_md, business_hours JSON, capabilities checkboxes. API: PUT /api/business/brain-config (upsert).

Troubleshooting

Sintoma: Agente cita preço errado

Causa: Cache do tool read_products ou produto antigo is_active=true. Diagnóstico:

SELECT slug, name, price_brl, is_active FROM business_product WHERE tenant_id=...;

Fix: atualizar preço; se outro produto antigo está ativo, marcar is_active=false.

Sintoma: Agente ignora forbidden_topic

Causa: Brain não chamou read_brain_config no turn (ocorre se contexto já tem conteúdo prévio sem essa info). Diagnóstico: verificar agents_run.output_payload.tool_calls — busca por read_brain_config. Fix: próximo turn deve chamar; se persistir, verificar se brain_config não está NULL.


11. Métricas e observabilidade

Logs estruturados-chave

Logger keyQuando emite
business.product.createdPOST /products
business.knowledge.updatedPATCH /knowledge/{id}
business.brain_config.updatedPUT /brain-config
business.price_inquiry.decidedPOST /price-inquiries/{id}/decide

Sem dashboards próprios — KPIs aparecem em /admin/intelligence/contact-attributes (no_show_rate, objection_count).


12. Limitações e débitos técnicos conhecidos

#ItemImpactoPlano
1Sem embeddings em knowledge_entryMid — busca só ILIKEpgvector planejado
2Appointments hard-deletedLow — sem audit historyTabela appointment_event futuro
3Backward compat QuoteIn/QuoteOutLow — código antigo ainda usa aliasesSem urgência de remover
4Currency hardcoded BRLLow — multi-moeda futuroTBD
5business_hours formato livre JSONBLow — sem validação no backendUI valida
6AgentResource sem upload no UIMid — só URL externa hojeImplementar S3 upload
7Sem extraction automática de objectionsMid — manual hojeSub-agent futuro: extrair de WA threads
8knowledge_entry sem versionamentoMid — overwrite totalAdicionar history table
9price_inquiry status enum hard-codedLow — adicionar status novo exige migrationOK
10scope_observes("business") mandatory pra toolsLow — se conselheiro está fora do scope, tool retorna _omitted("business")Documentado

13. Histórico relevante

  • 2026-05-13 (alembic 0095+0096) — Sprint 1.B+1.C: módulo business completo (knowledge + products + price_inquiries + objections + appointments + brain_config) deployado. 8 tools no Conselho. Memória [[brain-observatory-sprint-1bc]].

Apêndices

A. Capabilities canônicas

CapabilitySignificado
sells_physical_productsE-commerce físico
sells_digital_productsCursos, ebooks, software
offers_scheduled_serviceConsultoria, terapia, aula
offers_subscriptionAssinatura recorrente
offers_hospitalityHotel, retiro, evento presencial
hosts_eventsLives, workshops

Determina quais templates de agente fazem sentido (memória [[agentes-preenchidos-2026-05-13]]).

B. Glossário

  • PriceInquiry / Quote: mesma coisa (alias backward compat). Cotação = registro de pergunta de preço.
  • Brain config: config global do "Cérebro" do tenant — única linha por tenant.
  • Brand voice: instrução narrativa de tom da marca, injetada em todo system prompt.
  • Forbidden topics: lista de tópicos a evitar (proteção legal/reputacional).
  • Capability: marcador funcional do que o tenant faz/vende. Determina quais templates de agente sugerir.
  • Knowledge entry: artigo/FAQ/procedimento. Conteúdo markdown consultável por agentes.
  • Agent resource: anexo (PDF/link/template) que agente pode enviar.