

Case real: implementação de pipelines de IA em um órgão público, desafios únicos de compliance, LGPD, legado e os resultados que alcançamos.
Implementar IA em um órgão público é diferente de qualquer outro contexto. Há restrições de compliance, sistemas legados de décadas, processos burocráticos e — o mais desafiador — a necessidade de garantir que a IA não cometa erros em decisões que afetam cidadãos. Neste artigo, compartilho nossa jornada no Detran-RJ.
O Detran-RJ processa milhões de transações por ano: habilitações, registros de veículos, infrações, contratos de fornecedores. Muito desse trabalho era manual, repetitivo e propenso a erros humanos.
Em 2019, comecei a introduzir Python e automação. Em 2022, com a maturação dos LLMs, começamos a experimentar GenAI para casos de uso específicos.
O Problema: A equipe jurídica precisava analisar centenas de contratos por mês para identificar cláusulas problemáticas, prazos de vencimento e obrigações. Processo manual de 2-3 horas por contrato.
A Solução: Pipeline RAG com PGvector + Claude para análise automática.
from langchain_anthropic import ChatAnthropic
from langchain_community.vectorstores import PGVector
from langchain.chains import RetrievalQA
# Indexação de contratos
def index_contract(contract_pdf: bytes, contract_id: str):
text = extract_text_from_pdf(contract_pdf)
chunks = semantic_chunker.split_text(text)
documents = [
Document(
page_content=chunk,
metadata={
"contract_id": contract_id,
"chunk_index": i,
"indexed_at": datetime.now().isoformat(),
}
)
for i, chunk in enumerate(chunks)
]
vectorstore.add_documents(documents)
# Análise de contrato
async def analyze_contract(contract_id: str) -> ContractAnalysis:
retriever = vectorstore.as_retriever(
search_kwargs={
"filter": {"contract_id": contract_id},
"k": 10,
}
)
qa_chain = RetrievalQA.from_chain_type(
llm=ChatAnthropic(model="claude-3-5-sonnet-20241022"),
retriever=retriever,
return_source_documents=True,
)
analysis = await qa_chain.ainvoke({
"query": ANALYSIS_PROMPT
})
return parse_analysis(analysis["result"])
from langchain_anthropic import ChatAnthropic
from langchain_community.vectorstores import PGVector
from langchain.chains import RetrievalQA
# Indexação de contratos
def index_contract(contract_pdf: bytes, contract_id: str):
text = extract_text_from_pdf(contract_pdf)
chunks = semantic_chunker.split_text(text)
documents = [
Document(
page_content=chunk,
metadata={
"contract_id": contract_id,
"chunk_index": i,
"indexed_at": datetime.now().isoformat(),
}
)
for i, chunk in enumerate(chunks)
]
vectorstore.add_documents(documents)
# Análise de contrato
async def analyze_contract(contract_id: str) -> ContractAnalysis:
retriever = vectorstore.as_retriever(
search_kwargs={
"filter": {"contract_id": contract_id},
"k": 10,
}
)
qa_chain = RetrievalQA.from_chain_type(
llm=ChatAnthropic(model="claude-3-5-sonnet-20241022"),
retriever=retriever,
return_source_documents=True,
)
analysis = await qa_chain.ainvoke({
"query": ANALYSIS_PROMPT
})
return parse_analysis(analysis["result"])
Resultado: Redução de 80% no tempo de análise inicial. A equipe jurídica agora usa o sistema para triagem e foca seu tempo nas cláusulas flagradas como críticas.
O Problema: Servidores frequentemente tinham dúvidas sobre procedimentos internos, normas e regulamentos. O setor de RH e jurídico era sobrecarregado com perguntas repetitivas.
A Solução: Chatbot RAG com base de conhecimento dos manuais e normas internas.
SYSTEM_PROMPT = """
Você é um assistente interno do Detran-RJ especializado em procedimentos e normas.
IMPORTANTE:
- Responda APENAS com base nos documentos fornecidos
- Se não souber a resposta, diga claramente que não encontrou a informação
- Sempre cite o documento e seção de onde veio a informação
- Não faça suposições sobre procedimentos não documentados
- Para questões legais complexas, sempre recomende consulta ao setor jurídico
Contexto dos documentos:
{context}
"""
SYSTEM_PROMPT = """
Você é um assistente interno do Detran-RJ especializado em procedimentos e normas.
IMPORTANTE:
- Responda APENAS com base nos documentos fornecidos
- Se não souber a resposta, diga claramente que não encontrou a informação
- Sempre cite o documento e seção de onde veio a informação
- Não faça suposições sobre procedimentos não documentados
- Para questões legais complexas, sempre recomende consulta ao setor jurídico
Contexto dos documentos:
{context}
"""
Resultado: 60% de redução nas consultas ao RH para dúvidas de procedimento. Tempo médio de resposta: 30 segundos vs. 2 horas anteriormente.
O maior desafio foi garantir conformidade com a LGPD. Implementamos:
class LGPDCompliantProcessor:
SENSITIVE_FIELDS = ["cpf", "rg", "nome_completo", "endereco", "telefone"]
def anonymize_for_llm(self, data: dict) -> dict:
"""Remove/mascara dados pessoais antes de enviar ao LLM"""
anonymized = data.copy()
for field in self.SENSITIVE_FIELDS:
if field in anonymized:
anonymized[field] = self._mask(anonymized[field], field)
return anonymized
def _mask(self, value: str, field_type: str) -> str:
if field_type == "cpf":
return f"***.***.{value[-6:-3]}-**"
elif field_type == "nome_completo":
parts = value.split()
return f"{parts[0]} {'*' * len(parts[-1])}"
return "***REDACTED***"
class LGPDCompliantProcessor:
SENSITIVE_FIELDS = ["cpf", "rg", "nome_completo", "endereco", "telefone"]
def anonymize_for_llm(self, data: dict) -> dict:
"""Remove/mascara dados pessoais antes de enviar ao LLM"""
anonymized = data.copy()
for field in self.SENSITIVE_FIELDS:
if field in anonymized:
anonymized[field] = self._mask(anonymized[field], field)
return anonymized
def _mask(self, value: str, field_type: str) -> str:
if field_type == "cpf":
return f"***.***.{value[-6:-3]}-**"
elif field_type == "nome_completo":
parts = value.split()
return f"{parts[0]} {'*' * len(parts[-1])}"
return "***REDACTED***"
Muitos sistemas do Detran têm décadas e usam tecnologias antigas (COBOL, Delphi, Oracle Forms). A integração exigiu:
# Wrapper para sistema legado via API REST intermediária
class LegacySystemAdapter:
async def get_vehicle_data(self, placa: str) -> VehicleData:
# Sistema legado expõe dados via endpoint REST customizado
response = await self.http_client.get(
f"{LEGACY_API_URL}/veiculos/{placa}",
headers={"Authorization": f"Bearer {LEGACY_TOKEN}"},
timeout=30.0,
)
# Transforma formato legado para modelo moderno
raw = response.json()
return VehicleData(
placa=raw["VCL_PLACA"],
chassi=raw["VCL_CHASSI"],
proprietario=raw["PROP_NOME"],
# ... mapeamento de campos
)
# Wrapper para sistema legado via API REST intermediária
class LegacySystemAdapter:
async def get_vehicle_data(self, placa: str) -> VehicleData:
# Sistema legado expõe dados via endpoint REST customizado
response = await self.http_client.get(
f"{LEGACY_API_URL}/veiculos/{placa}",
headers={"Authorization": f"Bearer {LEGACY_TOKEN}"},
timeout=30.0,
)
# Transforma formato legado para modelo moderno
raw = response.json()
return VehicleData(
placa=raw["VCL_PLACA"],
chassi=raw["VCL_CHASSI"],
proprietario=raw["PROP_NOME"],
# ... mapeamento de campos
)
Toda decisão assistida por IA precisa ser auditável:
@dataclass
class AIDecisionLog:
decision_id: str
timestamp: datetime
user_id: str
input_data: dict # Anonimizado
llm_response: str
final_decision: str
human_override: bool
override_reason: str | None
async def log_ai_decision(decision: AIDecisionLog):
await audit_db.insert("ai_decisions", asdict(decision))
# Notifica supervisor para decisões de alto impacto
if decision.final_decision in HIGH_IMPACT_DECISIONS:
await notify_supervisor(decision)
@dataclass
class AIDecisionLog:
decision_id: str
timestamp: datetime
user_id: str
input_data: dict # Anonimizado
llm_response: str
final_decision: str
human_override: bool
override_reason: str | None
async def log_ai_decision(decision: AIDecisionLog):
await audit_db.insert("ai_decisions", asdict(decision))
# Notifica supervisor para decisões de alto impacto
if decision.final_decision in HIGH_IMPACT_DECISIONS:
await notify_supervisor(decision)
Após 2 anos de implementação gradual:
| Processo | Antes | Depois | Redução |
|---|---|---|---|
| Análise de contrato | 3h | 35min | 81% |
| Resposta a dúvidas internas | 2h | 30s | 99% |
| Triagem de documentos | 45min | 8min | 82% |
| Geração de relatórios | 4h | 20min | 92% |
1. Comece com processos internos, não cidadão-facing: é mais fácil iterar sem impacto direto no público.
2. Compliance primeiro: LGPD, auditoria e rastreabilidade não são opcionais. Construa desde o início.
3. Humano no loop é obrigatório: IA como assistente, não substituto. Toda decisão importante precisa de validação humana.
4. Documente tudo: em setor público, a documentação é parte do produto.
5. Treine a equipe: a resistência à mudança é o maior obstáculo, não a tecnologia.
Implementar GenAI no setor público é mais difícil do que no privado, mas o impacto pode ser enorme. A chave é começar pequeno, medir rigorosamente e expandir gradualmente. O Detran-RJ ainda está no início dessa jornada, mas os resultados já são visíveis.
Moises Costa é Senior Python Developer no Detran-RJ (DTIC) desde 2014. Conecte-se no LinkedIn.