diff --git a/lab/init.sql b/lab/init.sql index e22a863..be4bcaf 100644 --- a/lab/init.sql +++ b/lab/init.sql @@ -19,23 +19,25 @@ END blob_to_clob; / +set serveroutput on +set define off + CREATE OR REPLACE PROCEDURE CONVERT_MDFILES_TO_JSON AS l_md_content CLOB; l_prompt CLOB; - l_final_json CLOB; - -- CHR(10) agrega saltos de linea al prompt. + l_final_json CLOB; + -- CHR(10) agrega saltos de linea al prompt enviado al modelo. BEGIN SELECT md_clob INTO l_md_content FROM markdown_files WHERE STATUS = 'INGESTED' - AND ROWNUM = 1 FOR UPDATE; -- ===================================================================== - -- Llamada unica: metadatos de la promocion y lista de comercios + -- Llamada unica: genera todo el JSON estructurado desde el markdown. -- ===================================================================== l_prompt := 'Abaixo está um documento em markdown de uma promoção do ueno bank (Paraguai), em espanhol.' || CHR(10) || - 'Extraia os metadados da promoção e TODOS os comércios em formato JSON estruturado.' || CHR(10) || CHR(10) || + 'Extraia todos os dados relevantes da promoção em formato JSON estruturado.' || CHR(10) || CHR(10) || 'Retorne APENAS o objeto JSON abaixo (sem markdown fences, sem texto antes ou depois):' || CHR(10) || '{' || CHR(10) || ' "promocao": {' || CHR(10) || @@ -51,7 +53,7 @@ BEGIN ' "ecommerce_permitido": true' || CHR(10) || ' },' || CHR(10) || ' "beneficios_por_nivel": [' || CHR(10) || - ' {"nivel": "nivel 1", "percentual_reintegro": 0, "monto_compra_minimo_gs": 0, "reintegro_maximo_gs": 0}' || CHR(10) || + ' {"nivel": "nivel N", "percentual_reintegro": 0, "monto_compra_minimo_gs": 0, "reintegro_maximo_gs": 0}' || CHR(10) || ' ],' || CHR(10) || ' "cuotas": {' || CHR(10) || ' "maximo_sin_intereses": 0,' || CHR(10) || @@ -69,17 +71,16 @@ BEGIN '}' || CHR(10) || CHR(10) || 'REGRAS:' || CHR(10) || '- Datas no formato ISO YYYY-MM-DD.' || CHR(10) || - '- Campos booleanos devem usar true ou false.' || CHR(10) || - '- Campos numericos devem usar numbers, sem aspas.' || CHR(10) || + '- Campos booleanos devem usar true ou false sem aspas.' || CHR(10) || + '- Campos numéricos devem usar números, sem aspas.' || CHR(10) || '- Valores monetários em guaranies como integers (remover "Gs.", pontos e espaços).' || CHR(10) || '- Se um campo não aparecer no documento, use null.' || CHR(10) || - '- exclusoes: liste apenas os principais pontos de exclusão em frases curtas.' || CHR(10) || CHR(10) || - 'REGRAS PARA comercios:' || CHR(10) || - '- Extraia TODOS os comércios que aparecem literalmente no documento.' || CHR(10) || - '- category: seção em negrito mais próxima (**Belleza...**, **Grupo Zavidoro...**, etc).' || CHR(10) || - '- payment_processor: "upay" ou "bancard" conforme aparece ao lado do nome.' || CHR(10) || - '- Preserve os nomes exatamente como aparecem.' || CHR(10) || - '- Não invente comércios, categorias ou processadores de pagamento.' || CHR(10) || CHR(10) || + '- exclusoes: liste apenas os principais pontos de exclusão em frases curtas.' || CHR(10) || + '- comercios: extraia TODOS os comércios que aparecem literalmente no documento.' || CHR(10) || + '- comercios.category: seção em negrito mais próxima (**Belleza...**, **Grupo Zavidoro...**, etc).' || CHR(10) || + '- comercios.payment_processor: "upay" ou "bancard" conforme aparece ao lado do nome.' || CHR(10) || + '- Preserve os nomes dos comércios exatamente como aparecem.' || CHR(10) || + '- Não invente comércios nem complete nomes ausentes.' || CHR(10) || CHR(10) || 'DOCUMENTO:' || CHR(10) || l_md_content; l_final_json := DBMS_CLOUD_AI.GENERATE( @@ -87,15 +88,12 @@ BEGIN profile_name => 'PROFILE_GPT-5.5', action => 'chat'); - - DBMS_OUTPUT.PUT_LINE('Resposta bruta do LLM: ' || LENGTH(l_final_json) || ' chars'); - IF l_final_json IS NOT JSON THEN RAISE_APPLICATION_ERROR(-20210, - 'LLM retornou JSON inválido. Últimos 200 chars: ' || SUBSTR(l_final_json, -200)); + 'Modelo retornou JSON inválido. Últimos 200 chars: ' || SUBSTR(l_final_json, -200)); END IF; - -- Guarda el JSON estructurado generado por el modelo. + -- Graba el JSON completo generado por el modelo. INSERT INTO parsed_markdown_files (data) VALUES (l_final_json); UPDATE markdown_files SET STATUS = 'LOADED'; @@ -129,4 +127,4 @@ BEGIN DBMS_OUTPUT.PUT_LINE('Total de tablas eliminadas: ' || l_count); END; -/ \ No newline at end of file +/ diff --git a/lab/json/01_convert_md_to_json.sql b/lab/json/01_convert_md_to_json.sql index 1a59417..cb318a2 100644 --- a/lab/json/01_convert_md_to_json.sql +++ b/lab/json/01_convert_md_to_json.sql @@ -35,38 +35,55 @@ FROM parsed_markdown_files; -- merchants SELECT docs.category, - docs.name + docs.name, + docs.payment_processor FROM parsed_markdown_files, JSON_TABLE(data, '$.comercios[*]' COLUMNS ( name VARCHAR2(4000) PATH '$.name', - category VARCHAR2(4000) PATH '$.category' + category VARCHAR2(4000) PATH '$.category', + payment_processor VARCHAR2(20) PATH '$.payment_processor' )) docs ORDER BY category, name; - -- beneficios por nivel SELECT docs.nivel, - docs.percentual_reintegro, + docs.percentual_reintegro, + docs.monto_compra_minimo_gs, docs.reintegro_maximo_gs FROM parsed_markdown_files, JSON_TABLE(data, '$.beneficios_por_nivel[*]' COLUMNS ( - nivel VARCHAR2(100) PATH '$.nivel', - percentual_reintegro NUMBER PATH '$.percentual_reintegro', + nivel VARCHAR2(20) PATH '$.nivel', + percentual_reintegro NUMBER PATH '$.percentual_reintegro', + monto_compra_minimo_gs NUMBER PATH '$.monto_compra_minimo_gs', reintegro_maximo_gs NUMBER PATH '$.reintegro_maximo_gs' )) docs ORDER BY 1; +-- Tabela de benefícios +SELECT nivel, percentual_reintegro || '%' AS pct, + TO_CHAR(monto_compra_minimo_gs, '999G999G999') AS monto_min, + TO_CHAR(reintegro_maximo_gs, '999G999G999') AS reint_max + FROM parsed_markdown_files t, + JSON_TABLE(t.data, '$.beneficios_por_nivel[*]' + COLUMNS ( + nivel VARCHAR2(20) PATH '$.nivel', + percentual_reintegro NUMBER PATH '$.percentual_reintegro', + monto_compra_minimo_gs NUMBER PATH '$.monto_compra_minimo_gs', + reintegro_maximo_gs NUMBER PATH '$.reintegro_maximo_gs' + )) + ORDER BY nivel DESC; -- Comércios por categoria -SELECT category, COUNT(*) AS qtd +SELECT category, payment_processor, COUNT(*) AS qtd FROM parsed_markdown_files t, JSON_TABLE(t.data, '$.comercios[*]' COLUMNS ( - category VARCHAR2(200) PATH '$.category' + category VARCHAR2(200) PATH '$.category', + payment_processor VARCHAR2(20) PATH '$.payment_processor' )) - GROUP BY category + GROUP BY category, payment_processor ORDER BY qtd DESC; @@ -75,37 +92,9 @@ SELECT docs.data.promocao.titulo.string() as titulo, docs.data.promocao.vigencia.data_inicio || ' a ' || docs.data.promocao.vigencia.data_fim as vigencia, docs.data.medios_pago.rede as rede, - docs.data.medios_pago.qr_permitido as qr_permitido + docs.data.medios_pago.qr_permitido as qr_permitido, + docs.data.cuotas.maximo_sin_intereses as max_sin_intereses, + docs.data.acreditacao_reintegro.prazo_dias_habiles as prazo_dias_habiles FROM parsed_markdown_files docs; - -/* -------------------------------- - -DUALITY VIEW - --------------------------------- */ --- convert -DECLARE - schema_sql clob; -BEGIN - schema_sql := dbms_json_duality.infer_and_generate_schema( - json('{"tableNames" : [ "PARSED_MARKDOWN_FILES" ], - "viewNames" : [ "T" ], - "useFlexFields" : false, - "updatability" : true, - "sourceSchema" : "APP_USER"}') - ); - - execute immediate schema_sql; - - dbms_json_duality.import(table_name => 'PARSED_MARKDOWN_FILES', view_name => 'T'); - - execute immediate 'rename T TO V_PARSED_MARKDOWN_FILES'; -END; -/ - - -SELECT * FROM T_ROOT; -SELECT * FROM T_BENEFICIOS_POR_NIVEL; -SELECT * FROM T_COMERCIOS; diff --git a/lab/json/02_create_duality_view.sql b/lab/json/02_create_duality_view.sql new file mode 100644 index 0000000..5e02a89 --- /dev/null +++ b/lab/json/02_create_duality_view.sql @@ -0,0 +1,46 @@ + +set serveroutput on +set long 999999 +set linesize 200 +set pagesize 5000 + +/* -------------------------------- + +DUALITY VIEW + +-------------------------------- */ +-- convert +DECLARE + schema_sql clob; +BEGIN + schema_sql := dbms_json_duality.infer_and_generate_schema( + json('{"tableNames" : [ "PARSED_MARKDOWN_FILES" ], + "viewNames" : [ "T" ], + "useFlexFields" : false, + "updatability" : true, + "sourceSchema" : "APP_USER"}') + ); + + execute immediate schema_sql; + + dbms_json_duality.import(table_name => 'PARSED_MARKDOWN_FILES', view_name => 'T'); + + execute immediate 'rename T TO V_PARSED_MARKDOWN_FILES'; +END; +/ + + +SELECT * FROM T_ROOT; +SELECT * FROM T_COMERCIOS; +SELECT * FROM T_BENEFICIOS_POR_NIVEL; +SELECT * FROM T_ACREDITACAO_REINTEGRO; +SELECT * FROM T_CUOTAS; +SELECT * FROM T_MEDIOS_PAGO; +SELECT * FROM T_PROMOCAO; +SELECT * FROM T_VIGENCIA; + + +-- duality view collection +SELECT json_serialize(data returning clob pretty) AS json_data +FROM v_parsed_markdown_files; + diff --git a/lab/selectai/01_profiles.sql b/lab/selectai/01_profiles.sql new file mode 100644 index 0000000..4032f09 --- /dev/null +++ b/lab/selectai/01_profiles.sql @@ -0,0 +1,43 @@ + +EXEC DBMS_CLOUD_AI.DROP_PROFILE('PROFILE_LLAMA_4_17B'); + +BEGIN + DBMS_CLOUD_AI.CREATE_PROFILE( + profile_name => 'PROFILE_LLAMA_4_17B', + attributes => JSON_OBJECT( + 'provider' VALUE 'oci', + 'credential_name' VALUE 'OCI$RESOURCE_PRINCIPAL', + 'model' VALUE 'meta.llama-4-maverick-17b-128e-instruct-fp8', + 'region' VALUE 'us-chicago-1', + 'oci_compartment_id' VALUE 'ocid1.compartment.oc1..aaaaaaaa33ogmhasyvcqzuvkrfzo5mavh3q2le7mgwmy74yzpyqi7byxgrlq', + 'temperature' VALUE 0, + 'max_tokens' VALUE 4096 + )); +END; +/ + + +EXEC DBMS_CLOUD_AI.DROP_PROFILE('PROFILE_GPT-5.5'); + +BEGIN + DBMS_CLOUD_AI.CREATE_PROFILE( + profile_name => 'PROFILE_GPT-5.5', + attributes => JSON_OBJECT( + 'provider' VALUE 'oci', + 'credential_name' VALUE 'OCI$RESOURCE_PRINCIPAL', + 'model' VALUE 'openai.gpt-5.5', + 'region' VALUE 'us-chicago-1', + 'oci_compartment_id' VALUE 'ocid1.compartment.oc1..aaaaaaaa33ogmhasyvcqzuvkrfzo5mavh3q2le7mgwmy74yzpyqi7byxgrlq', + 'temperature' VALUE 1, + 'max_tokens' VALUE 8192 + )); +END; +/ + +set define off +var response CLOB +exec :response := DBMS_CLOUD_AI.GENERATE(prompt => 'Hola', profile_name => 'PROFILE_GPT-5.5', action => 'chat'); +print :response + + + diff --git a/lab/selectai/01_profiles_and_annotations.sql b/lab/selectai/01_profiles_and_annotations.sql deleted file mode 100644 index 48139a7..0000000 --- a/lab/selectai/01_profiles_and_annotations.sql +++ /dev/null @@ -1,22 +0,0 @@ -EXEC DBMS_CLOUD_AI.DROP_PROFILE('PROFILE_GPT-5.5'); - -BEGIN - DBMS_CLOUD_AI.CREATE_PROFILE( - profile_name => 'PROFILE_GPT-5.5', - attributes => JSON_OBJECT( - 'provider' VALUE 'oci', - 'credential_name' VALUE 'OCI$RESOURCE_PRINCIPAL', - 'model' VALUE 'openai.gpt-5.5', - 'region' VALUE 'us-chicago-1', - 'oci_compartment_id' VALUE 'ocid1.compartment.oc1..aaaaaaaa33ogmhasyvcqzuvkrfzo5mavh3q2le7mgwmy74yzpyqi7byxgrlq', - 'temperature' VALUE 1, - 'max_tokens' VALUE 8192 - )); -END; -/ - -set define off -var response CLOB -exec :response := DBMS_CLOUD_AI.GENERATE(prompt => 'Olá', profile_name => 'PROFILE_GPT-5.5', action => 'chat'); -print :response - diff --git a/lab/selectai/02_annotations.sql b/lab/selectai/02_annotations.sql new file mode 100644 index 0000000..8c10891 --- /dev/null +++ b/lab/selectai/02_annotations.sql @@ -0,0 +1,297 @@ +set define off + +-- ============================================================================ +-- Lab: Annotations para SELECT AI +-- ============================================================================ +-- Este script documenta tablas relacionales generadas desde una JSON COLLECTION +-- TABLE y la JSON Relational Duality View que recompone el documento. +-- +-- SELECT AI usa nombres, relaciones y metadatos para generar SQL. Las +-- annotations ayudan a explicar el significado de tablas y columnas con +-- lenguaje de negocio, sinonimos y pistas de uso. +-- +-- Nota: para ejecutar el lab mas de una vez, cambie ADD por REPLACE en las +-- annotations que ya existan. +-- ============================================================================ + +-- ============================================================================ +-- T_ROOT: tabla raiz del documento JSON +-- ============================================================================ + +ALTER TABLE t_root ANNOTATIONS ( + ADD display_name 'Documento de promocion', + ADD business_description 'Tabla raiz de cada promocion extraida desde markdown. Une promocion, medios de pago, cuotas, reintegro, exclusiones y listas hijas.', + ADD select_ai_hint 'Use esta tabla como punto de partida cuando la pregunta sea sobre una promocion completa o sobre condiciones generales.', + ADD example_questions 'Que promociones existen? Cuales son las exclusiones? Que banco emite la promocion?' +); + +ALTER TABLE t_root MODIFY "_id" ANNOTATIONS ( + ADD display_name 'Identificador del documento', + ADD business_description 'Clave tecnica RAW que identifica un documento JSON de promocion.', + ADD select_ai_hint 'No mostrar al usuario final salvo que pida identificadores tecnicos.' +); + +ALTER TABLE t_root MODIFY exclusoes ANNOTATIONS ( + ADD display_name 'Exclusiones de la promocion', + ADD business_description 'Lista JSON con reglas, restricciones y condiciones que excluyen compras o situaciones del beneficio.', + ADD synonyms 'restricciones, condiciones, limitaciones, no aplica, fuera de promocion', + ADD select_ai_hint 'Cuando el usuario pregunte que no aplica o que esta excluido, consulte este campo JSON.' +); + +ALTER TABLE t_root MODIFY titulo_t_promocao ANNOTATIONS ( + ADD display_name 'Titulo de la promocion', + ADD business_description 'Referencia al titulo principal de la promocion.', + ADD relationship_hint 'Clave foranea hacia T_PROMOCAO.TITULO.' +); + +ALTER TABLE t_root MODIFY emissor_t_medios_pago ANNOTATIONS ( + ADD display_name 'Emisor del medio de pago', + ADD business_description 'Banco o entidad emisora usada para enlazar los medios de pago aceptados.', + ADD relationship_hint 'Clave foranea hacia T_MEDIOS_PAGO.EMISSOR.' +); + +ALTER TABLE t_root MODIFY maximo_sin_intereses_t_cuotas ANNOTATIONS ( + ADD display_name 'Maximo de cuotas sin intereses', + ADD business_description 'Valor usado para enlazar las condiciones de cuotas sin intereses.', + ADD unit 'cuotas', + ADD relationship_hint 'Clave foranea hacia T_CUOTAS.MAXIMO_SIN_INTERESES.' +); + +ALTER TABLE t_root MODIFY prazo_dias_habiles_t_acreditacao_reintegro ANNOTATIONS ( + ADD display_name 'Plazo de acreditacion del reintegro', + ADD business_description 'Cantidad de dias habiles usada para enlazar la regla de acreditacion del reintegro.', + ADD unit 'dias habiles', + ADD relationship_hint 'Clave foranea hacia T_ACREDITACAO_REINTEGRO.PRAZO_DIAS_HABILES.' +); + +-- ============================================================================ +-- T_PROMOCAO y T_VIGENCIA: datos principales de la promocion +-- ============================================================================ + +ALTER TABLE t_promocao ANNOTATIONS ( + ADD display_name 'Promocion', + ADD business_description 'Datos principales de la oferta bancaria, incluyendo titulo y vigencia.', + ADD select_ai_hint 'Use esta tabla para responder preguntas sobre el nombre de la promocion y sus fechas.' +); + +ALTER TABLE t_promocao MODIFY titulo ANNOTATIONS ( + ADD display_name 'Titulo', + ADD business_description 'Nombre oficial de la promocion segun el documento markdown.', + ADD synonyms 'nombre de promocion, campana, oferta' +); + +ALTER TABLE t_promocao MODIFY data_inicio_t_vigencia ANNOTATIONS ( + ADD display_name 'Fecha de inicio', + ADD business_description 'Fecha inicial de vigencia enlazada con T_VIGENCIA.', + ADD relationship_hint 'Parte de la clave compuesta hacia T_VIGENCIA.' +); + +ALTER TABLE t_promocao MODIFY data_fim_t_vigencia ANNOTATIONS ( + ADD display_name 'Fecha de fin', + ADD business_description 'Fecha final de vigencia enlazada con T_VIGENCIA.', + ADD relationship_hint 'Parte de la clave compuesta hacia T_VIGENCIA.' +); + +ALTER TABLE t_vigencia ANNOTATIONS ( + ADD display_name 'Vigencia', + ADD business_description 'Rango de fechas en que la promocion esta activa.', + ADD example_questions 'La promocion sigue vigente? Cuando termina la promocion?' +); + +ALTER TABLE t_vigencia MODIFY data_inicio ANNOTATIONS ( + ADD display_name 'Inicio de vigencia', + ADD business_description 'Primer dia en que la promocion puede ser aplicada.', + ADD unit 'fecha' +); + +ALTER TABLE t_vigencia MODIFY data_fim ANNOTATIONS ( + ADD display_name 'Fin de vigencia', + ADD business_description 'Ultimo dia en que la promocion puede ser aplicada.', + ADD unit 'fecha' +); + +-- ============================================================================ +-- T_MEDIOS_PAGO: tarjetas, QR, ecommerce y billeteras +-- ============================================================================ + +ALTER TABLE t_medios_pago ANNOTATIONS ( + ADD display_name 'Medios de pago', + ADD business_description 'Condiciones de pago aceptadas por la promocion, como tarjetas, red, QR, ecommerce y billeteras electronicas.', + ADD select_ai_hint 'Use esta tabla para preguntas sobre como pagar para recibir el beneficio.' +); + +ALTER TABLE t_medios_pago MODIFY emissor ANNOTATIONS ( + ADD display_name 'Emisor', + ADD business_description 'Entidad emisora aceptada para participar de la promocion.', + ADD synonyms 'banco, entidad, issuer' +); + +ALTER TABLE t_medios_pago MODIFY rede ANNOTATIONS ( + ADD display_name 'Red de tarjeta', + ADD business_description 'Red o marca de tarjeta aceptada por la promocion.', + ADD synonyms 'marca, red, network' +); + +ALTER TABLE t_medios_pago MODIFY qr_permitido ANNOTATIONS ( + ADD display_name 'QR permitido', + ADD business_description 'Indica si el pago por QR participa de la promocion.', + ADD select_ai_hint 'Campo booleano: true significa que QR esta permitido.' +); + +ALTER TABLE t_medios_pago MODIFY ecommerce_permitido ANNOTATIONS ( + ADD display_name 'Ecommerce permitido', + ADD business_description 'Indica si compras online o ecommerce participan de la promocion.', + ADD select_ai_hint 'Campo booleano: true significa que ecommerce esta permitido.' +); + +ALTER TABLE t_medios_pago MODIFY tarjetas_aceitas ANNOTATIONS ( + ADD display_name 'Tarjetas aceptadas', + ADD business_description 'Array JSON con las tarjetas o productos de tarjeta que califican.', + ADD synonyms 'tarjetas validas, tarjetas admitidas, tarjetas participantes', + ADD select_ai_hint 'Use funciones JSON cuando necesite filtrar por una tarjeta especifica.' +); + +ALTER TABLE t_medios_pago MODIFY billeteras_electronicas ANNOTATIONS ( + ADD display_name 'Billeteras electronicas', + ADD business_description 'Array JSON con billeteras digitales aceptadas por la promocion.', + ADD synonyms 'wallets, billeteras, monederos digitales', + ADD select_ai_hint 'Use funciones JSON cuando el usuario pregunte por una billetera especifica.' +); + +-- ============================================================================ +-- T_CUOTAS: informacion de financiamiento +-- ============================================================================ + +ALTER TABLE t_cuotas ANNOTATIONS ( + ADD display_name 'Cuotas', + ADD business_description 'Reglas de financiamiento sin intereses para la promocion.', + ADD example_questions 'Cuantas cuotas sin intereses permite la promocion? El cliente debe pedir las cuotas?' +); + +ALTER TABLE t_cuotas MODIFY maximo_sin_intereses ANNOTATIONS ( + ADD display_name 'Maximo sin intereses', + ADD business_description 'Cantidad maxima de cuotas sin intereses permitidas.', + ADD unit 'cuotas' +); + +ALTER TABLE t_cuotas MODIFY requer_solicitacao_cliente ANNOTATIONS ( + ADD display_name 'Requiere solicitud del cliente', + ADD business_description 'Indica si el cliente debe solicitar expresamente el pago en cuotas.', + ADD select_ai_hint 'Campo booleano: true significa que el cliente debe solicitarlo.' +); + +-- ============================================================================ +-- T_ACREDITACAO_REINTEGRO: como se acredita el beneficio +-- ============================================================================ + +ALTER TABLE t_acreditacao_reintegro ANNOTATIONS ( + ADD display_name 'Acreditacion del reintegro', + ADD business_description 'Reglas de acreditacion del reintegro: plazo, porcentaje acreditado y tipo de cuenta.', + ADD select_ai_hint 'Use esta tabla para preguntas sobre cuando y donde se acredita el reintegro.' +); + +ALTER TABLE t_acreditacao_reintegro MODIFY prazo_dias_habiles ANNOTATIONS ( + ADD display_name 'Plazo en dias habiles', + ADD business_description 'Cantidad de dias habiles hasta acreditar el reintegro.', + ADD unit 'dias habiles', + ADD synonyms 'plazo, demora, tiempo de acreditacion' +); + +ALTER TABLE t_acreditacao_reintegro MODIFY percentual_creditado ANNOTATIONS ( + ADD display_name 'Porcentaje acreditado', + ADD business_description 'Porcentaje del reintegro que sera acreditado al cliente.', + ADD unit 'porcentaje' +); + +ALTER TABLE t_acreditacao_reintegro MODIFY tipo_conta ANNOTATIONS ( + ADD display_name 'Tipo de cuenta', + ADD business_description 'Cuenta o producto donde se acredita el reintegro.', + ADD synonyms 'cuenta destino, producto, cuenta de acreditacion' +); + +-- ============================================================================ +-- T_COMERCIOS: comercios participantes +-- ============================================================================ + +ALTER TABLE t_comercios ANNOTATIONS ( + ADD display_name 'Comercios participantes', + ADD business_description 'Lista de comercios que participan de la promocion, con categoria y procesador de pago.', + ADD select_ai_hint 'Use esta tabla para preguntas sobre donde comprar, rubros participantes o procesadores de pago.' +); + +ALTER TABLE t_comercios MODIFY name ANNOTATIONS ( + ADD display_name 'Nombre del comercio', + ADD business_description 'Nombre del comercio tal como aparece en el documento original.', + ADD synonyms 'tienda, local, merchant, comercio' +); + +ALTER TABLE t_comercios MODIFY category ANNOTATIONS ( + ADD display_name 'Categoria del comercio', + ADD business_description 'Rubro o seccion del markdown donde aparece el comercio.', + ADD synonyms 'rubro, segmento, categoria, grupo' +); + +ALTER TABLE t_comercios MODIFY payment_processor ANNOTATIONS ( + ADD display_name 'Procesador de pago', + ADD business_description 'Procesador asociado al comercio, por ejemplo upay o bancard.', + ADD synonyms 'procesadora, adquirente, payment processor' +); + +ALTER TABLE t_comercios MODIFY "_id_t_root" ANNOTATIONS ( + ADD display_name 'Documento de origen', + ADD business_description 'Clave tecnica que enlaza el comercio con el documento raiz de la promocion.', + ADD relationship_hint 'Clave foranea hacia T_ROOT."_id".' +); + +-- ============================================================================ +-- T_BENEFICIOS_POR_NIVEL: reglas de reintegro por nivel +-- ============================================================================ + +ALTER TABLE t_beneficios_por_nivel ANNOTATIONS ( + ADD display_name 'Beneficios por nivel', + ADD business_description 'Lista de niveles de cliente y sus reglas de reintegro, compra minima y tope maximo.', + ADD select_ai_hint 'Use esta tabla para comparar beneficios entre niveles.' +); + +ALTER TABLE t_beneficios_por_nivel MODIFY nivel ANNOTATIONS ( + ADD display_name 'Nivel del cliente', + ADD business_description 'Nivel o segmento del cliente dentro de la promocion.', + ADD synonyms 'segmento, nivel, categoria de cliente' +); + +ALTER TABLE t_beneficios_por_nivel MODIFY percentual_reintegro ANNOTATIONS ( + ADD display_name 'Porcentaje de reintegro', + ADD business_description 'Porcentaje de reintegro ofrecido para el nivel.', + ADD unit 'porcentaje' +); + +ALTER TABLE t_beneficios_por_nivel MODIFY monto_compra_minimo_gs ANNOTATIONS ( + ADD display_name 'Compra minima', + ADD business_description 'Monto minimo de compra requerido para aplicar el beneficio.', + ADD unit 'guaranies' +); + +ALTER TABLE t_beneficios_por_nivel MODIFY reintegro_maximo_gs ANNOTATIONS ( + ADD display_name 'Tope maximo de reintegro', + ADD business_description 'Monto maximo de reintegro que puede recibir el cliente en guaranies.', + ADD unit 'guaranies', + ADD synonyms 'tope, limite, maximo de devolucion' +); + +ALTER TABLE t_beneficios_por_nivel MODIFY "_id_t_root" ANNOTATIONS ( + ADD display_name 'Documento de origen', + ADD business_description 'Clave tecnica que enlaza el beneficio por nivel con el documento raiz de la promocion.', + ADD relationship_hint 'Clave foranea hacia T_ROOT."_id".' +); + +-- ============================================================================ +-- V_PARSED_MARKDOWN_FILES: vista JSON Relational Duality +-- ============================================================================ + +ALTER VIEW v_parsed_markdown_files ANNOTATIONS ( + ADD display_name 'Documento JSON de promocion', + ADD business_description 'JSON Relational Duality View que presenta las tablas normalizadas como un unico documento JSON consultable y actualizable.', + ADD select_ai_hint 'Use esta vista cuando el usuario pregunte por el documento completo o quiera ver la estructura JSON de la promocion.', + ADD example_questions 'Muestre el JSON completo de la promocion. Que comercios aparecen dentro del documento? Que condiciones tiene la promocion?' +); + diff --git a/lab/selectai/03_select.sql b/lab/selectai/03_select.sql new file mode 100644 index 0000000..fb52460 --- /dev/null +++ b/lab/selectai/03_select.sql @@ -0,0 +1,123 @@ +set serveroutput on +set long 9999999 +set pagesize 1000 +set linesize 200 +-- ============================================================================ +-- Profile especializado para el lab de SELECT AI con annotations +-- ============================================================================ +-- Este profile limita el contexto de SELECT AI a las tres tablas del ejercicio. +-- La opcion annotations=true envia las annotations de tablas y columnas al modelo. +-- enforce_object_list=true evita que el modelo use otras tablas accesibles. + +BEGIN + DBMS_CLOUD_AI.DROP_PROFILE( + profile_name => 'PROFILE_GPT_5_5_SELECTAI', + force => TRUE + ); +END; +/ + +BEGIN + DBMS_CLOUD_AI.CREATE_PROFILE( + profile_name => 'PROFILE_GPT_5_5_SELECTAI', + attributes => JSON_OBJECT( + 'provider' VALUE 'oci', + 'credential_name' VALUE 'OCI$RESOURCE_PRINCIPAL', + 'model' VALUE 'openai.gpt-5.5', + 'region' VALUE 'us-chicago-1', + 'oci_compartment_id' VALUE 'ocid1.compartment.oc1..aaaaaaaa33ogmhasyvcqzuvkrfzo5mavh3q2le7mgwmy74yzpyqi7byxgrlq', + 'temperature' VALUE 1, + 'max_tokens' VALUE 8192, + 'annotations' VALUE 'true', + 'object_list_mode' VALUE 'all', + 'enforce_object_list' VALUE 'true', + 'object_list' VALUE JSON_ARRAY( + JSON_OBJECT('owner' VALUE USER, 'name' VALUE 'T_ROOT'), + JSON_OBJECT('owner' VALUE USER, 'name' VALUE 'T_COMERCIOS'), + JSON_OBJECT('owner' VALUE USER, 'name' VALUE 'T_BENEFICIOS_POR_NIVEL') + ) + ), + description => 'Profile para SELECT AI usando annotations y solamente T_ROOT, T_COMERCIOS y T_BENEFICIOS_POR_NIVEL.' + ); +END; +/ + +-- ============================================================================ +-- Lab: Consultas SELECT AI usando annotations +-- ============================================================================ +-- Este script contiene ejemplos didacticos de SELECT AI. +-- +-- La accion SHOWSQL muestra el SQL que el modelo genera. Esto ayuda a ver como +-- las annotations de display_name, business_description, synonyms y +-- relationship_hint guian la seleccion de tablas, columnas y joins. + + +-- SELECT AI modes: +-- showsql: cuando quieres obtener el SQL generado, revisarlo, auditarlo, registrarlo en logs o ejecutarlo después con control propio. +-- runsql: cuando aceptas que la base de datos genere y ejecute la consulta directamente. +-- narrate: cuando quieres una respuesta en lenguaje natural basada en el resultado. +-- explainsql: cuando quieres explicar el SQL generado. +-- chat: conversación general con el modelo, no necesariamente sobre SQL. + +-- ============================================================================ + +EXEC DBMS_CLOUD_AI.SET_PROFILE('PROFILE_GPT_5_5_SELECTAI'); + +-- ============================================================================ +-- Ejemplo 1: sinonimos de comercios y categorias +-- ============================================================================ +-- Las annotations indican que T_COMERCIOS representa "Comercios participantes" +-- y que NAME tambien puede ser entendido como tienda, local, merchant o comercio. +-- El prompt usa lenguaje de negocio, no nombres tecnicos de columnas. + +-- select ai +SELECT AI runsql Muestra los locales participantes por rubro, indicando el comercio, la categoria y la procesadora de pago.; + + +SELECT JSON_SERIALIZE( + DBMS_CLOUD_AI.GENERATE( + prompt => 'Muestra los locales participantes por rubro, indicando el comercio, la categoria y la procesadora de pago.', + profile_name => 'PROFILE_GPT_5_5_SELECTAI', + action => 'runsql' + ) returning CLOB pretty); + + + +-- dbms_cloud_ai +DECLARE + l_response CLOB; + BEGIN + l_response := DBMS_CLOUD_AI.GENERATE( + prompt => 'Muestra los locales participantes por rubro, indicando el comercio, la categoria y la procesadora de pago.', + profile_name => 'PROFILE_GPT_5_5_SELECTAI', + action => 'showsql' + ); + + DBMS_OUTPUT.PUT_LINE(DBMS_LOB.SUBSTR(l_response, 4000, 1)); + END; + / + +-- ============================================================================ +-- Ejemplo 2: beneficio por segmento, compra minima y tope +-- ============================================================================ +-- Las annotations explican que "nivel" tambien puede significar segmento o +-- categoria de cliente, y que "tope" es el reintegro maximo en guaranies. +-- El prompt usa esos sinonimos para mostrar como SELECT AI entiende el negocio. + +SELECT AI runsql + Para cada segmento de cliente, lista el porcentaje de reintegro, + la compra minima requerida y el tope maximo de devolucion en guaranies. +; + +-- ============================================================================ +-- Ejemplo 3: pregunta de negocio cruzando comercios y niveles +-- ============================================================================ +-- Este ejemplo combina tres tablas. Las relationship_hint de T_ROOT, T_COMERCIOS +-- y T_BENEFICIOS_POR_NIVEL ayudan al modelo a entender que ambas listas hijas +-- pertenecen al mismo documento de promocion. + +SELECT AI runsql + Combine los comercios participantes con los beneficios por nivel de cliente del mismo documento. + Muestra rubro, comercio, procesadora, nivel del cliente, porcentaje de reintegro, compra minima + y limite maximo de devolucion. +; diff --git a/lab/vector/01_load_markdown.sql b/lab/vector/01_load_markdown.sql index 4bcecc8..1431e58 100644 --- a/lab/vector/01_load_markdown.sql +++ b/lab/vector/01_load_markdown.sql @@ -27,7 +27,7 @@ DECLARE l_clob CLOB; l_blob BLOB; l_uri VARCHAR2(1000) := 'https://objectstorage.us-chicago-1.oraclecloud.com/n/idi1o0a010nx/b/md-processed/o/'; - l_filename VARCHAR2(500) := 'Banco Atlas - Beneficios Estaciones de Servicio.md'; + l_filename VARCHAR2(500) := 'Promocion-Reintegro-30-de-reintegro-en-comercios-adheridos-12-cuotas-sin-interese-_-DIC-2025v1.md'; BEGIN l_clob := blob_to_clob(DBMS_CLOUD.GET_OBJECT(credential_name => 'OCI$RESOURCE_PRINCIPAL', object_uri => l_uri || l_filename)); diff --git a/lab/vector/03_vectorizing.sql b/lab/vector/03_vectorizing.sql index e221889..a499e9b 100644 --- a/lab/vector/03_vectorizing.sql +++ b/lab/vector/03_vectorizing.sql @@ -13,7 +13,7 @@ update markdown_files commit; -select md_clob, embedding from markdown_files; +select substr(md_clob, 50, 60) as md_preview, embedding from markdown_files; @@ -23,7 +23,7 @@ select VECTOR_DISTANCE(embedding, VECTOR_EMBEDDING( MULTILINGUAL_E5_BASE - USING 'estaciones de servicio' as DATA) + USING 'tarjeta de crédito' as DATA) ) as distance from markdown_files; @@ -68,7 +68,6 @@ FROM markdown_files m, ) ) et; -commit; -- ver los chunks generados select doc_id, file_name, chunk_id, chunk_text @@ -83,7 +82,7 @@ select chunk_id, VECTOR_DISTANCE(embedding, VECTOR_EMBEDDING(MULTILINGUAL_E5_BASE - USING 'estaciones de servicio' as DATA + USING 'tarjeta de crédito' as DATA ) ) as distance, chunk_text diff --git a/lab/vector/04_indexing.sql b/lab/vector/04_indexing.sql index 5a44bc2..08f345c 100644 --- a/lab/vector/04_indexing.sql +++ b/lab/vector/04_indexing.sql @@ -1,6 +1,6 @@ set long 999999 -set pages 1000 -set lines 200 +set pagesize 1000 +set linesize 200 /* -------------------------------- @@ -9,8 +9,6 @@ HNSW INDEX -------------------------------- */ -- crear indice HNSW para busquedas vectoriales aproximadas -DROP INDEX IF EXISTS markdown_chunks_hnsw_idx; - CREATE VECTOR INDEX markdown_chunks_hnsw_idx ON markdown_chunks (embedding) ORGANIZATION INMEMORY NEIGHBOR GRAPH @@ -22,6 +20,7 @@ PARAMETERS ( EFCONSTRUCTION 200 ); +DROP INDEX IF EXISTS markdown_chunks_hnsw_idx; /* -------------------------------- @@ -30,8 +29,6 @@ IVF INDEX -------------------------------- */ -- crear indice IVF con DOC_ID incluido para filtros por documento -DROP INDEX IF EXISTS markdown_chunks_ivf_idx; - CREATE VECTOR INDEX markdown_chunks_ivf_idx ON markdown_chunks (embedding) INCLUDE (doc_id) @@ -44,7 +41,7 @@ PARAMETERS ( MIN_VECTORS_PER_PARTITION 1 ); - +DROP INDEX IF EXISTS markdown_chunks_ivf_idx; /* -------------------------------- TEXT INDEX + VECTOR SEARCH @@ -63,24 +60,22 @@ select SCORE(1) as text_score, VECTOR_DISTANCE(embedding, VECTOR_EMBEDDING(MULTILINGUAL_E5_BASE - USING 'estaciones de servicio' as DATA + USING 'Sucursales adheridas' as DATA ) ) as vector_distance, chunk_text from markdown_chunks -where contains(chunk_text, 'Beneficio - AND FUZZY(Benefcio) - AND ABOUT(estaciones) - AND (Visa accum Mastercard) - AND (Clásica OR Oro) - AND (crédito NOT débito) - AND NEAR((POS, Infonet), 5) - AND NEAR((App, Premmia, Petrobras), 1) - AND (consumo AND personal)', 1) > 0 +where contains(chunk_text, '(Mastercard ACCUM NEAR((Dúo,Clásica),1))', 1) > 0 order by vector_distance fetch first 3 rows only; - +-- Promoción +-- FUZZY(Promción) +-- ABOUT(Promoción) +-- NEAR((tarjetas, físicas, billeteras, electrónicas), 5) +-- Black OR Albirroja +-- BENDITA ACCUM STYLE +-- (Mastercard ACCUM NEAR((Dúo,Clásica),1)) /* -------------------------------- @@ -103,14 +98,15 @@ PARALLEL 2; SELECT JSON_SERIALIZE( DBMS_HYBRID_VECTOR.SEARCH( JSON('{ - "hybrid_index_name" : "markdown_chunks_hybrid_idx", + "hybrid_index_name" : "markdown_chunks_hybrid_idx", "search_fusion" : "UNION", + "search_scorer" : "rsf", "vector" : { - "search_text" : "beneficios en estaciones de servicio", - "search_mode" : "CHUNK" + "search_text" : "Vigencia", + "search_mode" : "CHUNK" }, "text" : { - "contains" : "estaciones AND Petrobras" + "contains" : "Shopping NEAR San NEAR Lorenzo" }, "return" : { "values" : [ @@ -135,8 +131,8 @@ FROM 'hybrid_index_name' VALUE 'markdown_chunks_hybrid_idx', 'search_fusion' VALUE 'INTERSECT', 'search_scorer' VALUE 'rsf', - 'vector' VALUE json_object('search_text' VALUE 'beneficios en estaciones de servicio'), - 'text' VALUE json_object('contains' VALUE 'estaciones AND Petrobras'), + 'vector' VALUE json_object('search_text' VALUE 'COMERCIOS ADHERIDOS'), + 'text' VALUE json_object('contains' VALUE 'BENDITA ACCUM STYLE'), 'return' VALUE json_object( 'values' VALUE json_array('rowid', 'score', 'vector_score', 'vector_rank', 'text_score', 'text_rank', 'chunk_text'), 'topN' VALUE 3