Integrações de IA com Sistemas Legados: de COBOL a APIs modernas sem reescrever tudo

Como integrar IA com ERP antigo, mainframe e sistemas de 20 anos atrás. Middleware, APIs e wrappers práticos.

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:

  1. Contratar consultoria para reescrever sistema → Orçamento R$ 1,2M (inviável)
  2. 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étricaAntesDepoisVariação
Tempo/pedido8min35s-93%
Taxa de erro12%1,8%-85%
Pedidos/dia45180+300%
Atendentes31-67% custo
Satisfação cliente6,2/108,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

  1. Agendar call com equipe de TI (mapear sistemas legados)
  2. Listar 3 processos manuais mais dolorosos
  3. Prototipar middleware (1 endpoint simples)
  4. Testar integração IA + middleware (caso de uso piloto)
  5. 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.

Pronto para sair do manual?

Agende o diagnóstico gratuito. Vamos mapear o gargalo, estimar o impacto e definir o primeiro resultado mensurável.

Você sai com clareza — não com um pitch de vendas.