[Academy] Challenge 3 기술 회고

Last updated on

[Academy] Challenge 3 기술 회고


Academy Challenge 3를 돌아보며 진행하는 기술 회고

우리 팀의 이번 아이템의 시작은 “전화 형식의 회고 어시스턴트 앱”이었다.
본래 아이디어의 출발은 다음과 같았다.

회고를 어려워하는 사람들이 친구랑 대화하는 느낌으로 회고를 하면 어떨까?
그냥 자연스럽게 하루의 일을 썰 풀 듯이 풀어내면 회고를 알아서 해준다면 어떨까?

사실 어쩌면, 내가 가장 쓰고 싶은 서비스이기도 했다.
본래 회고를 잘 안하는 사람이지만, 친구랑 통화하는 것은 좋아해서, 보통 친구들에게 오늘의 일, 프로젝트에 대한 얘기를 자주 나누곤 했었기 때문이다.

이 아이디어를 구현하기 위해 우리는 Apple의 Foundation Models와, 회고 결과를 더 풍부하게 확장할 수 있는 음성 출력 가능성까지 염두에 둔 Piper 기반 TTS 학습이라는 두 축을 중심으로 실험을 진행했다.

이번 글에서는 단순히 “어떤 기술을 썼다”보다,
실제로 프로젝트 안에서 해당 기술을 왜 선택했고, 어떻게 붙였고, 어디서 막혔고, 무엇을 배웠는지를 회고하려고 한다.

1. Foundation Models을 선택한 이유

이번 프로젝트의 핵심 UX는 “회고를 쓰게 하는 것”이 아니라, “회고를 말하게 하는 것”이었다.

즉 사용자는 질문에 답하듯 자연스럽게 대화를 이어가고, 앱은 그 대화를 읽고 다음 질문을 던지며, 최종적으로는 회고 리포트까지 만들어야 했다.

이 지점에서 필요한 것은 단순한 텍스트 생성기가 아니라 다음 역할을 함께 수행할 수 있는 모델이었다.

  • 현재 사용자 발화를 이해하는 능력
  • 직전 맥락과 이어지는 자연스러운 질문 생성 능력
  • 회고 결과를 요약하고 정제하는 능력
  • 앱 내부에서 안전하게 동작할 수 있는 온디바이스 특성

이 조건에서 우리 팀은 Apple의 Foundation Models 를 선택했다.

프로젝트 안에서는 이 모델을 단순 챗봇처럼 사용하지 않았다.
실제로는 다음과 같은 생성 계층으로 사용했다.

  • 회고 채팅 단계의 다음 질문 생성
  • 4L 1차 분류 결과의 2차 검토
  • 리포트 문장 정제
  • 요약 생성
  • 주간 / 월간 인사이트 생성

코드 기준으로 보면 이 역할은 주로 다음 서비스들에 분산되어 있다.

  • ReflectionTurnGenerator
    • 사용자의 발화, 1차 4L 분류 결과, 현재 세션 문맥, 최근 질문 히스토리를 통해 “회고 채팅의 핵심 생성 서비스”
    • 2차 검토 결과 + 다음 질문을 함께 생성
    • Warm-Up 구조로 첫 응답 지연 감소
  • FoundationModelService
    • prompt 문자열을 받아 응답 문자열을 반환하는 공통 진입점 역할
    • Foundation Models를 공통적으로 호출하기 위한 가장 얇은 Wrapper 서비스
  • ReflectionInsightService
    • 감정 분석 결과 & 회고 기록을 바탕으로 사용자의 반복 패턴 인사이트를 만드는 서비스
    • 주간 / 월간 단위 Reflection Point & Strength Point를 뽑아냄
  • ReflectionFoundationModelService
    • 오늘의 회고 Report 전용 Foundation Models 묶음 서비스
    • 하나의 범용 세션이 아니라 목적(회고 정제, 오늘의 요약, 핵심 키워드 등)별 세션을 따로 두고 생성
  • ReflectionSummaryService
    • 리포트 생성 오케스트레이션 서비스
    • 채팅 메시지, 4L 분류 결과, 정제된 문장들을 입력으로 받아 다음을 조합
      • refined reflection
      • today summary
      • core keywords
      • emotion keywords
      • action items

즉 Foundation Models를 단순한 “대답하는 AI”의 형태가 아닌 대화의 흐름을 유지하고 결과를 문장화하는 생성 엔진에 가깝게 구현했다.

