[Developer] add KB Select AI Team ORDS MCP tool
This commit is contained in:
104
sql/adb/56_kb_select_ai_router_team.sql
Normal file
104
sql/adb/56_kb_select_ai_router_team.sql
Normal file
@@ -0,0 +1,104 @@
|
||||
-- ============================================================
|
||||
-- 56_kb_select_ai_router_team.sql
|
||||
--
|
||||
-- KB Select AI profile을 호출하는 최소 Agent / Tool / Task / Team 체인.
|
||||
-- Tool -> Task -> Router Agent -> Team
|
||||
-- Execute as POC_2 using SQLcl.
|
||||
-- ============================================================
|
||||
WHENEVER SQLERROR EXIT SQL.SQLCODE
|
||||
SET DEFINE OFF
|
||||
SET FEEDBACK ON
|
||||
|
||||
PROMPT === Resetting only this Select AI router chain ===
|
||||
BEGIN
|
||||
DBMS_CLOUD_AI_AGENT.DROP_TEAM('KB_SELECT_AI_ROUTER_TEAM', force => TRUE);
|
||||
DBMS_CLOUD_AI_AGENT.DROP_TASK('KB_SELECT_AI_ROUTER_TASK', force => TRUE);
|
||||
DBMS_CLOUD_AI_AGENT.DROP_AGENT('KB_SELECT_AI_ROUTER_AGENT', force => TRUE);
|
||||
DBMS_CLOUD_AI_AGENT.DROP_TOOL('KB_SELECT_AI_ROUTER_TOOL', force => TRUE);
|
||||
END;
|
||||
/
|
||||
|
||||
PROMPT === Creating Select AI SQL tool ===
|
||||
BEGIN
|
||||
DBMS_CLOUD_AI_AGENT.CREATE_TOOL(
|
||||
tool_name => 'KB_SELECT_AI_ROUTER_TOOL',
|
||||
attributes => q'~{
|
||||
"tool_type": "SQL",
|
||||
"tool_params": {
|
||||
"profile_name": "KB_AIDP_SELECTAI_GPT55_OCI_PROFILE_V2"
|
||||
},
|
||||
"instruction": "Use the configured Select AI profile for structured KB insurance questions. For this demo, generate SHOWSQL only: create a read-only SELECT or WITH statement, do not execute it, and never generate DDL, DML, transaction control, package calls, or data changes."
|
||||
}~',
|
||||
status => 'ENABLED',
|
||||
description => 'Calls KB_AIDP_SELECTAI_GPT55_OCI_PROFILE_V2 to generate read-only KB SQL.'
|
||||
);
|
||||
END;
|
||||
/
|
||||
|
||||
PROMPT === Creating router agent ===
|
||||
BEGIN
|
||||
DBMS_CLOUD_AI_AGENT.CREATE_AGENT(
|
||||
agent_name => 'KB_SELECT_AI_ROUTER_AGENT',
|
||||
attributes => q'~{
|
||||
"profile_name": "KB_AIDP_AGENT_GPT55_OCI_PROFILE_V2",
|
||||
"role": "You are the KB Select AI routing agent. Classify every user request. If it asks for KB structured insurance data, metrics, counts, contracts, claims, customers, products, coverage, holdings, or stakeholders, call the assigned KB Select AI SQL tool. Return a concise Korean answer and the generated SQL. Do not invent data, do not expose internal reasoning, and do not use any unassigned tool.",
|
||||
"enable_human_tool": false
|
||||
}~',
|
||||
status => 'ENABLED',
|
||||
description => 'Routes KB structured-data questions to the Select AI SQL tool.'
|
||||
);
|
||||
END;
|
||||
/
|
||||
|
||||
PROMPT === Registering tool task ===
|
||||
BEGIN
|
||||
DBMS_CLOUD_AI_AGENT.CREATE_TASK(
|
||||
task_name => 'KB_SELECT_AI_ROUTER_TASK',
|
||||
attributes => q'~{
|
||||
"instruction": "Route this user request to the KB Select AI SQL tool when it requires structured insurance data: {query}. Use SHOWSQL only. Explain briefly in Korean what the generated SQL will retrieve. If the request is outside the KB structured-data scope, state that this team only handles KB structured-data SQL generation.",
|
||||
"tools": ["KB_SELECT_AI_ROUTER_TOOL"],
|
||||
"enable_human_tool": false
|
||||
}~',
|
||||
status => 'ENABLED',
|
||||
description => 'Single Select AI SQL generation task for KB structured-data requests.'
|
||||
);
|
||||
END;
|
||||
/
|
||||
|
||||
PROMPT === Creating agent task team ===
|
||||
BEGIN
|
||||
DBMS_CLOUD_AI_AGENT.CREATE_TEAM(
|
||||
team_name => 'KB_SELECT_AI_ROUTER_TEAM',
|
||||
attributes => q'~{
|
||||
"agents": [
|
||||
{
|
||||
"name": "KB_SELECT_AI_ROUTER_AGENT",
|
||||
"task": "KB_SELECT_AI_ROUTER_TASK"
|
||||
}
|
||||
],
|
||||
"process": "sequential"
|
||||
}~',
|
||||
status => 'ENABLED',
|
||||
description => 'KB Select AI router Team: SQL tool, task, agent, and team.'
|
||||
);
|
||||
END;
|
||||
/
|
||||
|
||||
PROMPT === Registered chain ===
|
||||
SELECT tool_name, status
|
||||
FROM USER_AI_AGENT_TOOLS
|
||||
WHERE tool_name = 'KB_SELECT_AI_ROUTER_TOOL';
|
||||
|
||||
SELECT agent_name, status
|
||||
FROM USER_AI_AGENTS
|
||||
WHERE agent_name = 'KB_SELECT_AI_ROUTER_AGENT';
|
||||
|
||||
SELECT task_name, status
|
||||
FROM USER_AI_AGENT_TASKS
|
||||
WHERE task_name = 'KB_SELECT_AI_ROUTER_TASK';
|
||||
|
||||
SELECT agent_team_name, status
|
||||
FROM USER_AI_AGENT_TEAMS
|
||||
WHERE agent_team_name = 'KB_SELECT_AI_ROUTER_TEAM';
|
||||
|
||||
EXIT
|
||||
17
sql/adb/57_kb_select_ai_router_team_test.sql
Normal file
17
sql/adb/57_kb_select_ai_router_team_test.sql
Normal file
@@ -0,0 +1,17 @@
|
||||
-- Execute as POC_2 using SQLcl after 56.
|
||||
WHENEVER SQLERROR EXIT SQL.SQLCODE
|
||||
SET PAGESIZE 100
|
||||
SET LINESIZE 240
|
||||
SET LONG 30000
|
||||
SET LONGCHUNKSIZE 30000
|
||||
SET FEEDBACK ON
|
||||
|
||||
SELECT DBMS_CLOUD_AI_AGENT.RUN_TEAM(
|
||||
team_name => 'KB_SELECT_AI_ROUTER_TEAM',
|
||||
user_prompt => 'KB 고객 원장의 전체 고객 수를 계산하는 SQL을 만들어 줘. SQL만 생성하고 실행하지 마.',
|
||||
params => '{"conversation_id":"kb-select-ai-router-' ||
|
||||
TO_CHAR(SYSTIMESTAMP, 'YYYYMMDDHH24MISSFF3') || '"}'
|
||||
) AS agent_response
|
||||
FROM dual;
|
||||
|
||||
EXIT
|
||||
57
sql/adb/58_kb_select_ai_router_api.sql
Normal file
57
sql/adb/58_kb_select_ai_router_api.sql
Normal file
@@ -0,0 +1,57 @@
|
||||
-- ============================================================
|
||||
-- 58_kb_select_ai_router_api.sql
|
||||
--
|
||||
-- POC_2 소유 Select AI Team을 ORDS runtime(CB_ORDS)에서 안전하게
|
||||
-- 호출하기 위한 definer-rights API.
|
||||
--
|
||||
-- Execute as POC_2 using SQLcl after 56_kb_select_ai_router_team.sql.
|
||||
-- ============================================================
|
||||
WHENEVER SQLERROR EXIT SQL.SQLCODE
|
||||
SET ECHO OFF
|
||||
SET FEEDBACK ON
|
||||
SET DEFINE OFF
|
||||
|
||||
PROMPT === Creating POC_2 Select AI Team API ===
|
||||
CREATE OR REPLACE PACKAGE kb_select_ai_router_api AUTHID DEFINER AS
|
||||
FUNCTION run_team(
|
||||
p_prompt IN CLOB,
|
||||
p_conversation_id IN VARCHAR2 DEFAULT NULL
|
||||
) RETURN CLOB;
|
||||
END kb_select_ai_router_api;
|
||||
/
|
||||
|
||||
CREATE OR REPLACE PACKAGE BODY kb_select_ai_router_api AS
|
||||
FUNCTION run_team(
|
||||
p_prompt IN CLOB,
|
||||
p_conversation_id IN VARCHAR2 DEFAULT NULL
|
||||
) RETURN CLOB AS
|
||||
v_params CLOB;
|
||||
v_conversation_id VARCHAR2(128) := TRIM(p_conversation_id);
|
||||
BEGIN
|
||||
IF p_prompt IS NULL OR DBMS_LOB.GETLENGTH(TRIM(p_prompt)) = 0 THEN
|
||||
RAISE_APPLICATION_ERROR(-20801, 'prompt is required');
|
||||
END IF;
|
||||
|
||||
IF v_conversation_id IS NOT NULL THEN
|
||||
IF NOT REGEXP_LIKE(v_conversation_id, '^[A-Za-z0-9._:-]{1,128}$') THEN
|
||||
RAISE_APPLICATION_ERROR(-20802, 'conversationId contains unsupported characters');
|
||||
END IF;
|
||||
v_params := '{"conversation_id":"' || v_conversation_id || '"}';
|
||||
END IF;
|
||||
|
||||
RETURN DBMS_CLOUD_AI_AGENT.RUN_TEAM(
|
||||
team_name => 'KB_SELECT_AI_ROUTER_TEAM',
|
||||
user_prompt => p_prompt,
|
||||
params => v_params
|
||||
);
|
||||
END run_team;
|
||||
END kb_select_ai_router_api;
|
||||
/
|
||||
|
||||
SHOW ERRORS
|
||||
|
||||
PROMPT === Granting only the Team API to the ORDS runtime ===
|
||||
GRANT EXECUTE ON kb_select_ai_router_api TO cb_ords;
|
||||
|
||||
PROMPT === POC_2 Select AI Team API ready ===
|
||||
EXIT
|
||||
143
sql/adb/59_kb_select_ai_router_ords.sql
Normal file
143
sql/adb/59_kb_select_ai_router_ords.sql
Normal file
@@ -0,0 +1,143 @@
|
||||
-- ============================================================
|
||||
-- 59_kb_select_ai_router_ords.sql
|
||||
--
|
||||
-- POST /ords/cb-ords/kb-select-ai-agent/run
|
||||
-- Authorization: Bearer <VPD token>
|
||||
-- {"prompt":"...","conversationId":"optional-safe-id"}
|
||||
--
|
||||
-- The ORDS runtime validates the bearer and sets the application context
|
||||
-- before invoking the POC_2-owned Team API. The context is session scoped,
|
||||
-- so VPD still applies to any SQL action selected by the Select AI tool.
|
||||
--
|
||||
-- Execute as CB_ORDS after 22 and 58.
|
||||
-- ============================================================
|
||||
WHENEVER SQLERROR EXIT SQL.SQLCODE
|
||||
SET ECHO OFF
|
||||
SET FEEDBACK ON
|
||||
SET DEFINE OFF
|
||||
|
||||
PROMPT === Resetting only the KB Select AI Agent ORDS module ===
|
||||
BEGIN
|
||||
ORDS.DELETE_MODULE(p_module_name => 'kb.select.ai.agent');
|
||||
EXCEPTION
|
||||
WHEN OTHERS THEN NULL;
|
||||
END;
|
||||
/
|
||||
|
||||
PROMPT === Creating KB Select AI Agent ORDS endpoint ===
|
||||
BEGIN
|
||||
ORDS.DEFINE_MODULE(
|
||||
p_module_name => 'kb.select.ai.agent',
|
||||
p_base_path => 'kb-select-ai-agent/',
|
||||
p_items_per_page => 0,
|
||||
p_status => 'PUBLISHED'
|
||||
);
|
||||
|
||||
ORDS.DEFINE_TEMPLATE(
|
||||
p_module_name => 'kb.select.ai.agent',
|
||||
p_pattern => 'run'
|
||||
);
|
||||
|
||||
ORDS.DEFINE_HANDLER(
|
||||
p_module_name => 'kb.select.ai.agent',
|
||||
p_pattern => 'run',
|
||||
p_method => 'POST',
|
||||
p_source_type => ORDS.source_type_plsql,
|
||||
p_source => q'~
|
||||
DECLARE
|
||||
v_body_text CLOB;
|
||||
v_prompt VARCHAR2(32767);
|
||||
v_requested_conversation_id VARCHAR2(128);
|
||||
v_internal_conversation_id VARCHAR2(128);
|
||||
v_stakeholder_user_id VARCHAR2(4000);
|
||||
v_answer CLOB;
|
||||
BEGIN
|
||||
cb_ords_handler_pkg.set_vpd_context(:auth_header, :probe_id);
|
||||
|
||||
-- ORDS exposes request JSON as a stream bind. Read it once only.
|
||||
v_body_text := :body_text;
|
||||
v_prompt := JSON_VALUE(v_body_text, '$.prompt' RETURNING VARCHAR2(32767));
|
||||
v_requested_conversation_id := JSON_VALUE(v_body_text, '$.conversationId' RETURNING VARCHAR2(128));
|
||||
|
||||
IF v_prompt IS NULL OR TRIM(v_prompt) IS NULL THEN
|
||||
RAISE_APPLICATION_ERROR(-20801, 'prompt is required');
|
||||
END IF;
|
||||
IF v_requested_conversation_id IS NULL THEN
|
||||
v_requested_conversation_id := 'kb-' || LOWER(RAWTOHEX(SYS_GUID()));
|
||||
ELSIF NOT REGEXP_LIKE(v_requested_conversation_id, '^[A-Za-z0-9._:-]{1,128}$') THEN
|
||||
RAISE_APPLICATION_ERROR(-20802, 'conversationId contains unsupported characters');
|
||||
END IF;
|
||||
|
||||
-- A connection-pooled ORDS session can be reused. Scope the Team's
|
||||
-- conversation memory to the VPD subject as well as the caller's public ID.
|
||||
v_stakeholder_user_id := NVL(SYS_CONTEXT('CB_AGENT_CTX', 'STAKEHOLDER_USER_ID'), 'anonymous');
|
||||
v_internal_conversation_id := 'kb-' || LOWER(RAWTOHEX(STANDARD_HASH(
|
||||
v_stakeholder_user_id || ':' || v_requested_conversation_id,
|
||||
'SHA256'
|
||||
)));
|
||||
|
||||
v_answer := poc_2.kb_select_ai_router_api.run_team(
|
||||
p_prompt => v_prompt,
|
||||
p_conversation_id => v_internal_conversation_id
|
||||
);
|
||||
|
||||
:status_code := 200;
|
||||
OWA_UTIL.MIME_HEADER('application/json', FALSE);
|
||||
HTP.P('Cache-Control: no-store');
|
||||
OWA_UTIL.HTTP_HEADER_CLOSE;
|
||||
APEX_JSON.OPEN_OBJECT;
|
||||
APEX_JSON.WRITE('team', 'KB_SELECT_AI_ROUTER_TEAM');
|
||||
APEX_JSON.WRITE('conversationId', v_requested_conversation_id);
|
||||
APEX_JSON.WRITE('answer', v_answer);
|
||||
APEX_JSON.CLOSE_OBJECT;
|
||||
|
||||
cb_ords_handler_pkg.clear_vpd_context;
|
||||
EXCEPTION
|
||||
WHEN OTHERS THEN
|
||||
cb_ords_handler_pkg.clear_vpd_context;
|
||||
:status_code := CASE
|
||||
WHEN SQLCODE IN (-20801, -20802) OR SQLCODE BETWEEN -40599 AND -40400 THEN 400
|
||||
WHEN SQLCODE BETWEEN -20199 AND -20100 THEN 403
|
||||
ELSE 500
|
||||
END;
|
||||
OWA_UTIL.MIME_HEADER('application/json', FALSE);
|
||||
HTP.P('Cache-Control: no-store');
|
||||
OWA_UTIL.HTTP_HEADER_CLOSE;
|
||||
APEX_JSON.OPEN_OBJECT;
|
||||
APEX_JSON.WRITE('errorCode', SQLCODE);
|
||||
APEX_JSON.WRITE('error', SQLERRM);
|
||||
APEX_JSON.CLOSE_OBJECT;
|
||||
END;
|
||||
~',
|
||||
p_items_per_page => 0
|
||||
);
|
||||
|
||||
ORDS.DEFINE_PARAMETER(
|
||||
p_module_name => 'kb.select.ai.agent',
|
||||
p_pattern => 'run',
|
||||
p_method => 'POST',
|
||||
p_name => 'Authorization',
|
||||
p_bind_variable_name => 'auth_header',
|
||||
p_source_type => 'HEADER',
|
||||
p_param_type => 'STRING',
|
||||
p_access_method => 'IN'
|
||||
);
|
||||
|
||||
ORDS.DEFINE_PARAMETER(
|
||||
p_module_name => 'kb.select.ai.agent',
|
||||
p_pattern => 'run',
|
||||
p_method => 'POST',
|
||||
p_name => 'X-VPD-Probe-Id',
|
||||
p_bind_variable_name => 'probe_id',
|
||||
p_source_type => 'HEADER',
|
||||
p_param_type => 'STRING',
|
||||
p_access_method => 'IN'
|
||||
);
|
||||
|
||||
COMMIT;
|
||||
END;
|
||||
/
|
||||
|
||||
PROMPT === KB Select AI Agent ORDS endpoint ready ===
|
||||
PROMPT Path: /ords/cb-ords/kb-select-ai-agent/run
|
||||
EXIT
|
||||
Reference in New Issue
Block a user