{
  "name": "WF5 - Intelig\u00eancia Normativa | JurisAI",
  "nodes": [
    {
      "id": "sticky_wf5",
      "name": "\ud83d\udccb WF5 - Intelig\u00eancia Normativa",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -220,
        -100
      ],
      "parameters": {
        "content": "## WF5 \u2014 Intelig\u00eancia Normativa\n\n**Trigger:** Schedule (toda segunda-feira \u00e0s 06:00 UTC)  \n**Objetivo:** Detectar conflitos normativos entre documentos recentes e o corpus existente\n\n**Fluxo:**\n1. Busca documentos ingeridos na \u00faltima semana\n2. Para cada novo documento:\n   - Busca documentos relacionados no Qdrant (por \u00e1rea/territory)\n   - Envia ao Claude Haiku para an\u00e1lise de conflitos\n   - Se conflito detectado \u2192 insere em normative_conflicts\n3. Notifica curadores por e-mail\n\n\u2699\ufe0f **Ap\u00f3s importar:**\n- Configure credenciais Anthropic e Supabase\n- Configure n\u00f3 de e-mail com seus dados SMTP",
        "height": 300,
        "width": 400,
        "color": 3
      }
    },
    {
      "id": "schedule_wf5",
      "name": "Trigger Semanal",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        250,
        300
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "weeks",
              "triggerAtDay": [
                1
              ],
              "triggerAtHour": 6,
              "triggerAtMinute": 0
            }
          ]
        }
      }
    },
    {
      "id": "get_recent_docs",
      "name": "Buscar Documentos Recentes",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        500,
        300
      ],
      "parameters": {
        "method": "GET",
        "url": "={{ $env.SUPABASE_URL }}/rest/v1/documents",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "apikey",
              "value": "={{ $env.SUPABASE_SERVICE_KEY }}"
            },
            {
              "name": "Authorization",
              "value": "=Bearer {{ $env.SUPABASE_SERVICE_KEY }}"
            }
          ]
        },
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "status",
              "value": "eq.em_vigor"
            },
            {
              "name": "ingested_at",
              "value": "gte.{{ new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString() }}"
            },
            {
              "name": "select",
              "value": "id,title,doc_type,legal_area,territory,uf,issuer,published_at,source_url"
            },
            {
              "name": "order",
              "value": "ingested_at.desc"
            },
            {
              "name": "limit",
              "value": "50"
            }
          ]
        }
      }
    },
    {
      "id": "check_has_docs",
      "name": "H\u00e1 Documentos Novos?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        750,
        300
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "cond_has",
              "leftValue": "={{ $input.all().length }}",
              "rightValue": 0,
              "operator": {
                "type": "number",
                "operation": "gt"
              }
            }
          ],
          "combinator": "and"
        }
      }
    },
    {
      "id": "split_docs",
      "name": "Processar um por vez",
      "type": "n8n-nodes-base.splitInBatches",
      "typeVersion": 3,
      "position": [
        1000,
        200
      ],
      "parameters": {
        "batchSize": 1,
        "options": {}
      }
    },
    {
      "id": "search_related",
      "name": "Buscar Docs Relacionados (Qdrant)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1250,
        200
      ],
      "parameters": {
        "method": "POST",
        "url": "={{ $env.QDRANT_URL }}/collections/{{ $env.QDRANT_COLLECTION }}/points/scroll",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "api-key",
              "value": "={{ $env.QDRANT_API_KEY }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "contentType": "json",
        "body": "={{ JSON.stringify({ filter: { must: [{ key: 'status', match: { value: 'em_vigor' } }, { key: 'legal_area', match: { any: $json.legal_area || [] } }] }, limit: 10, with_payload: true, with_vector: false }) }}"
      },
      "continueOnFail": true
    },
    {
      "id": "analyze_conflicts",
      "name": "Analisar Conflitos (Claude Haiku)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1500,
        200
      ],
      "parameters": {
        "method": "POST",
        "url": "https://api.anthropic.com/v1/messages",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "x-api-key",
              "value": "={{ $env.ANTHROPIC_API_KEY }}"
            },
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "contentType": "json",
        "body": "={{ JSON.stringify({ model: $env.LLM_TRIAGE, max_tokens: 1024, temperature: 0, system: 'Voc\u00ea \u00e9 um analisador jur\u00eddico especializado em detectar conflitos normativos no direito brasileiro. Analise os documentos fornecidos e identifique poss\u00edveis conflitos. Responda APENAS em JSON v\u00e1lido no formato: { \"has_conflict\": boolean, \"conflict_type\": \"contradicao\"|\"lacuna\"|\"obsolescencia\"|\"divergencia_jurisprudencial\"|\"oportunidade\"|null, \"severity\": \"baixa\"|\"media\"|\"alta\"|\"critica\"|null, \"description\": \"string\"|null }', messages: [{ role: 'user', content: `Documento novo: ${JSON.stringify($('Processar um por vez').first().json)}\n\nDocumentos relacionados no corpus: ${JSON.stringify(($('Buscar Docs Relacionados (Qdrant)').first().json?.result || []).map(r => r.payload).slice(0, 5))}\n\nH\u00e1 conflito normativo entre o documento novo e os existentes?` }] }) }}"
      },
      "continueOnFail": true
    },
    {
      "id": "parse_conflict",
      "name": "Parsear Resposta",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1750,
        200
      ],
      "parameters": {
        "jsCode": "const currentDoc = $('Processar um por vez').first().json;\nconst anthropicResponse = $('Analisar Conflitos (Claude Haiku)').first().json;\n\nlet analysis = { has_conflict: false, conflict_type: null, severity: null, description: null };\ntry {\n  const text = anthropicResponse.content?.[0]?.text || '{}';\n  // Extrair JSON da resposta\n  const jsonMatch = text.match(/\\{[\\s\\S]*\\}/);\n  if (jsonMatch) {\n    analysis = JSON.parse(jsonMatch[0]);\n  }\n} catch (e) {\n  console.log('Erro ao parsear resposta do Claude:', e.message);\n}\n\n// Buscar documentos relacionados para montar o conflito\nconst relatedDocs = ($('Buscar Docs Relacionados (Qdrant)').first().json?.result || []).map(r => r.payload);\nconst relatedDocId = relatedDocs[0]?.document_id || null;\n\nreturn [{\n  json: {\n    doc_a_id: currentDoc.id,\n    doc_a_title: currentDoc.title,\n    doc_b_id: relatedDocId,\n    has_conflict: analysis.has_conflict === true,\n    conflict_type: analysis.conflict_type || null,\n    severity: analysis.severity || 'baixa',\n    description: analysis.description || 'An\u00e1lise autom\u00e1tica WF5'\n  }\n}];"
      }
    },
    {
      "id": "if_conflict",
      "name": "Conflito Detectado?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        2000,
        200
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "cond_conflict",
              "leftValue": "={{ $json.has_conflict }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            },
            {
              "id": "cond_has_b",
              "leftValue": "={{ $json.doc_b_id }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "isNotEmpty"
              }
            }
          ],
          "combinator": "and"
        }
      }
    },
    {
      "id": "insert_conflict",
      "name": "Registrar Conflito",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2250,
        100
      ],
      "parameters": {
        "method": "POST",
        "url": "={{ $env.SUPABASE_URL }}/rest/v1/normative_conflicts",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "apikey",
              "value": "={{ $env.SUPABASE_SERVICE_KEY }}"
            },
            {
              "name": "Authorization",
              "value": "=Bearer {{ $env.SUPABASE_SERVICE_KEY }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Prefer",
              "value": "return=representation"
            }
          ]
        },
        "sendBody": true,
        "contentType": "json",
        "body": "={{ JSON.stringify({ document_a: $json.doc_a_id, document_b: $json.doc_b_id, conflict_type: $json.conflict_type, description: $json.description, severity: $json.severity, status: 'pendente', detected_by: 'wf5_auto' }) }}"
      },
      "continueOnFail": true
    },
    {
      "id": "no_conflict",
      "name": "Sem Conflito",
      "type": "n8n-nodes-base.noOp",
      "typeVersion": 1,
      "position": [
        2250,
        300
      ],
      "parameters": {}
    },
    {
      "id": "no_new_docs",
      "name": "Sem Documentos Novos",
      "type": "n8n-nodes-base.noOp",
      "typeVersion": 1,
      "position": [
        1000,
        400
      ],
      "parameters": {}
    },
    {
      "id": "get_conflicts_count",
      "name": "Contar Conflitos Detectados",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2500,
        200
      ],
      "parameters": {
        "method": "GET",
        "url": "={{ $env.SUPABASE_URL }}/rest/v1/normative_conflicts?status=eq.pendente&select=id",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "apikey",
              "value": "={{ $env.SUPABASE_SERVICE_KEY }}"
            },
            {
              "name": "Authorization",
              "value": "=Bearer {{ $env.SUPABASE_SERVICE_KEY }}"
            },
            {
              "name": "Prefer",
              "value": "count=exact"
            }
          ]
        }
      },
      "continueOnFail": true
    },
    {
      "id": "notify_curator",
      "name": "Notificar Curador",
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 2.1,
      "position": [
        2750,
        200
      ],
      "parameters": {
        "fromEmail": "jurisai@{{$env.ORG_NAME || 'jurisai.app'}}",
        "toEmail": "={{ $env.CURATOR_EMAIL || 'curador@suaorganizacao.com' }}",
        "subject": "[JurisAI] Relat\u00f3rio semanal: conflitos normativos detectados",
        "emailType": "text",
        "message": "=Relat\u00f3rio Semanal - JurisAI\n\nData: {{ new Date().toLocaleDateString('pt-BR') }}\n\nConflitos normativos pendentes de revis\u00e3o: verifique o painel do JurisAI.\n\nAcesse: {{ $env.WEBHOOK_URL }}\n\n---\nJurisAI - {{ $env.ORG_NAME }}"
      },
      "credentials": {
        "smtp": {
          "name": "<your credential>"
        }
      },
      "continueOnFail": true
    }
  ],
  "connections": {
    "Trigger Semanal": {
      "main": [
        [
          {
            "node": "Buscar Documentos Recentes",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Buscar Documentos Recentes": {
      "main": [
        [
          {
            "node": "H\u00e1 Documentos Novos?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "H\u00e1 Documentos Novos?": {
      "main": [
        [
          {
            "node": "Processar um por vez",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Sem Documentos Novos",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Processar um por vez": {
      "main": [
        [
          {
            "node": "Buscar Docs Relacionados (Qdrant)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Buscar Docs Relacionados (Qdrant)": {
      "main": [
        [
          {
            "node": "Analisar Conflitos (Claude Haiku)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Analisar Conflitos (Claude Haiku)": {
      "main": [
        [
          {
            "node": "Parsear Resposta",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parsear Resposta": {
      "main": [
        [
          {
            "node": "Conflito Detectado?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Conflito Detectado?": {
      "main": [
        [
          {
            "node": "Registrar Conflito",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Sem Conflito",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Registrar Conflito": {
      "main": [
        [
          {
            "node": "Contar Conflitos Detectados",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Contar Conflitos Detectados": {
      "main": [
        [
          {
            "node": "Notificar Curador",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "saveManualExecutions": false,
    "errorWorkflow": ""
  },
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "tags": [
    {
      "id": "jurisai",
      "name": "JurisAI"
    },
    {
      "id": "normativa",
      "name": "inteligencia-normativa"
    }
  ]
}