Upload files to "/"

This commit is contained in:
2026-05-08 13:09:57 +00:00
parent 6359082956
commit 8a99fd99b8
5 changed files with 743 additions and 0 deletions

265
prc_26ai_nl2sql.sql Normal file
View File

@@ -0,0 +1,265 @@
create or replace procedure prc_26AI_NL2SQL (
p_app_user in varchar2,
p_prompt in clob,
p_profile in varchar2,
p_action in varchar2 default 'narrate',
p_limit_rows in number default 100,
p_force_runsql in varchar2 default 'N',
p_result out clob
)
as
/*
Criado por: fernando.leal@oracle.com
Data: Oct/2025
Objetivo: demonstrar casos de uso do Oracle AI Database 26ai
v1 - rotina para controle de regras ao SelectAI - leal
begin
dbms_cloud_ai.create_profile(
profile_name => 'PROF_CENSO_V1',
attributes =>
'{"provider": "oci",
"credential_name": "OCI_CRED",
"oci_compartment_id": "ocid1.compartment.oc1..xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"region": "us-chicago-1",
"model": "meta.llama-4-maverick-17b-128e-instruct-fp8",
"oci_apiformat": "GENERIC",
"object_list_mode": "automated",
"object_list": [
{"owner": "AICHAT1", "name": "TB_26AI_CENSO"}
],
"enforce_object_list": true,
"constraints":"true",
"comments": true,
"annotations": true,
"temperature": 0.1
}'
);
end;
*/
v_response clob;
v_narrate clob;
v_pre_prompt_geral clob;
v_pre_prompt_contexto clob;
v_contexto clob;
v_categoria varchar2(200);
v_profile varchar2(200);
v_estimated_rows number;
--
-- Limite de linhas que pode ser narrado
--
v_limit_rows number := p_limit_rows;
--
-- caso numero de linhas seja muito alto (definido por v_limit_rows), a acao narrate sera alterada para runwsql automaticamente
--
v_force_runsql varchar2(1) := upper(p_force_runsql);
v_dt_start TIMESTAMP ;
v_runsql clob;
begin
v_dt_start := current_timestamp;
v_pre_prompt_geral := 'Sempre utilize alias para referencias nome da tabela. Nunca cite owner.';
--
-- Ajustar e definir as categorias existentes. As categorias irao definir pre prompts mais especificos
--
SELECT DBMS_CLOUD_AI.GENERATE(prompt => 'Com base na pergunta: "' || p_prompt || '", defina qual a melhor categoria para ela: "cidades","estados","população","censo","outros".
A resposta deve ser em JSON, e deve seguir este exemplo: { "categoria": "exemplo" }. APENAS ESTE JSON DEVE SER RETORNADO.',
profile_name => p_profile,
action => 'chat') response
INTO v_contexto;
begin
SELECT jt.categoria
INTO v_categoria
FROM JSON_TABLE(
replace(replace(v_contexto,'```',''),'json',''),
'$[*]' COLUMNS (
categoria VARCHAR2(50) PATH '$.categoria'
)
) jt;
exception
when others then
p_result := v_contexto;
end;
dbms_output.put_line('v_categoria: ' || v_categoria);
if v_categoria in ('cidades','estados','população','censo') then
v_pre_prompt_contexto := 'Filtros para nome de estados sempre devem ser com 2 caracteres em MAISUCULO. Exemplo: Minas Gerais como MG, Amazonas como AM, Pará como PA';
-- pode haver definicao de varios profiles, sendo que cada um estara associado a uma categoria especifica. Neste exemplo, herda da propria chamada
v_profile := 'PROF_CENSO_V3';
else
p_result := 'O prompt informado não faz referência aos dados que podem ser retornados. Contexto: ' || v_categoria;
end if;
dbms_output.put_line('v_profile: ' || v_profile);
--
-- deve ter havido uma categorizacao da questao com associacao a um profile do SelectAI
--
if v_profile is not null then
-- resposta do showsql para analise do comando
SELECT DBMS_CLOUD_AI.GENERATE(prompt => v_pre_prompt_geral || ' ' || v_pre_prompt_contexto || ' ' || p_prompt,
profile_name => v_profile,
action => 'showsql') response
INTO v_response;
if upper(v_response) like 'SELECT%' or upper(v_response) like 'WITH%' then
-- estimativa do numero de linahs retornadas
-- baseado em estatisticas das tabelas e indices
BEGIN
EXECUTE IMMEDIATE 'EXPLAIN PLAN SET STATEMENT_ID = ''plan_selectai'' FOR ' || v_response;
SELECT MAX(cardinality)
INTO v_estimated_rows
FROM plan_table
WHERE id = 0
AND STATEMENT_ID = 'plan_selectai';
EXCEPTION
WHEN OTHERS THEN
v_estimated_rows := 0;
END;
dbms_output.put_line('v_response: ' || v_response);
dbms_output.put_line('v_estimated_rows: ' || v_estimated_rows);
dbms_output.put_line('v_limit_rows: ' || v_limit_rows);
dbms_output.put_line('v_force_runsql: ' || v_force_runsql);
-- validacoes
-- if instr(upper(v_response),'WHERE') = 0 and instr(upper(v_response),'GROUP') = 0 then
--p_result := 'O prompt informado não tem nenhum tipo de filtro. Procure adicionar algum critério, como filtros por data, empresa, região, SKU, etc';
if v_estimated_rows > v_limit_rows then
--
-- colocar recurso pra trazer dados parcisi (ate X linhas )
--
if lower(p_action) = 'narrate' and v_force_runsql = 'Y' then
-- chamada principal do SelectAI
BEGIN
SELECT DBMS_CLOUD_AI.GENERATE(prompt => v_pre_prompt_geral || ' ' || v_pre_prompt_contexto || ' ' || p_prompt,
profile_name => v_profile,
action => 'runsql') response
INTO v_narrate;
EXCEPTION
WHEN NO_DATA_FOUND THEN
v_narrate := 'Desculpe, mas não informações específicas sobre "' || p_prompt || '". regras de acesso definidas em seu perfil.';
END;
p_result := v_narrate;
else
p_result := 'Estimativa de linhas retornadas: ' || v_estimated_rows || '. Procure adicionar algum critério, como filtros por data, empresa, região, SKU, etc';
end if;
else
if p_action <> 'showsql' then
-- chamada principal do SelectAI
BEGIN
SELECT DBMS_CLOUD_AI.GENERATE(prompt => v_pre_prompt_geral || ' ' || v_pre_prompt_contexto || ' ' || p_prompt,
profile_name => v_profile,
action => p_action) response
INTO v_narrate;
/*
Feedback is available only on Oracle AI Database 26ai.
You can use it alongside the existing Select AI actions: runsql, showsql, and explainsql.
Ensure that your AI profile is configured for NL2SQL generation and not RAG.
*/
if p_action <> 'runsql' then
SELECT DBMS_CLOUD_AI.GENERATE(prompt => v_pre_prompt_geral || ' ' || v_pre_prompt_contexto || ' ' || p_prompt,
profile_name => v_profile,
action => 'runsql') response
INTO v_runsql;
end if;
EXCEPTION
WHEN NO_DATA_FOUND THEN
v_narrate := 'Desculpe, mas não informações específicas sobre "' || p_prompt || '". regras de acesso definidas em seu perfil.';
END;
p_result := v_narrate;
else
p_result := v_response; -- sql ja gerado
end if;
end if;
else
p_result := 'Não foi possível gerar uma consulta para atender sua questão: ' || v_response;
end if; -- select
end if; -- v_profile
if p_result is not null then
--
-- log
--
insert into TB_26AI_CENSO_HISTORY(USERNAME,
DT,
PROMPT,
RESPONSE,
PROFILE,
NARRATE,
DT_START,
DT_END,
PROMPT_CHARS,
RESPONSE_CHARS
)
values( lower( p_app_user ) ,
sysdate ,
p_prompt ,
v_response ,
v_profile,
--case
-- when lower(p_action) = 'narrate' then
p_result,
-- end,
v_dt_start,
current_timestamp,
DBMS_LOB.GETLENGTH(p_prompt),
case
when lower(p_action) = 'narrate' then
DBMS_LOB.GETLENGTH(v_response) + DBMS_LOB.GETLENGTH(p_result)
else
DBMS_LOB.GETLENGTH(p_result)
end
) ;
commit;
end if; -- save do p_result
dbms_output.put_line('p_result: ' || p_result);
end;
/