Script 1 de 2

Passo a passo: banco de dados

Guia de execução de sql/01_ampminsights_db_setup.sql, seção por seção, na ordem em que deve ser rodado.

Este roteiro acompanha sql/01_ampminsights_db_setup.sql seção por seção. Cada bloco abaixo é o código completo daquela seção — use o botão Copiar e cole direto no SQL Worksheet do Database Actions. Rode um bloco de cada vez, na ordem apresentada, e confira que não houve erro antes de seguir para o próximo.

Você só preenche valores uma vez

A partir da Seção 2, todo OCID, região, namespace e nome de bucket é digitado uma única vez, na tabela de configuração. Os blocos seguintes (credencial, modelo ONNX, SELECT AI) leem esses valores automaticamente — você não vai precisar colar o mesmo OCID de novo em nenhum outro lugar deste arquivo.

Seção 1 · Executar conectado como ADMIN do Autonomous Database

Conecte como ADMIN do Autonomous Database (Database Actions → SQL, logado como ADMIN). Este bloco cria o usuário/schema AMPMINSIGHTS, concede os privilégios de AI Vector Search / DBMS_CLOUD / SELECT AI / credenciais / jobs, habilita ORDS e cria a regra de rede (ACL) que autoriza o schema a chamar o OCI Generative AI.

Antes de copiar

Troque <SUBSTITUA_POR_UMA_SENHA_FORTE_SUA> por uma senha forte escolhida por você (mínimo 12 caracteres, com maiúsculas, minúsculas, número e caractere especial) antes de colar este bloco no Worksheet.

Seção 1 — copiar e colar
-- 1.1) Criar o schema/usuario da aplicacao.
--      Troque a senha abaixo por uma senha forte escolhida por voce ANTES de
--      rodar este comando (minimo 12 caracteres, com maiusculas, minusculas,
--      numero e caractere especial - regra padrao do Oracle Database).
CREATE USER AMPMINSIGHTS IDENTIFIED BY "<SUBSTITUA_POR_UMA_SENHA_FORTE_SUA>";

-- 1.2) Papeis basicos
GRANT CONNECT TO AMPMINSIGHTS;
GRANT RESOURCE TO AMPMINSIGHTS;
ALTER USER AMPMINSIGHTS DEFAULT ROLE CONNECT, RESOURCE;

-- 1.3) Habilitar REST (ORDS) para o schema e habilitar Data Sharing
BEGIN
    ORDS_ADMIN.ENABLE_SCHEMA(
        p_enabled             => TRUE,
        p_schema              => 'AMPMINSIGHTS',
        p_url_mapping_type    => 'BASE_PATH',
        p_url_mapping_pattern => 'ampminsights',
        p_auto_rest_auth      => TRUE
    );

    -- habilita o compartilhamento de dados usado por alguns recursos de AI/Vector
    C##ADP$SERVICE.DBMS_SHARE.ENABLE_SCHEMA(
        SCHEMA_NAME => 'AMPMINSIGHTS',
        ENABLED     => TRUE
    );
    COMMIT;
END;
/

-- 1.4) Quota de armazenamento no tablespace de dados
ALTER USER AMPMINSIGHTS QUOTA UNLIMITED ON "DATA";

-- 1.5) Privilegios usados pela aplicacao (AI Vector Search, DBMS_CLOUD,
--      SELECT AI, mineracao/embeddings, credenciais e jobs agendados)
GRANT EXECUTE ON dbms_vector_chain TO AMPMINSIGHTS;
GRANT EXECUTE ON dbms_cloud TO AMPMINSIGHTS;
GRANT EXECUTE ON dbms_cloud_ai TO AMPMINSIGHTS;
GRANT CONNECT TO AMPMINSIGHTS;
GRANT RESOURCE TO AMPMINSIGHTS;
GRANT CREATE MINING MODEL TO AMPMINSIGHTS;
GRANT CREATE CREDENTIAL TO AMPMINSIGHTS;
GRANT CREATE JOB TO AMPMINSIGHTS;

-- 1.6) Regras de rede (ACL) para o schema poder chamar o endpoint do OCI
--      Generative AI usado neste script (regiao us-chicago-1 por padrao - o
--      mesmo valor que vai para o parametro GENAI_REGION da SECAO 2, que
--      ainda nao existe neste ponto do script, por isso o host aqui e
--      digitado direto). Nem toda tenancy alcanca us-chicago-1 (ex.: algumas
--      tenancies com escopo regional restrito, como America Latina, podem
--      so alcancar sa-saopaulo-1) - se for o seu caso, troque o host abaixo
--      pela regiao que a sua tenancy alcanca, E use o mesmo valor no
--      parametro GENAI_REGION da SECAO 2 (repita este bloco para cada host
--      adicional que for necessario).
BEGIN
    DBMS_NETWORK_ACL_ADMIN.APPEND_HOST_ACE(
        host       => 'inference.generativeai.us-chicago-1.oci.oraclecloud.com',
        lower_port => 443,
        upper_port => 443,
        ace        => xs$ace_type(
            privilege_list => xs$name_list('connect'),
            principal_name => 'AMPMINSIGHTS',
            principal_type => xs_acl.ptype_db
        )
    );

    DBMS_NETWORK_ACL_ADMIN.APPEND_HOST_ACE(
        host => 'inference.generativeai.us-chicago-1.oci.oraclecloud.com',
        ace  => xs$ace_type(
            privilege_list => xs$name_list('resolve'),
            principal_name => 'AMPMINSIGHTS',
            principal_type => xs_acl.ptype_db
        )
    );
END;
/

-- 1.7) Conferir as regras de rede criadas
SELECT host, lower_port, upper_port, principal, privilege
FROM   dba_host_aces
WHERE  principal = 'AMPMINSIGHTS';



--------------------------------------------------------------------------------
-- A partir daqui, conecte-se como o usuario AMPMINSIGHTS (nao mais como ADMIN)
--------------------------------------------------------------------------------

Seção 2 · Tabela de configuracao (preencha os valores UMA UNICA VEZ aqui)

Agora conecte como o usuário AMPMINSIGHTS que você acabou de criar (não mais como ADMIN). Este bloco cria a tabela de configuração e insere, de uma vez, os 8 valores que os próximos blocos vão precisar. Veja em Pré-requisitos & credenciais onde obter cada um.

Antes de copiar

Substitua os 8 marcadores <SUBSTITUA_...> pelos valores reais do seu ambiente antes de colar. Depois de rodar, confira com o SELECT no final do bloco que nenhuma linha ainda mostra um marcador.

Seção 2 — copiar e colar
--
-- Esta e a UNICA seção onde voce vai colar OCIDs, regiao, namespace e bucket.
-- Todos os blocos seguintes (credencial OCI, modelo ONNX, SELECT AI, e o job
-- de geracao de insights criado pela propria aplicacao APEX) leem os valores
-- DAQUI automaticamente - voce nao precisa colar o mesmo OCID de novo em
-- nenhum outro lugar deste arquivo nem do aplicativo APEX.
--
-- Onde obter cada valor: ver docs/03-pre-requisitos-credenciais.html.
--
CREATE TABLE "TB_26AI_TECH_PARAMS"
   (	"PARAM_NAME"  VARCHAR2(100 CHAR)  NOT NULL ENABLE,
	"PARAM_VALUE" VARCHAR2(1000 CHAR) NOT NULL ENABLE,
	"DESCRIPTION" VARCHAR2(4000),
	 CONSTRAINT "TB_26AI_TECH_PARAMS_PK" PRIMARY KEY ("PARAM_NAME")
  USING INDEX ENABLE
   );

COMMENT ON TABLE "TB_26AI_TECH_PARAMS" IS 'Tabela de configuracao central da aplicacao ampmInsights - preencher uma unica vez.';

-- Preencha os 8 marcadores abaixo e rode o bloco UMA VEZ.
INSERT INTO TB_26AI_TECH_PARAMS (PARAM_NAME, PARAM_VALUE, DESCRIPTION) VALUES
  ('EMBEDDING_CREDENTIAL',  'OCI_CRED',
   'Nome da credencial que sera criada na SECAO 3. Nao precisa alterar este valor.');

INSERT INTO TB_26AI_TECH_PARAMS (PARAM_NAME, PARAM_VALUE, DESCRIPTION) VALUES
  ('EMBEDDING_COMPARTMENT', '<SUBSTITUA_PELO_OCID_DO_COMPARTIMENTO_DO_BANCO>',
   'OCID do compartimento OCI onde o Autonomous Database foi criado.');

INSERT INTO TB_26AI_TECH_PARAMS (PARAM_NAME, PARAM_VALUE, DESCRIPTION) VALUES
  ('OCI_TENANCY_OCID', '<SUBSTITUA_PELO_OCID_DO_TENANCY>',
   'OCID do tenancy OCI (campo "tenancy" da API key).');

INSERT INTO TB_26AI_TECH_PARAMS (PARAM_NAME, PARAM_VALUE, DESCRIPTION) VALUES
  ('OCI_USER_OCID', '<SUBSTITUA_PELO_OCID_DO_USUARIO_OCI>',
   'OCID do usuario OCI dono da API key (campo "user" da API key).');

INSERT INTO TB_26AI_TECH_PARAMS (PARAM_NAME, PARAM_VALUE, DESCRIPTION) VALUES
  ('OCI_FINGERPRINT', '<SUBSTITUA_PELA_FINGERPRINT_DA_API_KEY>',
   'Fingerprint da API key (campo "fingerprint" da API key).');

INSERT INTO TB_26AI_TECH_PARAMS (PARAM_NAME, PARAM_VALUE, DESCRIPTION) VALUES
  ('OCI_REGION', '<SUBSTITUA_PELA_SUA_REGIAO>',
   'Regiao OCI do seu tenancy, ex.: sa-saopaulo-1 (campo "region" da API key).');

INSERT INTO TB_26AI_TECH_PARAMS (PARAM_NAME, PARAM_VALUE, DESCRIPTION) VALUES
  ('OS_NAMESPACE', '<SUBSTITUA_PELO_SEU_NAMESPACE_DE_OBJECT_STORAGE>',
   'Namespace do Object Storage do seu tenancy (Console OCI > Storage > Buckets > "Namespace").');

INSERT INTO TB_26AI_TECH_PARAMS (PARAM_NAME, PARAM_VALUE, DESCRIPTION) VALUES
  ('OS_BUCKET', '<SUBSTITUA_PELO_NOME_DO_BUCKET_QUE_VOCE_VAI_CRIAR>',
   'Nome do bucket de Object Storage onde o modelo ONNX sera armazenado (ver SECAO 4).');

INSERT INTO TB_26AI_TECH_PARAMS (PARAM_NAME, PARAM_VALUE, DESCRIPTION) VALUES
  ('AI_LOG_RETENTION_DAYS', '90',
   'Dias de retencao do log de chamadas de IA/embeddings (TB_26AI_TECH_LOG) - ver docs/09-operacao-observabilidade.html para a query de purga.');

INSERT INTO TB_26AI_TECH_PARAMS (PARAM_NAME, PARAM_VALUE, DESCRIPTION) VALUES
  ('GENAI_REGION', 'us-chicago-1',
   'Regiao do OCI Generative AI usada pelo RAG/insights/embeddings (SECAO 5+). Pode ser diferente da OCI_REGION (que e a regiao "de casa" do seu tenancy/bucket) - o Generative AI so roda em regioes especificas. Se a sua tenancy nao alcancar us-chicago-1 (comum em tenancies com escopo regional restrito, ex. America Latina), troque para uma regiao que a sua tenancy alcance (ex. sa-saopaulo-1) E repita a ACL da SECAO 1.6 para o novo host - confirme antes com "oci generative-ai model-collection list-models" ou o Console OCI que o modelo usado (meta.llama-4-maverick-17b-128e-instruct-fp8) ainda esta ativo para uso on-demand na regiao escolhida, ja que a disponibilidade de modelos varia por regiao e muda com o tempo.');

COMMIT;

-- Confira: nenhuma linha abaixo deve ainda mostrar um valor "<SUBSTITUA...>"
-- antes de voce seguir para a SECAO 3.
SELECT param_name, param_value, description FROM TB_26AI_TECH_PARAMS ORDER BY param_name;

Seção 3 · Credencial de acesso a OCI (Generative AI + Object Storage)

Cria a credencial OCI_CRED, usada pelo banco para chamar o OCI Generative AI e o Object Storage. O bloco já lê tenancy/usuário/compartimento/fingerprint da tabela de configuração da Seção 2 — o único valor que você ainda precisa colar aqui é o conteúdo da chave privada (.pem), porque ela nunca é guardada em texto puro em nenhuma tabela.

