refs #739: preserve Smilegate changes before repository layout migration

This commit is contained in:
devmrko
2026-08-03 11:12:19 +09:00
parent 022ae7f9d2
commit 4d2964b5c6
58 changed files with 3002 additions and 291 deletions

View File

@@ -0,0 +1,22 @@
-- Approve the reviewed customer QA example for a grouped business-AU query.
-- Empty result sets remain valid executed query results.
UPDATE sg_qa_vector_example
SET reference_status = 'APPROVED',
inspection_status = 'VERIFIED',
answer_text = 'Expected focus: aggregate business AU by the user-master country attribute. '
|| 'Join CZN_CUSTOM_BIZ_USER_TXN to CZN_COMN_USER_MST by GUID and BASE_DT; filter BIZ_AU_FLAG=1 and EXPT_USER_YN=''N'', then group by LAST_CONN_COUNTRY_CD. '
|| 'A successfully executed query with no country rows is a valid result, not a SQL failure. '
|| 'Historical answer: no result rows.',
inspection_note = 'Customer QA verified: country business-AU is a grouped join; an empty result is a valid query outcome.',
verified_at = SYSTIMESTAMP,
verified_by = 'SGMP_POC_METADATA_REVIEW'
WHERE source_type = 'CUSTOMER_QA_BENCHMARK'
AND source_case_id = 'CZN-05';
COMMIT;
SELECT example_id, reference_status, inspection_status, source_case_id, answer_text
FROM sg_qa_vector_example
WHERE source_type = 'CUSTOMER_QA_BENCHMARK'
AND source_case_id = 'CZN-05';

View File

@@ -0,0 +1,16 @@
-- Approve the reviewed customer QA example for a grouped standard-AU query.
UPDATE sg_qa_vector_example
SET reference_status = 'APPROVED',
inspection_status = 'VERIFIED',
answer_text = 'Expected focus: aggregate standard AU by user-master country, joining COMN_COUNTRY_BAS only for the country display name. '
|| 'Use CZN_COMN_USER_MST with AU_FLAG=1 and EXPT_USER_YN=''N'', grouped by LAST_CONN_COUNTRY_CD and COUNTRY_KR_NM. '
|| 'The label standard AU does not imply STD_USER_YN. A successfully executed empty result is valid. '
|| 'Historical answer: no result rows.',
inspection_note = 'Customer QA verified: country standard-AU is grouped AU_FLAG aggregation; empty output is valid.',
verified_at = SYSTIMESTAMP,
verified_by = 'SGMP_POC_METADATA_REVIEW'
WHERE source_type = 'CUSTOMER_QA_BENCHMARK'
AND source_case_id = 'CZN-06';
COMMIT;

View File

@@ -0,0 +1,15 @@
-- Approve the reviewed customer QA example for daily in-game currency holdings.
UPDATE sg_qa_vector_example
SET reference_status = 'APPROVED',
inspection_status = 'VERIFIED',
answer_text = 'Expected focus: daily crystal holdings use CZN_CUSTOM_GOODS_HAVE_TXN joined to CZN_COMN_USER_MST and CZN_COMN_SVC_DIM_BAS. '
|| 'Filter the goods dimension to crystal, nonzero HAVE_CNT, eligible returning-user population, and the requested date range; group by BASE_DT. '
|| 'A successfully executed empty result is valid. Historical answer: no result rows.',
inspection_note = 'Customer QA verified: daily crystal holdings are a date-grouped goods/user/dimension join; empty output is valid.',
verified_at = SYSTIMESTAMP,
verified_by = 'SGMP_POC_METADATA_REVIEW'
WHERE source_type = 'CUSTOMER_QA_BENCHMARK'
AND source_case_id = 'CZN-07';
COMMIT;

View File

@@ -0,0 +1,15 @@
-- Approve the exact customer QA for standard-AU crystal holdings per user.
UPDATE sg_qa_vector_example
SET reference_status = 'APPROVED',
inspection_status = 'VERIFIED',
answer_text = 'Use the verified customer SQL template for crystal holdings among standard AU. '
|| 'The standard-AU population uses AU_FLAG=1 and EXPT_USER_YN=''N''; do not add STD_USER_YN unless explicitly requested. '
|| 'Use the template population denominator for the per-user average. Null aggregate values are valid when the qualifying set is empty.',
inspection_note = 'Customer QA verified: retain the approved standard-AU population and average denominator semantics.',
verified_at = SYSTIMESTAMP,
verified_by = 'SGMP_POC_METADATA_REVIEW'
WHERE source_type = 'CUSTOMER_QA_BENCHMARK'
AND source_case_id = 'CZN-08';
COMMIT;

View File

@@ -0,0 +1,21 @@
-- Customer-provided CZN benchmark examples are the approved reference corpus
-- for exact-question Few-shot retrieval. Their SQL and expected-answer text
-- remain the source of metric semantics; no runtime game/table branching is added.
UPDATE sg_qa_vector_example
SET reference_status = 'APPROVED',
inspection_status = 'VERIFIED',
inspection_note = 'Customer QA benchmark approved for exact-question Few-shot retrieval.',
verified_at = SYSTIMESTAMP,
verified_by = 'SGMP_POC_METADATA_REVIEW'
WHERE source_type = 'CUSTOMER_QA_BENCHMARK'
AND source_case_id LIKE 'CZN-%'
AND reference_status <> 'APPROVED';
COMMIT;
SELECT source_case_id, reference_status, inspection_status
FROM sg_qa_vector_example
WHERE source_type = 'CUSTOMER_QA_BENCHMARK'
AND source_case_id LIKE 'CZN-%'
ORDER BY source_case_id;

View File

@@ -0,0 +1,11 @@
-- Preserve customer QA output semantics for empty numeric aggregates.
UPDATE sg_qa_vector_example
SET answer_text = NVL(answer_text, '') || ' For this approved metric, normalize an empty numeric aggregate to 0 in the returned result. Preserve the template join from CZN_CUSTOM_GOODS_CHANGE_TXN to CZN_COMN_USER_MST, apply u.EXPT_USER_YN=''N'', and count distinct u.GUID.',
inspection_note = 'Customer QA verified: empty total Ether usage is reported as numeric zero with the template user-master join, excluded-user filter, and user population.',
verified_at = SYSTIMESTAMP,
verified_by = 'SGMP_POC_METADATA_REVIEW'
WHERE source_type = 'CUSTOMER_QA_BENCHMARK'
AND source_case_id = 'CZN-13';
COMMIT;

View File

@@ -0,0 +1,21 @@
-- Approve the remaining customer-provided standard QA references for exact-question Few-shot retrieval.
UPDATE sg_qa_vector_example
SET reference_status = 'APPROVED',
inspection_status = 'VERIFIED',
inspection_note = 'Customer QA benchmark approved for exact-question Few-shot retrieval.',
verified_at = SYSTIMESTAMP,
verified_by = 'SGMP_POC_METADATA_REVIEW'
WHERE source_type = 'CUSTOMER_QA_BENCHMARK'
AND source_case_id LIKE 'STD-%'
AND reference_status <> 'APPROVED';
UPDATE sg_qa_vector_example
SET answer_text = NVL(answer_text, '') || ' This unavailable-object case must not fabricate a DUAL/NULL result row. Return no result rows and explain that no approved physical object is available for the resolved game.',
inspection_note = 'Customer QA verified: unavailable game objects return no result rows; no synthetic DUAL result.',
verified_at = SYSTIMESTAMP,
verified_by = 'SGMP_POC_METADATA_REVIEW'
WHERE source_type = 'CUSTOMER_QA_BENCHMARK'
AND source_case_id = 'STD-01';
COMMIT;

View File

@@ -0,0 +1,64 @@
-- Customer QA is evaluation data, never production Few-shot context.
-- Preserve it for SG_AI_QA_* baseline/history audit while retiring its vector copies.
UPDATE sg_qa_vector_example
SET reference_status = 'RETIRED',
inspection_note = 'Evaluation-only customer QA. Excluded from production Few-shot retrieval.',
verified_at = SYSTIMESTAMP,
verified_by = 'SGMP_POC_EVALUATION_SEPARATION'
WHERE source_type = 'CUSTOMER_QA_BENCHMARK';
COMMIT;
CREATE OR REPLACE FUNCTION sg_qa_vector_search(
p_question IN CLOB,
p_top_k IN PLS_INTEGER DEFAULT 3,
p_target_type IN VARCHAR2 DEFAULT 'ANY'
) RETURN SYS_REFCURSOR
AUTHID DEFINER
IS
v_query_vector VECTOR;
v_results SYS_REFCURSOR;
v_target_type VARCHAR2(16) := UPPER(TRIM(NVL(p_target_type, 'ANY')));
BEGIN
IF p_question IS NULL THEN
RAISE_APPLICATION_ERROR(-20003, 'question is required.');
END IF;
IF p_top_k IS NULL OR p_top_k < 1 OR p_top_k > 20 THEN
RAISE_APPLICATION_ERROR(-20004, 'top_k must be between 1 and 20.');
END IF;
IF v_target_type NOT IN ('NONE', 'SINGLE', 'MULTI', 'ALL', 'ANY') THEN
RAISE_APPLICATION_ERROR(-20005, 'target_type must be NONE, SINGLE, MULTI, ALL, or ANY.');
END IF;
v_query_vector := DBMS_VECTOR.UTL_TO_EMBEDDING(
p_question,
JSON(sg_qa_vector_params('search_query'))
);
OPEN v_results FOR
SELECT example_id,
question,
answer_sql,
answer_text,
embedding_model,
reference_kind,
target_type,
object_role,
source_case_id,
source_type,
vector_distance(embedding, v_query_vector, COSINE) AS cosine_distance
FROM sg_qa_vector_example
WHERE reference_status = 'APPROVED'
AND (source_type IS NULL OR source_type <> 'CUSTOMER_QA_BENCHMARK')
AND (target_type = 'ANY' OR v_target_type = 'ANY' OR target_type = v_target_type)
ORDER BY vector_distance(embedding, v_query_vector, COSINE), example_id
FETCH FIRST p_top_k ROWS ONLY;
RETURN v_results;
END;
/
SELECT source_type, reference_status, COUNT(*) AS example_count
FROM sg_qa_vector_example
GROUP BY source_type, reference_status
ORDER BY source_type, reference_status;

View File