2. 실제 회고 파이프라인에서 Foundation Models를 어떻게 사용했는가

현재 회고 채팅 흐름은 대략 다음 순서로 구성되어 있다.

  1. 사용자 발화 입력
  2. 4L 분류 모델을 통한 1차 4L 분류
  3. 현재 세션 메모리 기반 문맥 검색
  4. Foundation Models를 통한 2차 검토 및 질문 생성
  5. 세션 메모리 저장
  6. 종료 가능 상태 평가
  7. 리포트 단계에서 정제 및 요약 생성

이 구조의 핵심은 Foundation Models에게 모든 판단을 맡기지 않았다는 점이었다.

우리는 먼저 FourLClassifier와 규칙 기반 상태 로직으로 회고를 1차적으로 구조화하고,
그 다음 Foundation Models이 그 결과를 자연어 수준에서 다듬도록 설계했다.

이렇게 한 이유는 명확했다.

  • Foundation Models와 같은 생성형 모델은 자연스러운 문장을 잘 생성
    • 하지만 항상 일관된 분류나 종료 판단을 하지는 않음
  • 따라서 “판단”과 “표현”을 분리하는 편이 더 안정적

실제로 ReflectionTurnGenerator는 세션을 유지한 채 다음 역할을 수행한다.

  • 현재 발화 재검토
  • 검토 결과 구조화
  • 현재 세션 문맥을 반영한 한 문장 질문 생성

그리고 여기에 다음 제약을 명시적으로 넣었다.

  • 질문은 과거 전체 맥락이 아니라 현재 세션에만 이어질 것
  • 최근 질문을 반복하지 않을 것
  • 회고 종료 상태에 따라 질문 톤을 다르게 할 것

이 부분은 나중에 반복 질문 문제를 줄이는 데 중요한 기반이 되었다.

3. Foundation Models를 붙이면서 겪은 실제 문제들

3-1. 첫 응답이 느린 문제

온디바이스 모델의 첫 호출은 체감상 약 5 ~ 6초 이상으로 느껴질 정도로 무거웠다.
질문 - 응답형 서비스의 실사용에서는 UX가 안좋다고 판단될 정도로 충분히 체감되는 딜레이였다.

그래서 우리는 채팅 화면 진입 시 warm-up을 먼저 수행하는 구조를 넣었다.

  • 실사용 세션과 분리된 임시 LanguageModelSession 생성 : 실제 회고 대화 컨텍스트에 영향을 주지 않기 위함
  • 매우 짧은 준비 확인용 프롬프트 호출
  • 이후 실제 회고 세션에서는 준비된 상태로 응답 시작

코드는 아래와 같다.

func warmUpIfNeeded() async {
  	// Warm-Up이 이미 되어있을 때
    guard !hasWarmedUp else {
        return
    }
	// Warm-Up을 못하는 경우(iOS Version Issue로 인한)
    guard #available(iOS 26.0, *), FoundationModelsAvailability.isAvailable else {
        return
    }
	// Warm-Up 진행
    do {
        let warmUpSession = LanguageModelSession(
            instructions: "간단한 준비 확인만 수행한다."
        )
        _ = try await warmUpSession.respond(
            to: "준비가 되었으면 ok 한 단어로만 답해."
        )
      	// Warm-Up 완료, 상태 변경
        hasWarmedUp = true
    } catch {
        print("[RAG][FoundationModel] warm-up failed: \(error.localizedDescription)")
    }
}

ㅈ 이 방식은 아주 극적인 개선이라기보다, 첫 응답의 체감 latency를 줄이는 데 의미가 있었다.

3-2. 같은 질문을 반복하는 문제

초기 버전에서 가장 자주 보인 문제는 모델이 계속 비슷한 질문을 반복하는 현상이었다.

원인을 뜯어보면 모델 자체 문제라기보다 파이프라인 문제에 가까웠다.

  • 이전에 어떤 질문을 했는지 충분히 반영하지 못함
  • fallback 질문이 차원별 고정 템플릿에 가까웠음
  • 매 턴마다 비슷한 문맥을 다시 주입하고 있었음

그래서 우리는 다음과 같은 보정을 넣었다.

  • 최근 질문 히스토리 전달
  • 프롬프트 안에 “반복 금지” 규칙 추가
  • 질문 유효성 검증
  • fallback 질문 다변화

이후부터는 모델이 생성한 질문을 그대로 쓰기보다, 앱이 “질문으로 쓸 만한가”를 한 번 더 점검하는 구조가 되었다.