Antes de copiar

Substitua o marcador da chave privada pelo conteúdo completo do seu arquivo .pem (incluindo as linhas BEGIN/END PRIVATE KEY). Depois de confirmar que a credencial foi criada (consulta no final do bloco), você pode apagar o arquivo .pem do seu computador com segurança.

Seção 3 — copiar e colar
--
-- Esta credencial (OCI_CRED) e o que permite o banco chamar o OCI Generative
-- AI (para gerar embeddings e texto/insights) e o OCI Object Storage (para
-- carregar o modelo ONNX). Ela usa autenticacao "API Signing Key" da OCI.
-- O bloco abaixo LE tenancy/usuario/compartimento/fingerprint automaticamente
-- da tabela de configuracao da SECAO 2 - voce so precisa colar a CHAVE
-- PRIVADA aqui (ela nao e guardada em nenhuma tabela; o proprio banco a
-- guarda de forma criptografada dentro da credencial).
--
-- PRE-REQUISITO (feito uma unica vez, no Console OCI, fora do banco):
--   Ver o passo a passo completo em docs/03-pre-requisitos-credenciais.html.
--   Resumo: Console OCI > usuario > User settings > API keys > Add API key >
--   Generate API key pair > Download private key > Add. Os 4 valores exibidos
--   (user, fingerprint, tenancy, region) vao para a SECAO 2 acima; a chave
--   privada baixada (.pem) vai direto no bloco abaixo.
--
--   IMPORTANTE (seguranca):
--     - A chave privada .pem equivale a uma senha: nao envie por e-mail/chat,
--       nao suba para repositorio de codigo, restrinja a politica IAM ao
--       minimo necessario e rotacione periodicamente.
--     - Depois de rodar este bloco, o arquivo .pem local pode ser removido
--       com seguranca - o banco guarda a chave de forma criptografada e ela
--       nao aparece em nenhuma consulta.
--
DECLARE
  v_tenancy     TB_26AI_TECH_PARAMS.PARAM_VALUE%TYPE;
  v_user        TB_26AI_TECH_PARAMS.PARAM_VALUE%TYPE;
  v_compartment TB_26AI_TECH_PARAMS.PARAM_VALUE%TYPE;
  v_fingerprint TB_26AI_TECH_PARAMS.PARAM_VALUE%TYPE;
  jo            JSON_OBJECT_T;
BEGIN
  SELECT param_value INTO v_tenancy     FROM TB_26AI_TECH_PARAMS WHERE param_name = 'OCI_TENANCY_OCID';
  SELECT param_value INTO v_user        FROM TB_26AI_TECH_PARAMS WHERE param_name = 'OCI_USER_OCID';
  SELECT param_value INTO v_compartment FROM TB_26AI_TECH_PARAMS WHERE param_name = 'EMBEDDING_COMPARTMENT';
  SELECT param_value INTO v_fingerprint FROM TB_26AI_TECH_PARAMS WHERE param_name = 'OCI_FINGERPRINT';

  jo := JSON_OBJECT_T();
  jo.put('user_ocid',        v_user);
  jo.put('tenancy_ocid',     v_tenancy);
  jo.put('compartment_ocid', v_compartment);
  -- Cole aqui o conteudo COMPLETO do arquivo .pem baixado (incluindo as
  -- linhas -----BEGIN PRIVATE KEY----- e -----END PRIVATE KEY-----, com as
  -- quebras de linha originais). Este e o UNICO lugar de todo o pacote onde
  -- a chave privada e digitada.
  jo.put('private_key',
'<SUBSTITUA_PELO_CONTEUDO_COMPLETO_DA_CHAVE_PRIVADA_PEM_INCLUINDO_AS_LINHAS_BEGIN_E_END_E_AS_QUEBRAS_DE_LINHA_ORIGINAIS>');
  jo.put('fingerprint', v_fingerprint);

  DBMS_VECTOR_CHAIN.CREATE_CREDENTIAL(
    credential_name => 'OCI_CRED',
    params          => JSON(jo.to_string));
END;
/

-- Conferir que a credencial foi criada (nao mostra a chave privada)
SELECT credential_name, username, comments
FROM   user_credentials
WHERE  credential_name = 'OCI_CRED';

Seção 4 · Modelo de embeddings ONNX

Opcional — só necessária se você for usar embeddings de imagem/texto combinados (CLIP). O fluxo padrão de texto do ampmInsights (embeddings de relatórios, RAG) não depende desta seção e pode ser pulado.

O arquivo CLIP_VIT_LARGE_PATCH14_TXT.onnx, já convertido e testado no formato que o Oracle AI Database espera, está incluído neste pacote em modelo-onnx/. O passo 4.1 é só localizar esse arquivo; depois é criar seu bucket e subir o arquivo (passo 4.2, via Console OCI ou OCI CLI), e o bloco de SQL (passo 4.3) monta a URL sozinho a partir da tabela de configuração e carrega o modelo no banco.

Seção 4 — copiar e colar
--
-- Esta secao carrega um modelo de embeddings do tipo imagem/texto (ONNX) para
-- casos de uso que combinem imagem e texto. O fluxo principal de texto do
-- ampmInsights (embeddings de relatorios, RAG, etc.) usa o modelo hospedado no
-- OCI Generative AI (cohere.embed-v4.0, chamado via DBMS_VECTOR_CHAIN na
-- SECAO 5) e NAO depende desta secao. Pule a SECAO 4 se voce so precisa do
-- fluxo de texto (a maioria dos casos).
--
-- 4.1) O arquivo CLIP_VIT_LARGE_PATCH14_TXT.onnx já convertido e testado no
--      formato que o Oracle AI Database espera está incluído neste pacote,
--      na pasta modelo-onnx/. Basta usá-lo diretamente no passo 4.2 abaixo.
--      (Se no futuro você quiser converter um modelo HuggingFace diferente,
--      o passo a passo está logo abaixo, em "Como converter um modelo
--      ONNX" — é opcional, não é necessário para usar o arquivo já incluído
--      aqui.)
--
-- 4.2) Crie um bucket no SEU Object Storage (o nome deve ser o mesmo que voce
--      colocou em OS_BUCKET na SECAO 2) e faca upload do arquivo:
--        Console OCI > Storage > Buckets > Create Bucket
--        (ou via OCI CLI: oci os bucket create --name <valor-de-OS_BUCKET> --compartment-id <OCID-do-compartimento>)
--        Console OCI > abra o bucket > Upload > selecione o CLIP_VIT_LARGE_PATCH14_TXT.onnx
--        (ou via OCI CLI: oci os object put -bn <valor-de-OS_BUCKET> --file CLIP_VIT_LARGE_PATCH14_TXT.onnx)
--
-- 4.3) Carregue o modelo no banco. O bloco abaixo monta a URI sozinho, usando
--      OS_REGION/OS_NAMESPACE/OS_BUCKET da tabela de configuracao (SECAO 2) -
--      voce nao precisa editar nenhuma URL na mao.
--
DECLARE
  v_region    TB_26AI_TECH_PARAMS.PARAM_VALUE%TYPE;
  v_namespace TB_26AI_TECH_PARAMS.PARAM_VALUE%TYPE;
  v_bucket    TB_26AI_TECH_PARAMS.PARAM_VALUE%TYPE;
  v_uri       VARCHAR2(1000);
BEGIN
  SELECT param_value INTO v_region    FROM TB_26AI_TECH_PARAMS WHERE param_name = 'OCI_REGION';
  SELECT param_value INTO v_namespace FROM TB_26AI_TECH_PARAMS WHERE param_name = 'OS_NAMESPACE';
  SELECT param_value INTO v_bucket    FROM TB_26AI_TECH_PARAMS WHERE param_name = 'OS_BUCKET';

  v_uri := 'https://objectstorage.' || v_region || '.oraclecloud.com/n/' || v_namespace || '/b/' || v_bucket || '/o/CLIP_VIT_LARGE_PATCH14_TXT.onnx';

  DBMS_VECTOR.LOAD_ONNX_MODEL_CLOUD(
    model_name => 'CLIP_VIT_LARGE_PATCH14_TXT',
    credential => 'OCI_CRED',
    uri        => v_uri,
    metadata   => JSON('{
      "function": "embedding",
      "embeddingOutput": "embedding",
      "input": {"input": ["DATA"]}
    }')
  );
END;
/

SELECT model_name, algorithm, mining_function
FROM   user_mining_models
WHERE  model_name = 'CLIP_VIT_LARGE_PATCH14_TXT';

Como converter um modelo ONNX (opcional)

O arquivo incluído neste pacote (modelo-onnx/CLIP_VIT_LARGE_PATCH14_TXT.onnx) já está pronto para uso — você não precisa desta seção para concluir a Seção 4. Ela fica aqui como referência para o caso de, no futuro, você querer carregar no banco um modelo ONNX diferente.

Por que um export "cru" pode não funcionar

Um export "cru", feito chamando torch.onnx.export (ou o exportador equivalente do HuggingFace optimum) diretamente, sem nenhum tratamento adicional, normalmente produz um tensor de entrada com dois eixos dinâmicos não vinculados: o tamanho do lote (batch_size) e o comprimento da sequência de tokens (sequence_length). O mecanismo de ONNX Runtime embutido no Oracle AI Database exige que, no máximo, um eixo do tensor de entrada seja dinâmico — os demais precisam ter um tamanho fixo conhecido em tempo de carga do modelo. Um arquivo com dois eixos livres é rejeitado pelo DBMS_VECTOR.LOAD_ONNX_MODEL_CLOUD com o erro ORA-54426, mesmo que o arquivo abra normalmente em qualquer visualizador ONNX e pareça válido.

A ferramenta certa: OML4Py ONNXPipeline

A Oracle disponibiliza um pacote Python próprio, OML4Py, com uma classe utilitária (oml.utils.ONNXPipeline) que já sabe converter uma lista de modelos populares do HuggingFace (incluindo o openai/clip-vit-large-patch14 usado nesta seção) no formato que o banco espera. Para esse modelo especificamente, o pipeline usa um template chamado multimodal_clip que faz duas coisas ao mesmo tempo:

Resultado prático: o ONNXPipeline não gera um, mas dois arquivos a partir do mesmo modelo — um com sufixo _txt (a torre de texto, que é a que interessa pra embeddings de relatórios/RAG) e um com sufixo _img (a torre de imagem). Use o _txt no LOAD_ONNX_MODEL_CLOUD da Seção 4.3 acima.

Não existe cliente OML4Py para macOS

O pacote OML4Py só tem build oficial para Linux. Em uma máquina macOS (como a usada neste roteiro), a conversão precisa rodar dentro de um container Docker Linux (--platform=linux/amd64). Os passos abaixo já contemplam isso.

Passo a passo (via Docker)

Pré-requisitos: Docker Desktop instalado, o wheel do cliente OML4Py baixado do site da Oracle (variante Linux x86_64), pelo menos ~15 GB de espaço livre em disco e Docker Desktop configurado com ~10–12 GB de memória (Settings > Resources > Memory) — a conversão baixa o modelo completo (~1,7 GB), instala torch e faz tracing + quantização, que usam bem mais RAM do que o tamanho do modelo em disco.

1 — criar a pasta de trabalho e o requirements.txt
mkdir -p ~/oracle-onnx-convert/{tmp,hf-cache,pip-cache,out}
cd ~/oracle-onnx-convert

cat > requirements.txt <<'EOF'
--extra-index-url https://download.pytorch.org/whl/cpu
pandas==2.2.3
setuptools==80.8.0
scipy==1.14.1
matplotlib==3.10.0
oracledb==3.3.0
scikit-learn==1.6.1
numpy==2.1.0
pyarrow==19.0.0
onnxruntime==1.20.0
onnxruntime-extensions==0.14.0
onnx==1.18.0
torch==2.9.0
transformers==4.56.1
sentencepiece==0.2.1
EOF

# copie o .whl do OML4Py (baixado do site da Oracle) para esta pasta, ex.:
#   oml-2.1.1-cp313-cp313-linux_x86_64.whl
2 — subir o container com os mounts corretos
cd ~/oracle-onnx-convert
docker run -d --platform=linux/amd64 --name onnx-convert \
  -v "$PWD":/work \
  -v "$PWD/tmp":/tmp \
  -v "$PWD/hf-cache":/root/.cache/huggingface \
  -v "$PWD/pip-cache":/root/.cache/pip \
  -e TMPDIR=/tmp \
  -w /work python:3.13-slim sleep infinity
