Sistemas legados = prisão tecnológica. Seu ERP tem 15 anos, funciona, mas não conversa com nada moderno.
Realidade brasileira:
- 68% das empresas médias usam ERP de 2010-2015
- 34% ainda têm processos em sistemas desktop (sem API)
- Custo de reescrever: R$ 500K-2M + 18-36 meses
- Risco: 72% dos projetos de migração falham ou atrasam
A solução: Integrar IA sem reescrever sistema legado.
Este artigo mostra arquitetura real de integração IA + legado.
O problema dos sistemas legados
Caso real: Distribuidora de autopeças (320 funcionários, R$ 85M/ano).
Stack tecnológico:
- ERP Protheus (Totvs) de 2012
- Sistema de estoque em Delphi desktop
- Planilhas Excel para controle de vendas
- Email como sistema de pedidos
Dores:
- Cliente faz pedido por WhatsApp → Atendente digita manualmente no sistema
- Tempo médio: 8min/pedido (poderia ser 30s com IA)
- Taxa de erro: 12% (produto errado, quantidade errada)
- Custo operacional: R$ 42K/mês (3 atendentes full-time)
Tentaram resolver:
- Contratar consultoria para reescrever sistema → Orçamento R$ 1,2M (inviável)
- Contratar dev para criar integração → 6 meses sem resultado (sistema muito complexo)
Solução implementada: IA como camada intermediária.
Arquitetura: IA + Legado
Conceito: IA não acessa sistema legado diretamente. Usa middleware (camada intermediária).
WhatsApp → IA (GPT-4) → Middleware (API) → Sistema Legado (ERP)
↓
Valida dados
Formata pedido
Registra no banco
Por que middleware:
- Sistema legado não tem API REST (só banco SQL ou telas desktop)
- IA precisa validar antes de gravar (evitar dados errados)
- Middleware traduz “linguagem IA” para “linguagem legado”
Tipos de integração
Tipo 1: Integração via banco de dados (SQL)
Quando usar: Sistema legado tem banco SQL (MySQL, SQL Server, Postgres).
Arquitetura:
// lib/legacy-integration/sql-wrapper.ts
import { createPool } from 'mysql2/promise'
// Pool de conexão com ERP (read-only para segurança)
const erpPool = createPool({
host: process.env.ERP_DB_HOST,
user: process.env.ERP_DB_USER_READONLY,
password: process.env.ERP_DB_PASSWORD,
database: 'erp_producao',
connectionLimit: 10
})
export async function buscarProduto(codigoOuDescricao: string) {
const [rows] = await erpPool.execute(`
SELECT
codigo,
descricao,
preco_venda,
estoque_disponivel,
unidade
FROM produtos
WHERE
codigo = ?
OR descricao LIKE ?
LIMIT 10
`, [codigoOuDescricao, `%${codigoOuDescricao}%`])
return rows
}
export async function criarPedido(pedidoData: {
clienteId: string
itens: Array<{ produtoId: string, quantidade: number }>
observacoes?: string
}) {
// Validações antes de gravar
const cliente = await buscarCliente(pedidoData.clienteId)
if (!cliente) throw new Error('Cliente não encontrado')
// Verifica estoque de todos itens
for (const item of pedidoData.itens) {
const produto = await buscarProduto(item.produtoId)
if (produto.estoque_disponivel < item.quantidade) {
throw new Error(`Estoque insuficiente: ${produto.descricao}`)
}
}
// Inicia transação
const connection = await erpPool.getConnection()
await connection.beginTransaction()
try {
// Cria cabeçalho do pedido
const [result] = await connection.execute(`
INSERT INTO pedidos (cliente_id, data_pedido, status, observacoes)
VALUES (?, NOW(), 'PENDENTE', ?)
`, [pedidoData.clienteId, pedidoData.observacoes || null])
const pedidoId = result.insertId
// Cria itens do pedido
for (const item of pedidoData.itens) {
await connection.execute(`
INSERT INTO pedidos_itens (pedido_id, produto_id, quantidade)
VALUES (?, ?, ?)
`, [pedidoId, item.produtoId, item.quantidade])
// Reserva estoque
await connection.execute(`
UPDATE produtos
SET estoque_disponivel = estoque_disponivel - ?
WHERE id = ?
`, [item.quantidade, item.produtoId])
}
await connection.commit()
return { pedidoId, status: 'CRIADO' }
} catch (error) {
await connection.rollback()
throw error
} finally {
connection.release()
}
}
⚠️ Cuidados:
- NUNCA dê acesso de escrita direto para IA (risco de dados corrompidos)
- Use view read-only para consultas
- Todas escritas passam por validação no middleware
- Log todas operações (auditoria)
Tipo 2: Integração via RPA (Robotic Process Automation)
Quando usar: Sistema legado não tem banco acessível (sistema desktop antigo).
Exemplo: Sistema de estoque em Delphi (1998) sem API, sem banco externo.
Ferramentas:
- Puppeteer (automação de navegadores)
- AutoIt (automação Windows desktop)
- UiPath (RPA comercial, R$ 5K/mês)
Arquitetura:
// lib/legacy-integration/rpa-wrapper.ts
import puppeteer from 'puppeteer'
export async function consultarEstoqueLegado(produtoId: string) {
// Sistema legado exposto via Citrix ou VNC web
const browser = await puppeteer.launch({
headless: true,
args: ['--no-sandbox']
})
const page = await browser.newPage()
try {
// 1. Faz login no sistema
await page.goto('http://sistema-legado.empresa.local/login')
await page.type('#usuario', process.env.LEGACY_USER)
await page.type('#senha', process.env.LEGACY_PASSWORD)
await page.click('#btnLogin')
await page.waitForNavigation()
// 2. Navega até tela de estoque
await page.click('#menuEstoque')
await page.click('#submenuConsulta')
// 3. Busca produto
await page.type('#campoBusca', produtoId)
await page.click('#btnBuscar')
await page.waitForSelector('.resultado')
// 4. Extrai dados da tela
const estoque = await page.evaluate(() => {
const descricao = document.querySelector('.produto-nome')?.textContent
const quantidade = document.querySelector('.estoque-qtd')?.textContent
const preco = document.querySelector('.produto-preco')?.textContent
return {
descricao,
quantidade: parseInt(quantidade || '0'),
preco: parseFloat(preco?.replace('R$', '').replace(',', '.') || '0')
}
})
return estoque
} finally {
await browser.close()
}
}
Prós:
- Funciona com qualquer sistema (até MS-DOS)
- Não precisa acesso ao código ou banco
Contras:
- Lento (3-5s por consulta vs 50ms SQL)
- Frágil (quebra se mudarem layout da tela)
- Requer manutenção constante
Tipo 3: Integração via API Gateway (REST wrapper)
Quando usar: Sistema legado tem API SOAP (antiga) ou protocolo proprietário.
Ferramentas:
- Kong (API gateway open-source)
- AWS API Gateway (R$ 100-500/mês)
- Mulesoft (R$ 8K+/mês, enterprise)
Arquitetura:
// api/routes/legacy-wrapper.ts
import { Router } from 'express'
import soap from 'soap'
const router = Router()
// Cliente SOAP do ERP
const erpSoapClient = await soap.createClientAsync(
'http://erp.empresa.local/webservice?wsdl'
)
// Endpoint moderno (REST) que chama SOAP antigo
router.post('/api/pedidos', async (req, res) => {
try {
const { clienteId, itens, observacoes } = req.body
// Valida payload
if (!clienteId || !itens?.length) {
return res.status(400).json({ error: 'Dados inválidos' })
}
// Converte para formato SOAP (XML)
const soapRequest = {
PedidoRequest: {
Cliente: { Id: clienteId },
Itens: {
Item: itens.map(item => ({
ProdutoId: item.produtoId,
Quantidade: item.quantidade
}))
},
Observacoes: observacoes
}
}
// Chama SOAP
const soapResponse = await erpSoapClient.CriarPedidoAsync(soapRequest)
// Converte resposta SOAP para JSON moderno
const pedidoId = soapResponse[0].PedidoResponse.PedidoId
res.json({
pedidoId,
status: 'CRIADO',
mensagem: 'Pedido criado com sucesso'
})
} catch (error) {
console.error('Erro ao criar pedido:', error)
res.status(500).json({ error: 'Erro ao processar pedido' })
}
})
router.get('/api/produtos/:id', async (req, res) => {
const { id } = req.params
try {
const soapResponse = await erpSoapClient.ConsultarProdutoAsync({
ProdutoRequest: { Id: id }
})
const produto = soapResponse[0].ProdutoResponse
res.json({
id: produto.Id,
descricao: produto.Descricao,
preco: parseFloat(produto.Preco),
estoque: parseInt(produto.Estoque)
})
} catch (error) {
res.status(404).json({ error: 'Produto não encontrado' })
}
})
export default router
Vantagens:
- IA usa API REST moderna (fácil)
- Centraliza lógica de conversão
- Cache para reduzir chamadas ao legado
IA como orquestrador
Fluxo completo: WhatsApp → IA → Middleware → ERP.
// lib/ai/order-agent.ts
import Anthropic from '@anthropic-ai/sdk'
import { buscarProduto, criarPedido } from '../legacy-integration/sql-wrapper'
const anthropic = new Anthropic({
apiKey: process.env.ANTHROPIC_API_KEY
})
const tools = [
{
name: 'buscar_produto',
description: 'Busca produto no ERP por código ou nome',
input_schema: {
type: 'object',
properties: {
termo_busca: {
type: 'string',
description: 'Código ou parte do nome do produto'
}
},
required: ['termo_busca']
}
},
{
name: 'criar_pedido',
description: 'Cria pedido no ERP após validação',
input_schema: {
type: 'object',
properties: {
cliente_id: { type: 'string' },
itens: {
type: 'array',
items: {
type: 'object',
properties: {
produto_id: { type: 'string' },
quantidade: { type: 'number' }
}
}
},
observacoes: { type: 'string' }
},
required: ['cliente_id', 'itens']
}
}
]
export async function processarPedidoWhatsApp(
clienteId: string,
mensagem: string
) {
const messages = [
{
role: 'user' as const,
content: `Cliente ${clienteId} enviou pedido via WhatsApp:
"${mensagem}"
Sua tarefa:
1. Extrair produtos e quantidades
2. Buscar cada produto no ERP para validar
3. Criar pedido se tudo estiver correto
4. Responder cliente confirmando ou informando erros`
}
]
let response = await anthropic.messages.create({
model: 'claude-3-5-sonnet-20241022',
max_tokens: 4096,
tools,
messages
})
// Loop de tool calling
while (response.stop_reason === 'tool_use') {
const toolUse = response.content.find(block => block.type === 'tool_use')
if (!toolUse) break
let toolResult
if (toolUse.name === 'buscar_produto') {
const produtos = await buscarProduto(toolUse.input.termo_busca)
toolResult = {
produtos: produtos.map(p => ({
id: p.codigo,
descricao: p.descricao,
preco: p.preco_venda,
estoque: p.estoque_disponivel
}))
}
}
if (toolUse.name === 'criar_pedido') {
try {
const resultado = await criarPedido({
clienteId,
itens: toolUse.input.itens,
observacoes: toolUse.input.observacoes
})
toolResult = {
sucesso: true,
pedido_id: resultado.pedidoId,
mensagem: 'Pedido criado com sucesso'
}
} catch (error) {
toolResult = {
sucesso: false,
erro: error.message
}
}
}
messages.push(
{ role: 'assistant', content: response.content },
{
role: 'user',
content: [{
type: 'tool_result',
tool_use_id: toolUse.id,
content: JSON.stringify(toolResult)
}]
}
)
response = await anthropic.messages.create({
model: 'claude-3-5-sonnet-20241022',
max_tokens: 4096,
tools,
messages
})
}
// Extrai resposta final
const textBlock = response.content.find(block => block.type === 'text')
return textBlock?.text || 'Erro ao processar pedido'
}
Exemplo de uso:
// Webhook do WhatsApp
app.post('/webhook/whatsapp', async (req, res) => {
const { from, body } = req.body.messages[0]
// Busca clienteId pelo telefone
const cliente = await buscarClientePorTelefone(from)
if (!cliente) {
await enviarWhatsApp(from, 'Telefone não cadastrado. Entre em contato com nosso suporte.')
return res.sendStatus(200)
}
// Processa pedido com IA
const resposta = await processarPedidoWhatsApp(cliente.id, body)
// Envia resposta
await enviarWhatsApp(from, resposta)
res.sendStatus(200)
})
Conversa real:
Cliente: "Preciso de 20 pastilhas de freio do Gol G5 e 10 discos"
IA: 🔍 Encontrei:
• Pastilha freio Gol G5 dianteira - R$ 89,90 (estoque: 45)
• Disco freio Gol G5 dianteiro - R$ 120,00 (estoque: 22)
Confirma pedido?
20x Pastilhas (R$ 1.798,00)
10x Discos (R$ 1.200,00)
Total: R$ 2.998,00
Cliente: "Confirma"
IA: ✅ Pedido #8472 criado!
Total: R$ 2.998,00
Previsão separação: hoje 16h
Previsão entrega: amanhã 10h
Obrigado! 🚗
Tempo: 30 segundos (vs 8min manual).
Sincronização bidirecional
Problema: IA cria pedido, mas vendedor altera no ERP. Como manter sincronizado?
Solução: CDC (Change Data Capture) + webhooks.
// lib/sync/cdc-listener.ts
import { createPool } from 'mysql2/promise'
import { enviarWebhook } from './webhook-sender'
const pool = createPool({
host: process.env.ERP_DB_HOST,
user: process.env.ERP_DB_USER_READONLY,
database: 'erp_producao'
})
// Polling de mudanças (a cada 30s)
setInterval(async () => {
// Busca pedidos modificados nos últimos 30s
const [rows] = await pool.execute(`
SELECT
p.id,
p.cliente_id,
p.status,
p.data_modificacao,
GROUP_CONCAT(
CONCAT(pi.produto_id, ':', pi.quantidade)
) as itens
FROM pedidos p
LEFT JOIN pedidos_itens pi ON p.id = pi.pedido_id
WHERE p.data_modificacao >= DATE_SUB(NOW(), INTERVAL 30 SECOND)
GROUP BY p.id
`)
for (const row of rows) {
// Envia webhook para sistema IA
await enviarWebhook('https://api.empresa.com/webhooks/pedido-atualizado', {
evento: 'PEDIDO_ATUALIZADO',
pedidoId: row.id,
clienteId: row.cliente_id,
status: row.status,
itens: row.itens.split(',').map(item => {
const [produtoId, quantidade] = item.split(':')
return { produtoId, quantidade: parseInt(quantidade) }
})
})
console.log(`Sincronizado pedido ${row.id}`)
}
}, 30000) // 30s
Alternativa melhor: Triggers no banco.
-- Cria trigger para detectar mudanças
CREATE TRIGGER pedidos_after_update
AFTER UPDATE ON pedidos
FOR EACH ROW
BEGIN
-- Grava mudança em tabela de eventos
INSERT INTO eventos_sync (
tabela,
registro_id,
tipo_evento,
dados_antigos,
dados_novos,
data_evento
) VALUES (
'pedidos',
NEW.id,
'UPDATE',
JSON_OBJECT(
'status', OLD.status,
'observacoes', OLD.observacoes
),
JSON_OBJECT(
'status', NEW.status,
'observacoes', NEW.observacoes
),
NOW()
);
END;
// Worker processa eventos
setInterval(async () => {
const [eventos] = await pool.execute(`
SELECT * FROM eventos_sync
WHERE processado = 0
ORDER BY data_evento ASC
LIMIT 100
`)
for (const evento of eventos) {
await enviarWebhook(WEBHOOK_URL, evento)
await pool.execute(
'UPDATE eventos_sync SET processado = 1 WHERE id = ?',
[evento.id]
)
}
}, 5000) // 5s
Case real: Distribuidora de autopeças
Implementação (8 semanas):
Semanas 1-2: Descoberta
- Mapeamento de tabelas do ERP (150 tabelas)
- Identificação de 12 tabelas críticas (produtos, clientes, pedidos)
- Documentação de regras de negócio (validações, estoque)
Semanas 3-4: Middleware
- API REST wrapper (Node.js + Express)
- Conexão read-only com banco ERP
- Funções: buscar produto, buscar cliente, criar pedido
Semanas 5-6: IA + Tool Calling
- Agente IA com Claude (function calling)
- Integração WhatsApp Business API
- Testes com 5 clientes piloto
Semanas 7-8: Produção
- Deploy AWS (ECS Fargate)
- Monitoramento (DataDog)
- Rollout para 100% dos clientes
Resultados (6 meses):
| Métrica | Antes | Depois | Variação |
|---|---|---|---|
| Tempo/pedido | 8min | 35s | -93% |
| Taxa de erro | 12% | 1,8% | -85% |
| Pedidos/dia | 45 | 180 | +300% |
| Atendentes | 3 | 1 | -67% custo |
| Satisfação cliente | 6,2/10 | 8,9/10 | +44% |
Investimento:
- Desenvolvimento: R$ 48K (8 semanas × R$ 6K)
- Infraestrutura: R$ 800/mês (AWS)
- APIs (Anthropic + WhatsApp): R$ 1.200/mês
- Total primeiro ano: R$ 72K
ROI:
- Economia atendentes: R$ 28K/mês (2 atendentes × R$ 14K)
- Payback: 2,6 meses
- ROI anual: 367%
Armadilhas comuns
1. Acessar legado direto da IA
Errado:
// IA tem acesso direto ao banco ❌
const connection = await mysql.createConnection({
host: 'erp-db',
user: 'root', // Acesso total 😱
password: 'senha123'
})
// IA poderia executar: DROP TABLE pedidos 💥
Certo:
// IA chama API intermediária ✅
const response = await fetch('https://api.empresa.com/pedidos', {
method: 'POST',
body: JSON.stringify(pedido)
})
2. Não validar antes de gravar
Errado:
// Grava direto sem validar ❌
await criarPedido(pedidoIA)
Certo:
// Valida antes de gravar ✅
const erros = validarPedido(pedidoIA)
if (erros.length mais de 0) {
return { erro: erros.join(', ') }
}
await criarPedido(pedidoIA)
3. Não ter rollback
Errado:
// Sem transação ❌
await criarPedidoCabecalho(pedido)
await criarPedidoItens(pedido.itens) // Falha aqui = pedido incompleto
await atualizarEstoque(pedido.itens)
Certo:
// Com transação ✅
const conn = await pool.getConnection()
await conn.beginTransaction()
try {
await conn.execute('INSERT INTO pedidos...')
await conn.execute('INSERT INTO pedidos_itens...')
await conn.execute('UPDATE produtos SET estoque...')
await conn.commit()
} catch (error) {
await conn.rollback()
throw error
} finally {
conn.release()
}
Ferramentas úteis
1. DBeaver (SQL client grátis):
- Conecta em qualquer banco (MySQL, SQL Server, Oracle, Postgres)
- Visualiza schema (tabelas, colunas, relacionamentos)
- Testa queries antes de implementar
2. Postman (API testing):
- Testa endpoints do middleware
- Simula chamadas da IA
- Documenta API
3. Ngrok (túnel local → internet):
- Expõe sistema legado local para IA na nuvem
- Útil para desenvolvimento
- R$ 0 (tier gratuito)
# Expõe porta 3000 local para internet
ngrok http 3000
# Gera URL pública: https://abc123.ngrok.io
# IA chama: https://abc123.ngrok.io/api/pedidos
4. Redis (cache):
- Cacheia consultas frequentes ao legado
- Reduz latência 80-95%
- R$ 50-200/mês (AWS ElastiCache)
import Redis from 'ioredis'
const redis = new Redis(process.env.REDIS_URL)
export async function buscarProdutoComCache(produtoId: string) {
// Tenta cache primeiro
const cached = await redis.get(`produto:${produtoId}`)
if (cached) {
return JSON.parse(cached)
}
// Cache miss → busca no ERP
const produto = await buscarProdutoERP(produtoId)
// Grava cache (expira em 1h)
await redis.set(
`produto:${produtoId}`,
JSON.stringify(produto),
'EX',
3600
)
return produto
}
Checklist de implementação
Fase 1: Descoberta (1-2 semanas)
- Mapear sistema legado (banco, APIs, telas)
- Identificar tabelas/funções críticas
- Documentar regras de negócio
- Definir escopo (quais processos automatizar)
Fase 2: Middleware (2-4 semanas)
- Setup ambiente (Node.js + Express ou Python + FastAPI)
- Conexão com sistema legado (SQL/SOAP/RPA)
- Endpoints REST (buscar, criar, atualizar)
- Validações e tratamento de erros
- Testes unitários
Fase 3: IA (1-2 semanas)
- Escolher LLM (GPT-4, Claude, Gemini)
- Implementar tool calling (function calling)
- Prompt engineering (instruções para IA)
- Testes com casos reais
Fase 4: Integração (1 semana)
- Conectar fonte (WhatsApp, email, chat)
- Fluxo completo (entrada → IA → middleware → legado)
- Logs e monitoramento
- Testes de carga
Fase 5: Produção (1 semana)
- Deploy (AWS, Azure, GCP)
- Rollout gradual (piloto → 100%)
- Documentação para time
- Treinamento usuários
Total: 6-10 semanas (depende da complexidade do legado).
Investimento esperado
Desenvolvimento (premissas: dev sênior R$ 12K/mês):
- Middleware simples (SQL): R$ 18-30K (1,5-2,5 meses)
- Middleware complexo (SOAP/RPA): R$ 36-60K (3-5 meses)
- IA + integração: R$ 12-18K (1-1,5 meses)
Infraestrutura (mensal):
- Servidor API (AWS EC2 t3.small): R$ 150/mês
- Banco de dados (RDS MySQL): R$ 300/mês
- Redis cache: R$ 100/mês
- LLM APIs (Anthropic/OpenAI): R$ 500-2K/mês
- Total: R$ 1.050-2.550/mês
Total primeiro ano:
- Setup: R$ 30-78K
- Operação: R$ 12,6-30,6K
- Total: R$ 42,6-108,6K
ROI esperado:
- Economia de tempo: 60-80% (automação de processos manuais)
- Redução de erros: 70-90%
- Payback: 3-12 meses
Próximos passos
- Agendar call com equipe de TI (mapear sistemas legados)
- Listar 3 processos manuais mais dolorosos
- Prototipar middleware (1 endpoint simples)
- Testar integração IA + middleware (caso de uso piloto)
- Rollout gradual (10 usuários → 100%)
Lembre-se: Não precisa reescrever sistema legado. IA como camada intermediária resolve 80% dos casos por 20% do custo.