refs #708 #739: use Cohere command and vector game scope

This commit is contained in:
devmrko
2026-07-29 10:50:38 +09:00
parent 8b04fb4e89
commit 016d1af025
10 changed files with 569 additions and 40 deletions

View File

@@ -1,32 +1,57 @@
-- OCI GenAI candidate selection. Vector distance only orders candidates; the
-- model may select one supplied game_key or explicitly reject all candidates.
-- Vector-score candidate selection. The closest catalog vector is accepted
-- only when it is within the customer-managed policy threshold.
CREATE OR REPLACE FUNCTION sg_game_match_candidate(
p_mention IN CLOB,
p_candidates_json IN CLOB
) RETURN CLOB AUTHID DEFINER
IS
v_prompt CLOB;
v_result CLOB;
v_candidates JSON_ARRAY_T;
v_candidate JSON_OBJECT_T;
v_result JSON_OBJECT_T := JSON_OBJECT_T();
v_threshold NUMBER;
v_distance NUMBER;
v_game_key VARCHAR2(128);
BEGIN
v_prompt := 'Decide whether the extracted game-name mention refers to exactly '
|| 'one game in the supplied candidate array. Candidate vector distance is '
|| 'retrieval evidence only and must not establish identity. Compare the '
|| 'mention with candidate gameName, aliases, gameId, and gamePrefix. '
|| 'If one candidate clearly identifies the same game, return exactly '
|| '{"status":"MATCHED","game_key":"candidate key","reason":"short reason"}. '
|| 'If none clearly identifies the same game, return exactly '
|| '{"status":"UNMATCHED","game_key":null,"reason":"short reason"}. '
|| 'Never invent a game_key and never return a key absent from the supplied '
|| 'candidate array. Mention: '
|| DBMS_LOB.SUBSTR(p_mention, 1000, 1)
|| ' Candidates: '
|| DBMS_LOB.SUBSTR(p_candidates_json, 12000, 1);
v_result := DBMS_CLOUD_AI.GENERATE(
prompt => v_prompt,
profile_name => 'SGMP_POC_OCI_GPT54MINI',
action => 'chat'
);
RETURN v_result;
SELECT number_value
INTO v_threshold
FROM sg_game_scope_policy
WHERE policy_key = 'GAME_ALIAS_MAX_COSINE_DISTANCE'
AND active_yn = 'Y';
v_candidates := JSON_ARRAY_T.parse(p_candidates_json);
IF v_candidates.get_size = 0 THEN
v_result.put('status', 'UNMATCHED');
v_result.put_null('game_key');
v_result.put('reason', 'No active game catalog vector candidate was returned.');
RETURN v_result.to_clob;
END IF;
v_candidate := TREAT(v_candidates.get(0) AS JSON_OBJECT_T);
v_distance := v_candidate.get_number('cosineDistance');
v_game_key := v_candidate.get_string('gameKey');
IF v_distance <= v_threshold THEN
v_result.put('status', 'MATCHED');
v_result.put('game_key', v_game_key);
v_result.put(
'reason',
'Closest catalog vector distance '
|| TO_CHAR(v_distance, 'FM0D000000')
|| ' is within configured maximum '
|| TO_CHAR(v_threshold, 'FM0D000000') || '.'
);
ELSE
v_result.put('status', 'UNMATCHED');
v_result.put_null('game_key');
v_result.put(
'reason',
'Closest catalog vector distance '
|| TO_CHAR(v_distance, 'FM0D000000')
|| ' exceeds configured maximum '
|| TO_CHAR(v_threshold, 'FM0D000000') || '.'
);
END IF;
RETURN v_result.to_clob;
END;
/
@@ -255,14 +280,14 @@ BEGIN
IF v_matched_candidate IS NOT NULL THEN
v_mention_result.put('status', 'MATCHED');
v_mention_result.put('reasonCode', 'LLM_CANDIDATE_MATCH');
v_mention_result.put('reasonCode', 'VECTOR_SCORE_MATCH');
v_mention_result.put('matchedGameKey', v_selected_key);
v_target := JSON_OBJECT_T.parse(v_matched_candidate.to_clob);
v_target.put('mention', v_mention);
v_target.put('status', 'MATCHED');
v_target.put('matchStatus', 'MATCHED');
v_target.put('reasonCode', 'LLM_CANDIDATE_MATCH');
v_target.put('reasonCode', 'VECTOR_SCORE_MATCH');
v_target.put('reason', v_reason);
v_matched_count := v_matched_count + 1;
@@ -282,14 +307,7 @@ BEGIN
END IF;
ELSE
v_mention_result.put('status', 'UNMATCHED');
IF v_decision_status = 'MATCHED' THEN
v_mention_result.put(
'reasonCode',
'LLM_SELECTED_UNKNOWN_CANDIDATE'
);
ELSE
v_mention_result.put('reasonCode', 'LLM_NO_CANDIDATE_MATCH');
END IF;
v_mention_result.put('reasonCode', 'VECTOR_SCORE_OVER_THRESHOLD');
v_target := JSON_OBJECT_T();
v_target.put('mention', v_mention);