Por que montar /tmp e os caches

Em ambientes com "Enhanced Container Isolation" (comum em Docker Desktop com política corporativa), o /tmp interno do container pode vir montado como somente-leitura, e a etapa de quantização do onnxruntime precisa escrever arquivos temporários ali. Montar hf-cache e pip-cache no host também evita baixar o modelo (~1,7 GB) de novo caso você precise recriar o container.

3 — instalar dependências dentro do container
docker exec onnx-convert bash -c "apt-get update -qq && apt-get install -y -qq build-essential"
docker exec onnx-convert bash -c "pip install --upgrade pip -q && pip install -r requirements.txt -q"
docker exec onnx-convert bash -c "pip install oml-*.whl -q"
4 — rodar a conversão
docker exec onnx-convert python3 -c "
from oml.utils import ONNXPipeline
import os
pipeline = ONNXPipeline(model_name='openai/clip-vit-large-patch14')
os.makedirs('/work/out', exist_ok=True)
pipeline.export2file('CLIP_VIT_LARGE_PATCH14', output_dir='/work/out')
print('EXPORT_DONE')
"

Ao final, ~/oracle-onnx-convert/out/ vai ter dois arquivos: CLIP_VIT_LARGE_PATCH14_txt.onnx (o que interessa) e CLIP_VIT_LARGE_PATCH14_img.onnx.

Verificar o resultado antes de subir pro bucket

Com o pacote onnx (pip install onnx, roda em qualquer SO, inclusive fora do container) dá pra inspecionar o grafo gerado e confirmar que o eixo duplicado sumiu, sem precisar de acesso ao banco:

verificação local
import onnx
m = onnx.load('CLIP_VIT_LARGE_PATCH14_txt.onnx')
for inp in m.graph.input:
    dims = [d.dim_param or d.dim_value for d in inp.type.tensor_type.shape.dim]
    print(inp.name, dims)
# esperado: input ['batch_size']  -> um so eixo dinamico, tipo string
# (elem_type 8 = STRING; o primeiro node do grafo deve ser um CLIPTokenizer)

Se a saída mostrar dois eixos no input (ex.: ['batch_size', 'sequence_length']), o arquivo ainda é um export cru e vai reproduzir o ORA-54426 — volte ao passo 4 do Docker. Com um só eixo confirmado, siga para a Seção 4.2/4.3 acima (upload no bucket e LOAD_ONNX_MODEL_CLOUD). Se o modelo já estava carregado no banco com o nome CLIP_VIT_LARGE_PATCH14_TXT, rode DBMS_VECTOR.DROP_ONNX_MODEL antes de carregar a versão nova (o mesmo nome de modelo não pode ser recarregado por cima).

Seção 5 · Testes rapidos de sanidade (embedding, geracao de texto, SELECT AI)

Três testes rápidos para confirmar que a credencial e a rede estão corretas antes de seguir para as tabelas e o PL/SQL da aplicação: um teste de embedding, um teste de geração de texto e a criação de um profile de SELECT AI (que já lê região e compartimento da tabela de configuração).

Seção 5 — copiar e colar
-- Rode os 3 blocos abaixo para confirmar que a credencial e a rede estao OK
-- antes de seguir para as tabelas e o PL/SQL da aplicacao.

-- 5.1) Teste de embedding (Cohere, via OCI Generative AI). A URL usa a
--      regiao configurada em GENAI_REGION (SECAO 2), nao um valor fixo.
SELECT VECTOR_SERIALIZE(
    DBMS_VECTOR_CHAIN.UTL_TO_EMBEDDING(
        TO_CLOB('Teste simples para gerar um embedding.'),
        JSON('{
            "provider": "ocigenai",
            "credential_name": "OCI_CRED",
            "url": "https://inference.generativeai.' || (SELECT param_value FROM TB_26AI_TECH_PARAMS WHERE param_name = 'GENAI_REGION') || '.oci.oraclecloud.com/20231130/actions/embedText",
            "model": "cohere.embed-v4.0"
        }')
    )
    RETURNING CLOB
) AS embedding
FROM dual;

-- 5.2) Teste de geracao de texto (Llama 4, via OCI Generative AI). Mesma
--      logica: a URL usa a regiao configurada em GENAI_REGION (SECAO 2).
SELECT DBMS_VECTOR_CHAIN.UTL_TO_GENERATE_TEXT(
    TO_CLOB('Responda somente com a palavra: sucesso'),
    JSON('{
        "provider": "ocigenai",
        "credential_name": "OCI_CRED",
        "url": "https://inference.generativeai.' || (SELECT param_value FROM TB_26AI_TECH_PARAMS WHERE param_name = 'GENAI_REGION') || '.oci.oraclecloud.com/20231130/actions/chat",
        "model": "meta.llama-4-maverick-17b-128e-instruct-fp8",
        "chatRequest": {
            "temperature": 0.1,
            "topK": 1,
            "maxTokens": 50
        }
    }')
) AS resposta
FROM dual;

-- 5.3) Teste de SELECT AI (perguntas em linguagem natural sobre as views/
--      tabelas do schema). O bloco abaixo le EMBEDDING_COMPARTMENT e
--      GENAI_REGION da tabela de configuracao (SECAO 2). As tabelas/view
--      referenciadas abaixo sao criadas na SECAO 6 - rode este bloco depois
--      de criar V_26AI_TECH e TB_26AI_TECH_CMDB.
--
--      A regiao do OCI Generative AI abaixo vem do parametro GENAI_REGION -
--      nao precisa (nem deve) ser trocada pelo valor de OCI_REGION da tabela
--      de configuracao (sao coisas diferentes de proposito), ja que o
--      servico so esta disponivel em algumas regioes especificas da OCI.
DECLARE
  v_compartment   TB_26AI_TECH_PARAMS.PARAM_VALUE%TYPE;
  v_genai_region  TB_26AI_TECH_PARAMS.PARAM_VALUE%TYPE;
BEGIN
  SELECT param_value INTO v_compartment  FROM TB_26AI_TECH_PARAMS WHERE param_name = 'EMBEDDING_COMPARTMENT';
  SELECT param_value INTO v_genai_region FROM TB_26AI_TECH_PARAMS WHERE param_name = 'GENAI_REGION';

  DBMS_CLOUD_AI.CREATE_PROFILE(
    profile_name => 'OCI_CRED2',
    attributes   => '{
      "provider": "oci",
      "credential_name": "OCI_CRED",
      "region": "' || v_genai_region || '",
      "model": "meta.llama-4-maverick-17b-128e-instruct-fp8",
      "temperature": 0,
      "comments": true,
      "object_list": [
        {"owner": "AMPMINSIGHTS", "name": "V_26AI_TECH"},
        {"owner": "AMPMINSIGHTS", "name": "TB_26AI_TECH_CMDB"}
      ],
      "enforce_object_list": "true"
    }',
    description => 'Profile ampmInsights para uso no SELECT AI (OCI Generative AI - Llama 4 Maverick). Compartimento: ' || v_compartment
  );
END;
/

SELECT DBMS_CLOUD_AI.GENERATE(
         prompt       => 'Responda apenas: CONECTADO',
         profile_name => 'OCI_CRED2',
         action       => 'chat'
       ) AS resposta
FROM dual;
O passo mais importante

TB_26AI_TECH_PARAMS (criada na Seção 2) guarda o nome da credencial e o OCID do compartimento. Todo o PL/SQL da aplicação — e o job de processamento em lote criado pelo botão da interface — lê esses valores dali. Se algo falhar mais adiante, confira primeiro se essa tabela está com os valores reais preenchidos.

Seção 6 · Tabelas, sequence, trigger e view da aplicacao

Cria TB_26AI_TECH_CHAT, TB_26AI_TECH_CMDB, TB_26AI_TECH_SYS, TB_26AI_TECH_V2 (+ trigger/sequence de ID_INSIGHT), a tabela legada TB_26AI_TECH, TB_26AI_TECH_VECTOR e a view V_26AI_TECH. Nomes idênticos aos usados no export da aplicação APEX — não renomear estes objetos.

Seção 6 — copiar e colar
-- Os nomes de tabelas, view, sequence e trigger abaixo sao os MESMOS nomes
-- referenciados internamente pelo export da aplicacao APEX
-- (02_ampminsights_apex_app.sql). Nao renomeie estes objetos: a aplicacao
-- tem centenas de referencias diretas a estes nomes exatos.

CREATE TABLE "TB_26AI_TECH_CHAT"
   (	"USER_NAME" VARCHAR2(200) ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Identificador, papel ou atributo de usuario/controle de acesso associado a interacoes de chat do dominio tecnico.'),
	"IS_OWN" VARCHAR2(6) ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Indicador booleano de is own no registro de interacoes de chat do dominio tecnico.'),
	"COMMENT_TEXT" CLOB ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Conteudo textual, documental ou binario associado a interacoes de chat do dominio tecnico; o nome da coluna indica o papel especifico no fluxo.'),
	"COMMENT_DATE" DATE ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Data, hora ou marco temporal associado ao registro de interacoes de chat do dominio tecnico.'),
	"SESSION_ID" NUMBER ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Identificador ou codigo de session no contexto de interacoes de chat do dominio tecnico.')
   )
  ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Armazena registros de interacoes de chat do dominio tecnico ("fale com os dados"); e classificada, pela nomenclatura e estrutura, como tabela operacional do dominio.');

CREATE TABLE "TB_26AI_TECH_CMDB"
   (	"ID" NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1 START WITH 1 CACHE 20 NOORDER  NOCYCLE  NOKEEP  NOSCALE  NOT NULL ENABLE ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Identificador unico do registro de itens de configuracao tecnica (CMDB); compoe a chave primaria da tabela. E coluna identity preenchida automaticamente pelo Oracle.'),
	"NOME" VARCHAR2(255) ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Nome ou titulo usado para identificar o registro de itens de configuracao tecnica (CMDB) em consultas e interfaces.'),
	"EMPRESA" VARCHAR2(50) ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Atributo EMPRESA do registro de itens de configuracao tecnica (CMDB).'),
	"CLASSE" VARCHAR2(50) ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Atributo de classificacao, versao, origem ou formato do registro de itens de configuracao tecnica (CMDB).'),
	"AMBIENTE" VARCHAR2(50) ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Atributo AMBIENTE do registro de itens de configuracao tecnica (CMDB).'),
	"STATUS_OPERACIONAL" VARCHAR2(50) ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Indicador de estado ou de controle do registro de itens de configuracao tecnica (CMDB).'),
	"DESCRIÇÃO" VARCHAR2(32767) ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Texto descritivo, detalhe ou observacao associado a itens de configuracao tecnica (CMDB).'),
	"SOX" NUMBER ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Atributo SOX do registro de itens de configuracao tecnica (CMDB).'),
	"COFRE" NUMBER ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Atributo COFRE do registro de itens de configuracao tecnica (CMDB).'),
	"ENDEREÇO_IP" VARCHAR2(50) ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Endereco IP registrado no contexto de itens de configuracao tecnica (CMDB).'),
	"SISTEMA_OPERACIONAL" VARCHAR2(255) ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Atributo SISTEMA_OPERACIONAL do registro de itens de configuracao tecnica (CMDB).'),
	"TIPO_DE_REDE" VARCHAR2(50) ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Atributo TIPO_DE_REDE do registro de itens de configuracao tecnica (CMDB).'),
	"É_VIRTUAL" NUMBER ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Atributo E_VIRTUAL do registro de itens de configuracao tecnica (CMDB).'),
	"CRITICIDADE" VARCHAR2(50) ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Atributo CRITICIDADE do registro de itens de configuracao tecnica (CMDB).'),
	"INSTÂNCIAS_BANCOS" VARCHAR2(50) ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Atributo INSTANCIAS_BANCOS do registro de itens de configuracao tecnica (CMDB).'),
	"APLICAÇÕES_RELACIONADAS" VARCHAR2(32767) ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Atributo APLICACOES_RELACIONADAS do registro de itens de configuracao tecnica (CMDB).'),
	"JANELAS_DE_MANUTENÇÃO" VARCHAR2(255) ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Atributo JANELAS_DE_MANUTENCAO do registro de itens de configuracao tecnica (CMDB).'),
	"ANALISTA_RESPONSAVEL" VARCHAR2(255) ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Atributo ANALISTA_RESPONSAVEL do registro de itens de configuracao tecnica (CMDB).'),
	"ANALISTAS_RESPONSÁVEIS" VARCHAR2(255) ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Atributo ANALISTAS_RESPONSAVEIS do registro de itens de configuracao tecnica (CMDB).'),
	"GERENCIADO_PELO_GRUPO" VARCHAR2(255) ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Atributo GERENCIADO_PELO_GRUPO do registro de itens de configuracao tecnica (CMDB).'),
	"GRUPOS_RESPONSÁVEIS" VARCHAR2(32767) ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Atributo GRUPOS_RESPONSAVEIS do registro de itens de configuracao tecnica (CMDB).'),
	"GRUPO_RESPONSÁVEL" VARCHAR2(255) ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Atributo GRUPO_RESPONSAVEL do registro de itens de configuracao tecnica (CMDB).'),
	"EDR" NUMBER ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Atributo EDR do registro de itens de configuracao tecnica (CMDB).'),
	"TENABLE" NUMBER ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Atributo TENABLE do registro de itens de configuracao tecnica (CMDB).'),
	"SIEM" NUMBER ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Atributo SIEM do registro de itens de configuracao tecnica (CMDB).'),
	"APPLIANCE" NUMBER ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Atributo APPLIANCE do registro de itens de configuracao tecnica (CMDB).'),
	 PRIMARY KEY ("ID")
  USING INDEX ENABLE
   )
  ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Armazena registros de itens de configuracao tecnica (CMDB); e utilizada por VIEW V_26AI_TECH.');

