snaps와 ohprintme로 분기되어 살아온 두 챗봇을 단일 코드베이스로 통합하고, LangGraph 1.x로 업그레이드, Lambda에서 ECS Fargate로 런타임을 옮긴 4주 리라이트 기록
계획부터 배포까지 — 각 주차의 핵심 결과만
스냅스와 오프린트미는 같은 회사의 두 브랜드지만, 챗봇 코드는 git 브랜치로 갈라져 각자 살아왔어. 같은 함수의 두 사본이 양쪽에서 따로 진화했고, 한쪽에 추가된 기능은 손으로 옮겨 붙여야 했어.
실행 시 BRAND 환경변수에 따라 어댑터를 선택. 비즈니스 로직은 모두 공유, 브랜드 차이는 어댑터에만 격리
코드, 에이전트, 런타임 — 각 층마다 단일 진입점을 두고 분기는 가장자리로 밀어냈어
Protocol 인터페이스 + per-brand 구현. 호출자는 브랜드를 모름. current_brand().order.cancel_order(...)
LLM 라우터가 4명의 워커(order/support/document/common) 중 하나를 골라 위임. 각 워커는 자기 도메인 도구만 들고 있어
Lambda와 FastAPI 두 런타임이 handler.process_event 단일 함수를 공유. 환경 차이는 진입점만
Protocol(Python의 구조적 서브타이핑)로 인터페이스를 정의하고, 브랜드별 클래스가 이를 구현. 호출자는 어떤 어댑터인지 모르고 어댑터의 메서드만 알면 됨
LLM이 라우터 역할을 맡아 사용자 질의를 4명의 도메인 워커 중 한 명에게 위임. 각 워커는 자신의 도구로 응답 생성, 마지막에 Zendesk 노드가 메시지를 사용자에게 전달
각자 도메인 prompt + 도구 세트만 가짐. 라우터는 Pydantic 구조화 출력으로 다음 노드 이름을 반환
Lambda와 FastAPI 두 런타임을 동시에 운영하면서도 코드 분기는 없도록 — 진입점 두 개가 같은 process_event() 함수를 호출
새로 추가된 모듈은 초록색, 의미가 바뀐 파일은 금색
prd 트레이스를 카테고리별로 stratified-샘플링해 Langfuse Dataset에 저장. 같은 데이터셋으로 v1과 v2를 동일 평가자(LLM judge)로 채점해 회귀 여부를 봐
코드 양과 구조의 변화 — 단순한 LOC 감소가 아니라 "한 곳에 한 번만 적힌" 코드의 비율이 늘어난 게 핵심