이 과정에서 배운 점은 명확했다.

하나의 서비스에서 생성형 AI를 도입할 때는, 좋은 프롬프트를 작성하는 것 뿐 아니라, 모델이 실수했을 때 앱이 어떻게 보정할 것인지까지 파이프라인을 설계하는 것의 문제라는 걸.

3-3. 모델 응답이 JSON처럼 오지만 실제로는 저장되지 않는 문제

인사이트 생성 단계에서는 Foundation Models가 JSON 형태의 후보를 반환하도록 설계했다.
처음에는 로그에 JSON이 잘 찍히길래 “이제 잘 되는구나”라고 생각했지만, 실제로는 저장 결과가 0개인 경우가 있었다.

이유는 모델 응답이 잘못된 것이 아니라, 앱 쪽 필터 조건과 충돌했기 때문이었다.

예를 들어 모델은 다음처럼 응답할 수 있다.

  • 프로젝트 어려움 count = 4
  • 공부 습관 count = 2
    • 여기서 count는, 단순한 단어 빈도가 아닌 “같은 주제가 서로 다른 회고 몇 개에서 반복되었는지”를 의미

문제는 앱 로직이 minimumRepeatCount = 3 기준으로 동작하고 있었다는 점이다.
즉, 모델이 보기에는 의미 있는 후보여도, count = 2라면 앱 저장 단계에서 최종 탈락하게 된다.

이 문제는 단순히 저장 조건을 완화하는 방식으로 풀어서는 안되었다.
중요하지 않은 내용이 나올 수도 있으니.

대신, 모델과 앱이 처음부터 같은 기준을 보도록 구조를 정리하였다.

  • 후보 추출 프롬프트에서, minimumRepeatCount 이상 반복된 주제만 뽑도록 명시
  • 최종 인사이트 생성 단계에서도 동일한 기준 유지
  • 앱 파싱 단계에서 count ?= minimumRepeatCount 조건 재검증
  • 후보 추출과 최종 문장 생성을 분리하여 기준 불일치 최소화

결과적으로 해당 문제는, “모델이 JSON을 잘 만들었느냐”의 문제가 아니라, 생성형 출력과 앱 비즈니스 로직 사이의 기준 정렬 문제였다고 할 수 있다.

4. Foundation Models를 쓰며 남은 판단

이번 프로젝트에서 Foundation Models는 분명 강력했다.
질문 생성과 문장 정제, 인사이트 표현 같은 부분에서는 규칙 기반 시스템만으로 만들기 어려운 자연스러움을 만들어줬다.

하지만 동시에 너무나도 한계가 명확하게 드러났다.

  • context window 제약
  • 구조화 출력 실패 가능성
  • 반복 질문 가능성
  • 종료 판단의 불안정성

결국 중요한 건 “모델을 얼마나 똑똑하게 쓰느냐”보다, 모델이 잘하는 작업과 앱이 책임져야 하는 일을 구분하는 파이프라인 설계였다.

우리 프로젝트는 그 구조를 다음처럼 정리하게 되었다.

  • Core ML / 규칙 기반 계층: 1차 분류, 상태 판단
  • Foundation Models 계층: 질문 생성, 문장 정제, 인사이트 생성
  • 앱 로직 계층: 저장, 종료 흐름, fallback, 정책 적용

5. Piper 학습을 왜 함께 보게 되었는가

다시 처음으로 돌아와서, 이번엔 Piper 학습과 관련된 이야기이다.

우리 서비스의 출발점은 “전화 형식 회고”였기 때문에, 텍스트 기반 회고 정리만으로는 끝나지 않았다.

사용자가 정말 “누군가와 통화하듯” 회고를 한다면, 결국 출력도 다시 음성으로 돌아가야 했다.

그 지점에서 우리가 탐색한 기술이 Piper였다.

Piper는 로컬 오프라인 TTS를 위한 음성 합성 스택이고, 현재 우리가 다룬 모델은 한국어 단일 화자 기반 Piper 모델이었다.