CREATE TABLE "TB_26AI_TECH_SYS"
   (	"ID_1" NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1 START WITH 1 CACHE 20 NOORDER  NOCYCLE  NOKEEP  NOSCALE  NOT NULL ENABLE ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Identificador unico do registro de cadastro de sistemas tecnicos; compoe a chave primaria da tabela.'),
	"ID" NUMBER ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Identificador tecnico do registro de cadastro de sistemas tecnicos.'),
	"TIMESTAMP" TIMESTAMP (9) WITH TIME ZONE ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Data, hora ou marco temporal associado ao registro de cadastro de sistemas tecnicos.'),
	"HOST" VARCHAR2(50) ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Atributo HOST do registro de cadastro de sistemas tecnicos.'),
	"FACILITY" VARCHAR2(50) ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Atributo FACILITY do registro de cadastro de sistemas tecnicos.'),
	"SEVERITY" VARCHAR2(50) ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Atributo SEVERITY do registro de cadastro de sistemas tecnicos.'),
	"PROCESS" VARCHAR2(50) ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Atributo PROCESS do registro de cadastro de sistemas tecnicos.'),
	"PID" NUMBER ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Atributo PID do registro de cadastro de sistemas tecnicos.'),
	"ORACLE_INSTANCE" VARCHAR2(50) ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Atributo ORACLE_INSTANCE do registro de cadastro de sistemas tecnicos.'),
	"MESSAGE" VARCHAR2(255) ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Mensagem, diagnostico ou informacao de log associada ao processamento de cadastro de sistemas tecnicos.'),
	"RAW_SYSLOG" VARCHAR2(255) ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Mensagem, diagnostico ou informacao de log associada ao processamento de cadastro de sistemas tecnicos.'),
	"INSIGHT_REPORT" CLOB ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Conteudo textual gerado por IA associado a cadastro de sistemas tecnicos.'),
	"INSIGHT_JSON" CLOB ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Conteudo estruturado em JSON relacionado ao insight gerado por IA para cadastro de sistemas tecnicos.'),
	 PRIMARY KEY ("ID_1")
  USING INDEX ENABLE
   )
  ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Armazena registros de cadastro de sistemas tecnicos (syslog); e utilizada por PROCEDURE PRC_26AI_TECH.');

CREATE SEQUENCE "SEQ_26AI_TECH" MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1 START WITH 1 CACHE 20 NOORDER NOCYCLE NOKEEP NOSCALE GLOBAL;

CREATE TABLE "TB_26AI_TECH_V2"
   (	"ID_1" NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1 START WITH 1 CACHE 20 NOORDER  NOCYCLE  NOKEEP  NOSCALE  NOT NULL ENABLE ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Identificador unico do registro de dados tecnicos (versao 2); compoe a chave primaria da tabela.'),
	"AGE_IN_DAYS" NUMBER ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Idade em dias do achado, referenciada em VIEW V_26AI_TECH.'),
	"ASSET_DISPLAY_IPV4_ADDRESS" VARCHAR2(255) ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Endereco IPv4 do ativo, referenciado em VIEW V_26AI_TECH.'),
	"ASSET_ID" VARCHAR2(255) ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Identificador do ativo, referenciado em VIEW V_26AI_TECH.'),
	"ASSET_NAME" VARCHAR2(100) ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Nome do ativo, referenciado em VIEW V_26AI_TECH.'),
	"ASSET_TAGS" VARCHAR2(32767) ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Tags do ativo, referenciadas em VIEW V_26AI_TECH.'),
	"DEFINITION_EXPLOITABILITY_EASE" VARCHAR2(50) ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Facilidade de exploracao da vulnerabilidade, referenciada em VIEW V_26AI_TECH.'),
	"DEFINITION_FAMILY" VARCHAR2(255) ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Familia da definicao de vulnerabilidade, referenciada em VIEW V_26AI_TECH.'),
	"DEFINITION_ID" NUMBER ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Identificador da definicao de vulnerabilidade, referenciado em VIEW V_26AI_TECH.'),
	"DEFINITION_NAME" VARCHAR2(255) ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Nome da definicao de vulnerabilidade, referenciado em VIEW V_26AI_TECH.'),
	"DEFINITION_VPR_SCORE" NUMBER ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Score VPR (Vulnerability Priority Rating), referenciado em VIEW V_26AI_TECH.'),
	"FIRST_OBSERVED" TIMESTAMP (9) WITH TIME ZONE ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Data da primeira observacao, referenciada em VIEW V_26AI_TECH.'),
	"ID" VARCHAR2(255) ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Identificador tecnico do achado, referenciado em VIEW V_26AI_TECH.'),
	"LAST_SEEN" TIMESTAMP (9) WITH TIME ZONE ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Data da ultima observacao, referenciada em VIEW V_26AI_TECH.'),
	"OUTPUT" VARCHAR2(32767) ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Saida/detalhe tecnico do scanner, referenciada em VIEW V_26AI_TECH.'),
	"PROTOCOL" VARCHAR2(50) ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Protocolo relacionado ao achado, referenciado em VIEW V_26AI_TECH.'),
	"RESURFACED_DATE" TIMESTAMP (9) WITH TIME ZONE ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Data de ressurgimento do achado, referenciada em VIEW V_26AI_TECH.'),
	"SEVERITY" VARCHAR2(50) ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Severidade do achado, referenciada em VIEW V_26AI_TECH.'),
	"STATE" VARCHAR2(50) ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Estado do achado, referenciado em VIEW V_26AI_TECH.'),
	"INSIGHT_REPORT" CLOB ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Relatorio de insight gerado por IA, referenciado em VIEW V_26AI_TECH.'),
	"INSIGHT_JSON" CLOB ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Insight estruturado em JSON gerado por IA.'),
	"ID_INSIGHT" NUMBER ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Identificador sequencial do insight, gerado pelo trigger BIU_TB_26AI_TECH_V2, referenciado em VIEW V_26AI_TECH.'),
	 PRIMARY KEY ("ID_1")
  USING INDEX ENABLE
   )
  ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Armazena os achados de vulnerabilidade (principal fonte de dados tecnicos); e utilizada por PROCEDURE PRC_26AI_TECH e VIEW V_26AI_TECH.');

CREATE OR REPLACE EDITIONABLE TRIGGER "BIU_TB_26AI_TECH_V2"
BEFORE INSERT OR UPDATE
ON TB_26AI_TECH_V2
FOR EACH ROW
BEGIN
   :new.ID_INSIGHT := SEQ_26AI_TECH.NEXTVAL;
END;
/
ALTER TRIGGER "BIU_TB_26AI_TECH_V2" ENABLE;

-- Tabela legada (mantida por compatibilidade: ainda e usada por uma pagina/
-- carga de dados mais antiga da aplicacao - "DL_TECH_VULNERABILIDADES").
CREATE TABLE "TB_26AI_TECH"
   (	"ID_1" NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1 START WITH 1 CACHE 20 NOORDER  NOCYCLE  NOKEEP  NOSCALE  NOT NULL ENABLE,
	"AGE_IN_DAYS" NUMBER,
	"ASSET_DISPLAY_IPV4_ADDRESS" VARCHAR2(255),
	"ASSET_ID" VARCHAR2(255),
	"ASSET_NAME" VARCHAR2(100),
	"EMPRESA" VARCHAR2(50),
	"AMBIENTE" VARCHAR2(50),
	"STATUS" VARCHAR2(50),
	"DESCRIÇÃO" VARCHAR2(32767),
	"ASSET_OPERATING_SYSTEM" VARCHAR2(255),
	"DEFINITION_EXPLOITABILITY_EASE" VARCHAR2(50),
	"DEFINITION_FAMILY" VARCHAR2(255),
	"DEFINITION_ID" NUMBER,
	"DEFINITION_NAME" VARCHAR2(255),
	"DEFINITION_VPR_SCORE" NUMBER,
	"FIRST_OBSERVED" DATE,
	"ID" VARCHAR2(255),
	"LAST_SEEN" DATE,
	"OUTPUT" VARCHAR2(32767),
	"PROTOCOL" VARCHAR2(50),
	"RESURFACED_DATE" DATE,
	"SEVERITY" VARCHAR2(50),
	"STATE" VARCHAR2(50),
	"INSIGHT_REPORT" CLOB,
	"INSIGHT_JSON" CLOB,
	"ID_INSIGHT" NUMBER,
	 PRIMARY KEY ("ID_1")
  USING INDEX ENABLE
   );

CREATE TABLE "TB_26AI_TECH_VECTOR"
   (	"EMBED_ID" NUMBER ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Representacao vetorial (embedding) associada ao conteudo de embeddings de conhecimento tecnico, destinada a buscas por similaridade e fluxos de recuperacao aumentada (RAG).'),
	"EMBED_DATA" CLOB ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Trecho de texto (chunk) que originou o vetor, referenciado em FUNCTION FNC_26AI_TECH_RAG.'),
	"EMBED_VECTOR" VECTOR ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Vetor (embedding) do trecho, referenciado em FUNCTION FNC_26AI_TECH_RAG.'),
	"TITULO" VARCHAR2(2000) ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Titulo/identificacao do registro de embeddings de conhecimento tecnico.'),
	"ID" VARCHAR2(200) ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Referencia (rowid/id) do registro de origem do insight, referenciada em FUNCTION FNC_26AI_TECH_RAG.'),
	"SOURCE_TYPE" VARCHAR2(3) ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Origem do dado vetorizado: VUL (vulnerabilidades) ou SYS (syslog).')
   )
  ANNOTATIONS("LLM_DESCRIPTION_PT_BR" 'Armazena os vetores (chunks) usados na busca semantica/RAG; e utilizada por FUNCTION FNC_26AI_TECH_RAG e PROCEDURE PRC_26AI_TECH.');

