← Docs hub

Day 2: 온디바이스 딕셔너리 및 E2E Test

1. 학습 목표

1. 학습 목표

Day 2는 온디바이스 딕셔너리 구조와 E2E 테스트 방법을 소스레벨로 정리합니다. LLM 운영 담당자는 딕셔너리를 “발화를 많이 넣는 파일”로 이해하면 안 됩니다. 딕셔너리는 STT 오인식, 기능명, 동작어, 상태조회, 설정 요청, 부정 케이스가 서로 충돌하지 않도록 설계해야 하는 라우팅 규칙입니다.

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

참조:

2. 본문 개요

2. 본문 개요

온디바이스 딕셔너리는 다음 관점에서 이해해야 합니다.
딕셔너리는 단순한 키워드 목록이 아닙니다.
잘못 설계하면 "오늘 날씨 알려줘"가 스케줄로 잡히거나,
"상태 알려줘"가 설정 명령으로 잡히는 문제가 생깁니다.

딕셔너리 개선의 핵심은 단어를 많이 넣는 것이 아니라,
기능명, 대상, 동작어, 상태조회 표현을 안전하게 조합하는 것입니다.

3. 딕셔너리 처리 순서

딕셔너리 처리 순서

ForegroundService.processLLAMA() 기준 실제 순서는 아래입니다.

1. STT 결과 trim
2. multiTurnBusy면 이전 fragment와 현재 STT 결합
3. `.`, `,`, `!`, `?` 제거
4. removePrefixIfExists()
5. CommandContainsFinder.replaceWithKey()
6. ThreeKeywordContainsTokenMatcher.match()
7. PreDefinedCommandMatcher.match()
8. CommandContainsMatcher.match()
9. 실패 시 mQaClient.Infer(prompt)

핵심:

3keyword가 가장 먼저 적용됩니다.
mapping exact match가 그 다음입니다.
contains_token은 그 다음입니다.
모두 실패하면 로컬 LLM 추론으로 넘어갑니다.

4. 파일별 역할

4. 파일별 역할

파일 처리기 용도 리스크
substitution_dict.json STTSubstitutor STT 오인식 보정 너무 넓은 치환 시 정상 문장 훼손
contains.json CommandContainsFinder 문장 내 phrase를 key로 치환 이후 matcher 입력 전체 왜곡 가능
3keyword_contains_token.json ThreeKeywordContainsTokenMatcher 3개 이상 조건으로 강한 매칭 조건이 너무 엄격하면 미매칭
mapping.json PreDefinedCommandMatcher 정확 발화 exact match 유사 발화 커버 부족
contains_token.json CommandContainsMatcher 그룹 조건 조합 매칭 일반 단어가 들어가면 오매칭

5. 정규화 방식

5. 정규화 방식

딕셔너리 비교는 DictionaryNormalizer.normalizeForDictionaryComparison()를 사용합니다.

입력 문자열
  -> lowercase
  -> 문자/숫자만 유지
  -> 공백/구두점/특수문자 제거

예:

"How many schedules are set?"
  -> "howmanyschedulesareset"

"공기질 맵 히스토리 보여줘."
  -> "공기질맵히스토리보여줘"

주의:

정규화는 매칭 안정성을 높이지만, 너무 짧은 keyword를 넣으면 충돌이 커집니다.
예를 들어 "show", "tell me", "알려줘", "상태" 같은 표현은 단독 조건으로 위험합니다.

6. STT 문제와 딕셔너리 문제 구분

오매칭 분석 경로

반드시 아래 네 값을 분리합니다.

1. 발화 원문
2. STT raw text 또는 originalSttResult
3. STT processed / filteredSttResult
4. parsedLlamaResponse

판단표:

관찰 원인 후보 수정 위치
발화와 STT raw가 다름 STT/음성 전처리/환경 STT 모델, 전처리, 제한적 substitution
raw는 맞고 processed가 다름 후처리/치환 substitution_dict.json, 전처리 코드
processed는 맞고 토큰이 다름 딕셔너리/로컬 LLM dictionary JSON, matcher 조건
토큰은 맞고 TTS/API가 다름 기능 구현 FunctionCallHandler, strings, Device API

6-1. STT 오인식 보정 기준

6-1. STT 오인식 보정 기준

STT 오인식 보정은 “들리는 대로 나온 모든 결과를 dictionary로 덮는 작업”이 아닙니다. 보정은 반복 재현되고, 기능 라우팅에 직접 영향을 주며, 정상 발화를 훼손하지 않는 경우에만 적용합니다.