기술적으로 보면 이 모델은 다음 성격을 가진다.

  • Piper 학습 스택 기반
    • Piper는 경량 TTS를 On-Device와 같은 저사양 환경에서 안정적으로 구동할 수 있는 음성 학습 스택
    • 좋은 음질 뿐 아니라 “실제 앱에서 구동이 가능한 단위”의 엔진이 필요
  • VITS 계열 학습 파이프라인 사용
    • VITS는 텍스트에서 음성을 직접 생성하는 종단간 계열 접근
    • 전통적인 다단계 TTS보다 복잡한 구성을 갖지만, 자연스러운 발화와 운율 표현에서 강점을 가지는 모델
    • VITS를 기반으로 하는 Piper 엔진은 한국어도 지원
  • eSpeak phonemizer 사용
    • 텍스트를 바로 음성으로 바꾸는 것이 아닌, 발음 가능한 음운 단위로 바꾸는 Phonemizer단계가 필요
    • 한국어처럼 표기와 실제 발화가 완전히 일치하지 않거나, 조사 결합, 받침, 숫자, 외래어 등으로 인해 발음 처리가 달라질 경우 전체 TTS에 영향을 주기 때문
  • ONNX export 기반 추론 구조
    • ONNX는 특정 학습 프레임워크에 종속되지 않고 모델을 배포 가능한 형태로 옮길 수 있게 해주는 중간 포맷 역할
    • 다시 말해, 학습 환경과 실제 운영 환경을 분리할 수 있다는 장점이 있음
  • 22.05kHz 한국어 단일 화자 모델
    • 샘플레이트의 경우, 음성 품질을 지키면서 On-Device에서 동작이 가능한 수준의 스트레스가 필요
    • 때문에 22.05kHz로 절충

이러한 기술을 바탕으로 우리는 오디오 데이터셋을 만들고 정제하고 export해서 실제 런타임에서 재생 가능한 음성 모델로 완성하는 전체 파이프라인을 구성하였다.

6. Piper에서 진짜 중요했던 것은 모델보다 데이터

Piper 쪽에서 가장 크게 배운 점은
“좋은 모델을 고르는 것”보다 “좋은 데이터를 만드는 것”이 훨씬 중요하다는 점이었다.

현재 PoC를 위한 원본 데이터는 유튜브/방송 계열의 단일 화자 음성이었다.
일반적으로 우리는 전문적인 녹음 장비와 수음실 / 방음실의 환경에서 녹음하는 것이 아닌 상황이었기에 최종적으로 PoC 테스트를 위해 유튜브 방송을 선택하였다.

이 말은 곧, 겉보기엔 깔끔해 보여도 실제로는 다음 요소가 섞여 있을 수 있다는 뜻이다.

  • 압축 질감
  • 저역 울림
  • 잔향
  • 마이크 특성
  • 방송용 후처리

즉 모델은 사람의 목소리만 배우는 것이 아니라, 해당 음향의 주변 환경까지 함께 학습할 가능성이 있다.

이 때문에 우리는 긴 오디오를 그대로 쓰지 않고 다음 흐름으로 데이터셋을 만들었다.

  1. 긴 원본 오디오 준비
  2. Whisper timestamp 기반 발화 단위 분할
  3. 각 클립별 전사 생성
  4. metadata.csv 생성

기본 데이터셋 규모는 1466개 클립이었다.

하지만 이 상태를 바로 학습에 넣는 건 위험했다.
실제로 전사 유사도만으로는 걸러지지 않는 데이터가 꽤 많았다.

  • 깨진 문자
  • 한국어 외 잡문
  • 지나치게 짧은 감탄사
  • 길이는 짧은데 score만 높은 파일
  • Whisper와 길이 차이가 큰 파일

그래서 단순 score 기반 필터링 대신, 다음의 선별 알고리즘들을 이용하여 정제를 수행했다. hangul_ratio, mismatch_ratio, suspicious_char_count, purity_score 같은 기준을 추가해 더 강한 정제를 수행했다.

6.1. hangul_ratio

텍스트 안에서 한글이 차지하는 비율을 확인하기 위 지표이다.
해당 값을 넣은 이유는, 한국어 모델을 학습할 때 다음과 같은 파일을 걸러내기 위함이다.

  • 영어 조각이 과하게 섞인 파일
  • 깨진 문자나 이상한 텍스트가 섞인 파일
  • 한국어 모델 학습에 적합하지 않은 잡문이 포함된 파일

다시 정리하면, 얼마나 한국어 중심으로 이루어져 있는 가를 보는 간단한 지표이다.

# 한글, 영문, 숫자를 각각 셈
hangul_count = len(HANGUL_RE.findall(reference_chars))
latin_count = len(LATIN_RE.findall(reference_chars))
digit_count = len(DIGIT_RE.findall(referece_chars))