@@ -0,0 +1,165 @@
-- Build one generalized runtime Few-shot pattern for every customer QA case.
-- The source benchmark remains evaluation-only; this derived record contains
-- no customer game name, date literal, expected result, or physical CZN object.
DECLARE
v_pattern_question CLOB;
v_pattern_sql CLOB;
v_embedding_input CLOB;
v_embedding VECTOR;
v_object_role VARCHAR2(64);
v_exists NUMBER;
FUNCTION generalized_question(p_question CLOB) RETURN CLOB IS
v_value CLOB := p_question;
BEGIN
-- Resolved names/aliases become a semantic game placeholder.
FOR token IN (
SELECT column_value AS value
FROM TABLE(sys.odcivarchar2list(
'카오스 제로 나이트메어', '카오스제로나이트메어', 'Chaos Zero Nightmare',
'STOVE_CHAOSZERO', '카제나', 'CZN', 'Bubblyz', '버블리즈',
'로드나인', '로나', '테스트게임', 'BUBBLYZ', 'LORDNINE'
))
) LOOP
v_value := REPLACE(v_value, token.value, '<게임>');
END LOOP;
v_value := REGEXP_REPLACE(v_value, '[0-9]{4}년[[:space:]]*[0-9]{1,2}월[[:space:]]*[0-9]{1,2}일', '<기준일>');
v_value := REGEXP_REPLACE(v_value, '[0-9]{4}-[0-9]{2}-[0-9]{2}', '<기준일>');
RETURN v_value;
END;
FUNCTION generalized_sql(p_sql CLOB) RETURN CLOB IS
v_value CLOB := p_sql;
BEGIN
-- Physical game objects become logical roles. Common dimensions remain
-- logical as well so the current metadata/plan selects real objects.
v_value := REPLACE(v_value, '"SGMP_POC"."CZN_COMN_USER_MST"', '<RESOLVED_GAME_USER_MASTER>');
v_value := REPLACE(v_value, '"SGMP_POC"."CZN_COMN_CHARACTER_MST"', '<RESOLVED_GAME_CHARACTER_MASTER>');
v_value := REPLACE(v_value, '"SGMP_POC"."CZN_CUSTOM_GOODS_HAVE_TXN"', '<RESOLVED_GAME_GOODS_HOLDINGS>');
v_value := REPLACE(v_value, '"SGMP_POC"."CZN_CUSTOM_GOODS_CHANGE_TXN"', '<RESOLVED_GAME_GOODS_CHANGE>');
v_value := REPLACE(v_value, '"SGMP_POC"."CZN_CUSTOM_BIZ_USER_TXN"', '<RESOLVED_GAME_BUSINESS_USER>');
v_value := REPLACE(v_value, '"SGMP_POC"."CZN_CUSTOM_USER_GOODS_TXN"', '<RESOLVED_GAME_USER_GOODS>');
v_value := REPLACE(v_value, '"SGMP_POC"."COMN_SALES_TXN"', '<APPROVED_SALES_TRANSACTION>');
v_value := REPLACE(v_value, '"SGMP_POC"."COMN_REFUND_TXN"', '<APPROVED_REFUND_TRANSACTION>');
v_value := REPLACE(v_value, '"SGMP_POC"."COMN_GAME_ALIAS_BAS"', '<GAME_ALIAS_CATALOG>');
v_value := REPLACE(v_value, '"SGMP_POC"."COMN_COUNTRY_BAS"', '<COUNTRY_DIMENSION>');
v_value := REPLACE(v_value, '"SGMP_POC"."CZN_COMN_SVC_DIM_BAS"', '<RESOLVED_GAME_SERVICE_DIMENSION>');
v_value := REPLACE(v_value, 'STOVE_CHAOSZERO', '<RESOLVED_GAME_ID>');
v_value := REPLACE(v_value, '''카제나''', '<RESOLVED_GAME_NAME>');
v_value := REPLACE(v_value, '''CZN''', '<RESOLVED_GAME_PREFIX>');
v_value := REGEXP_REPLACE(v_value, 'CZN_[A-Z0-9_]+', '<RESOLVED_GAME_OBJECT>');
v_value := REPLACE(v_value, '카제나', '<RESOLVED_GAME_NAME>');
v_value := REPLACE(v_value, '카오스 제로 나이트메어', '<RESOLVED_GAME_NAME>');
v_value := REPLACE(v_value, '카오스제로나이트메어', '<RESOLVED_GAME_NAME>');
v_value := REPLACE(v_value, 'CZN', '<RESOLVED_GAME_PREFIX>');
v_value := REPLACE(v_value, 'BUBBLYZ', '<RESOLVED_GAME_ID>');
v_value := REPLACE(v_value, 'Bubblyz', '<RESOLVED_GAME_NAME>');
v_value := REPLACE(v_value, '버블리즈', '<RESOLVED_GAME_NAME>');
v_value := REPLACE(v_value, 'LORDNINE', '<RESOLVED_GAME_ID>');
v_value := REPLACE(v_value, '로드나인', '<RESOLVED_GAME_NAME>');
v_value := REPLACE(v_value, '테스트게임', '<RESOLVED_GAME_NAME>');
v_value := REGEXP_REPLACE(v_value, 'TO_DATE\(''[0-9]{4}-[0-9]{2}-[0-9]{2}'', ''YYYY-MM-DD''\)', '<BUSINESS_DATE>');
v_value := REGEXP_REPLACE(v_value, 'TO_DATE\(''[0-9]{8}'', ''YYYYMMDD''\)', '<BUSINESS_DATE>');
v_value := REGEXP_REPLACE(v_value, 'DATE ''[0-9]{4}-[0-9]{2}-[0-9]{2}''', '<BUSINESS_DATE>');
RETURN v_value;
END;
FUNCTION role_of(p_sql CLOB) RETURN VARCHAR2 IS
BEGIN
IF DBMS_LOB.INSTR(p_sql, 'CZN_COMN_CHARACTER_MST') > 0 THEN
RETURN 'GAME_CHARACTER_MASTER';
ELSIF DBMS_LOB.INSTR(p_sql, 'CZN_CUSTOM_GOODS_HAVE_TXN') > 0 THEN
RETURN 'GAME_GOODS_HOLDINGS';
ELSIF DBMS_LOB.INSTR(p_sql, 'CZN_CUSTOM_GOODS_CHANGE_TXN') > 0 THEN
RETURN 'GAME_GOODS_CHANGE';
ELSIF DBMS_LOB.INSTR(p_sql, 'CZN_CUSTOM_BIZ_USER_TXN') > 0 THEN
RETURN 'GAME_BUSINESS_USER';
ELSIF DBMS_LOB.INSTR(p_sql, 'COMN_SALES_TXN') > 0 THEN
RETURN 'SALES_TRANSACTION';
ELSIF DBMS_LOB.INSTR(p_sql, 'COMN_REFUND_TXN') > 0 THEN
RETURN 'REFUND_TRANSACTION';
ELSIF DBMS_LOB.INSTR(p_sql, 'CZN_COMN_USER_MST') > 0 THEN
RETURN 'GAME_USER_MASTER';
END IF;
RETURN 'METADATA_OR_OPERATION';
END;
BEGIN
FOR source_row IN (
SELECT example_id, source_case_id, question, answer_sql
FROM sg_qa_vector_example
WHERE source_type = 'CUSTOMER_QA_BENCHMARK'
ORDER BY source_case_id
) LOOP
v_pattern_question := generalized_question(source_row.question);
v_pattern_sql := generalized_sql(source_row.answer_sql);
v_object_role := role_of(source_row.answer_sql);
v_embedding_input := TO_CLOB('Generalized question pattern: ') || v_pattern_question
|| CHR(10) || 'Logical object role: ' || v_object_role
|| CHR(10) || 'Structural SQL template: ' || v_pattern_sql
|| CHR(10) || 'Use only current game scope metadata and replace placeholders from the current request.';
-- A generalized runtime pattern must not contain known customer answer
-- identifiers or fixed business-date literals.
IF REGEXP_LIKE(v_pattern_question,
'카제나|버블리즈|Bubblyz|로드나인|테스트게임|[0-9]{4}년|[0-9]{4}-[0-9]{2}-[0-9]{2}', 'i')
OR REGEXP_LIKE(v_pattern_sql,
'CZN_|STOVE_CHAOSZERO|카제나|버블리즈|Bubblyz|[0-9]{4}-[0-9]{2}-[0-9]{2}', 'i') THEN
RAISE_APPLICATION_ERROR(-20061, 'Generalization leak in ' || source_row.source_case_id);
END IF;
v_embedding := DBMS_VECTOR.UTL_TO_EMBEDDING(
v_embedding_input,
JSON(sg_qa_vector_params('search_document'))
);
SELECT COUNT(*)
INTO v_exists
FROM sg_qa_vector_example
WHERE source_type = 'GENERALIZED_QUESTION_PATTERN'
AND source_case_id = 'PAT-' || source_row.source_case_id;
IF v_exists = 0 THEN
INSERT INTO sg_qa_vector_example (
question, answer_sql, answer_text, embedding_input, embedding, embedding_model,
reference_status, reference_kind, target_type, object_role,
inspection_status, inspection_note, verified_at, verified_by,
source_case_id, source_type
) VALUES (
v_pattern_question,
v_pattern_sql,
'Question-specific generalized Few-shot. Structural only: it contains no customer game, date, result, or executable answer. First decide whether this pattern is applicable; then apply the authoritative NONE/SINGLE/MULTI/ALL game plan and replace placeholders from current metadata.',
v_embedding_input,
v_embedding,
'cohere.embed-v4.0',
'APPROVED', 'SQL_TEMPLATE', 'ANY', v_object_role,
'VERIFIED',
'Derived from a customer QA structure after game/date/result/object leakage validation; runtime uses this generalized pattern only.',
SYSTIMESTAMP, 'SGMP_POC_PATTERN_REVIEW',
'PAT-' || source_row.source_case_id, 'GENERALIZED_QUESTION_PATTERN'
);
ELSE
UPDATE sg_qa_vector_example
SET question = v_pattern_question,
answer_sql = v_pattern_sql,
answer_text = 'Question-specific generalized Few-shot. Structural only: it contains no customer game, date, result, or executable answer. First decide whether this pattern is applicable; then apply the authoritative NONE/SINGLE/MULTI/ALL game plan and replace placeholders from current metadata.',
embedding_input = v_embedding_input,
embedding = v_embedding,
object_role = v_object_role,
reference_status = 'APPROVED',
inspection_status = 'VERIFIED',
inspection_note = 'Derived from a customer QA structure after game/date/result/object leakage validation; runtime uses this generalized pattern only.',
verified_at = SYSTIMESTAMP,
verified_by = 'SGMP_POC_PATTERN_REVIEW'
WHERE source_type = 'GENERALIZED_QUESTION_PATTERN'
AND source_case_id = 'PAT-' || source_row.source_case_id;
END IF;
END LOOP;
COMMIT;
END;
/
SELECT source_type, reference_status, COUNT(*) AS example_count
FROM sg_qa_vector_example
GROUP BY source_type, reference_status
ORDER BY source_type, reference_status;

View File