-- View consolidada usada pela aplicacao e pelo SELECT AI.
-- Observacao: as duas chamadas REPLACE() abaixo (colunas NOME/EMPRESA)
-- fazem parte da logica de negocio original do ambiente de origem e
-- generalizam nomes de marca especificos para "Company" em determinados
-- cenarios de apresentacao. Como este e o proprio ambiente da AM/PM, avalie
-- com o time de negocio se esse tratamento ainda faz sentido aqui ou se pode
-- ser removido (ver docs/07-seguranca-boas-praticas.html).
CREATE OR REPLACE FORCE EDITIONABLE VIEW "V_26AI_TECH" ("CID", "AGE_IN_DAYS", "ASSET_DISPLAY_IPV4_ADDRESS", "ASSET_ID", "ASSET_NAME", "ASSET_TAGS", "DEFINITION_EXPLOITABILITY_EASE", "DEFINITION_FAMILY", "DEFINITION_ID", "DEFINITION_NAME", "DEFINITION_VPR_SCORE", "FIRST_OBSERVED", "ID", "LAST_SEEN", "OUTPUT", "PROTOCOL", "RESURFACED_DATE", "SEVERITY", "STATE", "INSIGHT_REPORT", "ID_INSIGHT", "NOME", "EMPRESA", "CLASSE", "AMBIENTE", "STATUS_OPERACIONAL", "DESCRICAO", "SOX", "COFRE", "ENDERECO_IP", "SISTEMA_OPERACIONAL", "TIPO_DE_REDE", "IS_VIRTUAL", "CRITICIDADE", "INSTANCIAS_BANCOS", "APLICACOES_RELACIONADAS", "JANELAS_DE_MANUTENCAO", "ANALISTA_RESPONSAVEL", "ANALISTAS_RESPONSAVEIS", "GERENCIADO_PELO_GRUPO", "GRUPOS_RESPONSAVEIS", "GRUPO_RESPONSAVEL", "EDR", "TENABLE", "SIEM", "APPLIANCE") AS
  SELECT
	v.rowid cid,
    v.AGE_IN_DAYS	                   ,
	v.ASSET_DISPLAY_IPV4_ADDRESS	   ,
	v.ASSET_ID	                       ,
	v.ASSET_NAME	                   ,
	v.ASSET_TAGS	                   ,
	v.DEFINITION_EXPLOITABILITY_EASE	,
	v.DEFINITION_FAMILY	               ,
	v.DEFINITION_ID	                   ,
	v.DEFINITION_NAME	               ,
	v.DEFINITION_VPR_SCORE	           ,
	v.FIRST_OBSERVED	               ,
	v.ID	                           ,
	v.LAST_SEEN	                       ,
	v.OUTPUT	                       ,
	v.PROTOCOL	                       ,
	v.RESURFACED_DATE	               ,
	v.SEVERITY	                       ,
	v.STATE	                           ,
	v.INSIGHT_REPORT	               ,
	v.ID_INSIGHT	                   ,
	replace(replace(initcap(c.NOME),'Ipiranga','Company'),'Ampm','Company')	    NOME                        ,
	replace(replace(initcap(c.EMPRESA),'Ipiranga','Company'),'Ampm','Company')	  EMPRESA                     ,
	c.CLASSE	                       ,
	c.AMBIENTE	                       ,
	c.STATUS_OPERACIONAL	           ,
	c.DESCRIÇÃO	DESCRICAO            ,
	c.SOX	                           ,
	c.COFRE	                           ,
	c.ENDEREÇO_IP	  ENDERECO_IP                 ,
	c.SISTEMA_OPERACIONAL              ,
	c.TIPO_DE_REDE	                   ,
	c.É_VIRTUAL	   IS_VIRTUAL                    ,
	c.CRITICIDADE	                   ,
	c.INSTÂNCIAS_BANCOS	    INSTANCIAS_BANCOS           ,
	c.APLICAÇÕES_RELACIONADAS	 APLICACOES_RELACIONADAS      ,
	c.JANELAS_DE_MANUTENÇÃO	    JANELAS_DE_MANUTENCAO       ,
	c.ANALISTA_RESPONSAVEL	           ,
	c.ANALISTAS_RESPONSÁVEIS	  ANALISTAS_RESPONSAVEIS     ,
	c.GERENCIADO_PELO_GRUPO	           ,
	c.GRUPOS_RESPONSÁVEIS	GRUPOS_RESPONSAVEIS           ,
	c.GRUPO_RESPONSÁVEL	      GRUPO_RESPONSAVEL         ,
	c.EDR,
    c.TENABLE	                   ,
	c.SIEM	                           ,
	c.APPLIANCE
FROM TB_26AI_TECH_v2 v
LEFT OUTER JOIN
     (SELECT *
      FROM TB_26AI_TECH_CMDB
      WHERE nome IN (SELECT nome
                     FROM TB_26AI_TECH_CMDB
                     GROUP BY nome
                     HAVING count(1) = 1 )
      ) c
ON  ( upper(v.ASSET_NAME) = upper(c.nome) AND c.AMBIENTE IS NOT NULL );

-- Log de chamadas de IA/embeddings (sucesso e erro) - nao guarda prompt/
-- resposta completos, so metadados de uso. Ver docs/09-operacao-observabilidade.html.
CREATE TABLE "TB_26AI_TECH_LOG"
   (	"LOG_ID" NUMBER GENERATED ALWAYS AS IDENTITY MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1 START WITH 1 CACHE 20 NOORDER  NOCYCLE  NOKEEP  NOSCALE  NOT NULL ENABLE,
	"LOG_TS" TIMESTAMP(6) WITH TIME ZONE DEFAULT SYSTIMESTAMP NOT NULL ENABLE,
	"CALLING_OBJECT" VARCHAR2(128),
	"OPERATION" VARCHAR2(30),
	"SOURCE_TYPE" VARCHAR2(10),
	"SOURCE_ID" VARCHAR2(200),
	"PROVIDER" VARCHAR2(30),
	"MODEL_NAME" VARCHAR2(100),
	"APP_USER" VARCHAR2(100),
	"STATUS" VARCHAR2(10),
	"ERROR_CODE" NUMBER,
	"ERROR_MESSAGE" VARCHAR2(4000),
	"DURATION_MS" NUMBER,
	"PROMPT_CHARS" NUMBER,
	"RESPONSE_CHARS" NUMBER,
	 CONSTRAINT "TB_26AI_TECH_LOG_PK" PRIMARY KEY ("LOG_ID") USING INDEX ENABLE
   );

CREATE INDEX "TB_26AI_TECH_LOG_TS_IX" ON "TB_26AI_TECH_LOG" ("LOG_TS");
CREATE INDEX "TB_26AI_TECH_LOG_ST_IX" ON "TB_26AI_TECH_LOG" ("STATUS","LOG_TS");

COMMENT ON TABLE "TB_26AI_TECH_LOG" IS 'Log de cada chamada a Generative AI/embeddings feita pelo ampmInsights (sucesso ou erro) - nao guarda prompt/resposta completos, so metadados de uso (modelo, duracao, tamanho, status, erro). Usada para troubleshooting e monitoramento de consumo - ver docs/09-operacao-observabilidade.html.';

Seção 7 · PL/SQL de geracao de embeddings, RAG e insights (ampmInsights)

Cria PRC_26AI_TECH_LOG (log de cada chamada de IA/embedding — ver Operação & observabilidade) e as funções e a procedure descritas em Arquitetura: fnc_26ai_embed, fnc_26ai_tech_rag, FNC_26AI_TECH_MCP_V1, FNC_26ai_rag e prc_26ai_tech. É um bloco longo (a maior parte é o texto dos prompts de IA) — copie e cole inteiro de uma vez.

Seção 7 — copiar e colar
CREATE OR REPLACE PROCEDURE PRC_26AI_TECH_LOG (
  p_calling_object  IN VARCHAR2,
  p_operation       IN VARCHAR2,
  p_status          IN VARCHAR2,
  p_source_type     IN VARCHAR2 DEFAULT NULL,
  p_source_id       IN VARCHAR2 DEFAULT NULL,
  p_provider        IN VARCHAR2 DEFAULT NULL,
  p_model_name      IN VARCHAR2 DEFAULT NULL,
  p_error_code      IN NUMBER   DEFAULT NULL,
  p_error_message   IN VARCHAR2 DEFAULT NULL,
  p_duration_ms     IN NUMBER   DEFAULT NULL,
  p_prompt_chars    IN NUMBER   DEFAULT NULL,
  p_response_chars  IN NUMBER   DEFAULT NULL
) AS
  PRAGMA AUTONOMOUS_TRANSACTION;
/*
  Projeto: ampmInsights
  Objetivo: registrar em TB_26AI_TECH_LOG cada chamada a Generative AI/
            embeddings (sucesso ou erro), para troubleshooting e
            monitoramento de consumo (ver docs/09-operacao-observabilidade.html).
  Autonomous transaction: pode ser chamada de dentro de functions invocadas
  em contexto SQL sem interferir na transacao do chamador; nunca propaga erro
  (uma falha ao logar nao pode derrubar a chamada real de IA).
*/
BEGIN
  INSERT INTO TB_26AI_TECH_LOG (
    calling_object, operation, source_type, source_id, provider, model_name,
    app_user, status, error_code, error_message, duration_ms, prompt_chars,
    response_chars
  ) VALUES (
    p_calling_object, p_operation, p_source_type, p_source_id, p_provider,
    p_model_name, NVL(V('APP_USER'), SYS_CONTEXT('USERENV','SESSION_USER')),
    p_status, p_error_code, SUBSTR(p_error_message, 1, 4000), p_duration_ms,
    p_prompt_chars, p_response_chars
  );
  COMMIT;
EXCEPTION
  WHEN OTHERS THEN
    NULL; -- logging nunca pode quebrar a chamada real
END;
/

CREATE OR REPLACE FUNCTION fnc_26ai_embed( p_string      IN VARCHAR2,
                                            p_emb_type    IN VARCHAR2 DEFAULT 'COHERE',
                                            p_credential  IN VARCHAR2 DEFAULT 'OCI_CRED')
RETURN CLOB
AS
/*
  Projeto: ampmInsights
  Objetivo: gerar o embedding (vetor) de um texto, usando o provider indicado
            em p_emb_type: ONNX (modelo local carregado na SECAO 4),
            COHERE (OCI Generative AI - usado por padrao) ou vLLM (endpoint
            self-hosted, opcional).
*/
  v_embedding CLOB;
  v_string    CLOB;
  v_url       VARCHAR2(700) := 'https://inference.generativeai.sa-saopaulo-1.oci.oraclecloud.com/20231130/actions/embedText';
  v_body      JSON_OBJECT_T;
  r           DBMS_CLOUD_TYPES.RESP;

BEGIN

   v_string := replace(replace(replace(p_string,chr(13),' '),chr(10),' '),'"','')  ;

   IF p_emb_type = 'ONNX' THEN

     /* Pre-requisito: modelo ONNX carregado na SECAO 4 deste script (opcional). */

		SELECT VECTOR_EMBEDDING( CLIP_VIT_LARGE_PATCH14_TXT USING v_string AS data ) AS embedding
		INTO v_embedding;

   ELSIF p_emb_type = 'COHERE' THEN

/*  CUIDADO: dbms_vector_chain.utl_to_text(v_string) usa internamente o Oracle
    Text Filter (processo externo ctxfilt) para converter documentos (PDF,
    DOCX etc.) em texto puro. O erro DRG-11225 (third-party filter timed out)
    indica que esse processo demorou mais que o timeout configurado. */

		SELECT embed_vector
		INTO v_embedding
        FROM dual
            CROSS JOIN TABLE(
                dbms_vector_chain.utl_to_embeddings(
                        dbms_vector_chain.utl_to_text( v_string ),
					     json('{    "provider": "ocigenai",
                                "credential_name": "' || p_credential || '",
                                "url": "https://inference.generativeai.' || (SELECT param_value FROM TB_26AI_TECH_PARAMS WHERE param_name = 'GENAI_REGION') || '.oci.oraclecloud.com/20231130/actions/embedText",
                                "model": "cohere.embed-v4.0"
                            }')
                )
            )  t
            CROSS JOIN JSON_TABLE(
                t.column_value,
                '$[*]' COLUMNS (
                    embed_id NUMBER PATH '$.embed_id',
                    text_chunk VARCHAR2(4000) PATH '$.embed_data',
                    embed_vector CLOB PATH '$.embed_vector'
                )
        ) AS et;

     ELSIF p_emb_type = 'vLLM' THEN
                -- Endpoint self-hosted opcional (ex.: vLLM atras de um load
                -- balancer proprio). Ajuste "host" e "model" para o seu
                -- ambiente antes de usar esta opcao; nao e necessaria para o
                -- fluxo padrao (COHERE via OCI Generative AI).
                DECLARE
                        params clob;
                        v_embedding2 vector;
				BEGIN

					params := '{
							  "provider": "openai",
							  "url": "https://<SUBSTITUA_PELO_SEU_ENDPOINT_VLLM>/embed/v1/embeddings",
							  "host": "local",
							  "model": "<SUBSTITUA_PELO_MODELO_QUE_VOCE_HOSPEDA>",
                              "batch_size": 50}';

					v_embedding2 := dbms_vector_chain.utl_to_embedding( v_string , json(params)) ;

                    v_embedding := TO_CLOB(TO_CHAR( v_embedding2 ) ) ;

				EXCEPTION
					WHEN OTHERS THEN
					   DBMS_OUTPUT.PUT_LINE('Erro: ' || SQLCODE || ' - ' || SQLERRM);
					   PRC_26AI_TECH_LOG(
					     p_calling_object => 'FNC_26AI_EMBED',
					     p_operation      => 'EMBEDDING',
					     p_status         => 'ERROR',
					     p_provider       => 'vLLM',
					     p_error_code     => SQLCODE,
					     p_error_message  => SQLERRM
					   );
				END;

    END IF;

	RETURN v_embedding;

