Day 3: Cloud LLM 개선 작업 및 테스트
1. 학습 목표
Day 3은 Cloud LLM 서버 구조, Case 응답 계약, RAG 개입 조건, 온디바이스 재진입 구조를 정리합니다. 핵심은 Cloud가 제품 기능을 직접 실행하지 않는다는 점입니다. Cloud는 사용자 의도를 Case로 분류하고, 제품 기능이면 rewrite_query를 통해 다시 온디바이스 로컬 라우팅으로 돌려보냅니다.
이 장을 읽은 후에는 다음을 수행할 수 있어야 합니다.
- On-device에서 Cloud LLM 요청이 만들어지는 위치를 설명한다.
- Cloud request의
system/persona,#Information,#Environment,user메시지 구조를 설명한다. - Case 1/2/3/4 응답을 구분한다.
- Case 2의
rewrite_query가 온디바이스 딕셔너리와 왜 함께 검증되어야 하는지 설명한다. - File RAG와 Web Search RAG의 역할을 구분한다.
- Cloud 테스트 결과를 프롬프트/라우터/딕셔너리 개선 요청으로 분리한다.
참조:
- Cloud LLM Integration Source Guide
- Cloud Rewrite Query Quality Guide
- Operational Examples
- Cloud LLM Case 및 온디바이스 재진입 구조
- Cloud LLM Lambda/RAG 구조 - PDF-safe
- Cloud request 구성
- Case 응답 계약
- RAG 선택 기준
- Cloud Rewrite Quality Gate
- Cloud Rewrite Prompt Hardening
2. 본문 개요
Cloud LLM은 다음 관점에서 이해해야 합니다.
Cloud LLM은 일반 질문이나 로컬 이해 실패를 보완하는 중요한 경로지만,
제품 기능을 직접 실행하는 주체는 아닙니다.
Cloud가 "이건 제품 기능이다"라고 판단하면 Case 2로 응답하고,
온디바이스가 다시 이해할 수 있는 rewrite_query를 내려줍니다.
그 rewrite_query가 다시 온디바이스 딕셔너리와 로컬 LLM을 통과해야 실제 기능이 실행됩니다.
3. On-device Cloud 요청 소스
주요 파일:
app/src/main/java/com/skmagic/ondeviceai/agent/service/ForegroundService.kt
app/src/main/java/com/skmagic/ondeviceai/agent/service/repository/ChatRepository.kt
app/src/main/java/com/skmagic/ondeviceai/agent/common/NetworkModule.kt
app/src/main/assets/LLM_PROMPT_ko.text
app/src/main/assets/LLM_PROMPT_en.text
핵심 함수:
processVoiceCmd()
processCloudLLM()
callCloudLLMAPI()
runCloudResolvedQueryThroughLocalPipeline()
Cloud 진입 조건:
1. isCloudContinue == true && Wi-Fi connected
2. 로컬 결과가 error_answer && Wi-Fi connected
3. 특정 token handler에서 tossToCloudLlm() 호출
4. Cloud request 구조
callCloudLLMAPI()는 아래 4종류 메시지를 구성합니다.
Persona system
loadPrompt()
LLM_PROMPT_ko.text 또는 LLM_PROMPT_en.text
역할:
Cloud LLM의 페르소나, 기능 라우팅 기준, Case Router, 미지원 기능 정책을 정의합니다.
Information system
#Information
- Date
- Time
- DayOfWeek
- TimeZone
역할:
오늘/현재/요일/시간 관련 질문에 Cloud가 기기 기준 컨텍스트를 알 수 있게 합니다.
Environment system
#Environment
- Current Location
- Weather
- Humidity
- Temperature low/high
- PM 10
- PM 2.5
역할:
날씨/공기질/외출복장 질문에서 현재 환경 컨텍스트를 제공합니다.
User message
originalSttResult 기반
중요:
Cloud로 넘어가는 user content는 로컬 dictionary용 filteredSttResult가 아닙니다.
따라서 Cloud 오분류를 분석할 때는 STT raw/originalSttResult를 확인해야 합니다.
4-1. 국내 프롬프트 구성 기준
국내 프롬프트는 Cloud LLM이 제품 기능과 일반 질의를 섞지 않도록 아래 계층으로 구성됩니다.
| 계층 | 역할 | 주요 내용 |
|---|---|---|
| Persona | 응답 성격과 제품 정체성 | Namuh X, SK 계열 제품, 정중하고 간결한 응답 |
| Capabilities | 수행 가능한 기능 범위 | 청정, 이동, 바이탈, 고객센터, 일반 대화 |
| Router | Case 분류 기준 | Case 1/2/3/4, 제품 기능 우선순위, 미지원 정책 |
| Anti-hallucination | 과장/실시간 정보 제한 | 최신 정보 제한, 외부 정보 제한, 불확실 시 짧게 안내 |
| Time/Weather | 기기 컨텍스트 사용 기준 | device clock, weather context, 위치/날씨 질의 처리 |
| Output Contract | JSON 응답 계약 | case, response, rewrite_query, there_is_question_mark |
국내 프롬프트에서 가장 중요한 점은 제품 기능을 직접 실행하지 않게 하는 것입니다.
제품 기능 의도:
Cloud가 직접 "실행했습니다"라고 말하지 않음
Case 2 + rewrite_query로 온디바이스 재진입
일반 설명:
Case 1로 짧고 정중하게 답변
미지원 기능:
Case 4로 지원 불가 안내
말레이시아용 프롬프트를 만들 때도 이 구조는 유지합니다. 바뀌는 것은 제품 정책 문장, 현지 FAQ, 서비스 연락처, 현지 표현, RAG 문서셋입니다.
5. Cloud Lambda 구조
대상 저장소:
~/work/8.cloudLLM/backend-cloud-llm-lambda
핵심 파일:
lambda_function.py
gemini/gemini_full_rag_api.py
gemini/gemini_file_rag_api.py
gemini/gemini_rag_judge_api.py
gemini/gemini_gsearch_rag_api.py
gemini/search_keyword_generator.py
gemini/prompt_formatting.py
처리 개념:
API Gateway
-> lambda_handler
-> request body parse
-> CloudLLMRequest
-> full_rag()
-> 기본 Gemini 응답 / File RAG / Web RAG 후보
-> 최종 response JSON
-> OpenAI ChatCompletion 스타일 wrapper
6. Case 응답 계약
Case 1: 일반 답변
{
"case": 1,
"response": "짧고 정중한 답변입니다.",
"there_is_question_mark": false
}
온디바이스 처리:
response를 TTS로 출력합니다.
there_is_question_mark가 true거나 response가 ?로 끝나면 Cloud continue 상태를 유지합니다.
Case 2: 제품 기능 재해석
{
"case": 2,
"function": "Air quality map",
"available_function": "check for status",
"rewrite_query": "Check the air quality map history, please"
}
온디바이스 처리:
1. 현재 user turn 제거
2. resetMultiturn()
3. resetCloudMultiturn()
4. runCloudResolvedQueryThroughLocalPipeline(rewrite_query)
5. processLLAMA(rewrite_query)
6. 최종 `<sk_xx>` 생성
검증:
Case 2가 맞아도 rewrite_query가 온디바이스에서 기대 토큰으로 매칭되지 않으면 실패입니다.
rewrite_query 품질 기준:
| 항목 | 기준 |
|---|---|
| 문장 형태 | 사용자가 다시 말할 법한 명령형 문장 |
| 금지 형태 | I will..., ...하겠습니다처럼 실행 완료를 의미하는 문장 |
| 기능명 | 온디바이스 dictionary가 인식할 수 있는 기능명을 포함 |
| action | check/show/set/stop/start 같은 실제 action 포함 |
| slot | 공간명, 단위, 모드, 상태조회 대상 등 필수 slot 보존 |
| 최종 판정 | runCloudResolvedQueryThroughLocalPipeline() 이후 기대 <sk_xx> 매칭 |
Cloud rewrite 강화 기준:
| 단계 | 확인 항목 | 실패 시 수정 위치 |
|---|---|---|
| Cloud user content | originalSttResult가 실제 사용자 발화와 같은지 |
STT/전처리/Cloud request 구성 |
| Case Router | 제품 기능 요청이 Case 2로 분류되는지 | Cloud prompt router 예시/규칙 |
| rewrite_query | 기능명, action, slot이 보존된 명령형 문장인지 | Cloud prompt rewrite rule |
| Local re-entry | rewrite가 기대 <sk_xx>로 재매칭되는지 |
온디바이스 dictionary 또는 rewrite 표현 제한 |
운영 기준:
Case 2만 맞으면 성공이 아닙니다.
Cloud가 만든 rewrite_query가 로컬 딕셔너리와 FunctionCallHandler까지 이어져야 최종 성공입니다.
따라서 Cloud prompt 강화 작업은 항상 온디바이스 dictionary 테스트 결과와 함께 판단합니다.
Case 3: 준비 질문
{
"case": 3,
"prepared_question_list_number": "56"
}
처리:
일부 번호는 로컬 string resource로 즉시 응답합니다.
그 외에는 getRewriteQuery(number)로 문장화한 뒤 로컬 재진입할 수 있습니다.
Case 4: 미지원 기능
{
"case": 4,
"response": "현재 이 기능은 지원되지 않습니다.",
"there_is_question_mark": false
}
처리:
Cloud 응답을 TTS로 출력합니다.
제품 기능을 수행한 것처럼 말하면 안 됩니다.
7. RAG 선택 기준
File RAG
용도:
사전에 구축된 제품 문서/뉴스/FAQ성 문서에서 근거를 찾아 답변합니다.
근거가 없으면 False 또는 fallback 성격의 결과가 나올 수 있습니다.
Web Search RAG
용도:
최신성/외부 정보가 필요한 질문에서 Google Custom Search 결과를 Gemini가 재구성합니다.
RAG가 필요 없는 예
보안모드는 뭐야?
올타임 모드는 언제 써?
필터 청소 주기는 어떻게 돼?
RAG가 필요한 예
최근 공기질 관련 뉴스 알려줘
요즘 미세먼지 이슈 알려줘
오늘 외부 날씨와 관련된 최신 정보를 알려줘
7-1. 말레이시아 Custom RAG 운영 기준
말레이시아 현지 운영에서는 국내 문서 기준 답변을 그대로 쓰면 안 되는 영역이 있습니다. 현지 RAG는 아래 자료를 별도 문서셋으로 구성하는 것을 기준으로 합니다.
| 문서셋 | 포함 내용 | RAG 선택 기준 |
|---|---|---|
| 제품 기능 FAQ | 현지 판매 모델 기능, 앱 메뉴, 제한 사항 | 제품 설명 질문이지만 기능 실행이 아닌 경우 |
| 고객지원/서비스 | 현지 고객센터, 보증, AS 정책 | 연락처/서비스/보증 질문 |
| 설치/사용 가이드 | 현지 언어 사용 설명, 초기 설정, Wi-Fi 연결 | 사용법 질문 |
| 공기질/웰니스 콘텐츠 | 현지 환경 기준, PM 설명, 건강 관련 일반 가이드 | 제품 외 일반 설명, 단 의료 진단 금지 |
| 미지원/제한 정책 | 현지 앱 미지원 기능, 국가별 미제공 기능 | 사용자가 요구한 기능이 제품/지역에서 불가한 경우 |
주의:
1. 말레이시아 RAG는 제품 기능 실행 경로가 아닙니다.
2. 기능 실행은 여전히 On-device token과 Device API 경로에서 수행됩니다.
3. RAG 답변은 근거 문서가 없으면 추정으로 만들지 않습니다.
4. 현지 연락처/요금/정책은 최신 문서 기준으로만 답변합니다.
5. 한국 사양과 말레이시아 사양이 다르면 말레이시아 운영 문서를 우선합니다.
말레이시아 RAG와 제품 기능 실행의 경계:
현지 서비스/보증/앱 사용법 질문:
Case 1 + Malaysia Custom RAG
제품 기능 실행 요청:
Case 2 + rewrite_query + On-device 재진입
예: "Register the cleaning schedule"
Cloud가 RAG 답변으로 설명하지 않고,
schedule 기능 명령형 rewrite_query를 생성하거나
온디바이스 dictionary에서 직접 <sk_48>로 처리되어야 합니다.
말레이시아 Custom RAG 테스트 케이스:
| 질문 유형 | 예 | 기대 판정 |
|---|---|---|
| 현지 고객센터 | What is the Malaysia service center number? |
현지 서비스 문서 근거 답변 |
| 현지 앱 사용법 | How do I connect the device to the app in Malaysia? |
현지 앱 가이드 근거 답변 |
| 제품 기능 실행 | Turn on security mode |
RAG 답변이 아니라 Case 2 또는 로컬 기능 경로 |
| 제품 외부 최신 정보 | What is today's haze condition in KL? |
Web/RAG 필요 여부 판단, 불확실 시 제한 안내 |
| 미지원 기능 | Open YouTube on the device |
Case 4 미지원 안내 |
8. Cloud 테스트 방법
테스트는 단순히 “대답이 자연스러운가”가 아니라 아래를 봅니다.
1. request messages가 기대대로 구성됐는가
2. user content가 실제 STT 원문과 같은가
3. case가 기대와 맞는가
4. response 또는 rewrite_query가 사양과 맞는가
5. Case 2 rewrite_query가 온디바이스에서 기대 토큰으로 매칭되는가
6. Cloud continue가 불필요하게 켜지지 않는가
7. RAG가 필요한 질문에서만 RAG가 개입했는가
추가 판정 기준:
- JSON 형식이 자연스러워도
case가 틀리면 실패입니다. - Case 2는
rewrite_query가 온디바이스에서 기대 token으로 재매칭되어야 성공입니다. originalSttResult가 잘못 들어가면 프롬프트 수정만으로 해결되지 않을 수 있습니다.- 말레이시아 질의는 국내 프롬프트와 현지 RAG 문서셋 중 어느 근거를 사용했는지 기록합니다.
there_is_question_mark가 불필요하게true이면 다음 turn 상태관리 문제가 발생할 수 있습니다.
Cloud rewrite_query 검증 절차:
1. Cloud response raw에서 case/function/available_function/rewrite_query를 기록
2. case == 2이면 rewrite_query 문장이 사용자 명령형인지 확인
3. 온디바이스 로그에서 `Cloud rewrite_query enters local routing pipeline` 확인
4. 이어지는 `[DICTIONARY_MATCH]` 또는 `parsedLlamaResponse` 확인
5. 기대 `<sk_xx>`와 실제 token/parameter 비교
6. Validator/API/TTS까지 최종 결과 확인
7. 실패하면 Cloud prompt 수정인지 dictionary 보강인지 분리
9. 실습 케이스
운영 예제:
발화: Please show me the air quality map history
Cloud response: case=2, rewrite_query="Check the air quality map history, please"
온디바이스 재진입 기대: <sk_29>(get=True)
판정:
Cloud Case 2는 제품 기능 의도 분류가 맞다는 의미입니다.
하지만 최종 성공은 rewrite_query가 온디바이스 dictionary에서 `<sk_29>(get=True)`로 매칭되어야 확정됩니다.
잘못된 예:
발화: 공기질 맵 히스토리 보여줘
Cloud 답변: 공기질 맵 히스토리를 보여드릴게요.
판정:
Cloud가 직접 화면을 띄우거나 Device API를 호출한 것처럼 말하면 안 됩니다.
제품 기능은 Case 2 rewrite_query로 온디바이스에 돌려보내야 합니다.
| 발화 | 기대 Cloud 판정 |
|---|---|
오늘 날씨 어때 |
환경 컨텍스트 기반 응답 또는 날씨 경로 |
공기질 맵 히스토리 보여줘 |
Case 2 + rewrite_query 또는 로컬 직접 매칭 |
보안모드는 뭐야 |
Case 1 설명 |
Please show me the air quality map history |
Case 2 또는 로컬 재진입 가능한 rewrite |
유튜브 틀어줘 |
Case 4 미지원 |
10. 정리 과제
1. Cloud request message 구조 요약
2. Case 1/2/3/4 예시와 온디바이스 처리 방식
3. RAG 필요/불필요 질문 10개 분류
4. rewrite_query 검증표
5. Cloud 테스트 결과 리포트 1건
11. 점검 질문
1. Cloud Case 2와 Case 1의 가장 큰 차이는 무엇인가?
2. rewrite_query가 좋아 보여도 최종 기능 실행이 실패할 수 있는 이유는 무엇인가?
3. Cloud user content는 originalSttResult와 filteredSttResult 중 어느 쪽인가?
4. RAG는 제품 기능 실행을 위한 경로인가?
5. Cloud continue가 true가 되는 조건은 무엇인가?