보정 후보 적용 가능성 판단 기준
제품 기능명 오인식 높음 같은 언어/같은 발화에서 반복되고 특정 토큰 실패를 유발
공간명 오인식 조건부 실제 등록 공간명과 충돌하지 않아야 함
지역명/건물명 오인식 조건부 말레이시아 현지 지명/건물명 목록과 함께 검증 필요
일반 문장 일부 오인식 낮음 다른 기능으로 오매칭될 위험이 큼
숫자+단위 오인식 제한적 알람/거리/시간 등 제품 미지원 영역이면 기능 라우팅에 넣지 않음

적용 전 확인:

1. 발화 원문과 STT raw를 3회 이상 비교
2. processed 단계에서 이미 변형되는지 확인
3. dictionary 수정 없이 Cloud로 가면 어떤 Case가 나오는지 확인
4. 수정 후보 phrase가 다른 기능의 positive case와 충돌하는지 확인
5. 한국어/영어/말레이시아 현지 표현을 분리해 테스트

6-2. 지역명/건물명/공간명 처리 기준

6-2. 지역명/건물명/공간명 처리 기준

말레이시아 현지 운영팀이 테스트할 때는 지역명, 건물명, 공간명이 제품 기능명처럼 오인식되거나 dictionary 조건에 섞일 수 있습니다. 이 값들은 아래처럼 분리해서 관리합니다.

분류 처리 기준
지역명 Kuala Lumpur, Selangor, Johor Bahru Cloud/RAG 또는 날씨 위치 문맥에서 사용. 제품 기능 토큰 조건으로 직접 사용하지 않음
건물명 mall, office, apartment, tower 일반 질의/주소 문맥으로 취급. 청정 공간명과 직접 매칭하지 않음
제품 공간명 living room, bedroom, kitchen, master room Device API의 등록 공간 목록과 대조 후 기능 실행
사용자 별칭 공간명 grandma room, kids room, study room 실제 getAvailableCleaningPosition() 또는 공간 목록에 있을 때만 실행 후보
사양 외 위치 outside, car park, lobby 제품 공간이 아니면 실행하지 않고 안내 또는 Cloud 일반 답변

공간명 관련 기능은 “문장에 방 이름처럼 보이는 단어가 있다”만으로 실행하면 안 됩니다.

1. dictionary는 공간명 의도를 잡을 수 있음
2. 실제 실행 전에는 등록 공간 목록 또는 청정 가능 공간 목록을 확인해야 함
3. 매칭 실패 시 임의 공간으로 대체 실행하지 않음
4. 말레이시아 현지 공간명 별칭은 테스트 데이터로 별도 관리

7. 대표 오매칭 케이스 분석

7. 대표 오매칭 케이스 분석

날씨가 스케줄로 매칭되는 경우

발화: 오늘 날씨 알려줄래?
기대: 날씨 경로
실제: <sk_48>(action=4)

분석:

1. `[DICTIONARY_MATCH]` 로그 확인
2. 스케줄 dictionary에 `알려줘`, `tell me`, `schedule` 같은 넓은 표현이 있는지 확인
3. 날씨 기능명 없이 일반 동작어만으로 스케줄이 잡혔는지 확인
4. 스케줄 positive case와 날씨 negative case를 같이 추가

수정 원칙:

스케줄 등록은 "스케줄/예약" + "청정/고정/전체" + "등록/설정"처럼 기능명과 대상이 함께 있어야 안전합니다.

상태조회가 설정으로 매칭되는 경우

발화: 홈 잠금 상태 알려줘
기대: get=True
실제: action=off

분석:

상태조회 표현과 설정 표현이 같은 group에 들어갔을 가능성이 큽니다.

수정 원칙:

상태조회 group: 상태, 켜져, 꺼져, status, is, are, turned on
설정 group: 켜줘, 꺼줘, turn on, turn off, set to on/off

8. E2E 테스트 설계

딕셔너리 테스트 매트릭스

딕셔너리 수정 하나에는 최소 세 종류 테스트가 필요합니다.

Positive case

청정 스케줄 등록해줘 -> <sk_48>(action=1)
전체 청정 스케줄 등록해줘 -> <sk_48>(action=2)
등록된 스케줄 보여줘 -> <sk_48>(get=True)

Negative case

오늘 날씨 알려줘 -> 스케줄로 잡히면 안 됨
세이프케어 이용료는 얼마야 -> 스케줄로 잡히면 안 됨
what is pm 10 -> 2.0/스케줄로 잡히면 안 됨

Regression case

공기질 맵 히스토리 보여줘
홈 잠금 상태 알려줘
공간이 몇 개 있어
Tell me about the all time mode

8-1. E2E 성능 측정 및 결과 분석 기준

8-1. E2E 성능 측정 및 결과 분석 기준