EXCEPTION
  WHEN OTHERS THEN
    -- Cobre os ramos ONNX/COHERE (o vLLM ja trata e loga o proprio erro
    -- acima e nao propaga). Loga e repropaga sem mudar o comportamento
    -- existente.
    PRC_26AI_TECH_LOG(
      p_calling_object => 'FNC_26AI_EMBED',
      p_operation      => 'EMBEDDING',
      p_status         => 'ERROR',
      p_provider       => p_emb_type,
      p_error_code     => SQLCODE,
      p_error_message  => SQLERRM
    );
    RAISE;
END;
/



CREATE OR REPLACE FUNCTION fnc_26ai_tech_rag(p_query VARCHAR2,
                                              p_top_k IN NUMBER ,
                                              p_credential in varchar2 default 'OCI_CRED',
                                              p_app_user in varchar2 default V('APP_USER'),
                                              p_source_type varchar2 default 'VUL' )
RETURN CLOB IS
/*
  Projeto: ampmInsights
  Objetivo: funcao de RAG (Retrieval-Augmented Generation) para "fale com os
            seus dados" no dominio tecnico (vulnerabilidades e syslog).
*/
   v_context     CLOB;
   v_pre_prompt  CLOB;
   v_prompt      CLOB;
   v_pre_prompt2 CLOB;
   params_genai  CLOB;
   output        CLOB;
   query_vec     VECTOR;
   v_llm         VARCHAR2(20) := 'LLAMA4'; -- [ LLAMA4 | OPENAI ]
   v_genai_region TB_26AI_TECH_PARAMS.PARAM_VALUE%TYPE;

BEGIN

  SELECT param_value INTO v_genai_region FROM TB_26AI_TECH_PARAMS WHERE param_name = 'GENAI_REGION';

  -- embedding da pergunta do usuario
  query_vec := to_vector( fnc_26ai_embed ( p_string => p_query, p_emb_type => 'COHERE' ) ) ;

  FOR message_cursor IN (
                        SELECT lv.ID AS DOCID,
                               lv.EMBED_DATA AS BODY,
                               vector_distance( lv.EMBED_VECTOR, query_vec, cosine ) AS SCORE
                        FROM TB_26AI_TECH_VECTOR lv
                        WHERE source_type = p_source_type
                         ORDER BY SCORE
                        FETCH EXACT FIRST p_top_k ROWS ONLY
                    ) LOOP

        v_context := v_context || '"' || replace(replace(replace(replace(message_cursor.BODY,chr(10),null),chr(13),null),'"',''),'''','') || '",' ;

   END LOOP;

  -- Pre-requisito: credencial OCI_CRED criada na SECAO 3.
  -- Referencia do corpo da chamada:
  --   https://docs.oracle.com/en-us/iaas/api/#/en/generative-ai-inference/20231130/datatypes/GenerateTextDetails
  IF v_llm = 'LLAMA4' THEN

    params_genai := '{
		"provider" : "ocigenai",
     	"credential_name" : "' || p_credential || '",
		"url" : "https://inference.generativeai.' || v_genai_region || '.oci.oraclecloud.com/20231130/actions/chat",
		"model": "meta.llama-4-maverick-17b-128e-instruct-fp8"}';

  ELSIF v_llm = 'OPENAI' THEN

    params_genai := '{
        "provider" : "openai",
        "credential_name" : "CRED_OPENAI",
        "url" : "https://api.openai.com/v1/chat/completions",
        "model" : "gpt-4.1-mini",
        "temperature": 0.4
        }';

  END IF;

  v_pre_prompt := 'Voce e um Analista Senior de Inteligencia Operacional especializado em analisar relatorios tecnicos e executivos previamente gerados.

Contexto de trabalho:
- Voce recebera:
  1) A pergunta do usuario
  2) Um conjunto de trechos recuperados da base vetorizada de relatorios (com metadados como report_id, data, area, severidade, prioridade, fonte).
- Sua tarefa e encontrar evidencias relevantes nesses relatorios e responder com precisao, sem inventar informacoes.

Regras obrigatorias:
- Use SOMENTE informacoes presentes nos trechos recuperados.
- Se faltar dado para responder algo, escreva explicitamente: "Informacao nao fornecida".
- Sempre cite as evidencias no formato: [report_id | secao/campo | data].
- Quando houver conflito entre relatorios, sinalize divergencia e indique qual evidencia e mais confiavel (recencia, completude, consistencia).
- Priorize risco de negocio, severidade e urgencia.
- Nao repita conteudo desnecessario; seja objetivo e acionavel.

Metodo de resposta:
1) Entenda a intencao da pergunta (comparar, diagnosticar, priorizar, tendencia, causa, acao).
2) Selecione os trechos mais relevantes por aderencia semantica a pergunta.
3) Consolide achados em linguagem executiva + tecnica.
4) Gere recomendacoes praticas com prioridade e esforco.
5) Indique lacunas de dados que limitam a conclusao.

Formato obrigatorio de saida:
## Resposta objetiva
- Responda em formato markdown, e use fontes com mesmo tamanho, porem com titulos em NEGRITO.
- Resposta direta a pergunta em 3 a 6 linhas.

## Evidencias encontradas
- Evidencia 1: <achado> [report_id | secao/campo | data]
- Evidencia 2: <achado> [report_id | secao/campo | data]
- Evidencia 3: <achado> [report_id | secao/campo | data]

## Diagnostico consolidado
- Causa/hipotese principal (Alta/Media/Baixa)
- Risco de curto prazo
- Risco de medio prazo

## Recomendacoes priorizadas
- P1: <acao> | Impacto: <...> | Esforco: <Baixo/Medio/Alto>
- P2: <acao> | Impacto: <...> | Esforco: <Baixo/Medio/Alto>
- P3: <acao> | Impacto: <...> | Esforco: <Baixo/Medio/Alto>

## Lacunas e proximos dados necessarios
- Informacao nao fornecida: <campo/contexto ausente>
- Dado adicional recomendado para aumentar confianca da resposta: <...>';

    v_pre_prompt2 := '{ "role": "user","content": "Contexto:"' || v_context  || ' "Pergunta": ' || p_query   || '"}' ;

    output := dbms_vector_chain.utl_to_generate_text( replace(replace(replace(replace(v_pre_prompt || v_pre_prompt2,chr(10),null),chr(13),null),'"',''),'''','')  , json(params_genai));

   RETURN output;

EXCEPTION
  WHEN OTHERS THEN
    -- Nao havia handler antes; loga e repropaga (comportamento de
    -- propagar o erro ao chamador continua o mesmo).
    PRC_26AI_TECH_LOG(
      p_calling_object => 'FNC_26AI_TECH_RAG',
      p_operation      => 'RAG',
      p_status         => 'ERROR',
      p_source_type    => p_source_type,
      p_provider       => 'ocigenai',
      p_error_code     => SQLCODE,
      p_error_message  => SQLERRM
    );
    RAISE;
END;
/



-- Funcao auxiliar usada por integracoes do tipo MCP (Model Context Protocol),
-- caso a AM/PM/V8 Solution queira expor o RAG do ampmInsights como uma
-- ferramenta (tool) de um agente externo.
CREATE OR REPLACE FUNCTION FNC_26AI_TECH_MCP_V1 (
  p_query IN VARCHAR2
) RETURN CLOB AS
  V_TXT CLOB;
  V_SQL CLOB;
BEGIN
  SELECT fnc_26ai_tech_rag(p_query, 60, 'OCI_CRED', 'MCPUSER')
   INTO V_TXT;
  RETURN V_TXT;
END;
/



CREATE OR REPLACE FUNCTION FNC_26ai_rag (p_ai_prompt IN CLOB,
                                          p_oci_cred  IN VARCHAR2 DEFAULT 'OCI_CRED',
                                          p_comp      IN VARCHAR2
                                          )
RETURN CLOB AS
/*
  Projeto: ampmInsights
  Objetivo: funcao global de chamada ao LLM (Llama 4, via OCI Generative AI)
            usada pelo RAG e pela geracao de insights.
*/
      v_gen_ai_endpoint VARCHAR2(500);
      v_gen_ai_model    VARCHAR2(500) := 'meta.llama-4-maverick-17b-128e-instruct-fp8';
      resp DBMS_CLOUD_TYPES.RESP;
      v_t0     PLS_INTEGER := DBMS_UTILITY.GET_TIME;
      v_result CLOB;
BEGIN
    SELECT 'https://inference.generativeai.' || param_value || '.oci.oraclecloud.com'
      INTO v_gen_ai_endpoint
      FROM TB_26AI_TECH_PARAMS WHERE param_name = 'GENAI_REGION';

    resp := dbms_cloud.send_request(
        credential_name => p_oci_cred,
        uri => v_gen_ai_endpoint || '/20231130/actions/chat',
        method => dbms_cloud.METHOD_POST,
        body => utl_raw.cast_to_raw(json_object(
            'compartmentId'         value p_comp,
            'servingMode'           value
                (json_object(
                    'modelId'       value v_gen_ai_model,
                    'servingType'   value 'ON_DEMAND'
                )),
            'chatRequest'           value
                        (json_object(
                    'apiFormat'     value 'GENERIC',
                    'messages'            value
                        (json_array(JSON_OBJECT(
                                          'role'            value 'USER',
                            'content'     value
                                (json_array(JSON_OBJECT(
                                'type'   value 'TEXT',
                                'text'    value p_ai_prompt
                            )))
                    ))),
                    'temperature'       value 0.1,
                    'topK'             value -1
                        ))
        ))
    );
    v_result := json_value( dbms_cloud.get_response_text(resp) , '$.chatResponse.choices[0].message.content[0].text') ;

    PRC_26AI_TECH_LOG(
      p_calling_object => 'FNC_26AI_RAG',
      p_operation      => 'CHAT',
      p_status         => 'OK',
      p_provider       => 'ocigenai',
      p_model_name     => v_gen_ai_model,
      p_duration_ms    => (DBMS_UTILITY.GET_TIME - v_t0) * 10,
      p_prompt_chars   => LENGTH(p_ai_prompt),
      p_response_chars => LENGTH(v_result)
    );

    RETURN v_result;

EXCEPTION
 WHEN OTHERS THEN
     PRC_26AI_TECH_LOG(
       p_calling_object => 'FNC_26AI_RAG',
       p_operation      => 'CHAT',
       p_status         => 'ERROR',
       p_provider       => 'ocigenai',
       p_model_name     => v_gen_ai_model,
       p_error_code     => SQLCODE,
       p_error_message  => SQLERRM,
       p_duration_ms    => (DBMS_UTILITY.GET_TIME - v_t0) * 10,
       p_prompt_chars   => LENGTH(p_ai_prompt)
     );
     RETURN 'Nao foi possivel gerar um texto com base nas informacoes.';
END;
/



CREATE OR REPLACE PROCEDURE prc_26ai_tech (p_rowid        IN VARCHAR DEFAULT NULL,
                                            p_oci_cred     IN VARCHAR2 DEFAULT 'OCI_CRED',
                                            p_comp         IN VARCHAR2,
                                            p_source_type  VARCHAR2 DEFAULT 'VUL'  )
/*
  Projeto: ampmInsights
  Gera o insight de IA (analise + diagnostico + recomendacoes + plano de acao)
  para um registro de TB_26AI_TECH_V2 (vulnerabilidades) ou TB_26AI_TECH_SYS
  (syslog), grava o resultado e cria o vetor (chunk) correspondente em
  TB_26AI_TECH_VECTOR.

  Para processamento em lote / carga inicial (rode manualmente uma vez, ou use
  o botao "Gerar Insights em Lote" da aplicacao APEX, que cria um job via
  DBMS_SCHEDULER lendo EMBEDDING_CREDENTIAL/EMBEDDING_COMPARTMENT a partir de
  TB_26AI_TECH_PARAMS - ver SECAO 2). O bloco abaixo tambem le o compartimento
  direto da tabela de configuracao, entao nao ha nenhum OCID para colar aqui:

    DECLARE
      v_resp CLOB;
      v_comp TB_26AI_TECH_PARAMS.PARAM_VALUE%TYPE;
    BEGIN
      SELECT param_value INTO v_comp FROM TB_26AI_TECH_PARAMS WHERE param_name = 'EMBEDDING_COMPARTMENT';
      FOR r1 IN (SELECT rowid cid FROM TB_26AI_TECH_v2 WHERE insight_report IS NULL) LOOP
        PRC_26AI_TECH (p_rowid       => r1.cid,
                       p_oci_cred    => 'OCI_CRED',
                       p_comp        => v_comp,
                       p_source_type => 'VUL');
      END LOOP;
    END;
    /
*/
AS
  v_pre_prompt      CLOB;
  v_customer_data   CLOB;
  v_pre_prompt_json CLOB;
  v_response        CLOB;
  v_json            CLOB;
  v_error           CLOB;
  v_genai_region    TB_26AI_TECH_PARAMS.PARAM_VALUE%TYPE;
BEGIN

  SELECT param_value INTO v_genai_region FROM TB_26AI_TECH_PARAMS WHERE param_name = 'GENAI_REGION';

IF p_source_type = 'VUL' THEN -- vulnerabilidades

    v_pre_prompt :='Voce e um analista senior de dados e operacoes.
Sua tarefa e interpretar os dados, comparar os resultados e produzir um relatorio executivo e tecnico acionavel.

Objetivo:
Gerar insights em 4 blocos:
1) Analise
2) Diagnostico
3) Recomendacoes
4) Plano de acao

