7. 팀 분업
⚠ 2026-08-28 방향 전환 — 이 페이지보다 결정 기록이 우선한다
도메인을 다산콜센터 하나로 줄이고 외국인 고객 지원을 차별점으로 세웠다
(_project/decisions/201, 비공개). 아래 본문의 금융보험·쇼핑·질병관리본부 서술은
그 시점의 사실이라 지우지 않았다 — 절대 원칙 8(“실패를 지운 기록은 기록이 아니다”)과
같은 이유로, 계획이 바뀐 사실 자체가 기록이다.
이 페이지에서 바뀐 것: 4인 체제 + rev.5 신규 기능 배정. 기획서 7.1절이 rev.5 에서 갱신됐다 — 신규 기능 셋(A-5 통번역 · C-6 콜 가드 · D-5 감정분석)이 전부 ai/(류준)로 갔다. 셋 다 모델을 쓰는데 server/.importlinter 가 server/ 안에서 모델 라이브러리 import 를 막기 때문이다. ⚠ 7.2절이 경고한 부하 집중이 다시 생겼다 — A-5 스파이크 결과를 보고 재분배를 검토한다.
팀명: SOLIDBOB (4인 — 2026-08-26 개편)
원래 3인 체제(정성윤·류준·장민석)에서 플러터(Flutter) 앱 개발을 중단하고, 장민석이 앱·프론트엔드에서 류준과 함께 백엔드·AI로 옮겼다. 조서희가 신규 합류해 프론트엔드(웹)를 전담한다. 정성윤은 AWS·인프라를 그대로 유지한다. 근거·되돌리는 법:
_project/decisions/005-팀-개편-4인-체제.md.
7.1 담당
| 담당 | 인원 | 범위 |
|---|---|---|
| AWS·인프라 | 정성윤 | Node.js WebSocket 게이트웨이, Google STT 연동, 화자 분리, PostgreSQL 스키마, Docker·배포, 레이턴시 측정 인프라, CI 운영 |
백엔드·AI — AI (ai/) |
류준 | 데이터셋 기반 모델 학습, 지식베이스 청킹, BM25·임베딩·하이브리드 검색(RRF)·리랭킹, LangChain·LangGraph 오케스트레이션, 평가 하네스. 배포 ai.solidbob.cloud, 브랜치 ai |
백엔드·AI — 서버 (server/) |
장민석 | C-5 마스킹(2026-08-27 이관, _project/decisions/019), 파이프라인 구축과 배선, 클린 아키텍처 유지, 계약(포트·DTO) 정의, HTTP 경계·설정·저장(DB), F-2 게이트. 배포 server.solidbob.cloud, 브랜치 server |
| 프론트엔드 | 조서희 | React 상담원 대시보드(apps/dashboard: 자막·경고·하단 책갈피 카드·종결 모달), WebSocket 클라이언트, 데모 시나리오, 결과 시각화. 앱(Flutter)은 만들지 않는다 — 웹만. 고객 화면은 014로 철회(_project/decisions/014). |
| 공동 | 4인 | 골든셋 작성·라벨링, 발표 자료 |
ai/와server/의 경계 — 어느 쪽인지 헷갈리면 이렇게 가른다."품질을 만들거나 재는 코드인가?" → 예: ai/ (류준) "요청 하나를 처리하는 데 반드시 실행되는가?" → 예: server/ (장민석)C-5 마스킹·F-2 게이트가
server/에 있는 이유가 이것이다 — 둘 다 규칙 기반 판정이고 요청 경로에서 매번 실행된다. 모델이 관여하지 않는다(절대 원칙 9 “판정은 규칙이, 설명만 LLM이”). 각 디렉터리 루트의CLAUDE.md가 이 경계를 영역 규칙으로 못박아 두고 있다. 브랜치 이름도 디렉터리 이름과 같다 —ai(류준) ·server(장민석),_project/decisions/015.
7.2 부하 재분배 — 개편 이후 상태
이전에는 류준이 백엔드와 AI를 혼자 맡아 구조적으로 과부하였다. 4·5주차 핵심 실험과 F-2가 모두 한 사람에게 걸려 있었다. 2026-08-26 팀 개편으로 장민석이 백엔드·AI에 합류하면서 이 문제는 구조적으로 해소됐다.
기존 완화 조치는 계속 유지한다 — 과부하 회피 목적이 아니라 각자의 전문성에 맞는 정상 분업으로 재해석한다.
| 조치 | 내용 |
|---|---|
| C-5는 정성윤 담당 | 전사 직후 처리 지점이라 STT 파이프라인 담당이 자연스럽다. 정규식 중심이라 AI 전문성 의존도가 낮다 |
| CI 운영은 정성윤 담당 | 백엔드·AI(류준·장민석)는 하네스 설계에 집중하고, 운영·유지는 정성윤이 맡는다 |
| 골든셋은 4인 공동 | 라벨링은 도메인 이해만 있으면 되므로 분산 가능하다. F-2 케이스는 규정 작성자와 다른 사람이 만든다(보완지시서 3번) |
| 조서희가 시각화 담당 | matplotlib 성능 곡선·레이턴시 분포를 프론트 담당이 가져간다 |
| 백엔드·AI는 디렉터리로 나눈다 | 처음에는 “둘이 함께”로 정했으나(2026-08-26), 같은 날 fastapi/가 ai/·server/로 갈리면서 나눌 경계가 실제로 생겨 그 경계를 담당 경계로 삼았다 — 류준 ai/(품질을 만들고 재는 쪽), 장민석 server/(요청이 흐르는 길). 의존 방향은 ai → server 한쪽뿐이고 server/.importlinter가 역방향을 막는다. 경계에 걸친 작업(계약 변경 등)은 무엇이 깨지는지 먼저 확인한다. 합의를 절차로 요구하지는 않는다 — 같은 공간에서 일하므로 필요하면 그 자리에서 맞춘다(_project/decisions/023). _project/decisions/012 |
| F-2는 여전히 조건부 | 코어 작업이 지연되면 F-2를 설계 문서로 전환한다 (8절 체크포인트). 다만 담당이 2인으로 늘어 지연 위험은 낮아졌다 |
7.3 인터페이스 계약 우선
1주차에 모듈 간 스키마를 확정한다. 이것이 병렬 작업의 전제 조건이다.
v2 — 2026-08-25 조건부 컨펌 반영. v1(위 초안)은 정성윤이 DB 스키마·골든셋과 대조해 불일치 4건을 확인, 아래처럼 수정해 최종 확정했다. 결정 기록:
_project/decisions/003-인터페이스-스키마-v2.md(v1 최초 컨펌 기록은_project/decisions/002-인터페이스-스키마-확정.md에 남아 있다.)
// 전사 이벤트 (마스킹 적용 후)
{
"call_id": "c_001",
"segment_id": "seg_0031",
"speaker": "customer",
"text": "카드번호는 **** 입니다",
"masked": [{"type": "P2", "span": [7, 11]}],
"is_final": true,
"utterance_end_ms": 3100
}
// 추천 카드
{
"call_id": "c_001",
"trigger_at_ms": 3150,
"cards": [
{
"title": "부정사용 보상 기준 안내",
"summary": "분실 신고 접수 이전의 부정사용액은 고의·중과실이 없는 한 보상됩니다.",
"source": {"doc_id": "FIN-TERM-2.2", "title": "이용약관 2장 2항"},
"similarity_score": 0.87
}
],
"internal_latency_ms": 780,
"e2e_latency_ms": 1240
}
// 종결 판정 (F-2) — 도메인·closure_type별로 evidence의 키가 다르다 (아래 표 참고)
{
"call_id": "c_001",
"closure_type": "상품해지",
"reason": "고지 완료",
"evidence": {"중도해지수수료_안내": true, "약정혜택소멸_안내": false, "고객확인_기록": false},
"verdict": "blocked",
"missing": ["약정혜택소멸_안내", "고객확인_기록"],
"source": {"doc_id": "FIN-POLICY-CLOSE-1", "title": "내부 처리 규정 — 상품 해지 처리"}
}
2026-08-26 도메인 4종 정리 반영: 위 예시는 v2 확정 당시(2026-08-25) 통신 도메인 값(
TERM-3.2,closure_type: "해지")을 썼으나, 그 문서 ID·처리유형은 지금knowledge-base/에 존재하지 않는다. 구조(필드명·타입)는 v2 그대로이고 예시 값만 실제 스키마에 맞춰 금융보험 도메인 예시로 교체했다 —db/schema.sql도 같은 날closure_type·evidence 컬럼을 이 값들로 갱신했다(_project/decisions/006). 메시지에domain필드를 명시적으로 포함할지(현재는call_id로 서버가call.domain을 조회하는 방식을 가정)는 3.2절 도메인 라우팅과 함께 아직 미결이다.
v1 → v2 수정 사항 (DB db/schema.sql·골든셋과 대조해 발견, 정성윤 확인)
| 필드 | v1 | v2 | 이유 |
|---|---|---|---|
verdict |
(예시에 blocked만 노출) |
"approved" \| "blocked"로 명시 |
DB closure.verdict ENUM('approved','blocked'), 골든셋 expected_verdict, golden_set.py 전부 이미 approved 기준으로 통일돼 있었다. allowed가 아니다 |
source |
{"doc": "요금제약관", "clause": "3.2"} (사람이 읽는 이름) |
{"doc_id": "TERM-3.2", "title": "..."} (문서 ID + 표시용 제목) |
recommendation_card.source_doc_id가 document(document_id)를 참조하고, 골든셋 정답도 expected_doc_ids: ["TERM-3.2"] 형식이라 ID 기준이어야 Recall@5 채점 때 변환 없이 바로 비교된다 |
evidence |
전체 컬럼을 전부 채우는 것처럼 보임 | closure_type별 부분집합 — 원칙은 v2 그대로 유지. 2026-08-26 도메인 4종 정리 후 실제 값은 상품해지(중도해지수수료_안내·약정혜택소멸_안내·고객확인_기록) / 보상(사고경위_확인·귀책여부_확인, 이상 금융보험) / 반품(환불금액_안내·환불기간_안내·상품상태_확인) / 교환(교환가능_확인·재고_확인, 이상 쇼핑). 나머지는 응답에 아예 포함하지 않는다(NULL) |
closure 테이블 10개 boolean 컬럼이 유형별 전용이다. missing은 evidence 중 false인 키만 추출한다 — 해당 없는(NULL) 항목을 넣으면 다른 유형 항목이 오탐으로 잡힌다 |
| 전사 이벤트 | segment_id 없음 |
segment_id 추가 |
20초 발화에 interim이 199건(V4 실측) 오는데, call_id만으로는 프론트가 “어느 발화의 갱신인지” 구분할 수 없다. transcript_segment.segment_id와 1:1 |
score |
score |
similarity_score |
recommendation_card.similarity_score와 이름을 맞춰 직렬화 매핑 코드를 없앤다 |
rank |
없음 | 배열 순서 = rank (서버가 채움, 별도 필드 없음) | recommendation_card.rank는 있지만 배열 순서로 이미 표현되므로 필드를 중복시키지 않는다 |
span |
정의 없음 | 문자(코드포인트) 오프셋으로 확정 | masking_event.span_start/end는 INT. 한글은 UTF-8에서 3바이트라 byte 기준이면 프론트·백이 어긋난다 |
| latency 정의 | 정의 없음 | internal_latency_ms = 트리거 발동 → 카드 응답 완료 / e2e_latency_ms = utterance_end_ms → 화면 표시 |
오늘 V4 실측(최종 결과 = 발화 종료 +346ms)이 e2e_latency_ms에 이미 포함되는 값이라는 걸 명확히 해둔다 |
그대로 유지: masked[].type(P1~P7, masking_event.pattern과 일치) · is_final/utterance_end_ms(transcript_segment와 1:1) · closure_type이 DB ENUM과 정확히 일치해야 한다는 원칙(값 자체는 2026-08-26 "상품해지"/"보상"/"반품"/"교환"으로 갱신, 16절 ERD).
자막 렌더링 (정성윤 제안, 장민석이 프론트를 맡던 시점의 합의 — 현재 프론트는 조서희, 백엔드·AI 내부 분담은 7.2절 참고): interim은 누적하지 않고 최신 것만 segment_id 기준으로 교체, 갱신은 requestAnimationFrame 또는 100ms 디바운스로 묶는다. DB에는 is_final: true만 저장 — interim까지 저장하면 통화 1건에 수천 행이 쌓인다.
✅ 반영 완료 (2026-08-26): 위 두 항목(
evidence·closure_type)에서 예고했던 v3 변경 — 도메인 접두어 문서 ID(FIN-TERM-3.2), 도메인별closure_type값 집합 (금융보험:상품해지/보상, 쇼핑:반품/교환) — 을 위 JSON 예시와db/schema.sql에 함께 반영했다(_project/decisions/006). 메시지에 별도domain필드를 넣을지는 3.2절과 함께 아직 미결 —call_id로 서버가call.domain을 조회하는 방식을 임시로 가정한다.