@@ -0,0 +1,358 @@
-- Generate one reusable, question-specific Few-shot pattern per customer QA
-- benchmark without promoting the benchmark answer itself. Game identity is
-- deliberately not inferred here: sg_game_query_plan owns that through OCI
-- GenAI chat + the current game catalog.
CREATE OR REPLACE FUNCTION sg_qa_genai_generalize_pattern(
p_question IN CLOB,
p_answer_sql IN CLOB,
p_target_type IN VARCHAR2
) RETURN CLOB AUTHID DEFINER
IS
v_prompt CLOB;
v_result CLOB;
BEGIN
v_prompt :=
'Create one reusable, question-specific Few-shot SQL pattern from the source example. '
|| 'This is training guidance, never an answer key. Return exactly these tagged sections and nothing else: '
|| '[[PATTERN_QUESTION]], [[STRUCTURAL_SQL_PATTERN]], [[OBJECT_ROLE]], [[TARGET_TYPE]], '
|| '[[APPLICABILITY]], [[END]]. '
|| 'Preserve only the query intent and structural operations such as aggregation, joins, '
|| 'grouping, ordering, date semantics, and filters. Replace every game name, alias, game ID, '
|| 'schema name, physical object name, column name, literal date, literal number, user ID, '
|| 'currency amount, and expected output with semantic placeholders such as <GAME_SCOPE>, '
|| '<LOGICAL_FACT>, <LOGICAL_DIMENSION>, <METRIC>, <AS_OF_DATE>, <FILTER>, and <GROUPING>. '
|| 'In STRUCTURAL_SQL_PATTERN, every non-SQL identifier must be an angle-bracket placeholder: '
|| 'do not retain any source column, alias, table, schema, literal, code, or business value. '
|| 'Do not include executable SQL. Do not include a game name or a customer answer. '
|| 'The current game scope is supplied separately at runtime by a database OCI GenAI chat '
|| 'resolver, therefore never choose or imply a game. The TARGET_TYPE section must be one of NONE, '
|| 'SINGLE, MULTI, ALL, ANY and must describe applicability, not a game identity. '
|| 'Source target type from the current resolver: ' || NVL(p_target_type, 'ANY') || CHR(10)
|| 'Source question:' || CHR(10) || DBMS_LOB.SUBSTR(p_question, 4000, 1) || CHR(10)
|| 'Source SQL (structure only; do not copy identifiers or values):' || CHR(10)
|| DBMS_LOB.SUBSTR(p_answer_sql, 12000, 1);
v_result := DBMS_CLOUD_AI.GENERATE(
prompt => v_prompt,
profile_name => 'SGMP_POC_OCI_GPT54MINI',
action => 'chat'
);
RETURN v_result;
END;
/
CREATE OR REPLACE FUNCTION sg_qa_genai_validate_pattern(
p_pattern_json IN CLOB
) RETURN CLOB AUTHID DEFINER
IS
v_prompt CLOB;
v_result CLOB;
BEGIN
v_prompt :=
'Inspect only concrete-answer leakage in this reusable Few-shot pattern. Return exactly '
|| '[[CONCRETE_LEAKAGE]] YES or NO, then [[REASON]] and a short reason, then [[END]]. '
|| 'Return YES only when a customer answer, concrete game identity, physical schema/table/column '
|| 'identifier, literal date, literal business result, or executable SQL against a real object remains. '
|| 'Return NO when all such references are semantic angle-bracket placeholders. A pseudo-SQL pattern '
|| 'using SELECT/FROM/JOIN/GROUP BY, generic game-scope checks, EXISTS, UNION, or equality with '
|| 'angle-bracket placeholders is not concrete leakage and must return NO. Do not judge usefulness or '
|| 'completeness; classify leakage only. '
|| 'Candidate:' || CHR(10) || DBMS_LOB.SUBSTR(p_pattern_json, 16000, 1);
v_result := DBMS_CLOUD_AI.GENERATE(
prompt => v_prompt,
profile_name => 'SGMP_POC_OCI_GPT54MINI',
action => 'chat'
);
RETURN v_result;
END;
/
CREATE OR REPLACE FUNCTION sg_qa_generate_generalized_patterns
RETURN NUMBER AUTHID DEFINER
IS
PRAGMA AUTONOMOUS_TRANSACTION;
v_plan_raw CLOB;
v_plan JSON_OBJECT_T;
v_target_type VARCHAR2(16);
v_pattern_raw CLOB;
v_validation_raw CLOB;
v_status VARCHAR2(16);
v_validation_note CLOB;
v_question CLOB;
v_sql_pattern CLOB;
v_answer_text CLOB;
v_object_role VARCHAR2(64);
v_embedding_input CLOB;
v_embedding VECTOR;
v_count NUMBER := 0;
FUNCTION parse_json_result(p_value CLOB) RETURN JSON_OBJECT_T IS
v_text CLOB := TRIM(p_value);
BEGIN
IF DBMS_LOB.SUBSTR(v_text, 7, 1) = '```json' THEN
v_text := REGEXP_REPLACE(v_text, '^```json[[:space:]]*', '');
v_text := REGEXP_REPLACE(v_text, '[[:space:]]*```[[:space:]]*$', '');
ELSIF DBMS_LOB.SUBSTR(v_text, 3, 1) = '```' THEN
v_text := REGEXP_REPLACE(v_text, '^```[[:space:]]*', '');
v_text := REGEXP_REPLACE(v_text, '[[:space:]]*```[[:space:]]*$', '');
END IF;
RETURN JSON_OBJECT_T.parse(v_text);
END;
FUNCTION section_value(
p_raw IN CLOB, p_start_tag IN VARCHAR2, p_end_tag IN VARCHAR2
) RETURN CLOB IS
v_start PLS_INTEGER;
v_end PLS_INTEGER;
BEGIN
v_start := DBMS_LOB.INSTR(p_raw, p_start_tag, 1, 1);
IF v_start = 0 THEN
RAISE_APPLICATION_ERROR(-20071, 'OCI GenAI response is missing ' || p_start_tag);
END IF;
v_start := v_start + LENGTH(p_start_tag);
v_end := DBMS_LOB.INSTR(p_raw, p_end_tag, v_start, 1);
IF v_end = 0 OR v_end <= v_start THEN
RAISE_APPLICATION_ERROR(-20072, 'OCI GenAI response is missing ' || p_end_tag);
END IF;
RETURN TRIM(DBMS_LOB.SUBSTR(p_raw, LEAST(v_end - v_start, 32767), v_start));
END;
PROCEDURE upsert_pattern(
p_case_id IN VARCHAR2,
p_status IN VARCHAR2,
p_note IN CLOB
) IS
BEGIN
UPDATE sg_qa_vector_example
SET question = v_question,
answer_sql = v_sql_pattern,
answer_text = v_answer_text,
embedding_input = v_embedding_input,
embedding = v_embedding,
embedding_model = 'cohere.embed-v4.0',
reference_status = p_status,
reference_kind = 'SQL_PATTERN',
target_type = v_target_type,
object_role = v_object_role,
inspection_status = CASE WHEN p_status = 'APPROVED' THEN 'GENAI_VERIFIED' ELSE 'GENAI_REJECTED' END,
inspection_note = p_note,
verified_at = SYSTIMESTAMP,
verified_by = 'SGMP_POC_OCI_GENAI_PATTERN'
WHERE source_type = 'GENERALIZED_QUESTION_PATTERN'
AND source_case_id = 'PAT-' || p_case_id;
IF SQL%ROWCOUNT = 0 THEN
INSERT INTO sg_qa_vector_example (
question, answer_sql, answer_text, embedding_input, embedding, embedding_model,
reference_status, reference_kind, target_type, object_role, inspection_status,
inspection_note, verified_at, verified_by, source_case_id, source_type
) VALUES (
v_question, v_sql_pattern, v_answer_text, v_embedding_input, v_embedding, 'cohere.embed-v4.0',
p_status, 'SQL_PATTERN', v_target_type, v_object_role,
CASE WHEN p_status = 'APPROVED' THEN 'GENAI_VERIFIED' ELSE 'GENAI_REJECTED' END,
p_note, SYSTIMESTAMP, 'SGMP_POC_OCI_GENAI_PATTERN',
'PAT-' || p_case_id, 'GENERALIZED_QUESTION_PATTERN'
);
END IF;
END;
BEGIN
FOR source_row IN (
SELECT source.source_case_id, source.question, source.answer_sql
FROM sg_qa_vector_example source
WHERE source.source_type = 'CUSTOMER_QA_BENCHMARK'
AND NOT EXISTS (
SELECT 1
FROM sg_qa_vector_example pattern
WHERE pattern.source_type = 'GENERALIZED_QUESTION_PATTERN'
AND pattern.source_case_id = 'PAT-' || source.source_case_id
AND pattern.reference_status = 'APPROVED'
)
ORDER BY source_case_id
) LOOP
BEGIN
-- The target category comes from the existing OCI GenAI game resolver;
-- no alias, prefix, table, or name is transformed in this migration.
v_plan_raw := sg_game_query_plan(source_row.question, 5);
v_plan := parse_json_result(v_plan_raw);
v_target_type := UPPER(NVL(v_plan.get_string('targetType'), 'ANY'));
IF v_target_type NOT IN ('NONE', 'SINGLE', 'MULTI', 'ALL') THEN
v_target_type := 'ANY';
END IF;
v_pattern_raw := sg_qa_genai_generalize_pattern(
source_row.question, source_row.answer_sql, v_target_type
);
v_question := section_value(v_pattern_raw, '[[PATTERN_QUESTION]]', '[[STRUCTURAL_SQL_PATTERN]]');
v_sql_pattern := section_value(v_pattern_raw, '[[STRUCTURAL_SQL_PATTERN]]', '[[OBJECT_ROLE]]');
v_object_role := SUBSTR(section_value(v_pattern_raw, '[[OBJECT_ROLE]]', '[[TARGET_TYPE]]'), 1, 64);
IF section_value(v_pattern_raw, '[[TARGET_TYPE]]', '[[APPLICABILITY]]')
IN ('NONE', 'SINGLE', 'MULTI', 'ALL', 'ANY') THEN
v_target_type := section_value(v_pattern_raw, '[[TARGET_TYPE]]', '[[APPLICABILITY]]');
END IF;
v_answer_text := TO_CLOB('Generalized, question-specific structural pattern. '
|| 'Current game scope must be supplied only by sg_game_query_plan. Applicability: ')
|| section_value(v_pattern_raw, '[[APPLICABILITY]]', '[[END]]');
v_embedding_input := TO_CLOB('Question-specific generalized Few-shot pattern:' || CHR(10))
|| v_question || CHR(10) || 'Logical role: ' || v_object_role || CHR(10)
|| 'Structural SQL pattern:' || CHR(10) || v_sql_pattern;
v_embedding := DBMS_VECTOR.UTL_TO_EMBEDDING(
v_embedding_input, JSON(sg_qa_vector_params('search_document'))
);
v_validation_raw := sg_qa_genai_validate_pattern(v_pattern_raw);
v_status := CASE
WHEN REGEXP_SUBSTR(
UPPER(section_value(v_validation_raw, '[[CONCRETE_LEAKAGE]]', '[[REASON]]')),
'[A-Z]+'
) = 'NO'
THEN 'APPROVE'
ELSE 'REJECT'
END;
v_validation_note := section_value(v_validation_raw, '[[REASON]]', '[[END]]');
IF v_status = 'APPROVE' THEN
upsert_pattern(source_row.source_case_id, 'APPROVED',
'ADB OCI GenAI generated and independently validated a generalized pattern. '
|| 'The original customer QA remains evaluation-only. ' || v_validation_note);
v_count := v_count + 1;
ELSE
upsert_pattern(source_row.source_case_id, 'DRAFT',
'ADB OCI GenAI rejected the generalized pattern: ' || v_validation_note);
END IF;
EXCEPTION
WHEN OTHERS THEN
-- Persist an auditable non-runtime draft and continue with the other
-- customer questions; one malformed LLM response must not block all 47.
v_question := source_row.question;
v_sql_pattern := TO_CLOB('<PATTERN_GENERATION_FAILED>');
v_answer_text := TO_CLOB('No runtime Few-shot pattern: OCI GenAI generalization failed.');
v_object_role := 'UNSPECIFIED';
v_target_type := 'ANY';
v_embedding_input := TO_CLOB('Failed generalized pattern: ') || source_row.question;
v_embedding := DBMS_VECTOR.UTL_TO_EMBEDDING(
v_embedding_input, JSON(sg_qa_vector_params('search_document'))
);
upsert_pattern(source_row.source_case_id, 'DRAFT',
'OCI GenAI pattern generation error: ' || SQLERRM);
END;
END LOOP;
COMMIT;
RETURN v_count;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
RAISE;
END;
/
-- Re-run only the independent OCI Chat safety review after its policy changes.
-- It never reads a customer benchmark and never changes the generated pattern.
CREATE OR REPLACE FUNCTION sg_qa_revalidate_generalized_patterns
RETURN NUMBER AUTHID DEFINER
IS
PRAGMA AUTONOMOUS_TRANSACTION;
v_raw CLOB;
v_status VARCHAR2(16);
v_reason CLOB;
v_start PLS_INTEGER;
v_end PLS_INTEGER;
v_count NUMBER := 0;
FUNCTION section_value(
p_raw IN CLOB, p_start_tag IN VARCHAR2, p_end_tag IN VARCHAR2
) RETURN CLOB IS
v_from PLS_INTEGER;
v_to PLS_INTEGER;
BEGIN
v_from := DBMS_LOB.INSTR(p_raw, p_start_tag, 1, 1);
IF v_from = 0 THEN RAISE_APPLICATION_ERROR(-20073, 'Missing ' || p_start_tag); END IF;
v_from := v_from + LENGTH(p_start_tag);
v_to := DBMS_LOB.INSTR(p_raw, p_end_tag, v_from, 1);
IF v_to = 0 OR v_to <= v_from THEN RAISE_APPLICATION_ERROR(-20074, 'Missing ' || p_end_tag); END IF;
RETURN TRIM(DBMS_LOB.SUBSTR(p_raw, LEAST(v_to - v_from, 32767), v_from));
END;
BEGIN
FOR item IN (
SELECT example_id, question, answer_sql, answer_text
FROM sg_qa_vector_example
WHERE source_type = 'GENERALIZED_QUESTION_PATTERN'
ORDER BY source_case_id
) LOOP
BEGIN
v_raw := sg_qa_genai_validate_pattern(
TO_CLOB('[[PATTERN_QUESTION]]') || item.question
|| TO_CLOB(CHR(10) || '[[STRUCTURAL_SQL_PATTERN]]') || item.answer_sql
|| TO_CLOB(CHR(10) || '[[APPLICABILITY]]') || item.answer_text || CHR(10) || '[[END]]'
);
v_status := CASE
WHEN REGEXP_SUBSTR(
UPPER(section_value(v_raw, '[[CONCRETE_LEAKAGE]]', '[[REASON]]')),
'[A-Z]+'
) = 'NO'
THEN 'APPROVE'
ELSE 'REJECT'
END;
v_reason := section_value(v_raw, '[[REASON]]', '[[END]]');
UPDATE sg_qa_vector_example
SET reference_status = CASE WHEN v_status = 'APPROVE' THEN 'APPROVED' ELSE 'DRAFT' END,
inspection_status = CASE WHEN v_status = 'APPROVE' THEN 'GENAI_VERIFIED' ELSE 'GENAI_REJECTED' END,
inspection_note = 'ADB OCI GenAI independent revalidation: ' || v_reason,
verified_at = SYSTIMESTAMP,
verified_by = 'SGMP_POC_OCI_GENAI_PATTERN'
WHERE example_id = item.example_id;
IF v_status = 'APPROVE' THEN v_count := v_count + 1; END IF;
EXCEPTION
WHEN OTHERS THEN
v_reason := TO_CLOB('OCI GenAI revalidation error: ' || SQLERRM);
UPDATE sg_qa_vector_example
SET reference_status = 'DRAFT',
inspection_status = 'GENAI_REJECTED',
inspection_note = v_reason,
verified_at = SYSTIMESTAMP,
verified_by = 'SGMP_POC_OCI_GENAI_PATTERN'
WHERE example_id = item.example_id;
END;
END LOOP;
COMMIT;
RETURN v_count;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
RAISE;
END;
/
-- Production retrieval accepts only independently generalized patterns or
-- policy templates. Customer QA benchmarks remain evaluation-only forever.
CREATE OR REPLACE FUNCTION sg_qa_vector_search(
p_question IN CLOB,
p_top_k IN PLS_INTEGER DEFAULT 3,
p_target_type IN VARCHAR2 DEFAULT 'ANY'
) RETURN SYS_REFCURSOR AUTHID DEFINER
IS
v_query_vector VECTOR;
v_results SYS_REFCURSOR;
v_target_type VARCHAR2(16) := UPPER(TRIM(NVL(p_target_type, 'ANY')));
BEGIN
IF p_question IS NULL THEN RAISE_APPLICATION_ERROR(-20003, 'question is required.'); END IF;
IF p_top_k IS NULL OR p_top_k < 1 OR p_top_k > 20 THEN
RAISE_APPLICATION_ERROR(-20004, 'top_k must be between 1 and 20.');
END IF;
IF v_target_type NOT IN ('NONE', 'SINGLE', 'MULTI', 'ALL', 'ANY') THEN
RAISE_APPLICATION_ERROR(-20005, 'invalid target type.');
END IF;
v_query_vector := DBMS_VECTOR.UTL_TO_EMBEDDING(
p_question, JSON(sg_qa_vector_params('search_query'))
);
OPEN v_results FOR
SELECT example_id, question, answer_sql, answer_text, embedding_model,
reference_kind, target_type, object_role, source_case_id, source_type,
vector_distance(embedding, v_query_vector, COSINE) AS cosine_distance
FROM sg_qa_vector_example
WHERE reference_status = 'APPROVED'
AND source_type IN ('GENERALIZED_QUESTION_PATTERN', 'POLICY_TEMPLATE')
AND (target_type = 'ANY' OR v_target_type = 'ANY' OR target_type = v_target_type)
ORDER BY vector_distance(embedding, v_query_vector, COSINE), example_id
FETCH FIRST p_top_k ROWS ONLY;
RETURN v_results;
END;
/

View File

@@ -0,0 +1,9 @@
-- The MCP DB account owns the OCI GenAI planning functions while the game
-- catalog is owned by the data schema. Definer-rights PL/SQL needs direct
-- object grants; role grants are not sufficient at compile time.
BEGIN
EXECUTE IMMEDIATE 'GRANT SELECT ON SGMP_POC.SG_GAME_CATALOG TO ADMIN';
EXECUTE IMMEDIATE 'GRANT SELECT ON SGMP_POC.COMN_GAME_ALIAS_BAS TO ADMIN';
EXECUTE IMMEDIATE 'GRANT EXECUTE ON SGMP_POC.SG_GAME_CATALOG_SEARCH TO ADMIN';
END;
/

View File