Regras de qualidade:
- Nao invente dados ausentes. Quando faltar contexto, declare explicitamente "Informacao nao fornecida".
- Seja especifico, com linguagem executiva + tecnica.
- Priorize risco real de negocio e explorabilidade.
- Sempre indicar severidade e prioridade.
- Sempre incluir acoes de curto, medio e longo prazo.
- Quando possivel, referenciar boas praticas (OWASP, NIST, CIS, CVE/CWE).

Formato obrigatorio da resposta:
## 1. Analise
- Resumo executivo (5 a 8 linhas)
- Principais metricas observadas
- Pontos fora do padrao (outliers/anomalias)

## 2. Diagnostico
- Causas provaveis por ordem de impacto (Alta/Media/Baixa)
- Evidencias que sustentam cada causa (referenciando campos do JSON)
- Riscos de curto e medio prazo se nada for feito

## 3. Recomendacoes
Para cada recomendacao, informe:
- Acao recomendada
- Problema que resolve
- Impacto esperado
- Esforco estimado (Baixo/Medio/Alto)
- Prioridade (P1/P2/P3)

## 4. Plano de acao (30-60-90 dias)
- 30 dias: acoes imediatas e "quick wins"
- 60 dias: ajustes estruturais
- 90 dias: consolidacao e otimizacao
Para cada etapa, incluir:
- Responsavel sugerido (perfil/funcao)
- Dependencias
- Metrica de sucesso (KPI)
- Criterio de conclusao';


v_pre_prompt_json := '## FORMATO DE SAIDA JSON
Quando solicitado no modo JSON, responda EXCLUSIVAMENTE com o objeto JSON
valido conforme o schema abaixo, sem texto antes ou depois, sem markdown,
sem blocos de codigo. O JSON deve ser parseable diretamente.
Campos nao avaliaveis por qualidade de imagem devem receber null.
Nao inclua explicacoes, texto introdutorio, saudacoes ou blocos de Markdown como "```json" no comeco nem no final como "```".

## Exemplo JSON:
Retorne um JSON valido (sem comentarios), espelhando o relatorio:
{
  "id": "",
  "titulo": "",
  "analise": {
    "resumo_tecnico": "",
    "vetor_ataque": "",
    "pre_condicoes": [],
    "impacto_cia": {
      "confidencialidade": "",
      "integridade": "",
      "disponibilidade": ""
    }
  },
  "diagnostico": {
    "causa_raiz": "",
    "classificacao": {
      "cwe": "",
      "owasp": ""
    },
    "severidade": "",
    "probabilidade_exploracao": "",
    "impacto_negocio": "",
    "prioridade_final": "",
    "justificativa": ""
  },
  "recomendacoes": [
    {
      "acao": "",
      "tipo": "corretiva|preventiva|detectiva",
      "esforco": "baixo|medio|alto",
      "dependencias": []
    }
  ],
  "plano_acao": [
    {
      "prazo": "curto|medio|longo",
      "etapa": "",
      "responsavel_sugerido": "",
      "deadline_sugerido": "",
      "resultado_esperado": "",
      "criterio_aceite": ""
    }
  ],
  "lacunas_informacao": []
}';

   SELECT json_object(
        'AGE_IN_DAYS' VALUE AGE_IN_DAYS,
        'ASSET_DISPLAY_IPV4_ADDRESS' VALUE ASSET_DISPLAY_IPV4_ADDRESS,
        'ASSET_ID' VALUE ASSET_ID,
        'ASSET_NAME' VALUE ASSET_NAME,
        'ASSET_TAGS' VALUE ASSET_TAGS,
        'DEFINITION_EXPLOITABILITY_EASE' VALUE DEFINITION_EXPLOITABILITY_EASE,
        'DEFINITION_FAMILY' VALUE DEFINITION_FAMILY,
        'DEFINITION_ID' VALUE DEFINITION_ID,
        'DEFINITION_NAME' VALUE DEFINITION_NAME,
        'DEFINITION_VPR_SCORE' VALUE DEFINITION_VPR_SCORE,
        'FIRST_OBSERVED' VALUE FIRST_OBSERVED,
        'ID' VALUE ID,
        'LAST_SEEN' VALUE LAST_SEEN,
        'OUTPUT' VALUE OUTPUT,
        'PROTOCOL' VALUE PROTOCOL,
        'RESURFACED_DATE' VALUE RESURFACED_DATE,
        'SEVERITY' VALUE SEVERITY,
        'STATE' VALUE STATE,
        'INSIGHT_REPORT' VALUE INSIGHT_REPORT,
        'ID_INSIGHT' VALUE ID_INSIGHT,
        'NOME' VALUE NOME,
        'EMPRESA' VALUE EMPRESA,
        'CLASSE' VALUE CLASSE,
        'AMBIENTE' VALUE AMBIENTE,
        'STATUS_OPERACIONAL' VALUE STATUS_OPERACIONAL,
        'DESCRICAO' VALUE DESCRICAO,
        'SOX' VALUE SOX,
        'COFRE' VALUE COFRE,
        'ENDERECO_IP' VALUE ENDERECO_IP,
        'SISTEMA_OPERACIONAL' VALUE SISTEMA_OPERACIONAL,
        'TIPO_DE_REDE' VALUE TIPO_DE_REDE,
        'IS_VIRTUAL' VALUE IS_VIRTUAL,
        'CRITICIDADE' VALUE CRITICIDADE,
        'INSTANCIAS_BANCOS' VALUE INSTANCIAS_BANCOS,
        'APLICACOES_RELACIONADAS' VALUE APLICACOES_RELACIONADAS,
        'JANELAS_DE_MANUTENCAO' VALUE JANELAS_DE_MANUTENCAO,
        'ANALISTA_RESPONSAVEL' VALUE ANALISTA_RESPONSAVEL,
        'ANALISTAS_RESPONSAVEIS' VALUE ANALISTAS_RESPONSAVEIS,
        'GERENCIADO_PELO_GRUPO' VALUE GERENCIADO_PELO_GRUPO,
        'GRUPOS_RESPONSAVEIS' VALUE GRUPOS_RESPONSAVEIS,
        'GRUPO_RESPONSAVEL' VALUE GRUPO_RESPONSAVEL,
        'EDR' VALUE EDR,
        'TENABLE' VALUE TENABLE,
        'SIEM' VALUE SIEM,
        'APPLIANCE' VALUE APPLIANCE
    ) AS my_json
    INTO v_customer_data
    FROM v_26ai_tech a
    WHERE cid = p_rowid;

        v_response := FNC_26ai_rag( p_ai_prompt => v_pre_prompt || '. Dados: ' || v_customer_data,
                      p_oci_cred => p_oci_cred,
                      p_comp => p_comp
                    );

        v_json :=  replace( replace( FNC_26ai_rag( p_ai_prompt => 'MODO JSON: ' || v_pre_prompt || ' ' || v_pre_prompt_json || '. Dados: ' || v_customer_data,
                      p_oci_cred => p_oci_cred,
                      p_comp => p_comp
                    ) ,'```json','' ),'```','' );

       UPDATE TB_26AI_TECH_v2
       SET insight_report = v_response,
           insight_json =  v_json
       WHERE rowid = p_rowid;

    ELSIF p_source_type = 'SYS' THEN -- SYSLOG

        v_pre_prompt :='Voce e um Assistente de Decisao Operacional especializado em interpretacao de eventos Syslog
         gerados por servidores Linux que executam bancos de dados Oracle.

Seu objetivo e apoiar equipes de operacao, banco de dados, infraestrutura e seguranca a responder, de forma pratica e rastreavel,
perguntas como:
- Diante de um alarme, ocorrencia ou evento operacional, qual acao imediata e recomendada?
- Existe historico parecido nos dados recuperados?
- Qual procedimento operacional se aplica?
- Quando o caso deve ser escalado para DBA, infraestrutura, redes, seguranca, armazenamento ou gestao de crise?
- Qual evidencia do Syslog sustenta a recomendacao?

Esquema dos dados:
- `id`: identificador sequencial do evento no CSV.
- `timestamp`: data e hora do evento em formato ISO com fuso horario.
- `host`: servidor que gerou o evento.
- `facility`: origem Syslog, como `daemon`, `local0`, `kern`, `authpriv` ou `cron`.
- `severity`: severidade Syslog, como `info`, `notice`, `warning`, `err` ou `crit`.
- `process`: processo que registrou o evento, como `oracle`, `tnslsnr`, `rman`, `systemd`, `kernel`, `multipathd`, `crsd`, `cssd`, `oraagent`, `ora_lgwr_*`, `ora_arc*`, `ora_mrp*`, `ora_rfs*`.
- `pid`: identificador do processo.
- `oracle_instance`: instancia Oracle relacionada, como `PROD`, `STBY` ou `RAC1`.
- `message`: mensagem operacional normalizada.
- `raw_syslog`: linha Syslog original ou aproximada.

Contexto operacional esperado:
- `PROD` representa uma instancia Oracle primaria de producao.
- `STBY` representa uma instancia standby/Data Guard.
- `RAC1` representa um ambiente Oracle RAC/Clusterware.
- Eventos de `tnslsnr` estao associados ao listener Oracle e conectividade de clientes.
- Eventos de `rman` indicam operacoes de backup e recuperacao.
- Eventos `ora_arc*` indicam arquivamento de redo logs.
- Eventos `ora_lgwr*`, `ora_dbw*`, `ora_ckpt*` indicam processos criticos da instancia Oracle.
- Eventos `ora_mrp*` e `ora_rfs*` indicam replicacao/aplicacao de redo no standby.
- Eventos `crsd`, `cssd`, `ohasd` e `oraagent` indicam componentes de Oracle Clusterware/RAC.
- Eventos `kernel`, `multipathd`, `NetworkManager`, `systemd`, `chronyd`, `sshd`, `sudo` e `CROND` representam dependencias do sistema operacional, armazenamento, rede, tempo, acesso e automacao.

Regras de interpretacao:
1. Use prioritariamente os trechos recuperados pelo RAG. Nao invente eventos, historicos ou procedimentos que nao estejam sustentados pelo contexto.
2. Quando houver pouca evidencia, declare explicitamente a lacuna e indique quais dados adicionais seriam necessarios.
3. Classifique a criticidade operacional considerando `severity`, processo, instancia, mensagem, impacto potencial e recorrencia.
4. Diferencie eventos informativos de eventos que exigem acao.
5. Para eventos `err` ou `crit`, sempre avaliar impacto, acao imediata e escalacao.
6. Para eventos `warning`, avaliar tendencia, recorrencia e risco de evolucao para indisponibilidade.
7. Para eventos `info` ou `notice`, considerar se fazem parte de recuperacao, normalizacao ou sequencia posterior a uma falha.
8. Nunca recomendar comandos destrutivos ou mudancas irreversiveis sem etapa previa de validacao.
9. Quando sugerir comandos, priorize comandos de diagnostico, leitura e coleta de evidencia.
10. Use linguagem objetiva, orientada a operacao, sem excesso de explicacao teorica.

