Documento de referência técnica para os 3 motores de automação do módulo Eco Compliance: Pipeline Jurídico de Outorgas, Engine de Laudos e Gov Gateway.
Esquema relacional principal com suporte a JSONB para campos dinâmicos de laudos, parâmetros analíticos e logs de transmissão governamental.
| Coluna | Tipo | Descrição |
|---|---|---|
| id | UUID PK | Identificador único |
| cnpj | VARCHAR(18) UNIQUE | CNPJ da empresa |
| razao_social | TEXT | Razão social |
| uf | CHAR(2) | Estado de operação |
| cert_a1_enc | BYTEA | Certificado A1 criptografado (AES-256) |
| cert_a1_senha_hash | TEXT | Hash bcrypt da senha do certificado |
| cert_validade | TIMESTAMPTZ | Validade do certificado A1 |
| created_at | TIMESTAMPTZ | DEFAULT NOW() |
| Coluna | Tipo | Descrição |
|---|---|---|
| id | UUID PK | |
| client_id | UUID FK → clients | |
| codigo | VARCHAR(20) | Ex: P-01, P-02 |
| tipo | ENUM('artesiano','semi_artesiano','superficial','acumulacao') | |
| lat | DECIMAL(10,7) | Latitude WGS-84 |
| lng | DECIMAL(10,7) | Longitude WGS-84 |
| profundidade_m | DECIMAL(8,2) | Metros de profundidade |
| vazao_instalada_lps | DECIMAL(8,3) | L/s instalados |
| telemetria | JSONB | {"ultima_leitura": ..., "nivel_estatico_m": ..., "volume_diario_m3": ...} |
| Coluna | Tipo | Descrição |
|---|---|---|
| id | UUID PK | |
| client_id | UUID FK | |
| poco_id | UUID FK → pocos (nullable) | |
| numero_processo | VARCHAR(50) UNIQUE | Ex: DAEE-SP-2022/1453 |
| orgao | ENUM('DAEE','ANA','IGAM','CERH','INEMA',...) | |
| tipo | ENUM('captacao_superficial','captacao_sub','lancamento','acumulacao') | |
| volume_autorizado_m3d | DECIMAL(12,3) | Volume diário em m³ |
| vazao_maxima_m3h | DECIMAL(10,3) | Vazão pico |
| data_emissao | DATE | |
| data_vencimento | DATE | INDEXED — base dos cron jobs |
| status | ENUM('vigente','a_renovar','em_analise','vencida','cancelada') | |
| condicionantes | JSONB | Array de strings com obrigações |
| alerta_12m_enviado | BOOLEAN DEFAULT FALSE | |
| alerta_6m_enviado | BOOLEAN DEFAULT FALSE | |
| alerta_3m_enviado | BOOLEAN DEFAULT FALSE | |
| arquivo_original_url | TEXT | S3/Storage URL |
| ocr_extraido | JSONB | Dados brutos extraídos pelo OCR |
| Coluna | Tipo | Descrição |
|---|---|---|
| id | UUID PK | |
| client_id | UUID FK | |
| poco_id | UUID FK (nullable) | |
| outorga_id | UUID FK (nullable) | |
| tipo_amostra | ENUM('potavel','efluente_industrial','efluente_dom','corpo_receptor','subterranea') | |
| laboratorio | TEXT | |
| data_coleta | DATE | |
| numero_laudo_lab | VARCHAR(50) | |
| arquivo_url | TEXT | |
| parametros | JSONB | Array: [{nome, valor, unidade, limite_legal, norma, status}] |
| conformidade_geral | ENUM('conforme','nao_conforme','atencao','pendente') | |
| resumo_ia | TEXT | Narrativa gerada pelo LLM |
| status_validacao | ENUM('aguardando_ia','fila_biologo','em_revisao','aprovado','reprovado','notificado') | |
| biologo_id | UUID FK → especialistas (nullable) | |
| engenheiro_id | UUID FK → especialistas (nullable) | |
| assinatura_hash | TEXT | SHA-256 do conteúdo validado |
| art_numero | VARCHAR(30) | |
| proxima_coleta | DATE |
| Coluna | Tipo | Descrição |
|---|---|---|
| id | UUID PK | |
| nome | TEXT | |
| TEXT UNIQUE | ||
| perfil | ENUM('biologo','eng_ambiental','eng_civil','quimico','gestor') | |
| registro | VARCHAR(20) | CRBio / CREA / CRQ |
| uf_atuacao | TEXT[] | Array de UFs |
| art_ativa | VARCHAR(30) | |
| ativo | BOOLEAN DEFAULT TRUE |
| Coluna | Tipo | Descrição |
|---|---|---|
| id | UUID PK | |
| client_id | UUID FK | |
| laudo_id | UUID FK (nullable) | Se originado de inconformidade |
| tipo | ENUM('vazamento','contaminacao','falha_ete','amostra_fora','descarte_irr','ruptura') | |
| gravidade | ENUM('baixo','moderado','critico','emergencial') | |
| status | ENUM('em_apuracao','fila_tecnico','em_remediacao','resolvido','notificado_orgao') | |
| hash_auditoria | TEXT | SHA-256 para rastreabilidade jurídica |
| parecer_tecnico | JSONB | {especialista_id, texto, timestamp, hash} |
| medidas_corretivas | JSONB | Array de ações |
| Coluna | Tipo | Descrição |
|---|---|---|
| id | UUID PK | |
| client_id | UUID FK | |
| orgao | ENUM('DAEE','IBAMA','SISAGUA','CETESB','ANA') | |
| tipo_transmissao | ENUM('telemetria_mensal','rapp_anual','laudo_potabilidade','renovacao_licenca') | |
| payload_enviado | JSONB | Cópia do payload assinado |
| protocolo_retorno | TEXT | Número de protocolo governamental |
| status | ENUM('pendente','enviado','erro','confirmado') | |
| erro_detalhe | TEXT | |
| assinado_com_cert | BOOLEAN | |
| timestamp_envio | TIMESTAMPTZ |
-- Relacionamentos principais outorgas.client_id → clients.id (ON DELETE CASCADE) outorgas.poco_id → pocos.id (ON DELETE SET NULL) laudos.client_id → clients.id laudos.poco_id → pocos.id laudos.biologo_id → especialistas.id laudos.engenheiro_id → especialistas.id incidentes.laudo_id → laudos.id gov_transmissoes.client_id → clients.id -- Índices críticos de performance CREATE INDEX idx_outorgas_vencimento ON outorgas(data_vencimento) WHERE status != 'cancelada'; CREATE INDEX idx_laudos_conformidade ON laudos(conformidade_geral, status_validacao); CREATE INDEX idx_incidentes_gravidade ON incidentes(gravidade, status); CREATE INDEX idx_gov_status ON gov_transmissoes(orgao, status, timestamp_envio);
FUNÇÃO processar_upload_outorga(arquivo_pdf, client_id, tipo_solicitacao):
// ETAPA 1 — Armazenamento seguro
url_s3 ← storage.upload(arquivo_pdf, path=f"clients/{client_id}/outorgas/")
// ETAPA 2 — OCR + Extração estruturada via LLM
texto_raw ← ocr_engine.extract(arquivo_pdf) // Tesseract / AWS Textract
dados_extraidos ← llm.extract_json(
prompt = texto_raw,
schema = {
numero_processo: string,
orgao_emissor: string,
tipo: enum[captacao_sub|superficial|lancamento|acumulacao],
volume_m3_dia: float,
vazao_maxima_m3h: float,
data_emissao: date,
data_vencimento: date,
condicionantes: array[string],
coordenadas: {lat: float, lng: float} | null
}
)
// ETAPA 3 — Persistência
outorga_id ← db.insert('outorgas', {
client_id,
...dados_extraidos,
arquivo_original_url: url_s3,
ocr_extraido: dados_extraidos,
status: 'em_analise'
})
// ETAPA 4 — Agendar Cron Jobs de Alerta
agendar_alertas_vencimento(outorga_id, dados_extraidos.data_vencimento)
// ETAPA 5 — Enfileirar para revisão do Engenheiro
fila_tecnica.push({
tipo: 'revisao_outorga',
outorga_id,
client_id,
prioridade: calcular_prioridade(dados_extraidos.data_vencimento),
deadline: NOW() + 72h
})
RETORNAR { outorga_id, status: 'processando', eta_revisao: '72h' }// Executa diariamente às 06:00 UTC
CRON 'alertas_outorga' SCHEDULE '0 6 * * *':
outorgas_ativas ← db.query(
"SELECT * FROM outorgas
WHERE status IN ('vigente','a_renovar')
AND data_vencimento > NOW()"
)
PARA CADA outorga EM outorgas_ativas:
dias_restantes ← DATEDIFF(outorga.data_vencimento, NOW())
SE dias_restantes <= 90 AND NOT outorga.alerta_3m_enviado:
emitir_alerta(outorga, nivel='CRITICO', meses=3)
db.update('outorgas', outorga.id, {alerta_3m_enviado: true, status: 'a_renovar'})
notificar_dashboard(outorga.client_id, tipo='VENCIMENTO_3M', outorga_id=outorga.id)
SENÃO SE dias_restantes <= 180 AND NOT outorga.alerta_6m_enviado:
emitir_alerta(outorga, nivel='WARNING', meses=6)
db.update('outorgas', outorga.id, {alerta_6m_enviado: true})
SENÃO SE dias_restantes <= 365 AND NOT outorga.alerta_12m_enviado:
emitir_alerta(outorga, nivel='INFO', meses=12)
db.update('outorgas', outorga.id, {alerta_12m_enviado: true})
FUNÇÃO emitir_alerta(outorga, nivel, meses):
db.insert('alertas', {
client_id: outorga.client_id,
tipo: 'VENCIMENTO_OUTORGA',
nivel,
mensagem: f"Outorga {outorga.numero_processo} vence em {meses} meses ({outorga.data_vencimento})",
referencia_id: outorga.id,
lido: false
})
enviar_email(outorga.client_id, template='ALERTA_VENCIMENTO', dados=outorga)
enviar_whatsapp(outorga.client_id, template='ALERTA_VENCIMENTO_WPP', dados=outorga)FUNÇÃO gerar_dvi(outorga_id, dados_tecnicos_adicionais):
outorga ← db.get('outorgas', outorga_id)
poco ← db.get('pocos', outorga.poco_id)
client ← db.get('clients', outorga.client_id)
// Buscar normas vigentes do órgão
normas ← normas_db.get(orgao=outorga.orgao, uf=client.uf)
// LLM gera o documento técnico formal
dvi_conteudo ← llm.generate(
prompt = f"""
Elabore uma Declaração de Viabilidade Hídrica técnica e juridicamente fundamentada:
Empresa: {client.razao_social} | CNPJ: {client.cnpj} | UF: {client.uf}
Processo: {outorga.numero_processo} | Órgão: {outorga.orgao}
Tipo captação: {outorga.tipo} | Volume: {outorga.volume_m3_dia} m³/dia
Coordenadas: {poco.lat}, {poco.lng}
Dados adicionais: {dados_tecnicos_adicionais}
Base legal: {normas.base_legal}
""",
formato = 'documento_tecnico_formal'
)
db.update('outorgas', outorga_id, {dvi_gerada: true, dvi_conteudo: dvi_conteudo})
fila_tecnica.push({tipo: 'assinar_dvi', outorga_id, requer: 'engenheiro_ambiental'})
RETORNAR dvi_conteudo// Constantes imutáveis — atualizadas apenas por revisão de engenharia
LIMITES_LEGAIS = {
// Portaria GM/MS 888/2021 — Água para Consumo Humano (SISAGUA)
POTAVEL: {
pH: { min: 6.0, max: 9.5, norma: "MS888/2021" },
turbidez_NTU: { max: 5.0, norma: "MS888/2021" },
cloro_residual: { min: 0.2, max: 5.0, norma: "MS888/2021" },
coliformes_totais:{ max: 0, norma: "MS888/2021 §40" }, // ausência em 100mL
e_coli: { max: 0, norma: "MS888/2021 §40" },
nitrato_mg_L: { max: 10.0, norma: "MS888/2021" },
fluoreto_mg_L: { max: 1.5, norma: "MS888/2021" },
arsenio_ug_L: { max: 10.0, norma: "MS888/2021" },
chumbo_ug_L: { max: 10.0, norma: "MS888/2021" },
},
// CONAMA 430/2011 — Lançamento de Efluentes
EFLUENTE_LANCAMENTO: {
pH: { min: 5.0, max: 9.0, norma: "CONAMA430/2011" },
temperatura_C: { max: 40.0, norma: "CONAMA430/2011 §5" },
DBO_mg_L: { max: 120.0, norma: "CONAMA430/2011 (ou 60% remoção)" },
DQO_mg_L: { max: 200.0, norma: "CONAMA430/2011" },
SS_mg_L: { max: 150.0, norma: "CONAMA430/2011" },
oleo_mineral_mg_L:{ max: 20.0, norma: "CONAMA430/2011" },
oleo_vegetal_mg_L:{ max: 50.0, norma: "CONAMA430/2011" },
cianeto_mg_L: { max: 0.2, norma: "CONAMA430/2011" },
arsenio_mg_L: { max: 0.5, norma: "CONAMA430/2011" },
cadmio_mg_L: { max: 0.2, norma: "CONAMA430/2011" },
cromo_hexa_mg_L: { max: 0.1, norma: "CONAMA430/2011" },
chumbo_mg_L: { max: 0.5, norma: "CONAMA430/2011" },
mercurio_mg_L: { max: 0.01, norma: "CONAMA430/2011" },
niquel_mg_L: { max: 2.0, norma: "CONAMA430/2011" },
zinco_mg_L: { max: 5.0, norma: "CONAMA430/2011" },
},
// CONAMA 357/2005 — Corpo receptor (Classe II padrão)
CORPO_RECEPTOR: {
pH: { min: 6.0, max: 9.0, norma: "CONAMA357/2005" },
OD_mg_L: { min: 5.0, norma: "CONAMA357/2005 ClasseII" },
DBO_mg_L: { max: 5.0, norma: "CONAMA357/2005" },
turbidez_NTU: { max: 100.0, norma: "CONAMA357/2005" },
coliformes_term: { max: 1000, norma: "CONAMA357/2005 (NMP/100mL)" },
nitrogenio_total: { max: 2.18, norma: "CONAMA357/2005 pH>7.5" },
fosforo_total_mg: { max: 0.1, norma: "CONAMA357/2005 ambiente lêntico" },
}
}FUNÇÃO validar_laudo(laudo_id):
laudo ← db.get('laudos', laudo_id)
// PASSO 1 — Selecionar tabela de limites pelo tipo de amostra
SWITCH laudo.tipo_amostra:
'potavel' → limites ← LIMITES_LEGAIS.POTAVEL
'efluente_industrial' → limites ← LIMITES_LEGAIS.EFLUENTE_LANCAMENTO
'corpo_receptor' → limites ← LIMITES_LEGAIS.CORPO_RECEPTOR
DEFAULT → limites ← LIMITES_LEGAIS.POTAVEL
parametros_validados = []
inconformidades = []
alertas_atencao = []
// PASSO 2 — Iterar cada parâmetro extraído do laudo
PARA CADA param EM laudo.parametros:
limite ← limites[param.nome]
SE limite É NULO: CONTINUAR // parâmetro não regulado
valor_num ← to_float(param.valor)
violacao ← false
SE limite.min EXISTE E valor_num < limite.min:
violacao ← true
tipo_violacao ← 'ABAIXO_MINIMO'
SE limite.max EXISTE E valor_num > limite.max:
violacao ← true
tipo_violacao ← 'ACIMA_MAXIMO'
// Zona de atenção: 80-100% do limite máximo
SENÃO SE limite.max EXISTE E valor_num > (limite.max * 0.8):
alertas_atencao.append(param.nome)
status_param ← SE violacao ENTÃO 'NAO_CONFORME' SENÃO 'CONFORME'
parametros_validados.append({
...param,
limite_legal: limite,
status: status_param,
norma: limite.norma,
desvio_percentual: calcular_desvio(valor_num, limite)
})
SE violacao:
inconformidades.append({parametro: param.nome, valor: valor_num, limite, tipo_violacao})
// PASSO 3 — Determinar conformidade geral
SE inconformidades.length > 0:
conformidade_geral ← 'NAO_CONFORME'
grau_risco ← classificar_risco(inconformidades) // CRITICO|ALTO|MODERADO
// PASSO 4A — Rota de Inconformidade
db.update('laudos', laudo_id, {
parametros: parametros_validados,
conformidade_geral: 'NAO_CONFORME',
status_validacao: 'fila_biologo',
resumo_ia: gerar_resumo_ia(inconformidades)
})
// Gerar incidente associado
incidente_id ← db.insert('incidentes', {
client_id: laudo.client_id,
laudo_id,
tipo: inferir_tipo_incidente(inconformidades),
gravidade: grau_risco,
status: 'fila_tecnico'
})
// Notificar Biólogo com prioridade
biologo ← selecionar_biologo_disponivel(laudo.client_id)
notificar_especialista(biologo.id, {
tipo: 'INCONFORMIDADE_CRITICA',
laudo_id,
incidente_id,
parametros_violados: inconformidades,
sugestao_intervencao: gerar_sugestao_tratamento(inconformidades),
prazo: NOW() + 24h
})
SENÃO SE alertas_atencao.length > 0:
db.update('laudos', laudo_id, {conformidade_geral: 'ATENCAO', status_validacao: 'fila_biologo'})
SENÃO:
// PASSO 4B — Conformidade OK
db.update('laudos', laudo_id, {
conformidade_geral: 'CONFORME',
status_validacao: 'aprovado',
resumo_ia: 'Todos os parâmetros analisados estão dentro dos limites legais vigentes.'
})
RETORNAR { conformidade_geral, inconformidades, parametros_validados }
FUNÇÃO gerar_sugestao_tratamento(inconformidades):
// LLM gera recomendação técnica baseada nos parâmetros violados
RETORNAR llm.generate(
prompt = f"Para os parâmetros violados {inconformidades}, sugira tratamento técnico.",
modelo = 'claude_sonnet'
)MÓDULO cert_manager:
FUNÇÃO carregar_certificado(client_id):
client ← db.get('clients', client_id)
// Descriptografar certificado armazenado (AES-256-GCM)
cert_pem ← crypto.decrypt(
ciphertext = client.cert_a1_enc,
key = ENV.APP_ENCRYPTION_KEY,
iv = client.cert_iv
)
// Parse do certificado PKCS#12
{ cert, privateKey, chain } ← pkcs12.parse(cert_pem, client.cert_a1_senha_hash)
// Validar validade
SE cert.validTo < NOW():
LANÇAR ERRO 'CERTIFICADO_VENCIDO'
RETORNAR { cert, privateKey, chain }
FUNÇÃO assinar_payload_soap(payload_xml, client_id):
{ cert, privateKey } ← carregar_certificado(client_id)
// Assinatura XmlDSig (padrão ICP-Brasil)
assinatura ← xmldsig.sign(
xml = payload_xml,
privateKey = privateKey,
certificate = cert,
algoritmo = 'RSA-SHA256',
canonicalizacao = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315'
)
RETORNAR payload_xml_assinado
FUNÇÃO assinar_payload_rest(payload_json, client_id):
{ cert, privateKey } ← carregar_certificado(client_id)
// JWT assinado com chave privada do certificado (mTLS alternativo)
jwt_token ← jwt.sign(
payload = { ...payload_json, iat: NOW(), iss: cert.subject.cnpj },
privateKey = privateKey,
algorithm = 'RS256'
)
RETORNAR { jwt_token, client_cert: cert.pem }// Cron: Dia 1 de cada mês às 04:00 UTC
CRON 'transmissao_daee' SCHEDULE '0 4 1 * *':
clientes_daee ← db.query(
"SELECT c.* FROM clients c
JOIN outorgas o ON o.client_id = c.id
WHERE o.orgao = 'DAEE' AND o.status = 'vigente'"
)
PARA CADA client EM clientes_daee:
TRY:
// Agregar telemetria do mês anterior
telemetria ← db.query(f"""
SELECT poco_id,
SUM((telemetria->>'volume_diario_m3')::float) AS volume_total_m3,
AVG((telemetria->>'nivel_estatico_m')::float) AS nivel_medio_m,
MAX(updated_at) AS ultima_medicao
FROM pocos
WHERE client_id = '{client.id}'
AND updated_at >= DATE_TRUNC('month', NOW() - INTERVAL '1 month')
GROUP BY poco_id
""")
payload_xml ← templates.daee_telemetria({
cnpj: client.cnpj,
competencia: format(LAST_MONTH, 'YYYY-MM'),
outorgas: telemetria
})
payload_assinado ← cert_manager.assinar_payload_soap(payload_xml, client.id)
resposta ← http.post(
url = 'https://sisplu.daee.sp.gov.br/ws/medicoes',
headers = {'Content-Type': 'text/xml; charset=utf-8', 'SOAPAction': 'EnviarMedicao'},
body = payload_assinado,
tls = { cert: client.cert_pem, key: client.cert_key }
)
db.insert('gov_transmissoes', {
client_id: client.id,
orgao: 'DAEE',
tipo_transmissao: 'telemetria_mensal',
payload_enviado: { telemetria },
protocolo_retorno: xml.parse(resposta).protocolo,
status: 'confirmado',
assinado_com_cert: true
})
CATCH erro:
db.insert('gov_transmissoes', {..., status: 'erro', erro_detalhe: erro.message})
alertar_admin(client.id, 'FALHA_TRANSMISSAO_DAEE', erro)FUNÇÃO transmitir_laudo_sisagua(laudo_id):
laudo ← db.get('laudos', laudo_id)
// Só transmite se: potável + aprovado pelo biólogo
SE laudo.tipo_amostra != 'potavel' OR laudo.status_validacao != 'aprovado':
RETORNAR { ignorado: true, motivo: 'Critérios não atendidos' }
client ← db.get('clients', laudo.client_id)
payload ← {
cnpjPrestador: client.cnpj,
codigoFormulario: 'A05', // Padrão SISAGUA para análise física
dataColeta: laudo.data_coleta,
pontoCaptacao: laudo.poco_id,
parametros: laudo.parametros.map(p => ({
codigo: SISAGUA_CODIGOS[p.nome], // Mapa de codificação SISAGUA
valor: p.valor,
unidade: p.unidade
})),
responsavelTecnico: {
crbio: laudo.biologo.registro,
nome: laudo.biologo.nome
}
}
payload_assinado ← cert_manager.assinar_payload_rest(payload, client.id)
resposta ← http.post(
url = 'https://sisagua.saude.gov.br/sisagua/api/v1/amostras',
headers = { 'Authorization': f'Bearer {payload_assinado.jwt_token}',
'X-Client-Cert': payload_assinado.client_cert },
body = payload
)
db.insert('gov_transmissoes', {
client_id: client.id,
orgao: 'SISAGUA',
tipo_transmissao: 'laudo_potabilidade',
protocolo_retorno: resposta.numeroProtocolo,
status: 'confirmado'
})// Cron: 15 de março de cada ano (prazo RAPP)
CRON 'rapp_ibama' SCHEDULE '0 8 15 3 *':
PARA CADA client EM db.query("SELECT * FROM clients WHERE ativo = true"):
// Consolidar dados do ano anterior
dados_anuais ← {
volume_captado_m3: somar_telemetria_anual(client.id, ano=ANO_ANTERIOR),
efluentes_m3: somar_efluentes_lancados(client.id, ano=ANO_ANTERIOR),
laudos_conformes: contar_laudos(client.id, status='conforme', ano=ANO_ANTERIOR),
incidentes: listar_incidentes(client.id, ano=ANO_ANTERIOR),
outorgas_ativas: listar_outorgas(client.id, status='vigente')
}
// LLM pré-preenche o RAPP no formato CTF/IBAMA
rapp_preenchido ← llm.generate(
prompt = f"Preencha o RAPP {ANO_ANTERIOR} para empresa {client.razao_social} com dados: {dados_anuais}",
formato = 'xml_ibama_rapp_v4'
)
payload_assinado ← cert_manager.assinar_payload_soap(rapp_preenchido, client.id)
// Submeter ao CTF/IBAMA
resposta ← http.post(
url = 'https://servicos.ibama.gov.br/ctf/ws/rapp/envio',
body = payload_assinado
)
notificar_client(client.id, 'RAPP_ENVIADO', {protocolo: resposta.protocolo})Diagrama textual passo a passo de como uma inconformidade detectada pela IA migra para o Biólogo/Engenheiro e retorna tratada ao cliente.
📤 INPUT — Cliente faz upload do laudo PDF
O cliente (indústria/produtor) anexa o laudo físico-químico emitido pelo laboratório acreditado. O sistema registra o arquivo e cria registro com status 'aguardando_ia'.
🤖 IA ENGINE — OCR + Extração de Parâmetros
O motor OCR (Tesseract/Textract) extrai os parâmetros analíticos do PDF. O LLM estrutura os dados em JSON com: nome do parâmetro, valor numérico, unidade e método analítico.
⚖️ IA ENGINE — Validação contra Limites Legais Hardcoded
Cada parâmetro é comparado com a tabela de limites (MS 888/2021, CONAMA 430/2011, CONAMA 357/2005) conforme o tipo de amostra. Status individual: CONFORME | NÃO CONFORME | ATENÇÃO.
✅ ROTA VERDE — Conformidade OK
Todos os parâmetros dentro dos limites legais. Sistema atualiza status para 'APROVADO', gera resumo narrativo automático, agenda próxima coleta conforme periodicidade contratada e notifica cliente no dashboard.
🚨 ROTA VERMELHA — Inconformidade Crítica Detectada
Um ou mais parâmetros violam a norma. Sistema: (a) atualiza laudo para 'NÃO CONFORME', (b) cria registro de Incidente Hídrico vinculado, (c) gera hash de auditoria SHA-256 imutável para fins jurídicos.
🔔 NOTIFICAÇÃO PRIORITÁRIA — Fila do Biólogo
Sistema empurra notificação em tempo real (WebSocket) para o Biólogo responsável com: lista dos parâmetros violados, valores vs limites legais, sugestão automática de intervenção gerada pelo LLM e prazo de resposta de 24h.
🔬 BANCO TÉCNICO — Revisão do Biólogo (Painel Técnico)
Biólogo acessa o Painel Técnico do sistema, visualiza parâmetros violados, contexto histórico do poço/ETE, laudos anteriores e a sugestão de intervenção da IA. Pode: Validar sugestão, ajustar recomendação ou escalar para Engenheiro Ambiental.
✍️ BANCO TÉCNICO — Emissão de Parecer Técnico Formal
Biólogo/Engenheiro redige parecer técnico no sistema, define medidas corretivas específicas (tratamento, coleta de emergência, interdição parcial), aplica assinatura digital (SHA-256 + timestamp + CRBio/CREA) e assina digitalmente o documento.
📋 SISTEMA — Atualização e Rastreabilidade
Sistema atualiza o Incidente para 'EM_REMEDIAÇÃO', registra o hash de auditoria do parecer, atualiza o laudo para 'APROVADO_COM_RESSALVAS' e armazena trilha de auditoria completa (imutável) no banco.
📣 NOTIFICAÇÃO CLIENTE — Resultado da Mediação
Cliente recebe no dashboard: parecer técnico completo, medidas corretivas priorizadas (Plano de Ação 5W2H), prazo de reimplementação e nova data de coleta para reavaliação. Sistema agenda nova coleta automática.
🌐 GOV GATEWAY — Notificação Compulsória (se necessário)
Se gravidade = CRÍTICO ou EMERGENCIAL: sistema aciona automaticamente o órgão ambiental competente (CETESB/DAEE/IBAMA) via API com o payload assinado digitalmente pelo Certificado A1, anexando o laudo inconformidade e o parecer técnico.
✅ ENCERRAMENTO — Incidente Resolvido
Após nova coleta e novo laudo com conformidade confirmada, o Biólogo encerra o incidente no sistema. Status final: 'RESOLVIDO'. Hash de auditoria final gerado, cliente recebe certificado de regularidade ambiental digital.
📊 SLAs do Fluxo
| Etapa | Responsável | SLA | Escalação |
|---|---|---|---|
| OCR + Validação IA | Sistema Automático | < 3 min | Retry automático 3x |
| Notificação ao Biólogo | Sistema Automático | < 30 seg | Push + E-mail + WhatsApp |
| Parecer Técnico Biólogo | Biólogo | 24 horas | Escala para Engenheiro |
| Parecer Técnico Engenheiro | Eng. Ambiental | 48 horas | Alerta ao coordenador |
| Notificação ao Cliente | Sistema Automático | < 1 min após parecer | — |
| Transmissão GOV (crítico) | Sistema Automático | < 2 horas | Retry + alerta admin |