refs #749: compare HMM Select AI annotation profiles
This commit is contained in:
159
database/adb/85_hmm_hr_select_ai_no_annotation_profile.sql
Normal file
159
database/adb/85_hmm_hr_select_ai_no_annotation_profile.sql
Normal file
@@ -0,0 +1,159 @@
|
||||
-- Redmine #749
|
||||
-- Create an isolated HMM HR Select AI profile with annotations disabled.
|
||||
-- The source profile is never modified.
|
||||
|
||||
SET SERVEROUTPUT ON SIZE UNLIMITED
|
||||
WHENEVER SQLERROR EXIT SQL.SQLCODE
|
||||
|
||||
DECLARE
|
||||
c_source_profile CONSTANT VARCHAR2(128) := 'HMM_HR_DATA_GPT54_PROFILE';
|
||||
c_target_profile CONSTANT VARCHAR2(128) := 'HMM_HR_DATA_GPT54_NOANN_PROFILE';
|
||||
|
||||
l_attributes CLOB;
|
||||
l_object_list CLOB;
|
||||
l_provider VARCHAR2(4000);
|
||||
l_credential_name VARCHAR2(4000);
|
||||
l_model VARCHAR2(4000);
|
||||
l_region VARCHAR2(4000);
|
||||
l_compartment_id VARCHAR2(4000);
|
||||
l_max_tokens NUMBER;
|
||||
l_temperature NUMBER;
|
||||
l_profile_count PLS_INTEGER;
|
||||
l_difference_count PLS_INTEGER;
|
||||
l_annotations VARCHAR2(30);
|
||||
|
||||
FUNCTION attribute_clob(p_name IN VARCHAR2) RETURN CLOB IS
|
||||
l_value CLOB;
|
||||
BEGIN
|
||||
SELECT attribute_value
|
||||
INTO l_value
|
||||
FROM user_cloud_ai_profile_attributes
|
||||
WHERE profile_name = c_source_profile
|
||||
AND attribute_name = p_name;
|
||||
RETURN l_value;
|
||||
END attribute_clob;
|
||||
|
||||
FUNCTION attribute_text(p_name IN VARCHAR2) RETURN VARCHAR2 IS
|
||||
BEGIN
|
||||
RETURN DBMS_LOB.SUBSTR(attribute_clob(p_name), 4000, 1);
|
||||
END attribute_text;
|
||||
BEGIN
|
||||
SELECT COUNT(*)
|
||||
INTO l_profile_count
|
||||
FROM user_cloud_ai_profiles
|
||||
WHERE profile_name = c_source_profile
|
||||
AND status = 'ENABLED';
|
||||
|
||||
IF l_profile_count <> 1 THEN
|
||||
RAISE_APPLICATION_ERROR(-20080, 'Enabled source Select AI profile was not found.');
|
||||
END IF;
|
||||
|
||||
l_provider := attribute_text('provider');
|
||||
l_credential_name := attribute_text('credential_name');
|
||||
l_model := attribute_text('model');
|
||||
l_region := attribute_text('region');
|
||||
l_compartment_id := attribute_text('oci_compartment_id');
|
||||
l_object_list := attribute_clob('object_list');
|
||||
l_max_tokens := TO_NUMBER(
|
||||
attribute_text('max_tokens'),
|
||||
'9999999999',
|
||||
'NLS_NUMERIC_CHARACTERS=''.,'''
|
||||
);
|
||||
l_temperature := TO_NUMBER(
|
||||
attribute_text('temperature'),
|
||||
'9999999990D999999999',
|
||||
'NLS_NUMERIC_CHARACTERS=''.,'''
|
||||
);
|
||||
|
||||
SELECT COUNT(*)
|
||||
INTO l_profile_count
|
||||
FROM user_cloud_ai_profiles
|
||||
WHERE profile_name = c_target_profile;
|
||||
|
||||
IF l_profile_count = 0 THEN
|
||||
SELECT JSON_OBJECT(
|
||||
'provider' VALUE l_provider,
|
||||
'credential_name' VALUE l_credential_name,
|
||||
'model' VALUE l_model,
|
||||
'region' VALUE l_region,
|
||||
'oci_compartment_id' VALUE l_compartment_id,
|
||||
'object_list' VALUE l_object_list FORMAT JSON,
|
||||
'max_tokens' VALUE l_max_tokens,
|
||||
'temperature' VALUE l_temperature,
|
||||
'annotations' VALUE 'false' FORMAT JSON
|
||||
RETURNING CLOB
|
||||
)
|
||||
INTO l_attributes
|
||||
FROM dual;
|
||||
|
||||
DBMS_CLOUD_AI.CREATE_PROFILE(
|
||||
profile_name => c_target_profile,
|
||||
attributes => l_attributes,
|
||||
status => 'enabled',
|
||||
description => 'HMM HR GPT-5.4 mini comparison profile with annotations disabled'
|
||||
);
|
||||
DBMS_OUTPUT.PUT_LINE('PROFILE_CREATED|' || c_target_profile);
|
||||
ELSE
|
||||
DBMS_OUTPUT.PUT_LINE('PROFILE_EXISTS|' || c_target_profile);
|
||||
END IF;
|
||||
|
||||
SELECT COUNT(*)
|
||||
INTO l_difference_count
|
||||
FROM (
|
||||
SELECT attribute_name, attribute_value
|
||||
FROM user_cloud_ai_profile_attributes
|
||||
WHERE profile_name = c_source_profile
|
||||
AND attribute_name <> 'annotations'
|
||||
) source_attributes
|
||||
FULL OUTER JOIN (
|
||||
SELECT attribute_name, attribute_value
|
||||
FROM user_cloud_ai_profile_attributes
|
||||
WHERE profile_name = c_target_profile
|
||||
AND attribute_name <> 'annotations'
|
||||
) target_attributes
|
||||
ON target_attributes.attribute_name = source_attributes.attribute_name
|
||||
WHERE source_attributes.attribute_name IS NULL
|
||||
OR target_attributes.attribute_name IS NULL
|
||||
OR DBMS_LOB.COMPARE(
|
||||
source_attributes.attribute_value,
|
||||
target_attributes.attribute_value
|
||||
) <> 0;
|
||||
|
||||
SELECT LOWER(TRIM(DBMS_LOB.SUBSTR(attribute_value, 30, 1)))
|
||||
INTO l_annotations
|
||||
FROM user_cloud_ai_profile_attributes
|
||||
WHERE profile_name = c_target_profile
|
||||
AND attribute_name = 'annotations';
|
||||
|
||||
IF l_difference_count <> 0 OR l_annotations <> 'false' THEN
|
||||
RAISE_APPLICATION_ERROR(
|
||||
-20081,
|
||||
'Comparison profile differs from source beyond annotations=false.'
|
||||
);
|
||||
END IF;
|
||||
|
||||
DBMS_OUTPUT.PUT_LINE(
|
||||
'PROFILE_VALIDATED|source=' || c_source_profile
|
||||
|| '|target=' || c_target_profile
|
||||
|| '|annotations=false|other_attribute_differences=0'
|
||||
);
|
||||
END;
|
||||
/
|
||||
|
||||
SELECT profile_name, status, description
|
||||
FROM user_cloud_ai_profiles
|
||||
WHERE profile_name IN (
|
||||
'HMM_HR_DATA_GPT54_PROFILE',
|
||||
'HMM_HR_DATA_GPT54_NOANN_PROFILE'
|
||||
)
|
||||
ORDER BY profile_name;
|
||||
|
||||
SELECT profile_name, attribute_name, attribute_value
|
||||
FROM user_cloud_ai_profile_attributes
|
||||
WHERE profile_name IN (
|
||||
'HMM_HR_DATA_GPT54_PROFILE',
|
||||
'HMM_HR_DATA_GPT54_NOANN_PROFILE'
|
||||
)
|
||||
ORDER BY profile_name, attribute_name;
|
||||
|
||||
EXIT SUCCESS
|
||||
103
database/adb/86_hmm_select_ai_annotation_benchmark.sql
Normal file
103
database/adb/86_hmm_select_ai_annotation_benchmark.sql
Normal file
@@ -0,0 +1,103 @@
|
||||
-- Redmine #749
|
||||
-- Compare Select AI prompt and SQL generation with annotations enabled/disabled.
|
||||
-- The Korean prompt is reconstructed from UTF-8 Base64 inside Oracle.
|
||||
|
||||
SET SERVEROUTPUT ON SIZE UNLIMITED
|
||||
SET FEEDBACK OFF
|
||||
SET VERIFY OFF
|
||||
WHENEVER SQLERROR EXIT SQL.SQLCODE
|
||||
|
||||
DECLARE
|
||||
c_annotation_profile CONSTANT VARCHAR2(128) := 'HMM_HR_DATA_GPT54_PROFILE';
|
||||
c_no_annotation_profile CONSTANT VARCHAR2(128) := 'HMM_HR_DATA_GPT54_NOANN_PROFILE';
|
||||
c_prompt_base64 CONSTANT VARCHAR2(4000) :=
|
||||
'7J2067KIIOuLrCDtjIDsm5Drs4Qg7Zy06rCAIOyCrOyaqSDtmITtmansnYQg67O07Jes7KSY';
|
||||
|
||||
l_prompt VARCHAR2(4000) :=
|
||||
UTL_I18N.RAW_TO_CHAR(
|
||||
UTL_ENCODE.BASE64_DECODE(UTL_RAW.CAST_TO_RAW(c_prompt_base64)),
|
||||
'AL32UTF8'
|
||||
);
|
||||
|
||||
PROCEDURE run_one(
|
||||
p_run_no IN PLS_INTEGER,
|
||||
p_action IN VARCHAR2,
|
||||
p_profile_name IN VARCHAR2
|
||||
) IS
|
||||
l_started PLS_INTEGER;
|
||||
l_elapsed_ms PLS_INTEGER;
|
||||
l_result CLOB;
|
||||
l_result_head VARCHAR2(32767);
|
||||
l_result_hash VARCHAR2(128);
|
||||
l_valid_sql VARCHAR2(1) := '-';
|
||||
BEGIN
|
||||
l_started := DBMS_UTILITY.GET_TIME;
|
||||
l_result := DBMS_CLOUD_AI.GENERATE(
|
||||
l_prompt,
|
||||
p_profile_name,
|
||||
p_action
|
||||
);
|
||||
l_elapsed_ms := (DBMS_UTILITY.GET_TIME - l_started) * 10;
|
||||
l_result_head := DBMS_LOB.SUBSTR(l_result, 32767, 1);
|
||||
SELECT RAWTOHEX(STANDARD_HASH(l_result_head, 'SHA256'))
|
||||
INTO l_result_hash
|
||||
FROM dual;
|
||||
|
||||
IF p_action = 'showsql' THEN
|
||||
IF REGEXP_LIKE(LTRIM(l_result_head), '^(SELECT|WITH)[[:space:]]', 'i') THEN
|
||||
l_valid_sql := 'Y';
|
||||
ELSE
|
||||
l_valid_sql := 'N';
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
DBMS_OUTPUT.PUT_LINE(
|
||||
'BENCHMARK|run=' || p_run_no
|
||||
|| '|action=' || p_action
|
||||
|| '|profile=' || p_profile_name
|
||||
|| '|elapsed_ms=' || l_elapsed_ms
|
||||
|| '|result_chars=' || DBMS_LOB.GETLENGTH(l_result)
|
||||
|| '|valid_sql=' || l_valid_sql
|
||||
|| '|result_sha256=' || l_result_hash
|
||||
);
|
||||
EXCEPTION
|
||||
WHEN OTHERS THEN
|
||||
l_elapsed_ms := (DBMS_UTILITY.GET_TIME - l_started) * 10;
|
||||
DBMS_OUTPUT.PUT_LINE(
|
||||
'BENCHMARK_ERROR|run=' || p_run_no
|
||||
|| '|action=' || p_action
|
||||
|| '|profile=' || p_profile_name
|
||||
|| '|elapsed_ms=' || l_elapsed_ms
|
||||
|| '|error=' || REPLACE(SUBSTR(SQLERRM, 1, 500), '|', '/')
|
||||
);
|
||||
END run_one;
|
||||
|
||||
PROCEDURE run_pair(p_run_no IN PLS_INTEGER, p_action IN VARCHAR2) IS
|
||||
BEGIN
|
||||
IF MOD(p_run_no, 2) = 1 THEN
|
||||
run_one(p_run_no, p_action, c_annotation_profile);
|
||||
run_one(p_run_no, p_action, c_no_annotation_profile);
|
||||
ELSE
|
||||
run_one(p_run_no, p_action, c_no_annotation_profile);
|
||||
run_one(p_run_no, p_action, c_annotation_profile);
|
||||
END IF;
|
||||
END run_pair;
|
||||
BEGIN
|
||||
DBMS_OUTPUT.PUT_LINE(
|
||||
'BENCHMARK_START|prompt_utf8_bytes=' ||
|
||||
UTL_RAW.LENGTH(UTL_I18N.STRING_TO_RAW(l_prompt, 'AL32UTF8'))
|
||||
);
|
||||
|
||||
FOR run_no IN 1 .. 3 LOOP
|
||||
run_pair(run_no, 'showprompt');
|
||||
END LOOP;
|
||||
|
||||
FOR run_no IN 1 .. 3 LOOP
|
||||
run_pair(run_no, 'showsql');
|
||||
END LOOP;
|
||||
|
||||
DBMS_OUTPUT.PUT_LINE('BENCHMARK_END');
|
||||
END;
|
||||
/
|
||||
|
||||
EXIT SUCCESS
|
||||
@@ -0,0 +1,55 @@
|
||||
# HMM Select AI annotation 제외 프로파일 비교
|
||||
|
||||
Redmine: #749
|
||||
|
||||
상태: Approved
|
||||
|
||||
구현 대상: `database/adb/85_hmm_hr_select_ai_no_annotation_profile.sql`,
|
||||
`database/adb/86_hmm_select_ai_annotation_benchmark.sql`
|
||||
|
||||
## 목적
|
||||
|
||||
HMM HR Select AI에서 Oracle table/column annotation을 프롬프트에 포함할 때와 제외할 때의 응답
|
||||
시간과 SQL 생성 결과를 같은 조건으로 비교한다. 운영 프로파일과 MCP 설정은 변경하지 않는다.
|
||||
|
||||
## 범위와 결정사항
|
||||
|
||||
- 기준 프로파일은 `HMM_HR_DATA_GPT54_PROFILE`이며 현재 `annotations=true`다.
|
||||
- 비교 프로파일은 `HMM_HR_DATA_GPT54_NOANN_PROFILE`로 만들고 `annotations=false`를 명시한다.
|
||||
- provider, credential, model, region, compartment, object list, temperature, max tokens는 동일하게 유지한다.
|
||||
- 같은 한국어 질문으로 `showprompt` 크기·시간과 `showsql` 생성 시간·성공 여부를 교차 반복 측정한다.
|
||||
- 한국어 질문은 SQLcl에 직접 넣지 않고 UTF-8 Base64를 Oracle 안에서 복원한다.
|
||||
- 측정 결과만으로 운영 프로파일을 자동 전환하지 않는다.
|
||||
|
||||
## 전체 구성
|
||||
|
||||
```text
|
||||
동일 HR 질문
|
||||
├─ HMM_HR_DATA_GPT54_PROFILE ─ annotations=true ─ showprompt/showsql
|
||||
└─ HMM_HR_DATA_GPT54_NOANN_PROFILE ─ annotations=false ─ showprompt/showsql
|
||||
│
|
||||
시간·prompt 크기·SQL 비교
|
||||
```
|
||||
|
||||
두 경로의 유일한 의도적 차이는 `annotations`다. 기존 선사 Federation 프로파일은 이미 annotation을
|
||||
사용하지 않으므로 이번 A/B 대상이 아니다.
|
||||
|
||||
## 성공 기준
|
||||
|
||||
- 두 프로파일의 비교 대상 속성이 `annotations` 외에는 같다.
|
||||
- 각 프로파일이 같은 질문에 유효한 읽기 전용 SQL을 생성한다.
|
||||
- 측정 회차별 시간, 성공 여부, 생성 SQL 길이를 남긴다.
|
||||
- 기존 프로파일과 운영 `BACKOFFICE_SELECT_AI_PROFILE` 값이 바뀌지 않는다.
|
||||
|
||||
## 문서 지도
|
||||
|
||||
- [비교 설계](architecture.md)
|
||||
- [생성·측정·롤백 절차](cookbook.md)
|
||||
- [측정 결과](results.md)
|
||||
- [문제 해결](troubleshooting.md)
|
||||
|
||||
## 현재 상태
|
||||
|
||||
2026-08-11 비교 프로파일 생성과 3회 교차 측정을 완료했다. 현재 HR object list에는 실제
|
||||
annotation이 0건이어서 prompt 크기는 34자만 줄었고, `showsql` 평균은 annotation 사용 2.453초,
|
||||
제외 2.483초로 의미 있는 개선이 없었다. 운영 프로파일은 변경하지 않았다.
|
||||
@@ -0,0 +1,49 @@
|
||||
# 비교 설계
|
||||
|
||||
[개요](README.md) · [실행 절차](cookbook.md) · [측정 결과](results.md) · [문제 해결](troubleshooting.md)
|
||||
|
||||
## 비교 대상
|
||||
|
||||
| 속성 | annotation 사용 | annotation 제외 |
|
||||
|---|---|---|
|
||||
| 프로파일 | `HMM_HR_DATA_GPT54_PROFILE` | `HMM_HR_DATA_GPT54_NOANN_PROFILE` |
|
||||
| provider | `oci` | 동일 |
|
||||
| model | `openai.gpt-5.4-mini` | 동일 |
|
||||
| object list | HMM HR 5개 객체 | 동일 |
|
||||
| temperature | `0.1` | 동일 |
|
||||
| max tokens | `1500` | 동일 |
|
||||
| annotations | `true` | `false` |
|
||||
|
||||
credential 이름과 compartment OCID는 비밀값은 아니지만 환경 종속값이므로 문서에 복사하지 않고
|
||||
DB 원본 프로파일을 기준으로 검증한다. credential 원문이나 토큰은 조회·출력하지 않는다.
|
||||
|
||||
## 측정 방식
|
||||
|
||||
```text
|
||||
run 1: annotation → no annotation
|
||||
run 2: no annotation → annotation
|
||||
run 3: annotation → no annotation
|
||||
```
|
||||
|
||||
호출 순서를 번갈아 배치해 첫 호출 캐시와 일시적 모델 지연이 한쪽에만 몰리는 현상을 줄인다.
|
||||
각 호출은 `DBMS_CLOUD_AI.GENERATE(prompt, profile, action)` 한 번의 경과 시간을
|
||||
`DBMS_UTILITY.GET_TIME`으로 측정한다.
|
||||
|
||||
- `showprompt`: 모델에 전달되는 metadata prompt 크기와 구성 비용 비교
|
||||
- `showsql`: 실제 SQL 생성 시간과 성공 여부 비교
|
||||
|
||||
생성 SQL은 실행하지 않는다. SQL 생성 품질은 `SELECT`/`WITH` 시작 여부, 길이와 주요 객체 사용을
|
||||
검토한다. VPD 결과 행 비교는 운영 프로파일 전환을 결정한 뒤 별도 MCP 회귀 검증에서 수행한다.
|
||||
|
||||
## 안전 경계
|
||||
|
||||
- 기존 프로파일에는 `SET_ATTRIBUTE`, disable, drop을 실행하지 않는다.
|
||||
- 비교 프로파일이 이미 있으면 덮어쓰지 않고 정의를 검증한다.
|
||||
- 벤치마크는 DDL/DML을 생성하거나 실행하지 않고 `showprompt`와 `showsql`만 호출한다.
|
||||
- 새 프로파일은 운영 MCP 설정에 자동 연결하지 않는다.
|
||||
|
||||
Oracle 공식 문서에 따르면 `annotations=true`는 table/column annotation을 LLM metadata에 포함하며,
|
||||
프로파일 속성은 `DBA_CLOUD_AI_PROFILE_ATTRIBUTES`에서 확인할 수 있다.
|
||||
|
||||
- [Oracle DBMS_CLOUD_AI package](https://docs.oracle.com/en/database/oracle/oracle-database/26/arpls/dbms_cloud_ai1.html)
|
||||
- [Oracle DBMS_CLOUD_AI views](https://docs.oracle.com/en-us/iaas/autonomous-database-serverless/doc/dbms-cloud-ai-views.html)
|
||||
@@ -0,0 +1,65 @@
|
||||
# 생성·측정·롤백 절차
|
||||
|
||||
[개요](README.md) · [비교 설계](architecture.md) · [측정 결과](results.md) · [문제 해결](troubleshooting.md)
|
||||
|
||||
## 1. 준비
|
||||
|
||||
- ADMIN으로 HMM ADB에 연결한다.
|
||||
- `HMM_HR_DATA_GPT54_PROFILE`이 `ENABLED`이고 `annotations=true`인지 확인한다.
|
||||
- 운영 `BACKOFFICE_SELECT_AI_PROFILE` 값을 기록하되 credential과 비밀번호는 출력하지 않는다.
|
||||
|
||||
성공 판정: 원본 프로파일과 전체 속성 목록을 읽을 수 있다.
|
||||
|
||||
실패 시: [원본 프로파일이 없거나 소유자가 다름](troubleshooting.md#원본-프로파일이-없거나-소유자가-다름)
|
||||
|
||||
## 2. 비교 프로파일 생성
|
||||
|
||||
```text
|
||||
database/adb/85_hmm_hr_select_ai_no_annotation_profile.sql
|
||||
```
|
||||
|
||||
스크립트는 비교 프로파일이 없을 때만 생성한다. 생성 후 원본과 비교 프로파일의 속성을 조회해
|
||||
`annotations` 외의 차이가 없는지 확인한다.
|
||||
|
||||
성공 판정: `HMM_HR_DATA_GPT54_NOANN_PROFILE`이 `ENABLED`, `annotations=false`다.
|
||||
|
||||
실패 시: [비교 프로파일이 이미 있지만 속성이 다름](troubleshooting.md#비교-프로파일이-이미-있지만-속성이-다름)
|
||||
|
||||
## 3. A/B 측정
|
||||
|
||||
```text
|
||||
database/adb/86_hmm_select_ai_annotation_benchmark.sql
|
||||
```
|
||||
|
||||
동일 질문을 UTF-8 Base64에서 복원하고, 두 프로파일의 `showprompt`와 `showsql`을 번갈아 3회씩
|
||||
호출한다. `BENCHMARK|...` 행을 결과 파일에 저장하고 평균·중앙값을 계산한다.
|
||||
|
||||
성공 판정:
|
||||
|
||||
- 두 프로파일 모두 3회 결과가 있다.
|
||||
- `showsql`이 읽기 전용 SQL을 반환한다.
|
||||
- 시간과 prompt/SQL 길이를 비교할 수 있다.
|
||||
|
||||
실패 시: [호출이 간헐적으로 실패하거나 시간이 크게 흔들림](troubleshooting.md#호출이-간헐적으로-실패하거나-시간이-크게-흔들림)
|
||||
|
||||
## 4. 운영 전환 판단
|
||||
|
||||
이번 실험에서는 운영 값을 바꾸지 않는다. 속도 개선이 반복 확인되고 SQL 품질이 유지될 때만
|
||||
별도 변경으로 `BACKOFFICE_SELECT_AI_PROFILE` 전환과 MCP VPD 회귀 검증을 수행한다.
|
||||
|
||||
2026-08-11 측정에서는 의미 있는 속도 개선이 확인되지 않았으므로 전환하지 않았다. 자세한 수치는
|
||||
[측정 결과](results.md)를 본다.
|
||||
|
||||
## 5. 롤백
|
||||
|
||||
비교 프로파일이 문제를 일으키면 먼저 disable한다.
|
||||
|
||||
```sql
|
||||
BEGIN
|
||||
DBMS_CLOUD_AI.DISABLE_PROFILE('HMM_HR_DATA_GPT54_NOANN_PROFILE');
|
||||
END;
|
||||
/
|
||||
```
|
||||
|
||||
운영 프로파일은 실험 중 변경하지 않으므로 서비스 롤백은 필요 없다. 프로파일 삭제는 측정 이력과
|
||||
의존성을 확인한 뒤 별도 승인하에 수행한다.
|
||||
@@ -0,0 +1,49 @@
|
||||
# 측정 결과
|
||||
|
||||
[개요](README.md) · [비교 설계](architecture.md) · [실행 절차](cookbook.md) · [문제 해결](troubleshooting.md)
|
||||
|
||||
## 결론
|
||||
|
||||
현재 HMM HR 데이터에서는 annotation 제외에 따른 일관된 속도 개선이 확인되지 않았다.
|
||||
annotation 사용 프로파일의 object list에 실제 table/column annotation이 0건이기 때문이다.
|
||||
운영 프로파일은 기존 값을 유지한다.
|
||||
|
||||
## 측정 조건
|
||||
|
||||
| 항목 | 값 |
|
||||
|---|---|
|
||||
| 측정일 | 2026-08-11 |
|
||||
| DB 연결 | ADMIN / `hmmaipoc_low` |
|
||||
| 모델 | `openai.gpt-5.4-mini` |
|
||||
| 질문 | 이번 달 팀원별 휴가 사용 현황 조회 |
|
||||
| 반복 | action별·profile별 3회, 호출 순서 교차 |
|
||||
| annotation 사용 | `HMM_HR_DATA_GPT54_PROFILE` |
|
||||
| annotation 제외 | `HMM_HR_DATA_GPT54_NOANN_PROFILE` |
|
||||
|
||||
한국어 질문은 UTF-8 Base64로 전달해 Oracle에서 복원했다. `showsql` 결과는 실행하지 않았다.
|
||||
|
||||
## 결과 요약
|
||||
|
||||
| action | 프로파일 | 평균 | 중앙값 | 최소 | 최대 | 결과 크기 |
|
||||
|---|---|---:|---:|---:|---:|---:|
|
||||
| `showprompt` | annotation 사용 | 693.3ms | 130ms | 120ms | 1,830ms | 3,188자 |
|
||||
| `showprompt` | annotation 제외 | 196.7ms | 130ms | 120ms | 340ms | 3,154자 |
|
||||
| `showsql` | annotation 사용 | 2,453.3ms | 2,430ms | 2,410ms | 2,520ms | 522~547자 |
|
||||
| `showsql` | annotation 제외 | 2,483.3ms | 2,370ms | 2,350ms | 2,730ms | 522~533자 |
|
||||
|
||||
`showprompt` 첫 회차의 1,830ms는 이후 120~130ms로 줄어 초기 metadata cache 영향으로 판단한다.
|
||||
중앙값은 두 프로파일 모두 130ms다. `showsql`은 annotation 제외 프로파일이 평균 기준 30ms 느리고,
|
||||
중앙값 기준 60ms 빠르므로 측정 편차 범위다.
|
||||
|
||||
## 품질 확인
|
||||
|
||||
- `showsql` 6회 모두 `SELECT` 또는 `WITH`로 시작하는 유효한 읽기 전용 SQL을 반환했다.
|
||||
- 회차별 SQL hash가 달라 생성 SQL은 완전히 결정적이지 않았다.
|
||||
- 두 프로파일의 prompt 크기 차이는 34자, 약 1.1%다.
|
||||
- HR object list 5개 테이블의 `ALL_ANNOTATIONS_USAGE` 집계는 0건, 0자다.
|
||||
|
||||
## 판단
|
||||
|
||||
현재 조건에서는 `annotations=false` 프로파일로 운영 전환할 근거가 없다. annotation metadata가 실제로
|
||||
많은 다른 object list에서는 결과가 달라질 수 있으므로, 그 경우 같은 스크립트로 다시 측정해야 한다.
|
||||
비교 프로파일은 후속 시험용으로 유지하되 MCP 운영 설정에는 연결하지 않는다.
|
||||
@@ -0,0 +1,46 @@
|
||||
# 문제 해결
|
||||
|
||||
[개요](README.md) · [비교 설계](architecture.md) · [실행 절차](cookbook.md) · [측정 결과](results.md)
|
||||
|
||||
## 원본 프로파일이 없거나 소유자가 다름
|
||||
|
||||
**증상**: `HMM_HR_DATA_GPT54_PROFILE` 조회 결과가 없거나 `CREATE_PROFILE` 권한 오류가 발생한다.
|
||||
|
||||
**원인**: 다른 DB 또는 다른 사용자로 접속했거나 프로파일 소유자가 ADMIN이 아니다.
|
||||
|
||||
**확인**: `DBA_CLOUD_AI_PROFILES`에서 owner와 profile name을 확인한다.
|
||||
|
||||
**해결**: HMM ADB 지갑·service alias와 ADMIN 연결을 다시 확인한다. 이름이 비슷한 프로파일을 임의로
|
||||
대체하지 않는다.
|
||||
|
||||
## 비교 프로파일이 이미 있지만 속성이 다름
|
||||
|
||||
**증상**: 비교 프로파일은 존재하지만 모델, object list, temperature 또는 annotations 값이 설계와 다르다.
|
||||
|
||||
**원인**: 이전 실험의 프로파일이 같은 이름으로 남아 있다.
|
||||
|
||||
**확인**: `DBA_CLOUD_AI_PROFILE_ATTRIBUTES`에서 두 프로파일을 나란히 비교한다.
|
||||
|
||||
**해결**: 기존 비교 프로파일을 덮어쓰지 않는다. 먼저 disable하고 이력·의존성을 확인한 뒤 새 버전
|
||||
이름을 사용하거나 별도 승인으로 정리한다.
|
||||
|
||||
## 호출이 간헐적으로 실패하거나 시간이 크게 흔들림
|
||||
|
||||
**증상**: 같은 프로파일에서도 회차별 `showsql` 시간이 크게 다르거나 LLM 오류가 발생한다.
|
||||
|
||||
**원인**: 모델 endpoint 부하, 첫 호출 metadata cache, 생성형 SQL의 비결정성일 수 있다.
|
||||
|
||||
**확인**: 두 프로파일을 번갈아 호출했는지, 각 3회 이상 결과가 있는지, 오류 코드가 프로파일 설정
|
||||
오류인지 외부 모델 오류인지 구분한다.
|
||||
|
||||
**해결**: 실패 회차를 숨기지 않고 기록한다. 같은 조건으로 추가 회차를 실행하고 평균뿐 아니라
|
||||
중앙값·최솟값·최댓값을 함께 비교한다.
|
||||
|
||||
## 한국어 질문이 깨져 SQL 품질이 달라짐
|
||||
|
||||
**증상**: 질문의 한글이 깨지거나 관련 없는 SQL이 생성된다.
|
||||
|
||||
**원인**: SQLcl을 통해 한국어 literal을 직접 전달했다.
|
||||
|
||||
**해결**: UTF-8 질문을 Base64 ASCII로 전달하고 Oracle에서 `UTL_I18N.RAW_TO_CHAR`로 복원한다.
|
||||
직접 SQL string 또는 `q'[...]'`를 사용하지 않는다.
|
||||
Reference in New Issue
Block a user