핵심 원칙
- 자연어 API 프로덕션 구축 시 semantic parsing과 execution 분리 필수
- LLM은 자연어 → 구조화된 요청(canoical structured requests) 변환에만 사용
- 자연어를 입력으로만 취급, API 계약으로 삼지 말아야 함 (언어는 취약)
자연어 직접 사용 문제점
- 비결정적 행동(nondeterministic behavior)
- 프롬프트 기반 비즈니스 로직 → 디버깅·재현 어려움
- 암묵적 API 계약 → 작은 변화로 동작 변형
- 조용한 실패(silent failures) 발생, 시스템 취약
아키텍처: 2단계 레이어 분리
1. Semantic Parse API (자연어 → 구조 변환)
- 사용자 텍스트 입력 수용
- LLM으로 intent·entities 추출
- 미리 정의된 스키마 완성
- 정보 부족 시 clarification 질문 (비즈니스 로직 실행 금지)
- 컴파일러 역할 (e.g., “blue backpack but cheaper” → {intent: “recommend_similar”, reference_product_id: “blue_backpack_123”, price_bias: -0.8})
2. Structured Execution API (구조 → 실행)
- 구조화 입력만 수용
- 결정론적·버전 관리·테스트 가능
- 자연어 처리 없음, 안정적 백엔드 역할
주요 요소: Canonical Schemas
- 코드로 정의된 intent별 계약 (필수/선택 필드, 값 범위, 유효성 규칙)
- 자연어 변형 흡수 → 일관된 출력 보장
- API 계약의 backbone 역할
Schema Completion (Clarification)
- 정보 부족 시 “needs_clarification” 응답 (missing fields, targeted question, current state)
- 상태 객체로 메모리 관리 (API는 stateless)
- 클라이언트가 상태 전달하며 대화 유지 → 완성 시 canonical_request 실행
오케스트레이션: LangGraph 활용
- 구조화 워크플로우 모델링 (intent 분류 → entity 추출 → schema 병합 → 유효성 검사 → 완성/Clarification 라우팅)
- 코드 기반 결정, LLM은 제안만
- 명확한 상태 전환·관찰 가능·안전 재시도
안전장치: Confidence Gates
- LLM 출력에 confidence score 요구
- 임계값 미달 시 실행 차단, clarification 요청 (e.g., 모호한 “the bag” → 저신뢰도 → 추가 질문)
- silent misinterpretation 방지
정규화: Lightweight Ontologies
- 코드 기반 (허용 intent, synonym mappings, cross-field validation)
- LLM 제안 값 → 코드로 정규화 (e.g., “cheaper” → price_bias: -0.7)
- 논리 불일치 시 clarification (e.g., cheap + high quality 우선순위 질문)
성능 고려
- 지연 시간: intent 분류 ~40ms, entity 추출 ~200ms, 검증 1ms → 총 250300ms
- 채팅 UX에서 수용 가능, 오류 비용보다 저렴
주요 교훈 (Key Takeaways)
- 언어는 API 계약 아님, 구조로 변환
- 서버 측 schema 완성 소유
- LLM은 discovery·extraction에만, 실행 아님
- 안전·결정론성 최우선
- Azure OpenAI + LangGraph로 실제 시스템 구축 경험 기반
https://aisparkup.com/posts/9012