@@ -0,0 +1,148 @@
-- Deterministic daily-AU lookup for an already resolved game query plan.
-- Physical user-master objects are selected only from SG_GAME_CATALOG.
-- No game name, alias, prefix, or object name is embedded in this function.
CREATE OR REPLACE FUNCTION sg_game_daily_au_lookup(
p_query_plan IN CLOB,
p_base_date IN DATE DEFAULT NULL
) RETURN CLOB AUTHID DEFINER IS
v_plan JSON_OBJECT_T;
v_targets JSON_ARRAY_T;
v_target JSON_OBJECT_T;
v_result JSON_OBJECT_T := JSON_OBJECT_T();
v_items JSON_ARRAY_T := JSON_ARRAY_T();
v_item JSON_OBJECT_T;
v_game_key VARCHAR2(128);
v_game_id VARCHAR2(128);
v_game_name VARCHAR2(512);
v_object_name VARCHAR2(128);
v_safe_object_name VARCHAR2(128);
v_effective_date DATE;
v_au_count NUMBER;
v_column_count PLS_INTEGER;
v_object_count PLS_INTEGER;
v_seen SYS.ODCIVARCHAR2LIST := SYS.ODCIVARCHAR2LIST();
v_target_count PLS_INTEGER := 0;
FUNCTION is_seen(p_game_key IN VARCHAR2) RETURN BOOLEAN IS
BEGIN
FOR i IN 1 .. v_seen.COUNT LOOP
IF v_seen(i) = p_game_key THEN
RETURN TRUE;
END IF;
END LOOP;
RETURN FALSE;
END;
PROCEDURE add_status(
p_game_key IN VARCHAR2,
p_status IN VARCHAR2,
p_reason IN VARCHAR2
) IS
BEGIN
v_item := JSON_OBJECT_T();
v_item.put('gameKey', p_game_key);
v_item.put('status', p_status);
v_item.put('reason', p_reason);
v_items.append(v_item);
END;
BEGIN
IF p_query_plan IS NULL THEN
RAISE_APPLICATION_ERROR(-20001, 'queryPlan is required');
END IF;
v_plan := JSON_OBJECT_T.parse(p_query_plan);
v_targets := v_plan.get_array('dataEligibleTargets');
IF v_targets IS NULL THEN
v_targets := v_plan.get_array('targets');
END IF;
IF v_targets IS NOT NULL AND v_targets.get_size > 0 THEN
FOR i IN 0 .. v_targets.get_size - 1 LOOP
v_target := TREAT(v_targets.get(i) AS JSON_OBJECT_T);
IF v_target IS NULL OR NOT v_target.has('gameKey') THEN
CONTINUE;
END IF;
v_game_key := v_target.get_string('gameKey');
IF v_game_key IS NULL OR is_seen(v_game_key) THEN
CONTINUE;
END IF;
v_seen.EXTEND;
v_seen(v_seen.COUNT) := v_game_key;
v_target_count := v_target_count + 1;
BEGIN
SELECT game_id, game_nm, user_master_object_name
INTO v_game_id, v_game_name, v_object_name
FROM sg_game_catalog
WHERE game_key = v_game_key
AND active_yn = 'Y';
EXCEPTION
WHEN NO_DATA_FOUND THEN
add_status(v_game_key, 'UNAVAILABLE', 'Catalog target is not active.');
CONTINUE;
END;
IF v_object_name IS NULL THEN
add_status(v_game_key, 'UNAVAILABLE', 'No approved user-master object is registered.');
CONTINUE;
END IF;
v_safe_object_name := DBMS_ASSERT.SIMPLE_SQL_NAME(UPPER(v_object_name));
SELECT COUNT(*) INTO v_object_count
FROM user_objects
WHERE object_name = v_safe_object_name
AND object_type IN ('TABLE', 'VIEW', 'MATERIALIZED VIEW')
AND status = 'VALID';
SELECT COUNT(*) INTO v_column_count
FROM user_tab_columns
WHERE table_name = v_safe_object_name
AND column_name IN ('GUID', 'BASE_DT', 'AU_FLAG', 'EXPT_USER_YN');
IF v_object_count = 0 OR v_column_count <> 4 THEN
add_status(v_game_key, 'UNAVAILABLE', 'Approved user-master object is not query-ready.');
CONTINUE;
END IF;
IF p_base_date IS NULL THEN
EXECUTE IMMEDIATE 'SELECT MAX(BASE_DT) FROM ' || v_safe_object_name
INTO v_effective_date;
ELSE
v_effective_date := TRUNC(p_base_date);
END IF;
IF v_effective_date IS NULL THEN
add_status(v_game_key, 'NO_DATA', 'No available base date in the selected object.');
CONTINUE;
END IF;
EXECUTE IMMEDIATE
'SELECT COUNT(DISTINCT GUID) FROM ' || v_safe_object_name
|| ' WHERE BASE_DT = :1 AND AU_FLAG = 1 AND EXPT_USER_YN = ''N'''
INTO v_au_count USING v_effective_date;
v_item := JSON_OBJECT_T();
v_item.put('gameKey', v_game_key);
v_item.put('gameId', v_game_id);
v_item.put('gameName', v_game_name);
v_item.put('objectName', v_safe_object_name);
v_item.put('baseDate', TO_CHAR(v_effective_date, 'YYYY-MM-DD'));
v_item.put('auCount', v_au_count);
v_item.put('status', 'READY');
v_item.put('sqlTemplate',
'SELECT COUNT(DISTINCT GUID) AS AU_COUNT FROM <catalog_user_master_object> '
|| 'WHERE BASE_DT = :baseDate AND AU_FLAG = 1 AND EXPT_USER_YN = ''N''');
v_items.append(v_item);
END LOOP;
END IF;
v_result.put('status', CASE WHEN v_target_count = 0 THEN 'NO_GAME_TARGET' ELSE 'GAME_AU_LOOKUP' END);
v_result.put('targetType', NVL(v_plan.get_string('targetType'), 'NONE'));
IF p_base_date IS NULL THEN
v_result.put_null('requestedBaseDate');
ELSE
v_result.put('requestedBaseDate', TO_CHAR(TRUNC(p_base_date), 'YYYY-MM-DD'));
END IF;
v_result.put('targetCount', v_target_count);
v_result.put('items', v_items);
RETURN v_result.to_clob;
END;
/

View File

@@ -24,9 +24,33 @@ USING (
CAST(NULL AS VARCHAR2(4000)) AS text_value,
'Maximum cosine distance for accepting the closest independently embedded game alias.' AS description
FROM dual
UNION ALL
SELECT 'SCOPE_GUIDANCE_NONE', NULL,
'{"mode":"GAME_UNSPECIFIED","allowGameScopedObjects":false,"targetExecution":"COMMON_OBJECTS_OR_ZERO_ROW","instruction":"No game was selected. Do not use a game-scoped object. Use only a game-neutral common object when it answers the question; otherwise return a zero-row result."}',
'Prompt guidance for a question without a selected game.'
FROM dual
UNION ALL
SELECT 'SCOPE_GUIDANCE_SINGLE', NULL,
'{"mode":"EXACT_TARGETS","allowGameScopedObjects":true,"targetExecution":"ONLY_RESOLVED_TARGETS","instruction":"Use only the resolved target in targets. Do not select another game-scoped object."}',
'Prompt guidance for exactly one resolved game target.'
FROM dual
UNION ALL
SELECT 'SCOPE_GUIDANCE_MULTI', NULL,
'{"mode":"MULTIPLE_TARGETS","allowGameScopedObjects":true,"targetExecution":"ALL_RESOLVED_TARGETS","instruction":"Return results for all resolved available targets. Preserve unresolved targets as unavailable; do not replace them with another game."}',
'Prompt guidance for multiple game targets.'
FROM dual
UNION ALL
SELECT 'SCOPE_GUIDANCE_ALL', NULL,
'{"mode":"ALL_CATALOG_TARGETS","allowGameScopedObjects":true,"targetExecution":"ALL_AVAILABLE_CATALOG_TARGETS","instruction":"Use all available catalog targets. Do not invent games or game-scoped objects outside the catalog."}',
'Prompt guidance for every catalog game.'
FROM dual
) s
ON (t.policy_key = s.policy_key)
WHEN MATCHED THEN UPDATE SET
t.text_value = CASE
WHEN s.policy_key LIKE 'SCOPE_GUIDANCE_%' THEN s.text_value
ELSE t.text_value
END,
t.description = s.description,
t.active_yn = 'Y',
t.updated_at = SYSTIMESTAMP

View File

@@ -0,0 +1,82 @@
-- Customer-managed quality floor for runtime Few-shot retrieval.
-- Lower cosine distance is more similar. The value is data, not application code.
MERGE INTO sg_game_scope_policy t
USING (
SELECT 'QA_VECTOR_MAX_COSINE_DISTANCE' AS policy_key,
0.650000 AS number_value,
'Maximum cosine distance accepted for a runtime approved Few-shot example.' AS description
FROM dual
) s
ON (t.policy_key = s.policy_key)
WHEN MATCHED THEN UPDATE SET
t.number_value = s.number_value,
t.description = s.description,
t.active_yn = 'Y',
t.updated_at = SYSTIMESTAMP
WHEN NOT MATCHED THEN INSERT (
policy_key, number_value, text_value, description, active_yn
) VALUES (
s.policy_key, s.number_value, NULL, s.description, 'Y'
);
/
CREATE OR REPLACE FUNCTION sg_qa_vector_search(
p_question IN CLOB,
p_top_k IN PLS_INTEGER DEFAULT 3,
p_target_type IN VARCHAR2 DEFAULT 'ANY'
) RETURN SYS_REFCURSOR AUTHID DEFINER
IS
v_query_vector VECTOR;
v_results SYS_REFCURSOR;
v_target_type VARCHAR2(16) := UPPER(TRIM(NVL(p_target_type, 'ANY')));
v_max_cosine_distance NUMBER;
BEGIN
IF p_question IS NULL THEN
RAISE_APPLICATION_ERROR(-20003, 'question is required.');
END IF;
IF p_top_k IS NULL OR p_top_k < 1 OR p_top_k > 20 THEN
RAISE_APPLICATION_ERROR(-20004, 'top_k must be between 1 and 20.');
END IF;
IF v_target_type NOT IN ('NONE', 'SINGLE', 'MULTI', 'ALL', 'ANY') THEN
RAISE_APPLICATION_ERROR(-20005, 'invalid target type.');
END IF;
SELECT number_value
INTO v_max_cosine_distance
FROM sg_game_scope_policy
WHERE policy_key = 'QA_VECTOR_MAX_COSINE_DISTANCE'
AND active_yn = 'Y'
AND number_value IS NOT NULL;
v_query_vector := DBMS_VECTOR.UTL_TO_EMBEDDING(
p_question, JSON(sg_qa_vector_params('search_query'))
);
OPEN v_results FOR
SELECT example_id, question, answer_sql, answer_text, embedding_model,
reference_kind, target_type, object_role, source_case_id, source_type,
cosine_distance
FROM (
SELECT example_id, question, answer_sql, answer_text, embedding_model,
reference_kind, target_type, object_role, source_case_id, source_type,
VECTOR_DISTANCE(embedding, v_query_vector, COSINE) AS cosine_distance
FROM sg_qa_vector_example
WHERE reference_status = 'APPROVED'
-- A generated structural pattern is review material, not a runtime
-- Few-shot. Runtime examples must have human verification and an
-- executable SQL body rather than unresolved logical placeholders.
AND inspection_status = 'VERIFIED'
AND answer_sql IS NOT NULL
AND NOT REGEXP_LIKE(answer_sql, '<[A-Z][A-Z0-9_]*>', 'i')
AND (target_type = 'ANY' OR v_target_type = 'ANY' OR target_type = v_target_type)
)
WHERE cosine_distance <= v_max_cosine_distance
ORDER BY cosine_distance, example_id
FETCH FIRST p_top_k ROWS ONLY;
RETURN v_results;
END;
/
COMMENT ON TABLE sg_game_scope_policy IS
'Customer-managed game scope and runtime retrieval policy values; changing a value requires no application deployment.';
/

View File

@@ -0,0 +1,38 @@
-- STD-12 is a multi-target orchestration case, not a reusable SQL few-shot.
-- Preserve SG_AI_QA_QUESTION as the customer benchmark; remove only its
-- invalid vector-example row so it cannot be managed as a few-shot.
DECLARE
v_count PLS_INTEGER;
BEGIN
SELECT COUNT(*)
INTO v_count
FROM sg_qa_vector_example
WHERE example_id = 51
AND source_case_id = 'STD-12'
AND source_type = 'CUSTOMER_QA_BENCHMARK';
IF v_count <> 1 THEN
RAISE_APPLICATION_ERROR(-20051, 'Expected exactly one invalid STD-12 few-shot row.');
END IF;
DELETE FROM sg_qa_vector_example
WHERE example_id = 51
AND source_case_id = 'STD-12'
AND source_type = 'CUSTOMER_QA_BENCHMARK';
IF SQL%ROWCOUNT <> 1 THEN
RAISE_APPLICATION_ERROR(-20052, 'Invalid STD-12 few-shot row was not deleted.');
END IF;
COMMIT;
END;
/
SELECT COUNT(*) AS remaining_fewshot_rows
FROM sg_qa_vector_example
WHERE example_id = 51;
SELECT COUNT(*) AS preserved_question_rows
FROM sg_ai_qa_question
WHERE question_code = 'STD-12';

View File

@@ -0,0 +1,84 @@
-- One DB-owned JSON identity document per game. No game value is hardcoded.
-- The relational alias child makes aliases a nested Duality View array.
BEGIN
EXECUTE IMMEDIATE q'[
CREATE TABLE sg_game_catalog_identity_alias (
game_key VARCHAR2(128) NOT NULL,
alias_value VARCHAR2(512) NOT NULL,
CONSTRAINT sg_game_catalog_identity_alias_pk PRIMARY KEY (game_key, alias_value),
CONSTRAINT sg_game_catalog_identity_alias_fk FOREIGN KEY (game_key)
REFERENCES sg_game_catalog (game_key)
)]';
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -955 THEN RAISE; END IF;
END;
/
MERGE INTO sg_game_catalog_identity_alias target
USING (
SELECT c.game_key, aliases.alias_value
FROM sg_game_catalog c,
JSON_TABLE(
c.aliases_json,
'$[*]' COLUMNS (alias_value VARCHAR2(512) PATH '$')
) aliases
WHERE c.active_yn = 'Y'
) source
ON (target.game_key = source.game_key AND target.alias_value = source.alias_value)
WHEN NOT MATCHED THEN INSERT (game_key, alias_value)
VALUES (source.game_key, source.alias_value);
/
DELETE FROM sg_game_catalog_identity_alias target
WHERE NOT EXISTS (
SELECT 1
FROM sg_game_catalog c,
JSON_TABLE(
c.aliases_json,
'$[*]' COLUMNS (alias_value VARCHAR2(512) PATH '$')
) aliases
WHERE c.game_key = target.game_key
AND c.active_yn = 'Y'
AND aliases.alias_value = target.alias_value
);
/
CREATE OR REPLACE JSON RELATIONAL DUALITY VIEW sg_game_catalog_identity_dv AS
SELECT JSON {
'_id' : c.game_key,
'gameId' : c.game_id,
'gamePrefix' : c.game_prefix,
'gameName' : c.game_nm,
'gameAliases' : [
SELECT JSON {
'_id' : { 'gameKey' : a.game_key, 'value' : a.alias_value }
}
FROM sg_game_catalog_identity_alias a
WHERE a.game_key = c.game_key
]
}
FROM sg_game_catalog c
WHERE c.active_yn = 'Y'
WITH CHECK OPTION;
/
-- Serialize the DB JSON document itself before embedding. GAME_ID, GAME_PREFIX,
-- names and every alias therefore share one vector search document.
UPDATE sg_game_catalog c
SET c.embedding = DBMS_VECTOR.UTL_TO_EMBEDDING(
(
SELECT JSON_SERIALIZE(d.data RETURNING CLOB)
FROM sg_game_catalog_identity_dv d
WHERE JSON_VALUE(d.data, '$._id') = c.game_key
),
JSON(sg_qa_vector_params('search_document'))
),
c.updated_at = SYSTIMESTAMP
WHERE c.active_yn = 'Y';
/
COMMENT ON TABLE sg_game_catalog_identity_dv IS
'DB JSON identity document for each active game; the canonical embedding source for game-name, alias, GAME_ID and GAME_PREFIX resolution.';
/