# 전체 문자군 대비 한글 비율을 계산 -> 값이 낮을수록 한국어 외의 요소가 많이 섞였다고 해석
alnum_count = max(1, hangul_count + latin_count + digit_count)
hangul_ratio = hangul_count / alnum_count

6.2. mismatch_ratio

기존 전사와 Whisper 재전사 길이가 얼마나 어긋나는지 보는 값이다.
해당 지표를 넣은 이유는 다음과 같다.

  • 기존 txt 전사가 실제 음성과 많이 다를 수 있음
  • Whisper가 다시 들었을 때 전혀 다른 길이의 문장이 나온다면 다음의 가능성이 커짐
    • 전사 불일치
    • 분할 경계 문제
    • 음성이 너무 불안정한 샘플

다시 말해, 이 값은 “기존 전사와 재전사 간의 어긋남 정도”를 보는 간단한 길이 기반 mismatch 지표다.

# 기존 전사와 Whisper 전사의 글자 수 차이를 구함
# 더 긴 쪽 길이로 나눠서 0 ~ 1 사이 비율처럼 정규화
# 값이 클수록 두 텍스트가 많이 어긋나 있다고 판단
mismatch_ratio = abs(len(reference_chars) - len(whisper_chars)) / max(1, max(len(reference_chars), len(whisper_chars)))
6.3. suspicious_char_count

음성 데이터에서, 허용하지 않은 이상 문자나 깨진 문자 개수를 센 값을 측저한다.
해당 지표를 넣은 이유는, 실제 데이터는 다음과 같은 샘플이 있었기 때문이다.

  • 깨진 인코딩 문자
  • 비정상 기호
  • 한국어 / 영문 / 숫자 / 기본 문장부호 외의 이 문자

위와 같은 이상 데이터를 제거하기 위해 다음을 추가했다.

suspicious_count = 0

for char in reference_text:
  if char.isspace():
    continue

  # 미리 허용한 문자셋에 들어가지 않는 문자를 세서
  # count != 0 이라면 오염된 문자로 판단
  if not ALLOWED_SYMBOL_RE.fullmatch(char):
    suspicious_count += 1
6.4. purity_score

위의 지표들을 포함하여, 해당 샘플이 최종적으로 얼마나 순수성을 띄며, 안정적인지를 종합적으로 판단하는 최종 점수이다.
전사 유사도 + 한국어 비율 + mismatch + 이상 문자 + 길이 정보 를 함께 반영하여 점수를 산출했고, 이는 다음과 같이 정리하였다.

# score로 시작하여, "학습용 위험 조건이 있을 경우" 감점하는 방식
purity = score

# 너무 짧거나 너무 긴 클립
if duration_seconds < preferred_min_seconds:
    purity -= min(20.0, (preferred_min_seconds - duration_seconds) * 10.0)
elif duration_seconds > preferred_max_seconds:
    purity -= min(12.0, (duration_seconds - preferred_max_seconds) * 4.0)

# 단어 수가 적은 경우
if token_count <= 1:
    purity -= 15.0
elif token_count == 2:
    purity -= 6.0

# 텍스트 길이가 너무 짧은 경우
if text_length < 6:
    purity -= 10.0
elif text_length < 10:
    purity -= 4.0

# 한글 비율이 낮은 경우
if hangul_ratio < 0.9:
    purity -= (0.9 - hangul_ratio) * 30.0

purity -= mismatch_ratio * 40.0
purity -= suspicious_char_count * 8.0

즉, 샘플을 정말 학습에 남겨도 되는가를 보수적으로 판단하기 위한 점수라고 보면 된다.

6.5. Result

최종 strict filtering에서는 아래 조건을 함께 사용했다.

  • score >= 80
  • purity_score >= 88
  • hangul_ratio >= 0.7
  • mismatch_ratio <= 0.25
  • suspicious_char_count == 0

즉 어떤 파일을 keep할 때:

  • 전사 유사도만 높은 것이 아니라
  • 한국어 중심이고
  • Whisper와 과하게 어긋나지 않고
  • 이상 문자가 없고
  • 학습용으로도 비교적 안정적이어야 한다

는 기준을 적용한 셈이다.

이로 인해 우리는 결과적으로:

  • 원본 1466개
  • 최종 keep 1031개
  • remove 435개

정도로 줄이고, 데이터의 품질을 높일 수 있었다.

7. Piper 및 전화형 회고를 도입하지 않은 이유

