← Docs hub

Day 1: 제품 기능 사양 및 LLM 구조

1. 학습 목표

1. 학습 목표

Day 1은 전체 구조의 기준점을 잡는 장입니다. LLM 운영 담당자가 가장 먼저 이해해야 하는 것은 “음성 인식 결과가 바로 기능 실행으로 이어지는 단순 구조가 아니다”라는 점입니다. 실제 제품은 STT, 온디바이스 딕셔너리, 로컬 LLM, Cloud LLM, 기능 토큰, 사전 검증, Device API, TTS, 상태관리까지 이어지는 E2E 시스템입니다.

이 장을 읽은 후에는 다음을 설명할 수 있어야 합니다.

참조:

2. 본문 개요

2. 본문 개요

제품 음성 기능의 전체 구조는 다음 관점에서 이해해야 합니다.
사용자가 말한 문장은 바로 기능으로 실행되지 않습니다.
먼저 STT가 텍스트를 만들고, 온디바이스 딕셔너리와 로컬 LLM이 기능 토큰을 만들고,
그 토큰이 현재 기기 상태에서 실행 가능한지 ApiCallValidator가 검증한 뒤,
FunctionCallHandler가 실제 Device API 호출과 TTS를 결정합니다.

Cloud LLM도 중요하지만, Cloud가 제품 기능을 직접 실행하는 구조는 아닙니다.
Cloud는 일반 답변을 하거나, 제품 기능 의도를 다시 온디바이스가 이해할 수 있는 문장으로 바꿔줍니다.
따라서 테스트 중 문제가 생기면 "LLM이 틀렸다"라고 묶어서 보면 안 됩니다.
STT 문제인지, 딕셔너리 문제인지, Cloud Case 문제인지, 기능 구현 문제인지, 기기 상태 문제인지 분리해야 합니다.

3. 전체 구성도 설명

제품 음성 E2E 전체 구성도

음성 입력부터 STT까지

전체 구성도는 아래 순서로 읽습니다.

사용자 음성
  -> Kardome 이벤트/WUW/VAD/SAE
  -> ForegroundService 녹음 상태
  -> Sherpa ONNX STT
  -> STT raw/originalSttResult
  -> 로컬 전처리 filteredSttResult
  -> 딕셔너리 matcher
  -> 로컬 LLM fallback
  -> parsedChunk `<sk_xx>`
  -> LLMResponseParser
  -> ApiCallValidator
  -> FunctionCallHandler
  -> DeviceCommunicator
  -> TTS/화면/상태 reset

핵심 포인트:

1. STT raw와 로컬 LLM 입력은 같지 않을 수 있습니다.
2. Cloud로 넘어가는 문장은 `originalSttResult` 기반입니다.
3. 로컬 기능 실행은 최종적으로 `<sk_xx>` 토큰을 기준으로 합니다.
4. `<sk_xx>`가 맞아도 기기 상태나 에러코드 때문에 실행이 막힐 수 있습니다.
5. TTS 문구가 틀린 경우는 LLM 문제가 아니라 리소스/FunctionCallHandler 문제일 수 있습니다.

4. 소스레벨 진입점

로컬 기능 실행 경로

ForegroundService.processVoiceCmd(sttResult)

역할:

1. STT 결과를 받은 뒤 전체 처리 시작
2. STT 로그/AI Log 저장
3. Cloud continue 여부 판단
4. 로컬 LLM 또는 Cloud LLM 호출
5. 로컬 이해 실패 시 Cloud fallback
6. TTS 입력/재생

설명:

processVoiceCmd는 제품 음성 기능의 중앙 제어점입니다.
테스트 로그에서 processVoiceCmd가 찍히지 않으면 STT 이후 처리까지 오지 못한 것입니다.
processVoiceCmd는 로컬 경로와 Cloud 경로를 선택하는 위치이므로,
Cloud가 왜 호출됐는지 확인하려면 이 함수를 기준으로 로그를 따라가야 합니다.

ForegroundService.processLLAMA(sttResult)

역할:

1. 상태를 `STATE_LLM_RUNNING`으로 변경
2. STT 결과를 로컬 라우팅용으로 정리
3. 딕셔너리 matcher 적용
4. matcher 실패 시 `mQaClient.Infer(prompt)` 호출
5. `<sk_end>`를 붙여 최종 토큰 문자열 구성
6. `LLMResponseParser`로 파싱
7. `ApiCallValidator.preApiCallValidate()` 수행
8. `mappingTtsTextAndDeviceApi()`로 기능 실행 연결

아래 로그가 이 단계의 기준 로그입니다.

Processing with on-device LLM
LLM inference started
[DICTIONARY_MATCH][...]
parsedLlamaResponse: [ <sk_48>(action=1) ]
Cutted parsedLlamaResponse: [ <sk_48>(action=1)<sk_end> ]
HI_NAMUH_LLMResponseParser: llmResponse : <sk_48>(action=1)<sk_end>