View File

@@ -0,0 +1,144 @@
-- STD-18 asks for an aggregate over qualifying orders, not an individual
-- transaction list. Remove the invalid customer-derived references and keep
-- one reusable, data-neutral aggregate pattern for runtime retrieval.
DELETE FROM sg_qa_vector_example
WHERE source_case_id IN ('STD-18', 'PAT-STD-18')
AND source_type IN ('CUSTOMER_QA_BENCHMARK', 'GENERALIZED_QUESTION_PATTERN');
/
DECLARE
v_input CLOB;
v_embedding VECTOR;
v_exists NUMBER;
BEGIN
v_input := TO_CLOB('질문 패턴: 전체 매출에서 금액 조건을 만족하는 주문을 집계해줘.')
|| CHR(10) || 'Question pattern: summarize whole-scope sales after a payment amount filter.'
|| CHR(10) || 'Logical object role: SALES_TRANSACTION'
|| CHR(10) || 'Required result grain: one aggregate row with total sales amount, distinct buyer count, and order count.'
|| CHR(10) || 'A reference to orders does not by itself request individual order detail.';
v_embedding := DBMS_VECTOR.UTL_TO_EMBEDDING(
v_input,
JSON(sg_qa_vector_params('search_document'))
);
SELECT COUNT(*)
INTO v_exists
FROM sg_qa_vector_example
WHERE source_type = 'POLICY_TEMPLATE'
AND source_case_id = 'FILTERED_SALES_AGGREGATE';
IF v_exists = 0 THEN
INSERT INTO sg_qa_vector_example (
question, answer_sql, answer_text, embedding_input, embedding, embedding_model,
reference_status, reference_kind, target_type, object_role,
inspection_status, inspection_note, verified_at, verified_by,
source_case_id, source_type
) VALUES (
'전체 매출에서 금액 조건을 만족하는 주문을 집계해줘.',
TO_CLOB('SELECT SUM(CAST(s."PAYMT_AMT" AS NUMBER)) AS "TOTAL_SALES_AMOUNT",' || CHR(10)
|| ' COUNT(DISTINCT s."GUID") AS "BUYER_COUNT",' || CHR(10)
|| ' COUNT(*) AS "ORDER_COUNT"' || CHR(10)
|| 'FROM "SGMP_POC"."COMN_SALES_TXN" s' || CHR(10)
|| 'WHERE s."PAYMT_DTM" >= <BUSINESS_DATE_START>' || CHR(10)
|| ' AND s."PAYMT_DTM" < <BUSINESS_DATE_END>' || CHR(10)
|| ' AND CAST(s."PAYMT_AMT" AS NUMBER) <AMOUNT_CONDITION>' || CHR(10)
|| ' AND s."EXPT_USER_YN" = ''N'''),
'Structural Few-shot: return one aggregate row containing total sales amount, distinct buyer count, and order count after the requested payment-amount filter. Do not return individual orders unless the user explicitly asks for a list or detail rows.',
v_input,
v_embedding,
'cohere.embed-v4.0',
'APPROVED', 'SQL_TEMPLATE', 'NONE', 'SALES_TRANSACTION',
'VERIFIED',
'Reusable whole-scope filtered-sales aggregate. No customer date, amount, game, or expected result is stored.',
SYSTIMESTAMP, 'SGMP_POC_METADATA_REVIEW',
'FILTERED_SALES_AGGREGATE', 'POLICY_TEMPLATE'
);
ELSE
UPDATE sg_qa_vector_example
SET question = '전체 매출에서 금액 조건을 만족하는 주문을 집계해줘.',
answer_sql = TO_CLOB('SELECT SUM(CAST(s."PAYMT_AMT" AS NUMBER)) AS "TOTAL_SALES_AMOUNT",' || CHR(10)
|| ' COUNT(DISTINCT s."GUID") AS "BUYER_COUNT",' || CHR(10)
|| ' COUNT(*) AS "ORDER_COUNT"' || CHR(10)
|| 'FROM "SGMP_POC"."COMN_SALES_TXN" s' || CHR(10)
|| 'WHERE s."PAYMT_DTM" >= <BUSINESS_DATE_START>' || CHR(10)
|| ' AND s."PAYMT_DTM" < <BUSINESS_DATE_END>' || CHR(10)
|| ' AND CAST(s."PAYMT_AMT" AS NUMBER) <AMOUNT_CONDITION>' || CHR(10)
|| ' AND s."EXPT_USER_YN" = ''N'''),
answer_text = 'Structural Few-shot: return one aggregate row containing total sales amount, distinct buyer count, and order count after the requested payment-amount filter. Do not return individual orders unless the user explicitly asks for a list or detail rows.',
embedding_input = v_input,
embedding = v_embedding,
reference_status = 'APPROVED',
reference_kind = 'SQL_TEMPLATE',
target_type = 'NONE',
object_role = 'SALES_TRANSACTION',
inspection_status = 'VERIFIED',
inspection_note = 'Reusable whole-scope filtered-sales aggregate. No customer date, amount, game, or expected result is stored.',
verified_at = SYSTIMESTAMP,
verified_by = 'SGMP_POC_METADATA_REVIEW'
WHERE source_type = 'POLICY_TEMPLATE'
AND source_case_id = 'FILTERED_SALES_AGGREGATE';
END IF;
COMMIT;
END;
/
-- A SQL template is prompt context, never an executable statement. Permit
-- reviewed policy templates to retain logical placeholders while continuing
-- to require executable SQL for automatically generalized patterns.
CREATE OR REPLACE FUNCTION sg_qa_vector_search(
p_question IN CLOB,
p_top_k IN PLS_INTEGER DEFAULT 3,
p_target_type IN VARCHAR2 DEFAULT 'ANY'
) RETURN SYS_REFCURSOR AUTHID DEFINER
IS
v_query_vector VECTOR;
v_results SYS_REFCURSOR;
v_target_type VARCHAR2(16) := UPPER(TRIM(NVL(p_target_type, 'ANY')));
v_max_cosine_distance NUMBER;
BEGIN
IF p_question IS NULL THEN RAISE_APPLICATION_ERROR(-20003, 'question is required.'); END IF;
IF p_top_k IS NULL OR p_top_k < 1 OR p_top_k > 20 THEN
RAISE_APPLICATION_ERROR(-20004, 'top_k must be between 1 and 20.');
END IF;
IF v_target_type NOT IN ('NONE', 'SINGLE', 'MULTI', 'ALL', 'ANY') THEN
RAISE_APPLICATION_ERROR(-20005, 'invalid target type.');
END IF;
SELECT number_value INTO v_max_cosine_distance
FROM sg_game_scope_policy
WHERE policy_key = 'QA_VECTOR_MAX_COSINE_DISTANCE'
AND active_yn = 'Y'
AND number_value IS NOT NULL;
v_query_vector := DBMS_VECTOR.UTL_TO_EMBEDDING(
p_question, JSON(sg_qa_vector_params('search_query'))
);
OPEN v_results FOR
SELECT example_id, question, answer_sql, answer_text, embedding_model,
reference_kind, target_type, object_role, source_case_id, source_type,
cosine_distance
FROM (
SELECT example_id, question, answer_sql, answer_text, embedding_model,
reference_kind, target_type, object_role, source_case_id, source_type,
VECTOR_DISTANCE(embedding, v_query_vector, COSINE) AS cosine_distance
FROM sg_qa_vector_example
WHERE reference_status = 'APPROVED'
AND inspection_status = 'VERIFIED'
AND answer_sql IS NOT NULL
AND (source_type = 'POLICY_TEMPLATE'
OR NOT REGEXP_LIKE(answer_sql, '<[A-Z][A-Z0-9_]*>', 'i'))
AND (target_type = 'ANY' OR v_target_type = 'ANY' OR target_type = v_target_type)
)
WHERE cosine_distance <= v_max_cosine_distance
ORDER BY cosine_distance, example_id
FETCH FIRST p_top_k ROWS ONLY;
RETURN v_results;
END;
/
SELECT example_id, source_case_id, source_type, reference_status, inspection_status
FROM sg_qa_vector_example
WHERE source_case_id IN ('STD-18', 'PAT-STD-18', 'FILTERED_SALES_AGGREGATE')
ORDER BY example_id;

View File

@@ -0,0 +1,33 @@
-- This customer question is game-unscoped. Keep the existing NONE-compatible
-- Few-shot path; game_query_plan remains responsible for scope resolution.
UPDATE sg_qa_vector_example
SET question = '매출에서 금액 조건을 만족하는 주문의 총액, 구매자 수, 주문 수를 집계해줘.',
answer_sql = TO_CLOB('SELECT' || CHR(10)
|| ' SUM(CAST(s."PAYMT_AMT" AS NUMBER)) AS "TOTAL_SALES_AMOUNT",' || CHR(10)
|| ' COUNT(DISTINCT s."GUID") AS "BUYER_COUNT",' || CHR(10)
|| ' COUNT(*) AS "ORDER_COUNT"' || CHR(10)
|| ' FROM <COMMON_SALES_TRANSACTION> s' || CHR(10)
|| ' WHERE s."PAYMT_DTM" >= <BUSINESS_DATE_START>' || CHR(10)
|| ' AND s."PAYMT_DTM" < <BUSINESS_DATE_END>' || CHR(10)
|| ' AND CAST(s."PAYMT_AMT" AS NUMBER) <AMOUNT_CONDITION>' || CHR(10)
|| ' AND s."EXPT_USER_YN" = ''N'''),
answer_text = 'Aggregate result-shape reference: return one row with total sales amount, distinct buyer count, and order count. The game plan separately supplies any game scope; use this pattern only when the question is semantically similar.',
embedding_input = TO_CLOB('Question pattern: summarize sales after a payment amount condition.' || CHR(10)
|| 'Logical object role: SALES_TRANSACTION' || CHR(10)
|| 'Result grain: one aggregate row with total sales amount, distinct buyer count, and order count.'),
embedding = DBMS_VECTOR.UTL_TO_EMBEDDING(
TO_CLOB('Question pattern: summarize sales after a payment amount condition.' || CHR(10)
|| 'Logical object role: SALES_TRANSACTION' || CHR(10)
|| 'Result grain: one aggregate row with total sales amount, distinct buyer count, and order count.'),
JSON(sg_qa_vector_params('search_document'))),
target_type = 'NONE',
source_case_id = 'PORTAL-STD-18',
inspection_note = 'Generalized aggregate pattern for the current game-unscoped question; game_query_plan controls scope separately.',
verified_at = SYSTIMESTAMP,
verified_by = 'SGMP_POC_METADATA_REVIEW'
WHERE example_id = 141
AND source_type = 'POLICY_TEMPLATE';
/
COMMIT;
/

View File

@@ -0,0 +1,23 @@
-- The reviewed customer benchmark answer is part of this Few-shot guidance.
-- It clarifies that the requested result is one aggregate row, not detail rows.
UPDATE sg_qa_vector_example
SET answer_text = TO_CLOB('Expected answer shape: return exactly one aggregate row, not individual order rows.' || CHR(10)
|| 'Expected answer:' || CHR(10)
|| 'TOTAL_SALES_AMOUNT BUYER_COUNT ORDER_COUNT' || CHR(10)
|| '------------------ ----------- -----------' || CHR(10)
|| ' 204720 6 6'),
embedding_input = TO_CLOB('Question pattern: summarize sales after a payment amount condition.' || CHR(10)
|| 'Expected output: TOTAL_SALES_AMOUNT, BUYER_COUNT, ORDER_COUNT as one aggregate row.' || CHR(10)
|| 'Expected result example: 204720, 6, 6.'),
embedding = DBMS_VECTOR.UTL_TO_EMBEDDING(
TO_CLOB('Question pattern: summarize sales after a payment amount condition.' || CHR(10)
|| 'Expected output: TOTAL_SALES_AMOUNT, BUYER_COUNT, ORDER_COUNT as one aggregate row.' || CHR(10)
|| 'Expected result example: 204720, 6, 6.'),
JSON(sg_qa_vector_params('search_document'))),
verified_at = SYSTIMESTAMP,
verified_by = 'SGMP_POC_METADATA_REVIEW'
WHERE example_id = 141
AND source_type = 'POLICY_TEMPLATE';
/
COMMIT;
/

View File

