From c3aca16a0980297b60ede324b78746e8f5360725 Mon Sep 17 00:00:00 2001 From: devmrko Date: Thu, 23 Jul 2026 13:45:55 +0900 Subject: [PATCH] refs #708: move Smilegate Select AI to OCI GPT 5.4 Mini --- .env.example | 2 +- .../72_sgmp_select_ai_oci_genai_profile.sql | 74 +++++++++++++++++++ .../README.md | 48 ++++++++++++ .../src/main/resources/application.yml | 2 +- .../src/main/resources/templates/mcp-sse.html | 4 +- .../service/McpSseServiceTest.java | 4 +- 6 files changed, 128 insertions(+), 6 deletions(-) create mode 100644 database/adb/72_sgmp_select_ai_oci_genai_profile.sql create mode 100644 docs/design/708-smilegate-select-ai-oci-genai/README.md diff --git a/.env.example b/.env.example index 9c140a6..c5f4582 100644 --- a/.env.example +++ b/.env.example @@ -57,7 +57,7 @@ export BACKOFFICE_ORDS_DB_PASSWORD="" export BACKOFFICE_SELECT_AI_DB_URL="${BACKOFFICE_DB_URL}" export BACKOFFICE_SELECT_AI_DB_USERNAME="SGMP_POC" export BACKOFFICE_SELECT_AI_DB_PASSWORD="" -export BACKOFFICE_SELECT_AI_PROFILE="SGMP_POC_HAIKU45" +export BACKOFFICE_SELECT_AI_PROFILE="SGMP_POC_OCI_GPT54MINI" # --- (2c) OpenAI 호환 AI 호출 (MCP-style Reasoning 탭) --- export BACKOFFICE_AI_ENABLED="false" diff --git a/database/adb/72_sgmp_select_ai_oci_genai_profile.sql b/database/adb/72_sgmp_select_ai_oci_genai_profile.sql new file mode 100644 index 0000000..3906b23 --- /dev/null +++ b/database/adb/72_sgmp_select_ai_oci_genai_profile.sql @@ -0,0 +1,74 @@ +-- 72_sgmp_select_ai_oci_genai_profile.sql +-- Run as SGMP_POC after creating SGMP_POC_OCI_DEFAULT_CRED from the local +-- ~/.oci/config DEFAULT API signing key. No private-key material belongs in +-- this script or the repository. +-- +-- The source external profile is retained. Metadata attributes are copied +-- individually so object_list, comments, annotations and instructions remain +-- intact while external endpoint, credential and model settings are replaced. + +DECLARE + v_exists PLS_INTEGER; +BEGIN + SELECT COUNT(*) + INTO v_exists + FROM user_cloud_ai_profiles + WHERE profile_name = 'SGMP_POC_OCI_GPT54MINI'; + + IF v_exists > 0 THEN + DBMS_CLOUD_AI.DROP_PROFILE( + profile_name => 'SGMP_POC_OCI_GPT54MINI', + force => TRUE + ); + END IF; + + DBMS_CLOUD_AI.CREATE_PROFILE( + profile_name => 'SGMP_POC_OCI_GPT54MINI', + attributes => '{ + "provider": "oci", + "credential_name": "SGMP_POC_OCI_DEFAULT_CRED", + "model": "openai.gpt-5.4-mini", + "region": "us-chicago-1", + "oci_compartment_id": "" + }', + description => 'Smilegate Text2SQL on OCI GenAI GPT-5.4 Mini' + ); + + FOR source_attribute IN ( + SELECT attribute_name, + attribute_value + FROM user_cloud_ai_profile_attributes + WHERE profile_name = 'SGMP_POC_HAIKU45' + AND attribute_name NOT IN ( + 'credential_name', + 'model', + 'provider', + 'provider_endpoint', + 'region', + 'oci_compartment_id', + 'oci_endpoint_id', + 'oci_apiformat', + 'oci_runtimetype' + ) + ) LOOP + DBMS_CLOUD_AI.SET_ATTRIBUTE( + profile_name => 'SGMP_POC_OCI_GPT54MINI', + attribute_name => source_attribute.attribute_name, + attribute_value => source_attribute.attribute_value + ); + END LOOP; +END; +/ + +SELECT attribute_name, + attribute_value + FROM user_cloud_ai_profile_attributes + WHERE profile_name = 'SGMP_POC_OCI_GPT54MINI' + AND attribute_name IN ( + 'provider', + 'model', + 'credential_name', + 'region', + 'oci_compartment_id' + ) + ORDER BY attribute_name; diff --git a/docs/design/708-smilegate-select-ai-oci-genai/README.md b/docs/design/708-smilegate-select-ai-oci-genai/README.md new file mode 100644 index 0000000..439f18e --- /dev/null +++ b/docs/design/708-smilegate-select-ai-oci-genai/README.md @@ -0,0 +1,48 @@ +# #708 Smilegate Select AI OCI GenAI 전환 + +## 프로젝트 개요 + +Smilegate DATA & AI PoC는 Autonomous Database의 게임 데이터에서 Select AI Text2SQL을 생성하고, MCP와 백오피스를 통해 결과를 조회한다. 현재 운영 프로파일은 외부 OpenRouter 경유 Claude를 사용한다. + +## 목표 + +`SGMP_POC_HAIKU45`가 가진 게임 데이터 object list와 메타데이터 활용 범위는 유지하면서, LLM 호출 경로만 OCI Generative AI `openai.gpt-5.4-mini`로 전환한다. ADB Resource Principal이 아닌 이 머신의 `~/.oci/config` DEFAULT API signing credential을 ADB credential으로 등록한다. + +## 전환 설계 + +| 구분 | 기존 | 전환 후 | +| --- | --- | --- | +| 프로파일 | `SGMP_POC_HAIKU45` | `SGMP_POC_OCI_GPT54MINI` | +| 제공자 | OpenAI 호환 외부 경로 | OCI Generative AI (`provider: oci`) | +| 모델 | Claude Haiku 4.5 | `openai.gpt-5.4-mini` | +| 인증 | 외부 API credential | DEFAULT API signing key 기반 `SGMP_POC_OCI_DEFAULT_CRED` | +| OCI 리전 | 외부 서비스 | `us-chicago-1` (GPT-5.4 Mini OCI inference route) | +| 게임 데이터 범위 | 기존 object list | 기존 profile attributes에서 복제 | +| MCP 설정 | 기존 프로파일명 | `BACKOFFICE_SELECT_AI_PROFILE=SGMP_POC_OCI_GPT54MINI` | + +신규 프로파일은 기존 프로파일의 metadata 관련 attributes를 복사하고, 외부 endpoint·credential·model은 OCI 값으로 새로 설정한다. 따라서 object list, comment, annotation, constraint 기반 Text2SQL 문맥은 유지된다. 기존 외부 프로파일은 삭제하지 않으며, 전환 실패 시 환경변수만 원래 값으로 되돌린다. + +## 사전 조건 + +1. `~/.oci/config` DEFAULT의 user, tenancy, fingerprint, key_file이 유효한 OCI API signing key여야 한다. +2. DEFAULT API signing user가 Chicago 리전 root compartment에서 OCI Generative AI `openai.gpt-5.4-mini` 호출 권한을 가져야 한다. DEFAULT config의 signing region과 GPT inference route는 독립적이므로 profile attribute `region`은 `us-chicago-1`로 명시한다. +3. 스크립트는 `SGMP_POC` 프로파일 소유자로 실행한다. + +## 구현 순서 + +1. `sql/adb/72_sgmp_select_ai_oci_genai_profile.sql`로 신규 OCI 프로파일을 만든다. +2. 새 프로파일로 한글 Text2SQL `SHOWSQL`과 생성 SQL의 읽기 전용 실행을 검증한다. +3. 운영 서버의 `BACKOFFICE_SELECT_AI_PROFILE`만 새 프로파일로 교체하고 백오피스를 재기동한다. +4. 운영 MCP의 `oracle.select_ai.smilegate_game_text2sql` 응답 profile과 집계 결과를 검증한다. + +## 롤백 + +새 프로파일을 삭제하지 않는다. MCP에서 오류가 나거나 SQL 품질이 허용 기준을 충족하지 않으면 `/etc/smilegate/backoffice.env`의 `BACKOFFICE_SELECT_AI_PROFILE`을 `SGMP_POC_HAIKU45`로 되돌린 뒤 서비스를 재기동한다. + +## 검증 기준 + +- 새 프로파일 provider=`oci`, model=`openai.gpt-5.4-mini`, credential=`SGMP_POC_OCI_DEFAULT_CRED`, region=`us-chicago-1` +- 한글 질의는 SQLcl UTF-8 base64 복원식으로 전달한다. +- `DBMS_CLOUD_AI.GENERATE(..., 'showsql')`이 읽기 전용 `SELECT` 또는 `WITH` SQL을 생성한다. +- 해당 SQL을 읽기 전용 트랜잭션에서 실행해 결과를 반환한다. +- 운영 MCP 응답의 `profile`이 `SGMP_POC_OCI_GPT54MINI`이다. diff --git a/vpd-backoffice/src/main/resources/application.yml b/vpd-backoffice/src/main/resources/application.yml index b2c1c34..483842c 100644 --- a/vpd-backoffice/src/main/resources/application.yml +++ b/vpd-backoffice/src/main/resources/application.yml @@ -74,4 +74,4 @@ backoffice: db-url: ${BACKOFFICE_SELECT_AI_DB_URL:} db-username: ${BACKOFFICE_SELECT_AI_DB_USERNAME:} db-password: ${BACKOFFICE_SELECT_AI_DB_PASSWORD:} - profile: ${BACKOFFICE_SELECT_AI_PROFILE:SGMP_POC_HAIKU45} + profile: ${BACKOFFICE_SELECT_AI_PROFILE:SGMP_POC_OCI_GPT54MINI} diff --git a/vpd-backoffice/src/main/resources/templates/mcp-sse.html b/vpd-backoffice/src/main/resources/templates/mcp-sse.html index c7f5e64..feffd5f 100644 --- a/vpd-backoffice/src/main/resources/templates/mcp-sse.html +++ b/vpd-backoffice/src/main/resources/templates/mcp-sse.html @@ -8,7 +8,7 @@