결론적으로 우리 프로젝트는, 기존에 바라보던 방향성에서 완전히 틀고 채팅형 UX를 도입하였다.

그에는 몇 가지 현실적인 이유가 있었다.

7.1. 학습 품질의 문제

사실상 UX를 바꾸게 된 결정적인 계기였다.

생각보다 Piper 학습은 그렇게 좋은 결과를 내지 못했고, 거기에는 여러가지 부가적인 이유가 있었다.

  • 원본 데이터(WAV)가 아닌 유튜브를 통해 압축된 형태의 음성 파일(mp3, AAC / M4A)
    • 압축 노이즈와 비트레이트 손실로 인여 실질적으로 원본 주파수가 아닌 형태의 음성으로 학습
  • 원본 데이터의 주변 노이즈로 인한 학습 데이터의 낮은 무결성
    • 최대한 Whisper와 기타 다른 무결성 검사 로직을 추가하여 최대한 정제된 데이터를 뽑아 냈음에도
    • 어쩔 수 없이 원본 녹음 과정에서 생기는 노이즈 및 기타 다른 환경적 요인들로 인해 무결성이 낮은 데이터로 학습

해당 원인들은 결국 좋은 품질의 음성 복제 모델로 이어지지 않았고, 다른 방법을 찾아야 했다.

7.2. 하드웨어와 시간의 한계

Piper모델은 꽤나 높은 하드웨어 환경을 요구하였다.

현재 필자가 사용하고 있는 M5 MacBook Pro(24GB) 모델로도 학습이 어려워, 하드웨어를 빌려서 활용해야했다.

  • 문제 상황
    • Epoch 10번 돌파 시 메모리 부족으로 인한 Interrupt 현상 발생
    • 10번 까지도 약 2~3시간의 학습 시간 소요
  • 해결 방안
    • 하드웨어 업그레이드를 통한 메모리 부족 문제 해결

하지만, 여기서도 문제가 발생한다.

쓸만한 결과를 얻으려면 약 100번의 반복 학습이 필요했는데, 한번 학습을 마치고 나면 최소 반나절(12시간) 이상이 물리적으로 필요했다.

짧은 챌린지 기간에, 지속적으로 학습을 위해서 데이터를 정제하고 학습을 진행하는 것이 너무 비용적인 측면에서 비효율적이라는 판단이 들어, 팀원들과의 소통 후에 UX를 변경하기로 확정하였다.

필자에게는 개인적으로 아쉬웠지만, 프로젝트의 다른 Feature에 대한 완성도도 중요했던만큼 빠르게 피벗하고 개발을 진행했다.

8. 정리하며

이번 프로젝트에서 흥미로웠던 점은 Foundation ModelPiper가 둘 다 AI 기술이지만, 실제로 마주한 문제의 종류는 완전히 달랐다는 점이다.

Foundation Models에서 얻은 것들은 다음과 같았다.

  • 프롬프트 설계
  • 상태 흐름 설계
  • context window 관리
  • fallback 설계
  • 응답 안정성 확보

반면 Piper를 통해 얻은 것들은 다음과 같았다.

  • 데이터셋 구축
  • 전사 품질 검수
  • 음성 순수성 필터링
  • export 산출물 정합성
  • 학습용 데이터의 품질 관리

즉 하나는 모델과 대화하는 시스템 설계의 문제,
다른 하나는 모델이 배울 재료를 어떻게 만들 것인가의 문제였다.

이 두 경험을 함께 하면서 느낀 건, 종합적으로 이 정도로 정리가 가능할 것 같다.

  • 모델의 선택과 활용
  • 데이터의 입력 구조 설계
  • 실패 시 조건 별 fallback
  • 저장 전략
  • 데이터 품질 관리
  • 사용자 경험과 연결되는 출력 형식 설계

우리 팀의 회고 앱은 그 모든 걸 짧은 Challenge 기간 안에서 직접 부딪혀보며 정리해본 사례에 가까웠다.

Foundation Models를 붙이며 얻은 교훈은 시스템 설계의 중요성이었고,
Piper를 학습하며 얻은 교훈은 모델 학습은 사실상 데이터가 다 한다 정도로 정리할 수 있었다.

그리고 이 두 경험 모두, 앞으로 우리 팀이 다시 AI 기능을 만들 때 더 좋은 출발점이 되어줄 것 같다.

Special Thanks To
Arya, Chaem, Cindy, Steve