@@ -7,13 +7,14 @@ IS
v_result CLOB;
v_extract JSON_OBJECT_T;
BEGIN
v_prompt := 'Extract only game-name mentions from the user question. '
|| 'Metrics, acronyms, dates, filters, and database object or column names are not game names unless they are themselves an explicit game title. '
v_prompt := 'Extract only game identity mentions from the user question. '
|| 'A registered game title, alias, GAME_ID, GAME_PREFIX, or catalog key is a game mention and must be preserved exactly as written. '
|| 'Metrics, acronyms, dates, filters, and database object or column names are not game mentions unless they are themselves an explicit registered game identity. '
|| 'When a title-like noun directly qualifies a game data request such as user master, character, sales, AU, NRU, server, or game log, preserve that noun as a game-name mention even when it is not in a catalog. '
|| 'Do not discard an unknown title merely because it cannot be resolved. General scope words such as common, overall, all, total, or every are not game-name mentions unless they are part of an explicit title. '
|| 'Return exactly one JSON object with keys game_mentions (array of strings) '
|| 'and scope_hint (GLOBAL, SINGLE_GAME, MULTI_GAME, ALL_GAMES, UNKNOWN). '
|| 'Do not resolve names to IDs and do not generate SQL. '
|| 'Do not resolve one game identity to another and do not generate SQL. '
|| 'Return raw JSON only: no prose, no Markdown, and no code fence. Question: '
|| DBMS_LOB.SUBSTR(p_question, 4000, 1);

View File

@@ -93,8 +93,12 @@ IS
v_unresolved JSON_ARRAY_T := JSON_ARRAY_T();
v_unmatched JSON_ARRAY_T := JSON_ARRAY_T();
v_mention_results JSON_ARRAY_T := JSON_ARRAY_T();
v_execution_tasks JSON_ARRAY_T := JSON_ARRAY_T();
v_seen_game_keys t_seen_game_keys;
v_result JSON_OBJECT_T := JSON_OBJECT_T();
v_reference_summary JSON_OBJECT_T := JSON_OBJECT_T();
v_select_ai_reference CLOB;
v_plan_status VARCHAR2(30);
v_matched_count PLS_INTEGER := 0;
v_data_eligible_count PLS_INTEGER := 0;
v_unmatched_count PLS_INTEGER := 0;
@@ -111,6 +115,14 @@ IS
v_mention_result JSON_OBJECT_T;
v_target JSON_OBJECT_T;
v_unmatched_item JSON_OBJECT_T;
v_execution_task JSON_OBJECT_T;
v_worker_arguments JSON_OBJECT_T;
v_fewshot_arguments JSON_OBJECT_T;
v_task_plan JSON_OBJECT_T;
v_task_reference JSON_OBJECT_T;
v_task_targets JSON_ARRAY_T;
v_excluded_mentions JSON_ARRAY_T;
v_task_question CLOB;
v_cursor SYS_REFCURSOR;
v_game_key VARCHAR2(128);
@@ -345,21 +357,52 @@ BEGIN
END LOOP;
END IF;
v_result.put('contractVersion', '1.0');
IF v_target_type = 'NONE' THEN
v_plan_status := 'NO_TARGET';
ELSIF v_matched_count = 0 THEN
v_plan_status := 'UNMATCHED';
ELSIF v_data_eligible_count = 0 THEN
v_plan_status := 'UNAVAILABLE';
ELSIF v_unmatched_count > 0 THEN
v_plan_status := 'PARTIAL';
ELSE
v_plan_status := 'SUPPORTED';
END IF;
-- This is the sole Select AI handoff contract. It is deliberately generic:
-- game values and physical objects come only from the database lookup above.
v_reference_summary.put('targetType', v_target_type);
v_reference_summary.put('status', v_plan_status);
v_reference_summary.put('gameTargets', v_supported);
v_select_ai_reference := '[GAME QUERY REFERENCE]' || CHR(10) || CHR(10)
|| v_reference_summary.to_clob()
|| CHR(10) || CHR(10)
|| 'Field meanings:' || CHR(10) || CHR(10)
|| '* targetType:' || CHR(10)
|| ' * NONE: No game was resolved.' || CHR(10)
|| ' * SINGLE: Exactly one game was resolved.' || CHR(10)
|| ' * MULTI: Multiple specific games were resolved.' || CHR(10)
|| ' * ALL: The query applies to all supported games.' || CHR(10)
|| '* status: The result of game-target resolution.' || CHR(10)
|| '* gameTargets: The exact games resolved by the DB lookup. Each item may include:' || CHR(10)
|| ' * gameKey: Canonical game identifier.' || CHR(10)
|| ' * gamePrefix: Prefix used for game-scoped objects.' || CHR(10)
|| ' * userMasterObjectName: Resolved user-master object for that game.' || CHR(10) || CHR(10)
|| 'Object-selection guidance:' || CHR(10) || CHR(10)
|| '* Use only the targets listed in gameTargets.' || CHR(10)
|| '* For NONE, use a game-neutral common object when it directly answers the question.' || CHR(10)
|| '* If no suitable common object exists, state that a game name is required.' || CHR(10)
|| '* Do not infer an unlisted game or game-scoped object.';
v_result.put('contractVersion', '2.0');
v_result.put('targetType', v_target_type);
v_result.put('scopeType', v_target_type);
v_result.put('selectAiReference', v_select_ai_reference);
v_result.put('extractScopeHint', v_scope_type);
IF v_target_type = 'NONE' THEN
v_result.put('status', 'NO_TARGET');
ELSIF v_matched_count = 0 THEN
v_result.put('status', 'UNMATCHED');
ELSIF v_data_eligible_count = 0 THEN
v_result.put('status', 'UNAVAILABLE');
ELSIF v_unmatched_count > 0 THEN
v_result.put('status', 'PARTIAL');
ELSE
v_result.put('status', 'SUPPORTED');
END IF;
v_result.put('status', v_plan_status);
-- `gameTargets` is the public downstream contract. The detailed `targets`
-- collection remains diagnostic evidence only for the MCP response.
v_result.put('gameTargets', v_supported);
v_result.put('targets', v_targets);
v_result.put('matchedGames', v_supported);
v_result.put('mentionResults', v_mention_results);
@@ -368,6 +411,66 @@ BEGIN
v_result.put('unresolvedTargets', v_unresolved);
v_result.put('dataIneligibleTargets', v_data_ineligible);
v_result.put('unmatchedGames', v_unmatched);
-- Dynamic ReAct work items. Every game identity and availability state comes
-- from the catalog lookup above; clients must not infer their own targets.
FOR i IN 0 .. v_targets.get_size - 1 LOOP
v_target := TREAT(v_targets.get(i) AS JSON_OBJECT_T);
IF v_target.get_string('gameKey') IS NOT NULL THEN
v_execution_task := JSON_OBJECT_T();
v_execution_task.put(
'action',
CASE
WHEN v_target.get_string('dataStatus') = 'AVAILABLE'
AND v_target.get_string('userMasterObjectName') IS NOT NULL
THEN 'QUERY'
ELSE 'REPORT_UNAVAILABLE'
END
);
v_execution_task.put('scopeGameKey', v_target.get_string('gameKey'));
v_execution_task.put('target', v_target);
IF v_execution_task.get_string('action') = 'QUERY' THEN
v_excluded_mentions := JSON_ARRAY_T();
FOR j IN 0 .. v_targets.get_size - 1 LOOP
v_candidate := TREAT(v_targets.get(j) AS JSON_OBJECT_T);
IF v_candidate.get_string('gameKey') <> v_target.get_string('gameKey')
AND v_candidate.get_string('mention') IS NOT NULL THEN
v_excluded_mentions.append(v_candidate.get_string('mention'));
END IF;
END LOOP;
-- Preserve the original question verbatim. The target-specific
-- queryPlan below is the separate, authoritative scope contract.
v_task_question := p_question;
v_task_reference := JSON_OBJECT_T();
v_task_targets := JSON_ARRAY_T();
v_task_targets.append(v_target);
v_task_reference.put('targetType', 'SINGLE');
v_task_reference.put('status', 'SUPPORTED');
v_task_reference.put('gameTargets', v_task_targets);
v_task_plan := JSON_OBJECT_T();
v_task_plan.put('contractVersion', '2.0');
v_task_plan.put('targetType', 'SINGLE');
v_task_plan.put('status', 'SUPPORTED');
v_task_plan.put('gameTargets', v_task_targets);
v_task_plan.put('selectAiReference',
'[GAME QUERY REFERENCE]' || CHR(10) || CHR(10) || v_task_reference.to_clob());
v_worker_arguments := JSON_OBJECT_T();
v_worker_arguments.put('prompt', v_task_question);
v_worker_arguments.put('scopeGameKey', v_target.get_string('gameKey'));
v_worker_arguments.put('queryPlan', v_task_plan);
v_fewshot_arguments := JSON_OBJECT_T();
v_fewshot_arguments.put('question', v_task_question);
v_fewshot_arguments.put('topK', 3);
v_execution_task.put('workerTool', 'oracle.select_ai.smilegate_fewshot_nl2sql');
v_execution_task.put('workerArguments', v_worker_arguments);
v_execution_task.put('fewShotArguments', v_fewshot_arguments);
END IF;
v_execution_tasks.append(v_execution_task);
END IF;
END LOOP;
v_result.put('executionTasks', v_execution_tasks);
v_result.put('nextAction', 'CALL_FEWSHOT');
CASE v_target_type

View File

@@ -1,5 +1,6 @@
-- General target-contract guidance belongs to the Select AI profile, not application branches.
-- It contains no current game, prefix, ID, or physical object name.
-- Select AI profile instructions contain only common SQL-generation guidance.
-- Game target routing and execution policy are supplied at runtime by
-- SG_GAME_QUERY_PLAN; they do not belong in the profile-wide prompt.
BEGIN
DBMS_CLOUD_AI.SET_ATTRIBUTE(
@@ -7,7 +8,7 @@ BEGIN
attribute_name => 'additional_instructions',
attribute_value => q'~Generate Oracle SQL only for the listed approved objects. Do not reference external tables. Use English aliases only. Use database comments and annotations as the source of business rules.
When an authoritative game query plan is supplied in the user request, use its targets and statuses as the only game-scope source; do not independently rematch a game, infer a default game, or substitute one target's object for another. A plan with no selected game does not by itself prohibit a query: use an approved common object when it can answer the operation. If an operation inherently requires a game-scoped logical object and the relevant plan target is unresolved or its physical object is unavailable, return an appropriate zero-row result. A zero-row result is not a synthetic row containing a NULL value and is not an execution failure. For multiple or all resolved targets, use the supplied target identifiers to group a common object when applicable, or combine only the supplied available objects. When filtering an approved common object by game, derive the allowed game identifiers through the active approved game-alias catalog lookup; do not put a game identifier or prefix directly as a SQL literal predicate. Preserve an alias lookup with no matching data as an empty or aggregate-null result, never as a substituted game. Never invent identifiers, prefixes, or physical object names.~'
~'
);
END;
/

View File

