Cloud LLM Integration Source Guide
이 문서는 On-device Agent와 Cloud LLM Lambda가 어떤 계약으로 연결되는지 설명합니다. 핵심은 Cloud LLM이 제품 기능을 직접 실행하지 않고, Case 구조와 rewrite_query를 통해 다시 온디바이스 기능 라우팅으로 들어온다는 점입니다.
1. 관련 저장소와 파일
On-device Agent
| 파일 | 역할 |
|---|---|
ForegroundService.kt |
Cloud 호출 진입, request message 구성, response 처리, 로컬 재진입 |
ChatRepository.kt |
Cloud API HTTP 호출 |
NetworkModule.kt |
네트워크 모듈/클라이언트 구성 |
LLM_PROMPT_ko.text, LLM_PROMPT_en.text |
온디바이스에서 Cloud로 보내는 system prompt |
Cloud LLM Lambda
| 파일 | 역할 |
|---|---|
lambda_function.py |
Lambda handler, body 파싱, 응답 포맷 래핑 |
gemini/gemini_full_rag_api.py |
Gemini RAG 전체 경로 |
gemini/gemini_file_rag_api.py |
File RAG |
gemini/gemini_rag_judge_api.py |
최신 정보/RAG 필요 여부 판단 |
gemini/gemini_gsearch_rag_api.py |
Google Search 기반 Web RAG |
gemini/search_keyword_generator.py |
검색 키워드 생성 |
gemini/prompt_formatting.py |
prompt/message 포맷 보조 |
2. Cloud 호출 진입 조건
On-device에서 Cloud LLM은 대표적으로 아래 상황에서 호출됩니다.
1. `isCloudContinue == true`이고 Wi-Fi 연결됨
2. 로컬 `processLLAMA()` 결과가 `error_answer`이고 Wi-Fi 연결됨
3. 특정 기능 토큰에서 `foregroundService.tossToCloudLlm()`를 호출하는 분기
4. 시간/일부 지식성 질의처럼 Cloud 위임 경로가 구현된 경우
Cloud 호출 전에는 아래 상태가 기록됩니다.
setLlmStatus(STATE_CLOUD_LLM_RUN)
Detected <sk_19>, switching to cloud LLM API
REQ size=...
REQ token_estimate=...
send json: ...
3. 요청 메시지 구조
callCloudLLMAPI()는 system message를 세 덩어리로 분리합니다.
1. personaSystemMessage
- loadPrompt()
- LLM_PROMPT_ko/en 또는 외부 prompt 파일
2. informationSystemMessage
- Date
- Time
- DayOfWeek
- TimeZone
3. environmentSystemMessage
- Weather data
- Current Location
- Humidity
- Temperature low/high
- PM10 / PM2.5
4. currentUserMessage
- originalSttResult 기반 user content
요청 JSON 개념:
{
"request_type": "chat.completions",
"model": "gpt-4o-mini",
"max_tokens": 2048,
"stream": false,
"temperature": 0,
"top_p": 1,
"device_id": "DEVICE_SERIAL",
"response_format": {"type": "json_object"},
"messages": [
{"role": "system", "content": "Persona..."},
{"role": "system", "content": "#Information..."},
{"role": "system", "content": "#Environment..."},
{"role": "user", "content": "사용자 STT 원문"}
]
}
4. Case 응답 계약
Case 1
일반 답변입니다. 제품 기능 실행 없이 Cloud 응답을 TTS로 말합니다.
{
"case": 1,
"response": "답변 문장",
"there_is_question_mark": false
}
there_is_question_mark == true이거나 response가 ?로 끝나면 Cloud continue 상태가 유지됩니다.
Case 2
제품 기능 재해석입니다. Cloud가 직접 Device API를 호출하지 않고 rewrite_query를 만듭니다.
{
"case": 2,
"function": "Air quality map",
"available_function": "check for status",
"rewrite_query": "Check the air quality map history, please"
}
On-device 처리:
1. 현재 user turn을 conversationHistory에서 제거
2. resetMultiturn()
3. resetCloudMultiturn()
4. runCloudResolvedQueryThroughLocalPipeline(rewrite_query)
5. processLLAMA(rewrite_query)
6. 딕셔너리/로컬 LLM이 다시 `<sk_xx>` 생성
Case 3
준비 질문 번호 기반 응답입니다. 일부 번호는 로컬 string resource로 즉시 답변하고, 그 외에는 getRewriteQuery()로 문장화해 로컬 재진입합니다.
Case 4
미지원 기능 또는 음성 미지원 안내입니다. 보통 Cloud 응답을 그대로 TTS로 사용합니다.
5. Cloud RAG 흐름
Cloud Lambda 기본 구조는 아래와 같이 설명합니다.
lambda_handler
├─ request body 파싱
├─ CloudLLMRequest 구성
├─ full_rag()
│ ├─ 기본 Gemini 응답 후보
│ ├─ RAG 필요 여부 판단
│ ├─ File RAG 후보
│ ├─ Web Search RAG 후보
│ └─ 최종 응답 선택
└─ OpenAI ChatCompletion 스타일로 반환
Cloud 운영에서 중요한 점:
RAG는 제품 기능 실행을 위한 경로가 아닙니다.
RAG는 최신성/외부 지식/문서 기반 답변 품질을 높이는 경로입니다.
제품 기능 실행은 Case 2 -> rewrite_query -> 온디바이스 재진입으로 처리해야 합니다.
말레이시아 Custom RAG 확인 항목:
| 항목 | 확인 기준 |
|---|---|
| 문서셋 | 현지 FAQ, 고객센터, 보증, 앱 사용법, 국가별 제한 기능 포함 여부 |
| 검색 기준 | File RAG와 Web Search가 필요한 질문을 분리했는지 |
| 제품 기능 분리 | 기능 실행 요청을 RAG 답변으로 처리하지 않는지 |
| 근거성 | 답변이 현지 문서에 없는 내용을 추정하지 않는지 |
| 최신성 | 연락처/요금/정책은 문서 버전 또는 Web Search 필요 여부를 기록하는지 |
6. 테스트 판정 기준
Cloud 테스트는 아래 네 가지를 동시에 봐야 합니다.
| 항목 | 정상 기준 |
|---|---|
| Case | 의도에 맞는 case가 나와야 함 |
| response | 사용자에게 바로 말해도 되는 문장이어야 함 |
| rewrite_query | 온디바이스 딕셔너리에서 기대 토큰으로 매칭 가능해야 함 |
| there_is_question_mark | 추가 질문이 필요한 경우에만 true여야 함 |
7. 대표 장애 분석
Cloud Case는 맞지만 온디바이스 실행 실패
Cloud: case=2, rewrite_query 정상처럼 보임
On-device: parsedLlamaResponse가 error_answer
원인 후보:
1. rewrite_query 표현이 온디바이스 딕셔너리에 없음
2. 영어 rewrite_query가 너무 일반적임
3. Cloud가 기능명을 바꿔서 표현함
4. On-device dictionary가 한국어 위주로만 보강됨
개선:
Cloud prompt에 rewrite_query 표현을 온디바이스 dictionary 기준으로 제한합니다.
동시에 온디바이스 contains_token에 Cloud rewrite 표현을 positive case로 추가합니다.
Cloud가 Case 1로 답변해버림
사용자: 공기질 맵 히스토리 보여줘
Cloud: case=1, response="공기질 맵은..."
원인 후보:
제품 기능 실행 요청을 일반 설명 요청으로 분류했습니다.
Cloud prompt의 function router 예시/우선순위가 부족합니다.
개선:
제품 기능 요청은 Case 2로 보내고, Cloud가 직접 실행한 것처럼 말하지 않도록 prompt에 제약을 추가합니다.
8. Cloud 테스트 리포트 필수 필드
입력 발화:
언어:
On-device originalSttResult:
Cloud request messages 요약:
Cloud response raw:
case:
response:
rewrite_query:
prepared_question_list_number:
there_is_question_mark:
온디바이스 재진입 토큰:
최종 TTS:
판정:
개선 필요 위치: