-- 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; /