MCP 연동

도움말 -

사용자 Bearer Token을 검증한 뒤 SGMP_POC_HAIKU45 Select AI 프로파일로 게임 데이터 Text2SQL을 생성하는 단일 MCP tool을 제공합니다. Select AI는 comment, annotation, constraint 메타데이터를 함께 사용하며, 생성 SQL은 자동 실행하지 않습니다.

+

사용자 Bearer Token을 검증한 뒤 SGMP_POC_OCI_GPT54MINI Select AI 프로파일로 게임 데이터 Text2SQL을 생성하는 단일 MCP tool을 제공합니다. Select AI는 comment, annotation, constraint 메타데이터를 함께 사용하며, 생성 SQL은 자동 실행하지 않습니다.

@@ -110,7 +110,7 @@
{
   "prompt": "카제나의 최신 BASE_DT 기준 AU(활성 사용자 수)를 조회하는 SQL을 만들어줘."
 }
-

등록 tool은 oracle.select_ai.smilegate_game_text2sql 하나입니다. 활성 사용자 Bearer Token을 확인한 뒤 SGMP_POC_HAIKU45가 comment, annotation, constraint를 참고해 읽기 전용 게임 데이터 SQL을 생성합니다. 실행은 자동으로 수행하지 않습니다.

+

등록 tool은 oracle.select_ai.smilegate_game_text2sql 하나입니다. 활성 사용자 Bearer Token을 확인한 뒤 SGMP_POC_OCI_GPT54MINI가 comment, annotation, constraint를 참고해 읽기 전용 게임 데이터 SQL을 생성합니다. 실행은 자동으로 수행하지 않습니다.