@@ -0,0 +1,63 @@
-- Benchmark correction: common-fact eligibility is determined by the active
-- alias source. Registry-only games are reported separately while eligible
-- targets continue through the common-fact query.
-- Korean baseline text and SQL are reconstructed from UTF-8 base64 for SQLcl safety.
UPDATE sg_ai_qa_question
SET expected_focus = utl_i18n.raw_to_char(
utl_encode.base64_decode(utl_raw.cast_to_raw(
'7Jes65+sIOqyjOyehCDruYTqtZDsl5DshJwg7Lm07YOI66Gc6re466GcIO2ZleyduOuQnCDrjIDsg4HsnYAg67OE7LmtIOy5tO2TiOuhnOq3uOulvCDqtazrj5kg7KeR7ZWp7Jy866GcIO2VmOqzoCDqs7XthrUg7IKs7IukIO2FjOydtOu4lOydhCBMRUZUIEpPSU7tlZjsl6wg6rKM7J6E67OEIOynkeqzhO2VnOuLpC4g7IKs7IukIO2WieydtCDsl4bripQg64yA7IOB7J2AIDDsnLzroZwg67O07KG07ZWY6rOgLCDrp6Tsua3rkJwg64uk66W4IOuMgOyDgeydmCDqsrDqs7zrpbwg7IOd65617ZWY7KeAIOyViuuKlOuLpC4gRFVBTCBVTklPTuycvOuhnCDrjIDsg4HrqoXqs7wg6rCS7J2EIO2VqeyEse2VmOyngCDslYrripTri6Qu'
)),
'AL32UTF8'
),
baseline_answer = utl_i18n.raw_to_char(
utl_encode.base64_decode(utl_raw.cast_to_raw(
'7Lm07YOI66Gc6re4IOunpOy5rSDrjIDsg4Hrs4Qg66ek7Lac7J2EIOuwmO2ZmO2VnOuLpC4g7IKs7IukIO2WieydtCDsl4bripQg64yA7IOB7J2AIDAsIOuLpOuluCDrp6Tsua0g64yA7IOB7J2AIO2VtOuLuSDsnbzsnpDsnZgg7KeR6rOE6rCS7J2EIOuwmO2ZmO2VnOuLpC4='
)),
'AL32UTF8'
),
baseline_sql = utl_i18n.raw_to_char(
utl_encode.base64_decode(utl_raw.cast_to_raw(
'V0lUSCByZXF1ZXN0ZWRfZ2FtZXMgQVMgKAogIFNFTEVDVCBESVNUSU5DVCBnLiJHQU1FX0lEIiwgZy4iR0FNRV9OTSIKICBGUk9NICJTR01QX1BPQyIuIkNPTU5fR0FNRV9BTElBU19CQVMiIGcKICBXSEVSRSBnLiJVU0VfWU4iID0gJ1knCiAgICBBTkQgKAogICAgICBVUFBFUihnLiJHQU1FX05NIikgTElLRSBVUFBFUignJUJ1YmJseXolJykKICAgICAgT1IgVVBQRVIoZy4iR0FNRV9BTElBU19OTSIpIExJS0UgVVBQRVIoJyVCdWJibHl6JScpCiAgICAgIE9SIFVQUEVSKGcuIkdBTUVfTk0iKSBMSUtFIFVQUEVSKCcl7Lm07KCc64KYJScpCiAgICAgIE9SIFVQUEVSKGcuIkdBTUVfQUxJQVNfTk0iKSBMSUtFIFVQUEVSKCcl7Lm07KCc64KYJScpCiAgICApCikKU0VMRUNUIHJnLiJHQU1FX05NIiBBUyAiR0FNRV9OQU1FIiwKICAgICAgIE5WTChTVU0oQ0FTRQogICAgICAgICBXSEVOIHMuIlBBWU1UX0RUTSIgPj0gVE9fREFURSgnMjAyNjA3MTUnLCAnWVlZWU1NREQnKQogICAgICAgICAgQU5EIHMuIlBBWU1UX0RUTSIgPCBUT19EQVRFKCcyMDI2MDcxNicsICdZWVlZTU1ERCcpCiAgICAgICAgICBBTkQgcy4iRVhQVF9VU0VSX1lOIiA9ICdOJwogICAgICAgICBUSEVOIENBU1Qocy4iUEFZTVRfQU1UIiBBUyBOVU1CRVIpIEVMU0UgMCBFTkQpLCAwKSBBUyAiU0FMRVNfQU1UIgpGUk9NIHJlcXVlc3RlZF9nYW1lcyByZwpMRUZUIEpPSU4gIlNHTVBfUE9DIi4iQ09NTl9TQUxFU19UWE4iIHMKICBPTiBzLiJHQU1FX0lEIiA9IHJnLiJHQU1FX0lEIgpHUk9VUCBCWSByZy4iR0FNRV9OTSIKT1JERVIgQlkgcmcuIkdBTUVfTk0iCg=='
)),
'AL32UTF8'
)
WHERE question_code = 'STD-11';
UPDATE sg_qa_vector_example
SET inspection_status = 'REVIEW',
inspection_note = 'Customer benchmark criterion updated: multi-target common-fact comparisons preserve catalog-resolved zero-fact targets through a catalog-driven left join.'
WHERE source_type = 'CUSTOMER_QA_BENCHMARK'
AND source_case_id = 'STD-11';
COMMIT;
-- Correct the earlier catalog-only interpretation in this same migration. A
-- registry can identify a game to the operator, but does not by itself make it
-- an approved source for a common fact query.
UPDATE sg_ai_qa_question
SET expected_focus = utl_i18n.raw_to_char(
utl_encode.base64_decode(utl_raw.cast_to_raw(
'67O17IiYIOqyjOyehCDqs7XthrUg7IKs7IukIOuNsOydtO2EsCDsp4jsnZjripQg7Zmc7ISxIOqyjOyehCDrs4Tsua0g7JuQ7LKc7JeQ7IScIO2ZleyduOuQnCDrjIDsg4Hrp4wg7IKs7IukIOyhsO2ajCDrjIDsg4HsnLzroZwg7IKs7Jqp7ZWY6rOgLCDrs4Tsua0g7JuQ7LKc7J20IOyXhuuKlCDroIjsp4DsiqTtirjrpqwg7KCE7JqpIOuMgOyDgeydgCDsobDtmowg67aI6rCAIOyDge2DnOuhnCDrs4Trj4Qg7JWI64K07ZWc64ukLiDsobDtmowg6rCA64ql7ZWcIOuMgOyDgeydmCDqsrDqs7zripQg7Jyg7KeA7ZWY66mwIOyghOyytCDsmpTssq3snYQg7LCo64uo7ZWY7KeAIOyViuuKlOuLpC4gRFVBTCBVTklPTuycvOuhnCDrjIDsg4Eg7ZaJ7J2064KYIOqwkuydhCDrp4zrk6Tsp4Ag7JWK64qU64ukLg=='
)),
'AL32UTF8'
),
baseline_answer = utl_i18n.raw_to_char(
utl_encode.base64_decode(utl_raw.cast_to_raw(
'7Lm07KCc64KYIOunpOy2nOydgCAyMjcsNjgx7J6F64uI64ukLiBCdWJibHl664qUIO2ZnOyEsSDqsozsnoQg67OE7LmtIOybkOyynOydtCDsl4bslrQg66ek7LacIOyhsO2ajCDrjIDsg4HsnbQg7JWE64uZ64uI64ukLg=='
)),
'AL32UTF8'
)
WHERE question_code = 'STD-11';
UPDATE sg_qa_vector_example
SET inspection_status = 'REVIEW',
inspection_note = 'Customer benchmark criterion updated: common-fact comparison queries use active-alias eligible targets; registry-only targets are separately unavailable and do not become synthetic fact rows.'
WHERE source_type = 'CUSTOMER_QA_BENCHMARK'
AND source_case_id = 'STD-11';
COMMIT;
SELECT question_code, expected_focus, baseline_answer
FROM sg_ai_qa_question
WHERE question_code = 'STD-11';

View File

@@ -0,0 +1,22 @@
-- Object-specific, data-driven guidance for comparison queries on a common fact table.
-- No game, prefix, ID, or physical per-game object is embedded in this annotation.
DECLARE
v_result VARCHAR2(4000);
BEGIN
v_result := sgmp_set_annotation(
'SGMP_POC',
'TABLE',
'COMN_SALES_TXN',
NULL,
'Game fact scope: query this table only for plan targets marked ACTIVE_ALIAS. Derive target GAME_ID values through active COMN_GAME_ALIAS_BAS aliases rather than direct identifier or prefix literals. For a mixed request, retain the eligible target results and report other plan statuses separately; do not substitute or manufacture a target result.',
'MULTI_TARGET_COMPARISON'
);
DBMS_OUTPUT.PUT_LINE(v_result);
END;
/
SELECT annotation_name, annotation_value
FROM user_annotations_usage
WHERE object_name = 'COMN_SALES_TXN'
AND annotation_name = 'MULTI_TARGET_COMPARISON';

View File

@@ -0,0 +1,92 @@
-- Generic Few-shot structure for multi-target comparisons on a common fact object.
-- The template is intentionally logical: no current game, prefix, ID, date, or result value is embedded.
DECLARE
v_exists NUMBER;
v_input CLOB;
v_embedding VECTOR;
BEGIN
SELECT COUNT(*)
INTO v_exists
FROM sg_qa_vector_example
WHERE source_type = 'POLICY_TEMPLATE'
AND source_case_id = 'MULTI_COMMON_FACT_LEFT_JOIN';
IF v_exists = 0 THEN
v_input := TO_CLOB('Question pattern: Compare a common fact metric across multiple resolved games. Use only fact-query-eligible targets and separately report known registry-only or unavailable targets.')
|| CHR(10) || 'Logical object role: SALES_TRANSACTION'
|| CHR(10) || 'Required structure: active alias catalog distinct game set, left join fact, aggregate by catalog display identifier.';
v_embedding := DBMS_VECTOR.UTL_TO_EMBEDDING(
v_input,
JSON(sg_qa_vector_params('search_document'))
);
INSERT INTO sg_qa_vector_example (
question, answer_sql, answer_text, embedding_input, embedding, embedding_model,
reference_status, reference_kind, target_type, object_role,
inspection_status, inspection_note, verified_at, verified_by,
source_case_id, source_type
) VALUES (
'Compare a common fact metric across multiple resolved games, including games with no fact rows.',
TO_CLOB('WITH resolved_games AS (' || CHR(10)
|| ' SELECT DISTINCT a."GAME_ID", a."GAME_NM"' || CHR(10)
|| ' FROM "SGMP_POC"."COMN_GAME_ALIAS_BAS" a' || CHR(10)
|| ' WHERE a."USE_YN" = ''Y''' || CHR(10)
|| ' AND (<ACTIVE_ALIAS_MATCHES_FOR_EACH_REQUESTED_GAME_TERM>)' || CHR(10)
|| ')' || CHR(10)
|| 'SELECT g."GAME_NM" AS "GAME_NAME",' || CHR(10)
|| ' NVL(SUM(CASE WHEN <FACT_DATE_AND_EXCLUSION_CONDITION>' || CHR(10)
|| ' THEN CAST(f."<METRIC_COLUMN>" AS NUMBER) ELSE 0 END), 0) AS "METRIC_VALUE"' || CHR(10)
|| 'FROM resolved_games g' || CHR(10)
|| 'LEFT JOIN "SGMP_POC"."<APPROVED_COMMON_FACT_OBJECT>" f' || CHR(10)
|| ' ON f."GAME_ID" = g."GAME_ID"' || CHR(10)
|| 'GROUP BY g."GAME_NM"' || CHR(10)
|| 'ORDER BY g."GAME_NM"'),
'Structural Few-shot only. Replace every angle-bracket placeholder from the current approved object metadata, the current game query plan, and the original question. Only ACTIVE_ALIAS targets enter the alias-driven LEFT JOIN and grouping; report registry-only or unavailable targets from the plan without manufacturing fact rows with DUAL/UNION.',
v_input,
v_embedding,
'cohere.embed-v4.0',
'APPROVED', 'SQL_TEMPLATE', 'MULTI', 'SALES_TRANSACTION',
'VERIFIED',
'Generic, non-customer-specific comparison structure. Verified against the game-alias and common-fact metadata contract; not an executable answer key.',
SYSTIMESTAMP, 'SGMP_POC_METADATA_REVIEW',
'MULTI_COMMON_FACT_LEFT_JOIN', 'POLICY_TEMPLATE'
);
END IF;
COMMIT;
END;
/
-- Keep the approved template current when the policy text evolves. The vector
-- is rebuilt from its generic retrieval text; no customer answer is embedded.
DECLARE
v_input CLOB;
v_embedding VECTOR;
BEGIN
v_input := TO_CLOB('Question pattern: Compare a common fact metric across multiple resolved games. Use only fact-query-eligible targets and separately report known registry-only or unavailable targets.')
|| CHR(10) || 'Logical object role: SALES_TRANSACTION'
|| CHR(10) || 'Required structure: active alias catalog distinct game set, left join fact, aggregate by catalog display identifier.';
v_embedding := DBMS_VECTOR.UTL_TO_EMBEDDING(
v_input,
JSON(sg_qa_vector_params('search_document'))
);
UPDATE sg_qa_vector_example
SET question = 'Compare a common fact metric across multiple resolved games, using only fact-query-eligible targets.',
answer_text = 'Structural Few-shot only. Replace every angle-bracket placeholder from the current approved object metadata, the current game query plan, and the original question. Only ACTIVE_ALIAS targets enter the alias-driven LEFT JOIN and grouping; report registry-only or unavailable targets from the plan without manufacturing fact rows with DUAL/UNION.',
embedding_input = v_input,
embedding = v_embedding,
inspection_note = 'Generic, non-customer-specific comparison structure. Active-alias targets are fact-query eligible; registry-only targets are reported separately. Not an executable answer key.',
verified_at = SYSTIMESTAMP,
verified_by = 'SGMP_POC_METADATA_REVIEW'
WHERE source_type = 'POLICY_TEMPLATE'
AND source_case_id = 'MULTI_COMMON_FACT_LEFT_JOIN';
COMMIT;
END;
/
SELECT example_id, reference_status, reference_kind, target_type, object_role,
source_case_id, source_type
FROM sg_qa_vector_example
WHERE source_type = 'POLICY_TEMPLATE'
AND source_case_id = 'MULTI_COMMON_FACT_LEFT_JOIN';

View File

@@ -0,0 +1,13 @@
-- Retire the overly specific no-target template. The profile and table
-- metadata carry this general scope policy without a case-shaped example.
DELETE FROM sg_qa_vector_example
WHERE source_type = 'POLICY_TEMPLATE'
AND source_case_id = 'NO_ELIGIBLE_FACT_TARGET';
COMMIT;
SELECT COUNT(*) AS remaining_template_count
FROM sg_qa_vector_example
WHERE source_type = 'POLICY_TEMPLATE'
AND source_case_id = 'NO_ELIGIBLE_FACT_TARGET';

View File

@@ -0,0 +1,70 @@
-- Generic Few-shot structure for a filtered transaction/order detail request.
-- It fixes the output grain through an approved object pattern, not a global
-- instruction or a customer-specific answer.
DECLARE
v_input CLOB;
v_embedding VECTOR;
v_exists NUMBER;
BEGIN
v_input := TO_CLOB('Question pattern: List individual payment orders that match a business date and an amount condition.')
|| CHR(10) || 'Logical object role: SALES_TRANSACTION'
|| CHR(10) || 'Required output: transaction identifiers, game, user, payment timestamp, and payment amount.';
v_embedding := DBMS_VECTOR.UTL_TO_EMBEDDING(
v_input,
JSON(sg_qa_vector_params('search_document'))
);
SELECT COUNT(*)
INTO v_exists
FROM sg_qa_vector_example
WHERE source_type = 'POLICY_TEMPLATE'
AND source_case_id = 'FILTERED_TRANSACTION_DETAIL';
IF v_exists = 0 THEN
INSERT INTO sg_qa_vector_example (
question, answer_sql, answer_text, embedding_input, embedding, embedding_model,
reference_status, reference_kind, target_type, object_role,
inspection_status, inspection_note, verified_at, verified_by,
source_case_id, source_type
) VALUES (
'List payment orders matching a date and amount condition.',
TO_CLOB('SELECT t."PAYMT_TRANSAC_ID" AS "PAYMENT_TRANSACTION_ID",' || CHR(10)
|| ' t."PAYMT_TRANSAC_DTL_ID" AS "PAYMENT_TRANSACTION_DETAIL_ID",' || CHR(10)
|| ' t."GAME_ID" AS "GAME_ID",' || CHR(10)
|| ' t."GUID" AS "USER_ID",' || CHR(10)
|| ' t."PAYMT_DTM" AS "PAYMENT_DATETIME",' || CHR(10)
|| ' t."PAYMT_AMT" AS "PAYMENT_AMOUNT"' || CHR(10)
|| 'FROM "SGMP_POC"."COMN_SALES_TXN" t' || CHR(10)
|| 'WHERE t."PAYMT_DTM" >= <BUSINESS_DATE_START>' || CHR(10)
|| ' AND t."PAYMT_DTM" < <BUSINESS_DATE_END>' || CHR(10)
|| ' AND CAST(t."PAYMT_AMT" AS NUMBER) <AMOUNT_CONDITION>' || CHR(10)
|| ' AND t."EXPT_USER_YN" = ''N''' || CHR(10)
|| 'ORDER BY t."PAYMT_DTM", t."PAYMT_TRANSAC_ID", t."PAYMT_TRANSAC_DTL_ID"'),
'Structural Few-shot only. Replace placeholders using the original request and approved metadata. Use the business payment timestamp for a payment-date condition. This pattern is for individual transaction detail; do not substitute an aggregate-only result for a requested order list.',
v_input,
v_embedding,
'cohere.embed-v4.0',
'APPROVED', 'SQL_TEMPLATE', 'NONE', 'SALES_TRANSACTION',
'VERIFIED',
'Generic transaction-detail output shape with no customer date, amount, game, or result value; not an executable answer key.',
SYSTIMESTAMP, 'SGMP_POC_METADATA_REVIEW',
'FILTERED_TRANSACTION_DETAIL', 'POLICY_TEMPLATE'
);
ELSE
UPDATE sg_qa_vector_example
SET embedding_input = v_input,
embedding = v_embedding,
verified_at = SYSTIMESTAMP,
verified_by = 'SGMP_POC_METADATA_REVIEW'
WHERE source_type = 'POLICY_TEMPLATE'
AND source_case_id = 'FILTERED_TRANSACTION_DETAIL';
END IF;
COMMIT;
END;
/
SELECT example_id, reference_status, target_type, object_role, source_case_id
FROM sg_qa_vector_example
WHERE source_type = 'POLICY_TEMPLATE'
AND source_case_id = 'FILTERED_TRANSACTION_DETAIL';