Padroes relevantes para decisao:
- `ORA-00600`: erro interno Oracle. Tratar como criticidade alta ou critica. Recomendar coleta de alert log, trace, incidente ADRCI e escalacao para DBA/fornecedor.
- `ORA-00257`: erro de archiver/FRA cheia. Tratar como risco direto de bloqueio de conexoes e indisponibilidade. Recomendar liberar espaco, validar archivelogs, backup RMAN e destino de archive.
- `RMAN-03009`: falha de backup. Recomendar verificar canal, destino, espaco, permissao, catalogo e janela de backup.
- `ORA-01034`: instancia Oracle indisponivel. Recomendar validar status da instancia, listener, alert log e servico systemd.
- `TNS-12535`: timeout de listener/conectividade. Recomendar validar rede, firewall, listener, carga e origem do cliente.
- `ORA-00060`: deadlock. Recomendar identificar sessoes/SQL envolvidos, avaliar aplicacao e recorrencia antes de escalar.
- `CRS-5017` ou falha de recurso RAC: avaliar Clusterware, recursos, logs do CRS e impacto no servico.
- Latencia em `LGWR`, `DBWR`, voting file, disco ou multipath: avaliar armazenamento, I/O, SAN, filesystem, ASM e impacto em performance.
- Eventos de Data Guard como gap, espera por sequencia, MRP/RFS: avaliar atraso de replicacao, transporte e aplicacao de redo.
- Eventos de memoria/cgroup/kernel: avaliar risco de OOM, consumo por instancia e limites de sistema.
- Eventos de `sudo`, `sshd` e autenticacao: correlacionar com mudancas operacionais ou incidentes de seguranca.

Tarefa:
Gere um report de decisao operacional com base na pergunta do usuario e no contexto recuperado.

Formato obrigatorio da resposta:
1. Resumo executivo
- Informe em 2 a 4 frases o que ocorreu, qual ativo/instancia foi afetado e qual o nivel de risco operacional.
2. Evidencias encontradas no Syslog
- Liste os eventos mais relevantes.
- Para cada evidencia, inclua `id`, `timestamp`, `host`, `oracle_instance`, `severity`, `process` e a mensagem.
- Se o contexto nao trouxer evidencia suficiente, diga isso explicitamente.
3. Interpretacao operacional
- Explique o significado provavel do evento no contexto Oracle/Linux.
- Indique se o evento parece isolado, recorrente, parte de uma sequencia de falha ou parte de recuperacao.
4. Acao recomendada
- Liste a acao imediata recomendada.
- Priorize contencao, diagnostico e reducao de impacto.
- Separe claramente o que deve ser feito agora do que deve ser tratado como analise posterior.
5. Procedimento aplicavel
- Indique o procedimento operacional mais adequado, por exemplo:
  - verificar alert log Oracle;
  - validar listener;
  - validar servico systemd;
  - checar FRA/archive logs;
  - validar backup RMAN;
  - investigar storage/multipath;
  - investigar Data Guard;
  - investigar Clusterware/RAC;
  - acionar seguranca para eventos de autenticacao suspeitos.
6. Confianca da recomendacao
- Informe `Alta`, `Media` ou `Baixa`.
- Justifique com base na quantidade e qualidade das evidencias recuperadas.
Regras finais:
- Responda em portugues do Brasil.
- Seja objetivo e operacional.
- Nao omita risco quando houver `err`, `crit`, `ORA-*`, `RMAN-*`, `CRS-*`, falha de disco, latencia de I/O, falha de listener, Data Guard gap ou falha de servico.
- Nao declare causa raiz definitiva sem evidencia suficiente.
- Sempre diferencie fato observado, inferencia e recomendacao.';

        v_pre_prompt_json := '## FORMATO DE SAIDA JSON
Quando solicitado no modo JSON, responda EXCLUSIVAMENTE com o objeto JSON
valido conforme o schema abaixo, sem texto antes ou depois, sem markdown,
sem blocos de codigo. O JSON deve ser parseable diretamente.
Campos nao avaliaveis por qualidade de imagem devem receber null.
Nao inclua explicacoes, texto introdutorio, saudacoes ou blocos de Markdown como "```json" no comeco nem no final como "```".

## Exemplo JSON:
Retorne um JSON valido (sem comentarios), espelhando o relatorio:
{
  "tipo_relatorio": "decisao_operacional_syslog_oracle_linux",
  "versao_schema": "1.0",
  "fonte_dados": {
    "arquivo_csv": "syslog_oracle_linux_examples.csv",
    "mecanismo": "RAG",
    "modelo_destino": "Llama 4",
    "campos_csv": [
      "id",
      "timestamp",
      "host",
      "facility",
      "severity",
      "process",
      "pid",
      "oracle_instance",
      "message",
      "raw_syslog"
    ]
  },
  "classificacao_evento": {
    "nivel_risco": "Baixo|Medio|Alto|Critico",
    "severidade_syslog": "info|notice|warning|err|crit",
    "categoria": "Banco de dados|Sistema operacional|Rede|Storage|Seguranca|Backup|Data Guard|RAC|Autenticacao",
    "impacto_potencial": "Descrever impacto operacional provavel",
    "estado": "Informativo|Em investigacao|Acao requerida|Escalado|Resolvido"
  },
  "resumo_executivo": {
    "descricao": "Resumo em 2 a 4 frases do evento ou alarme analisado",
    "ativo_afetado": {
      "host": "Nome do servidor afetado",
      "oracle_instance": "Instancia Oracle afetada",
      "processo": "Processo principal relacionado ao evento"
    },
    "risco_operacional": "Descricao objetiva do risco",
    "recomendacao_curta": "Acao recomendada em uma frase"
  },
  "evidencias_syslog": [
    {
      "id": 0,
      "timestamp": "YYYY-MM-DDTHH:MM:SS-03:00",
      "host": "host_origem",
      "facility": "facility_syslog",
      "severity": "severity_syslog",
      "process": "processo_origem",
      "pid": 0,
      "oracle_instance": "instancia_oracle",
      "message": "mensagem_normalizada",
      "raw_syslog": "linha_syslog_original",
      "relevancia": "Alta|Media|Baixa"
    }
  ],
  "interpretacao_operacional": {
    "fato_observado": "O que foi observado diretamente no Syslog",
    "inferencia": "Interpretacao operacional baseada nas evidencias",
    "possivel_causa": "Hipotese provavel, sem declarar causa raiz definitiva sem evidencia",
    "sequencia_eventos": "Evento isolado, sequencia de falha, recuperacao ou recorrencia",
    "recorrencia": "Isolado|Recorrente|Sequencia de falha|Recuperacao|Indeterminado"
  },
  "acoes_recomendadas": {
    "imediatas": [
      {
        "prioridade": 1,
        "acao": "Acao imediata recomendada",
        "objetivo": "Resultado esperado da acao",
        "responsavel_sugerido": "Operacao N1|Operacao N2|DBA Oracle|Linux/Infraestrutura|Storage|Redes|Seguranca|Sustentacao de Aplicacao|Gestao de Crise"
      }
    ]
  }';

   SELECT json_object(
        'ID_1' VALUE a."ID_1",
        'ID' VALUE a."ID",
        'TIMESTAMP' VALUE a."TIMESTAMP",
        'HOST' VALUE a."HOST",
        'FACILITY' VALUE a."FACILITY",
        'SEVERITY' VALUE a."SEVERITY",
        'PROCESS' VALUE a."PROCESS",
        'PID' VALUE a."PID",
        'ORACLE_INSTANCE' VALUE a."ORACLE_INSTANCE",
        'MESSAGE' VALUE a."MESSAGE",
        'RAW_SYSLOG' VALUE a."RAW_SYSLOG"
    ) AS my_json
    INTO v_customer_data
    FROM "TB_26AI_TECH_SYS" a
    WHERE rowid = p_rowid;

        v_response := FNC_26ai_rag( p_ai_prompt => v_pre_prompt || '. Dados: ' || v_customer_data,
                      p_oci_cred => p_oci_cred,
                      p_comp => p_comp
                    );

       UPDATE TB_26AI_TECH_SYS
       SET insight_report = v_response,
           insight_json =  v_json
       WHERE rowid = p_rowid;

    END IF;


    IF length(v_response) > 0 THEN

       -- vetorizacao do relatorio gerado
       INSERT INTO TB_26AI_TECH_VECTOR
            SELECT  et.embed_id,
                    v_response,
                    et.embed_vector,
                    null,
                    p_rowid,
                    p_source_type
                FROM dual
                CROSS JOIN TABLE(
                    DBMS_VECTOR_CHAIN.UTL_TO_EMBEDDINGS(
                           (  v_response ) ,
                        json('{ "provider": "ocigenai",
                                "credential_name": "' || p_oci_cred || '",
                                "url": "https://inference.generativeai.' || v_genai_region || '.oci.oraclecloud.com/20231130/actions/embedText",
                                "model": "cohere.embed-v4.0"
                            }')
                    )
                ) t
                CROSS JOIN JSON_TABLE(
                    t.column_value, '$[*]'
                    COLUMNS (
                        embed_id     NUMBER         PATH '$.embed_id',
                        text_chunk   VARCHAR2(4000) PATH '$.embed_data',
                        embed_vector CLOB           PATH '$.embed_vector'
                    )
                ) AS et;

       PRC_26AI_TECH_LOG(
         p_calling_object => 'PRC_26AI_TECH',
         p_operation      => 'EMBEDDING',
         p_status         => 'OK',
         p_source_type    => p_source_type,
         p_source_id      => p_rowid,
         p_provider       => 'ocigenai',
         p_model_name     => 'cohere.embed-v4.0',
         p_prompt_chars   => LENGTH(v_response)
       );

    END IF;

    PRC_26AI_TECH_LOG(
      p_calling_object => 'PRC_26AI_TECH',
      p_operation      => 'INSIGHT_PIPELINE',
      p_status         => 'OK',
      p_source_type    => p_source_type,
      p_source_id      => p_rowid
    );

    COMMIT;

EXCEPTION
  WHEN OTHERS THEN
    v_error := sqlerrm;
    PRC_26AI_TECH_LOG(
      p_calling_object => 'PRC_26AI_TECH',
      p_operation      => 'INSIGHT_PIPELINE',
      p_status         => 'ERROR',
      p_source_type    => p_source_type,
      p_source_id      => p_rowid,
      p_error_code     => SQLCODE,
      p_error_message  => v_error
    );
    -- Antes, o erro tecnico (sqlerrm) era gravado direto em insight_report e
    -- ficava visivel pro usuario de negocio no modal. Agora o erro completo
    -- vai so pro log (TB_26AI_TECH_LOG); o usuario ve uma mensagem amigavel.
    IF p_source_type = 'VUL' THEN
        UPDATE TB_26AI_TECH_v2
           SET insight_report = 'Nao foi possivel gerar o insight para este registro no momento.',
               insight_json =  null
           WHERE rowid = p_rowid;
    ELSIF p_source_type = 'SYS' THEN
        UPDATE TB_26AI_TECH_SYS
           SET insight_report = 'Nao foi possivel gerar o insight para este registro no momento.',
               insight_json =  null
           WHERE rowid = p_rowid;
    END IF;
    COMMIT;
END;
/

Seção 8 · Instalar a aplicacao APEX (ampmInsights)

Últimos passos, agora fora do SQL Worksheet: instalar a aplicação APEX. Detalhado em Passo a passo: aplicação APEX.

Seção 8 — copiar e colar
-- etapa 1) Criar um workspace APEX (ou reutilizar um existente).
-- etapa 2) Acessar o workspace usando o usuario de banco AMPMINSIGHTS criado
--          na SECAO 1 (App Builder > associar o schema AMPMINSIGHTS ao
--          workspace, se ainda nao estiver associado).
-- etapa 3) Fazer upload do arquivo 02_ampminsights_apex_app.sql (instalacao
--          da aplicacao). Confirmar que o "parsing schema" e AMPMINSIGHTS.
--          Confirmar "Run and Build".
-- etapa 4) Executar a aplicacao (logar com um usuario criado dentro da
--          propria aplicacao, ou com o esquema de autenticacao configurado).
-- etapa 5) Carregar os dados de CMDB (ativos/servidores).
-- etapa 6) Carregar os dados de vulnerabilidades.
-- etapa 7) Iniciar o processo de geracao de relatorios (insights) e
--          vetorizacao (botao "Gerar Insights em Lote" na aplicacao, que cria
--          um job via DBMS_SCHEDULER usando os parametros da SECAO 2).
--
-- Veja o passo a passo detalhado, com telas e checklist, em:
--   docs/05-passo-a-passo-apex.html
Terminou o script 1?

Siga para Passo a passo: aplicação APEX para importar sql/02_ampminsights_apex_app.sql.