diff --git a/vpd-backoffice/src/test/java/com/cloudhandson/vpdbackoffice/service/McpSseServiceTest.java b/vpd-backoffice/src/test/java/com/cloudhandson/vpdbackoffice/service/McpSseServiceTest.java index 7980879..335a649 100644 --- a/vpd-backoffice/src/test/java/com/cloudhandson/vpdbackoffice/service/McpSseServiceTest.java +++ b/vpd-backoffice/src/test/java/com/cloudhandson/vpdbackoffice/service/McpSseServiceTest.java @@ -49,7 +49,7 @@ class McpSseServiceTest { assertThat(response.path("result").path("isError").asBoolean()).isFalse(); String payload = response.path("result").path("content").get(0).path("text").asText(); assertThat(payload) - .contains("SGMP_POC_HAIKU45") + .contains("SGMP_POC_OCI_GPT54MINI") .contains("SELECT 1 FROM DUAL") .contains("SHOWSQL_AND_EXECUTED") .contains("READ_ONLY_EXECUTED") @@ -94,7 +94,7 @@ class McpSseServiceTest { this.bearerToken = bearerToken; this.prompt = prompt; ObjectNode response = new ObjectMapper().createObjectNode() - .put("profile", "SGMP_POC_HAIKU45") + .put("profile", "SGMP_POC_OCI_GPT54MINI") .put("generatedSql", "SELECT 1 FROM DUAL") .put("status", "SHOWSQL_AND_EXECUTED") .put("execution", "READ_ONLY_EXECUTED")