View File

@@ -0,0 +1,57 @@
-- Customer question wording requests individual orders. Align the benchmark
-- with the transaction-detail output pattern rather than forcing KPI summary.
UPDATE sg_ai_qa_question
SET expected_focus = utl_i18n.raw_to_char(
utl_encode.base64_decode(utl_raw.cast_to_raw(
'Q09NTl9TQUxFU19UWE7sl5DshJwgUEFZTVRfRFRNIOq4sOykgOydvCwgUEFZTVRfQU1UID4gMTAwMDAsIEVYUFRfVVNFUl9ZTj0nTicg7KGw6rG07J2YIOqwnOuzhCDso7zrrLgg7IOB7IS466W8IOyhsO2ajO2VnOuLpC4g7KO866y4IOyLneuzhOyekCwg6rKM7J6ELCDsgqzsmqnsnpAsIOqysOygnCDsnbzsi5zsmYAg6riI7JWh7J2EIOygnOqzte2VnOuLpC4='
)),
'AL32UTF8'
),
baseline_sql = TO_CLOB('SELECT s."PAYMT_TRANSAC_ID" AS "PAYMENT_TRANSACTION_ID",' || CHR(10)
|| ' s."PAYMT_TRANSAC_DTL_ID" AS "PAYMENT_TRANSACTION_DETAIL_ID",' || CHR(10)
|| ' s."GAME_ID" AS "GAME_ID",' || CHR(10)
|| ' s."GUID" AS "USER_ID",' || CHR(10)
|| ' s."PAYMT_DTM" AS "PAYMENT_DATETIME",' || CHR(10)
|| ' s."PAYMT_AMT" AS "PAYMENT_AMOUNT"' || CHR(10)
|| 'FROM "SGMP_POC"."COMN_SALES_TXN" s' || CHR(10)
|| 'WHERE s."PAYMT_DTM" >= DATE ''2026-07-15''' || CHR(10)
|| ' AND s."PAYMT_DTM" < DATE ''2026-07-16''' || CHR(10)
|| ' AND CAST(s."PAYMT_AMT" AS NUMBER) > 10000' || CHR(10)
|| ' AND s."EXPT_USER_YN" = ''N''' || CHR(10)
|| 'ORDER BY s."PAYMT_DTM", s."PAYMT_TRANSAC_ID", s."PAYMT_TRANSAC_DTL_ID"'),
baseline_answer = utl_i18n.raw_to_char(
utl_encode.base64_decode(utl_raw.cast_to_raw(
'6rKw7KCc6riI7JWhIDHrp4zsm5Ag7LSI6rO8IOyjvOusuCA26rG07J2EIOyjvOusuCDsi53rs4TsnpAsIOqyjOyehCwg7IKs7Jqp7J6QLCDqsrDsoJwg7J287IucLCDqsrDsoJzquIjslaHqs7wg7ZWo6ruYIOuwmO2ZmO2VnOuLpC4='
)),
'AL32UTF8'
)
WHERE question_code = 'STD-18';
UPDATE sg_qa_vector_example
SET answer_sql = TO_CLOB('SELECT s."PAYMT_TRANSAC_ID" AS "PAYMENT_TRANSACTION_ID",' || CHR(10)
|| ' s."PAYMT_TRANSAC_DTL_ID" AS "PAYMENT_TRANSACTION_DETAIL_ID",' || CHR(10)
|| ' s."GAME_ID" AS "GAME_ID",' || CHR(10)
|| ' s."GUID" AS "USER_ID",' || CHR(10)
|| ' s."PAYMT_DTM" AS "PAYMENT_DATETIME",' || CHR(10)
|| ' s."PAYMT_AMT" AS "PAYMENT_AMOUNT"' || CHR(10)
|| 'FROM "SGMP_POC"."COMN_SALES_TXN" s' || CHR(10)
|| 'WHERE s."PAYMT_DTM" >= DATE ''2026-07-15''' || CHR(10)
|| ' AND s."PAYMT_DTM" < DATE ''2026-07-16''' || CHR(10)
|| ' AND CAST(s."PAYMT_AMT" AS NUMBER) > 10000' || CHR(10)
|| ' AND s."EXPT_USER_YN" = ''N''' || CHR(10)
|| 'ORDER BY s."PAYMT_DTM", s."PAYMT_TRANSAC_ID", s."PAYMT_TRANSAC_DTL_ID"'),
answer_text = 'Approved customer Few-shot: return individual qualifying payment orders with transaction identifiers, game, user, payment timestamp, and payment amount. Use PAYMT_DTM for the payment business date.',
reference_status = 'APPROVED',
inspection_status = 'VERIFIED',
inspection_note = 'Customer benchmark aligned to detailed qualifying orders and the payment business timestamp.',
verified_at = SYSTIMESTAMP,
verified_by = 'SGMP_POC_METADATA_REVIEW'
WHERE source_type = 'CUSTOMER_QA_BENCHMARK'
AND source_case_id = 'STD-18';
COMMIT;
SELECT question_code, expected_focus, baseline_answer
FROM sg_ai_qa_question
WHERE question_code = 'STD-18';

View File

@@ -0,0 +1,19 @@
-- Customer benchmark evidence is retained for evaluation only. It is not a
-- runtime Few-shot because the vector store must not become a collection of
-- case-specific benchmark overrides.
UPDATE sg_qa_vector_example
SET reference_status = 'RETIRED',
inspection_status = 'RETIRED',
inspection_note = 'Retired from runtime Few-shot retrieval; retained as customer QA evaluation evidence.',
verified_at = SYSTIMESTAMP,
verified_by = 'SGMP_POC_METADATA_REVIEW'
WHERE source_type = 'CUSTOMER_QA_BENCHMARK'
AND source_case_id = 'STD-21';
COMMIT;
SELECT example_id, reference_status, inspection_status, source_case_id
FROM sg_qa_vector_example
WHERE source_type = 'CUSTOMER_QA_BENCHMARK'
AND source_case_id = 'STD-21';

View File

@@ -0,0 +1,17 @@
-- Promote the reviewed customer QA example for the NRU metric.
UPDATE sg_qa_vector_example
SET reference_status = 'APPROVED',
inspection_status = 'VERIFIED',
inspection_note = 'Customer QA reviewed: NRU is measured with NRU_FLAG, with the stated date and excluded-user condition.',
verified_at = SYSTIMESTAMP,
verified_by = 'SGMP_POC_METADATA_REVIEW'
WHERE source_type = 'CUSTOMER_QA_BENCHMARK'
AND source_case_id = 'STD-22';
COMMIT;
SELECT example_id, reference_status, inspection_status, source_case_id
FROM sg_qa_vector_example
WHERE source_type = 'CUSTOMER_QA_BENCHMARK'
AND source_case_id = 'STD-22';

View File

@@ -0,0 +1,17 @@
-- Promote the reviewed customer QA example for the standard AU metric.
UPDATE sg_qa_vector_example
SET reference_status = 'APPROVED',
inspection_status = 'VERIFIED',
inspection_note = 'Customer QA reviewed: standard AU is measured with AU_FLAG and excluded-user filtering.',
verified_at = SYSTIMESTAMP,
verified_by = 'SGMP_POC_METADATA_REVIEW'
WHERE source_type = 'CUSTOMER_QA_BENCHMARK'
AND source_case_id = 'CZN-02';
COMMIT;
SELECT example_id, reference_status, inspection_status, source_case_id
FROM sg_qa_vector_example
WHERE source_type = 'CUSTOMER_QA_BENCHMARK'
AND source_case_id = 'CZN-02';

View File

@@ -0,0 +1,57 @@
-- Prefer an exact approved customer QA question over semantically adjacent
-- vector neighbours. This is a general retrieval rule; it does not encode
-- a game, metric, table, or customer-case-specific SQL policy.
CREATE OR REPLACE FUNCTION sg_qa_vector_search(
p_question IN CLOB,
p_top_k IN PLS_INTEGER DEFAULT 3,
p_target_type IN VARCHAR2 DEFAULT 'ANY'
) RETURN SYS_REFCURSOR
AUTHID DEFINER
IS
v_query_vector VECTOR;
v_results SYS_REFCURSOR;
v_target_type VARCHAR2(16) := UPPER(TRIM(NVL(p_target_type, 'ANY')));
BEGIN
IF p_question IS NULL THEN
RAISE_APPLICATION_ERROR(-20003, 'question is required.');
END IF;
IF p_top_k IS NULL OR p_top_k < 1 OR p_top_k > 20 THEN
RAISE_APPLICATION_ERROR(-20004, 'top_k must be between 1 and 20.');
END IF;
IF v_target_type NOT IN ('NONE', 'SINGLE', 'MULTI', 'ALL', 'ANY') THEN
RAISE_APPLICATION_ERROR(-20005, 'target_type must be NONE, SINGLE, MULTI, ALL, or ANY.');
END IF;
v_query_vector := DBMS_VECTOR.UTL_TO_EMBEDDING(
p_question,
JSON(sg_qa_vector_params('search_query'))
);
OPEN v_results FOR
SELECT example_id,
question,
answer_sql,
answer_text,
embedding_model,
reference_kind,
target_type,
object_role,
source_case_id,
source_type,
vector_distance(embedding, v_query_vector, COSINE) AS cosine_distance
FROM sg_qa_vector_example
WHERE reference_status = 'APPROVED'
AND (target_type = 'ANY' OR v_target_type = 'ANY' OR target_type = v_target_type)
ORDER BY CASE
WHEN DBMS_LOB.COMPARE(
LOWER(TRIM(question)), LOWER(TRIM(p_question))
) = 0 THEN 0
ELSE 1
END,
vector_distance(embedding, v_query_vector, COSINE),
example_id
FETCH FIRST p_top_k ROWS ONLY;
RETURN v_results;
END;
/

View File

@@ -0,0 +1,20 @@
-- Strengthen the approved customer QA example itself. The wording belongs
-- to the benchmark Few-shot record, not to a global Select AI profile rule.
UPDATE sg_qa_vector_example
SET answer_text = 'Expected focus: CZN_COMN_USER_MST, BASE_DT=2026-07-15, AU_FLAG=1, EXPT_USER_YN=''N''. '
|| 'The phrase standard AU is the report metric label; do not add STD_USER_YN unless the question separately asks for the standard-user cohort. '
|| 'Historical answer: STD_AU_COUNT=0',
inspection_note = 'Customer QA verified: standard AU uses the AU flag and excluded-user filtering; standard-user cohort is a separate request.',
verified_at = SYSTIMESTAMP,
verified_by = 'SGMP_POC_METADATA_REVIEW'
WHERE source_type = 'CUSTOMER_QA_BENCHMARK'
AND source_case_id = 'CZN-02'
AND reference_status = 'APPROVED';
COMMIT;
SELECT example_id, reference_status, inspection_status, answer_text
FROM sg_qa_vector_example
WHERE source_type = 'CUSTOMER_QA_BENCHMARK'
AND source_case_id = 'CZN-02';

View File

@@ -0,0 +1,23 @@
-- Approve the reviewed customer QA comparison example. Metric definitions
-- stay in the exact Few-shot example rather than becoming global profile text.
UPDATE sg_qa_vector_example
SET reference_status = 'APPROVED',
inspection_status = 'VERIFIED',
answer_text = 'Expected focus: compare two independently aggregated metrics for the same resolved game and date. '
|| 'Standard AU: CZN_COMN_USER_MST with AU_FLAG=1 and EXPT_USER_YN=''N''. '
|| 'Business AU: CZN_CUSTOM_BIZ_USER_TXN with BIZ_AU_FLAG=1 and EXPT_USER_YN=''N''. '
|| 'The labels standard AU and business AU do not imply STD_USER_YN. '
|| 'Historical answer: STD_AU_COUNT=0, BIZ_AU_COUNT=1.',
inspection_note = 'Customer QA verified: standard and business AU are separate aggregates with their respective AU flags.',
verified_at = SYSTIMESTAMP,
verified_by = 'SGMP_POC_METADATA_REVIEW'
WHERE source_type = 'CUSTOMER_QA_BENCHMARK'
AND source_case_id = 'CZN-03';
COMMIT;
SELECT example_id, reference_status, inspection_status, source_case_id, answer_text
FROM sg_qa_vector_example
WHERE source_type = 'CUSTOMER_QA_BENCHMARK'
AND source_case_id = 'CZN-03';