ForegroundService.processCloudLLM()

역할:

1. Wi-Fi 연결 확인
2. Device serial 확인
3. Cloud request 구성
4. ChatRepository.sendMessage()
5. Case 1/2/3/4 처리
6. Case 2/3 일부는 `runCloudResolvedQueryThroughLocalPipeline()`으로 로컬 재진입

핵심 포인트:

Cloud 응답이 Case 2라면 최종 기능 실행은 아직 끝난 것이 아닙니다.
rewrite_query가 다시 processLLAMA로 들어가서 온디바이스 딕셔너리/로컬 LLM을 통과해야 합니다.

5. On-device LLM 책임

5. On-device LLM 책임

On-device는 제품 기능 실행의 중심입니다.

책임:

예:

발화: 전체 청정 스케줄 등록해줘
로컬 결과: <sk_48>(action=2)<sk_end>
후속 처리:
  - 맵 유무 확인
  - 공간 목록 확인
  - 온디바이스/서버 스케줄 경로 분기
  - 성공/중복/실패 TTS

6. Cloud LLM 책임

Cloud fallback과 재진입

Cloud는 보완/확장 경로입니다.

책임:

Cloud가 하지 않는 것:

Cloud LLM은 일반적으로 Device API를 직접 호출하지 않습니다.
Cloud가 기능 실행 의도를 이해해도, 최종 실행은 온디바이스 FunctionCallHandler/DeviceCommunicator 경로에서 이뤄집니다.

7. 기능 토큰과 제품 사양

7. 기능 토큰과 제품 사양

기능 토큰은 음성 기능의 내부 계약입니다.

예:

토큰 의미
<sk_29>(get=True) 공기질 맵 히스토리 조회/앱 안내
<sk_39>(theme=1) 대기화면 상세 테마 설정
<sk_46> 보안모드 실행
<sk_48>(action=1) 고정청정 스케줄 등록
<sk_48>(action=2) 전체청정 스케줄 등록
<sk_48>(action=4) 일반 스케줄 요청/지원 불가 안내
<sk_50> 등록된 공간 개수 안내
<sk_108>(type=1) 현재 시간 질의

핵심 포인트:

테스트 기대값은 "어떤 답변이 나왔는가"만으로 잡으면 안 됩니다.
기대 토큰, 기대 Device API, 기대 TTS를 함께 정의해야 합니다.

7-1. 음성 지원 가능 범위와 지원/미지원 케이스

7-1. 음성 지원 가능 범위와 지원/미지원 케이스

말레이시아 현지 운영에서는 “사용자가 말한 모든 기능을 제품이 수행할 수 있다”는 관점이 아니라, 아래 세 범위를 먼저 구분해야 합니다.

범위 처리 경로 대표 예 기대 결과
제품 기능 지원 On-device token -> Validator -> Device API 청정, 이동, 보안모드, 스케줄, 대기화면, 공기질 맵 기능 실행 또는 상태 안내
제품 설명/일반 질의 Cloud Case 1 또는 prepared answer 보안모드 설명, 올타임 모드 설명, 이용료 안내 짧은 설명 TTS
미지원 기능 Cloud Case 4 또는 로컬 미지원 안내 유튜브 실행, 임의 앱 실행, 제품 외부 제어 미지원 안내

지원 가능 범위를 판단할 때는 아래 순서를 사용합니다.

1. 발화가 제품 내 기능인지 확인
2. 해당 기능에 `<sk_xx>` 토큰이 있는지 확인
3. 토큰이 있으면 FunctionCallHandler 분기가 있는지 확인
4. 실행 전 Validator 차단 조건이 있는지 확인
5. Device API가 실제로 존재하는지 확인
6. 사용자에게 말할 TTS/Subtitle 문구가 있는지 확인

기능별 동작 사양은 “토큰만 맞으면 성공”이 아닙니다. 아래 네 값이 동시에 맞아야 제품 사양과 일치합니다.

사양 항목 확인 위치
기대 토큰 parsedLlamaResponse, LLMResponseParser
실행 가능 조건 ApiCallValidator, errorcode, device status
실행 API DeviceCommunicator Device API Input Parameter
사용자 안내 ttsText, string resource, subtitle

8. 상태관리와 멀티턴

상태관리 핵심 플래그

주요 상태:

STATE_PRE_RECORDING
STATE_MAIN_RECORDING
STATE_STT_RUNNING
STATE_LLM_RUNNING
STATE_TTS_RUNNING
STATE_IDLE

주요 변수:

변수 의미
multiTurnBusy 후속 발화 대기
multiTurnTextFragment 이전 불완전 발화 조각
multiTurnCount 멀티턴 반복 횟수
isCloudContinue Cloud 대화 지속 여부
skipResume TTS 후 청취 재개 스킵 여부
isDetermineMultiturnNeeded parsedChunk 기반 멀티턴 판단 여부