딕셔너리 품질은 “몇 개 발화가 통과했는가”만으로 보지 않습니다. 말레이시아 현지 테스트에서는 아래 지표를 같이 기록합니다.

지표 의미 계산 기준
STT raw 일치율 사용 발화가 텍스트로 제대로 들어왔는지 발화 원문 대비 raw 의미 일치
processed 보존율 후처리가 원문 의도를 훼손하지 않았는지 raw 대비 processed 의미 일치
token 정확도 기대 <sk_xx>와 실제 token 일치 기대 token / 실제 token 비교
오매칭률 다른 기능으로 잘못 들어간 비율 wrong token 발생 건수
미매칭률 기능 발화가 error/fallback으로 빠진 비율 기대 기능인데 token 없음
Cloud fallback률 로컬에서 처리되지 않고 Cloud로 넘어간 비율 UNDERSTANDING ERROR TO CLOUD 발생 건수
TTS 일치율 기능 후 사용자 안내가 사양과 맞는지 play/subtitle 비교

결과 분석 시 우선순위:

1. STT raw가 틀리면 dictionary 성능으로 계산하지 않음
2. raw는 맞고 token이 틀리면 dictionary/로컬 LLM 이슈로 계산
3. token은 맞고 실행이 안 되면 Validator/API/상태 이슈로 분리
4. Cloud fallback은 실패가 아니라 fallback 이후 최종 결과까지 함께 판정

9. logcat 실습

9. logcat 실습

운영 예제:

스케줄 등록해줘
  -> 일반 스케줄 요청
  -> <sk_48>(action=4)
  -> 지원 불가 안내

청정 스케줄 등록해줘
  -> 고정청정 스케줄 요청
  -> <sk_48>(action=1)
  -> 스케줄 등록 플로우

오늘 날씨 알려줘
  -> 스케줄로 잡히면 안 됨
  -> 날씨 기능 또는 Cloud/weather 경로

운영 기준:

새로운 스케줄 dictionary를 추가할 때는 날씨/시간/요금/설명 질문을 negative case로 같이 돌립니다.

추가 운영 기준:

  1. 영어의 show, tell, check 같은 짧은 동사는 기능명 group 없이 단독 매칭 조건으로 사용하지 않습니다.
  2. 상태조회와 설정 요청은 같은 group에 넣지 않습니다.
  3. 일반 스케줄과 청정 스케줄은 대상 단어가 다르므로 schedule 하나로 묶지 않습니다.
  4. 말레이시아 현지 표현을 추가할 때는 한국어/영어 기존 regression 세트를 같이 돌립니다.
  5. 공간명/지역명/건물명은 같은 사전에서 관리하지 않고 목적별로 분리합니다.

필터:

adb logcat | grep -E "DICTIONARY_MATCH|parsedLlamaResponse|Cutted parsedLlamaResponse|LLMResponseParser|FunctionCallHandler|ttsText"

결과 기록:

발화:
STT raw:
STT processed:
매칭 파일:
matched_phrase:
matched_phrases:
기대 토큰:
실제 토큰:
판정:
수정 대상:

10. 기능별 실습 세트

10. 기능별 실습 세트

스케줄

스케줄 등록해줘
청정 스케줄 등록해줘
전체 청정 스케줄 등록해줘
등록된 스케줄 보여줘
How many schedules are set
Register the schedule

판정:

일반 스케줄 요청은 action=4
고정청정은 action=1
전체청정은 action=2
조회는 get=True

공기질 맵

공기질 맵 그려줘
공기질 맵 히스토리 보여줘
Please create the air quality map
Please show me the air quality map history

판정:

생성 요청과 히스토리 조회가 구분되어야 합니다.

공간/맵

공간이 몇 개 있어?
How many spaces are set?
Tell me the space number
Can you confirm how many rooms are in the map?

판정:

맵 없음, 공간 없음, 공간 있음 케이스를 별도 검증합니다.

11. 정리 과제

11. 정리 과제

1. 딕셔너리 파일별 역할 요약
2. 딕셔너리 매칭 순서 도식화
3. 오매칭 케이스 3건 분석표
4. positive/negative/regression 테스트 세트
5. 수정 요청서 초안

12. 점검 질문

12. 점검 질문

1. mapping.json과 contains_token.json의 차이는 무엇인가?
2. 3keyword가 필요한 경우는 언제인가?
3. "알려줘" 같은 단어가 위험한 이유는 무엇인가?
4. STT raw가 틀린 경우 dictionary 수정이 항상 적절하지 않은 이유는 무엇인가?
5. Cloud rewrite_query가 로컬에서 실패하면 어느 쪽을 수정해야 하는가?

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