예:

로컬 LLM 결과에 `-1` 파라미터가 포함되면 슬롯이 비었다고 보고 multiTurnBusy가 true가 될 수 있습니다.
Cloud Case 1 응답이 질문으로 끝나면 Cloud continue가 유지되고, 이때는 로컬 parsedChunk 기준 멀티턴 판단을 하지 않습니다.

누락되기 쉬운 운영 관점:

  1. Cloud continue 상태에서는 parsedChunk 기준 멀티턴 판단을 그대로 적용하지 않습니다.
  2. skipResumetrue이면 TTS 이후 일반 resume 흐름과 다르게 동작할 수 있습니다.
  3. 멀티턴에서 이전 fragment가 현재 STT 앞에 붙기 때문에 단일 발화 테스트와 결과가 달라질 수 있습니다.
  4. 상태관리 이슈는 token만 봐서는 확인되지 않으므로 resetState, determineMultiTurn, skipResume 로그를 같이 봐야 합니다.
  5. Cloud Case 2는 Cloud 응답 완료가 아니라 온디바이스 재진입의 시작점입니다.

9. 처리 예제

9. 처리 예제

예제 1: 정상 온디바이스 기능

발화: 등록된 스케줄 보여줘
기대 흐름:
  STT -> dictionary -> <sk_48>(get=True) -> FunctionCallHandler -> TTS/스케줄 화면

관찰 로그:

[DICTIONARY_MATCH]
parsedLlamaResponse
LLMResponseParser
FunctionCallHandler
mappingTtsTextAndDeviceApi
ttsText

예제 2: Cloud fallback

발화: 로컬이 이해하기 어려운 일반 질문
기대 흐름:
  processLLAMA -> error_answer -> UNDERSTANDING ERROR TO CLOUD -> processCloudLLM

관찰 로그:

UNDERSTANDING ERROR TO CLOUD
REQ size
send json
API Response

예제 3: Cloud Case 2 재진입

발화: Please show me the air quality map history
기대 흐름:
  Cloud Case 2 또는 로컬 dictionary
  rewrite_query가 있으면 processLLAMA로 재진입
  <sk_29>(get=True)

10. 실습

10. 실습

운영 예제:

상황 먼저 볼 로그 1차 판정
로컬 이해 실패 후 Cloud로 넘어감 UNDERSTANDING ERROR TO CLOUD 로컬 딕셔너리/LLM 미매칭 또는 기능 분기 누락
Cloud가 Case 2를 내려줌 rewrite_query Cloud는 기능 의도로 봄. 온디바이스 재진입 성공 여부 확인 필요
토큰은 맞는데 에러 TTS ApiCallValidator, errorcode 기기 상태/에러 핸들러 차단
TTS만 이상함 ttsText, string resource 리소스/문구 문제

아래 발화는 기대 경로를 먼저 정의한 뒤 실제 로그와 비교합니다.

발화 기대 토큰/경로
공기질 맵 히스토리 보여줘 <sk_29>(get=True)
대기화면 상세로 바꿔줘 <sk_39>(theme=1)
보안모드 켜줘 <sk_46>
고정 청정 스케줄 등록해줘 <sk_48>(action=1)
등록된 스케줄 보여줘 <sk_48>(get=True)
오늘 날씨 어때 날씨 기능 또는 Cloud/날씨 경로

11. 정리 과제

11. 정리 과제

아래 항목을 정리합니다.

1. 전체 E2E 단계 요약
2. On-device와 Cloud LLM 책임 차이
3. `<sk_xx>` 토큰의 역할
4. 상태관리 변수 5개 설명
5. 같은 증상이더라도 원인이 다를 수 있는 예시 3개

12. 점검 질문

12. 점검 질문

1. Cloud LLM이 제품 기능을 직접 실행하지 않는다는 말의 의미는 무엇인가?
2. STT raw와 filteredSttResult는 왜 다를 수 있는가?
3. `<sk_xx>` 토큰이 맞는데도 기능이 실행되지 않을 수 있는 이유는 무엇인가?
4. Cloud Case 2가 성공하기 위한 온디바이스 조건은 무엇인가?
5. 멀티턴과 Cloud continue는 어떤 차이가 있는가?

Keyboard shortcuts

⌘K / Ctrl+KOpen command palette
/Focus search
g hGo to home
g pGo to projects
g sGo to sessions
j / kNext / prev row (tables)
?Show this help
EscClose dialogs

Structured queries

Mix key:value filters with free text in the palette:

type:sessionOnly session pages
project:llm-wikiFilter by project name (substring)
model:claudeFilter by model name (substring)
date:>2026-03-01Sessions after a date
date:<2026-04-01Sessions before a date
tags:rustPages mentioning a tag/topic
sort:dateSort results by date (newest first)

Example: type:session project:llm-wiki date:>2026-04 sort:date