All projects

멀티모달 대화형 로봇 HERA

A real-time multimodal HRI system where vision and language models run as separate processes behind a routing gateway, so one slow path never blocks the conversation.

시각·언어 모델을 각각 별도 프로세스로 띄우고 게이트웨이가 라우팅만 맡아, 한 경로의 지연이 대화를 막지 않는 실시간 멀티모달 HRI 시스템

Role · System architect & engineer — 3-tier inference split, layered response pipeline, hybrid RAG redesign, Jetson porting, 4-way deployment matrix, web demo rebuild.

MSAEdgeHybrid RAGAI Agent

Key Metrics

0.19s
End-to-end Latency
63x
Rule vs LLM Path
-68%
Model Size

Why it matters

Four deployment targets on a single codebase: RTX 5080 server (0.7-0.8s, 82% QA) versus Jetson llama.cpp (1.8s, 67% QA, offline-capable). The architecture is held invariant while models and optimization strategies are swapped per target. Reconstructed as a browser-accessible web stack on an RTX 3070, the system achieves 0.19s end-to-end latency, 0.003s on rule-routed queries (63x reduction), and co-resides a 4B VLM with a 1.7B LLM within 5.9GB of 8GB VRAM.

단일 코드베이스로 4가지 배포 타깃을 지원한다. RTX 5080 서버(0.7–0.8s, QA 82%)와 Jetson llama.cpp(1.8s, QA 67%, 오프라인 동작)의 트레이드오프를 정량화하였으며, 아키텍처는 고정한 채 타깃별로 모델과 최적화 전략만 교체하는 구조를 채택하였다. 이후 RTX 3070 환경에 브라우저 기반 웹 스택으로 재구성하여 종단 응답 0.19초, 룰 라우팅 질의 0.003초(63배 단축), VLM 4B와 LLM 1.7B의 8GB VRAM 내 5.9GB 동시 상주를 확보하였다.

Problem

In humanoid HRI, image, speech, and text arrive concurrently on different inference paths. VLM inference would occupy the event loop and block conversational responses entirely — the robot could not react to speech while analyzing the camera feed. I reframed this as a system design problem rather than a model performance one: hold the models fixed, change only the execution structure. A 4-way deployment matrix (Server/Edge x GPU/llama.cpp) quantified the trade-offs: RTX 5080 server = 0.7-0.8s / 82% QA / 4.2GB; Jetson llama.cpp = 1.8s / 67% QA / 1.1GB / offline-capable.

휴머노이드 HRI 환경에서는 이미지·음성·텍스트가 서로 다른 추론 경로로 동시에 유입되며, 각 입력은 상이한 처리 시간을 갖는다. VLM 추론이 이벤트 루프를 점유할 경우 대화 응답 전체가 블로킹되어, 로봇이 카메라 프레임을 분석하는 동안 사용자 발화에 반응하지 못하는 현상이 반복적으로 발생하였다. 기존 접근은 이를 모델 성능 문제로 다루어 상위 모델로 교체하는 방식을 취하나, Jetson과 같이 자원이 고정된 환경에서는 해당 선택지가 존재하지 않는다. 이에 본 프로젝트는 문제를 시스템 설계 영역으로 재정의하고, 모델 구성을 고정한 채 실행 구조만을 변경하는 접근을 채택하였다. 실행 위치(서버/엣지)와 추론 엔진(GPU/llama.cpp)을 축으로 4가지 시나리오를 구성해 트레이드오프를 정량화한 결과, RTX 5080 서버는 0.7–0.8s / QA 82% / 4.2GB, Jetson llama.cpp 단독 실행은 1.8s / QA 67% / 1.1GB / 오프라인 동작이 확인되었다.

System Architecture

추론 엔진을 독립 프로세스로 분리하고 FastAPI 게이트웨이가 오케스트레이션한다. 특정 모델의 지연이 다른 경로를 블로킹하지 않으며, 게이트웨이는 라우팅만 담당하여 병목 지점이 되지 않도록 설계하였다.

FrontendNext.js 15 · React 19 · TypeScript웹캠Vision1.5초 간격 캡처 · 모션 차분 스킵마이크 STTVoiceWeb Speech API (continuous)채팅 · 페르소나 · RAG 업로드UI슬라이더 기반 특성 조정, PDF 문서 주입API Gateway · FastAPI :8000라우팅만 담당. 추론 로직은 외부 프로세스로 분리대화 라우터0.003s / 0.19sRAG → 룰 → LLM 계층 분기, 회피 응답 시 룰 폴백Hybrid RAG+0.1msbge-m3(Dense) + BM25(Sparse) → RRF 융합 k=60얼굴 임베딩Sessionface_recognition 128D · 코사인 유사도로 동일인 판별Ring BufferMemorydeque(maxlen=20) — 500턴 주입 후에도 20턴 유지TTSVoiceedge-tts (웹) / piper (온디바이스 오프라인)Inference Engines · llama.cpp독립 프로세스. VRAM 예산에 따라 GPU/CPU 차등 배치VLM :8081GPUQwen3-VL-4B Q4_K_M + mmproj — 시각 질의에만 조건부 호출LLM :8080GPUqwen3-igris-1.7b Q4_K_M (QLoRA) · temp 0.3 은 정체성 일관성LLM :8082CPUQwen3-1.7B Q8_0 · temp 0.7 은 표현 다양성 · VRAM 절약

VLM 4B와 LLM 1.7B를 단일 프로세스에 로드할 경우 OOM이 발생하나, 프로세스 분리 후 VRAM 5.9GB / 8GB로 동시 상주가 가능하다. 동일 구조를 Jetson에 배포할 때는 PROFILE 환경변수로 YAML 프로파일만 교체한다. VLM은 SmolVLM-500M, LLM은 -ngl 0(CPU), 컨텍스트는 4096에서 1024로, TTS는 piper(오프라인)로 전환된다.

What I built

Key Decisions (trade-offs)

1

추론 엔진 프로세스 분리

5.9GB / 8GB
문제

VLM(4B)과 LLM(1.7B)을 단일 프로세스에 로드하면 8GB VRAM에서 OOM이 발생하고, 단일 추론이 이벤트 루프를 점유하면 전체 요청이 블로킹된다.

결정

추론 엔진을 독립 llama.cpp 프로세스(8080·8081·8082)로 분리하고, FastAPI 게이트웨이는 라우팅만 담당하도록 재설계. VRAM 예산에 따라 VLM·파인튜닝 LLM은 GPU(-ngl 99), 범용 LLM은 CPU(-ngl 0)로 차등 배치.

성과

VLM 4B + LLM 1.7B 동시 상주(5.9GB/8GB), OOM 없이 동작. 엔진 장애가 전체로 전파되지 않는 격리 구조 확보.

English

Loading a 4B VLM and 1.7B LLM in one process caused OOM on 8GB VRAM, and a single inference blocked the whole event loop. Split into independent llama.cpp processes (8080/8081/8082) with a routing-only FastAPI gateway; VLM and fine-tuned LLM on GPU (-ngl 99), general LLM on CPU (-ngl 0). Result: 5.9GB/8GB co-resident, no OOM, failures isolated.

2

계층형 응답 파이프라인

63x
문제

모든 질의를 LLM에 전달하면 로봇 사양 같은 정형 질의까지 추론 비용을 부담하고, 경량 LLM(1.7B)이 모르는 값을 지어내는 환각이 발생한다.

결정

응답 경로를 RAG → 룰 → LLM 3단계로 계층화. 업로드 문서는 RAG 최우선, 로봇 정보 질의는 키워드 룰로 즉시 처리, 나머지만 LLM으로 전달. 회피 응답 시 룰로 폴백.

성과

룰 경로 0.003초 vs LLM 경로 0.19초. 63배 지연 차이(n=5, 세션 격리, 중앙값). 지연 절감과 환각 차단 동시 달성.

English

Routing every query through the LLM paid inference cost even for fixed questions, and a 1.7B model fabricates unknown values. Layered the path into RAG → rule → LLM. Measured 0.003s on the rule path vs 0.19s through the LLM — a 63x gap.

3

Hybrid RAG 재설계. RRF 융합

+0.1ms
문제

초기 FAISS+BM25 구성은 임베딩(paraphrase-albert-small-v2)이 영어 전용이라 한국어 Dense 검색이 무작위에 가깝고, 융합이 순위 없는 단순 합집합이었다.

결정

임베딩을 bge-m3(멀티링구얼)로 교체하고 ChromaDB로 전환. 스케일이 다른 Dense 거리와 BM25 점수를 직접 합산하는 대신, 순위만 사용하는 RRF로 정규화 없이 융합(k=60).

성과

BM25 융합 추가 비용 +0.1ms(Dense 39.5ms 대비). 사실상 무비용. 한국어 검색 정상화.

English

The initial FAISS+BM25 build was English-only (near-random Korean dense retrieval) and fused by plain set union. Swapped to bge-m3 on ChromaDB and rebuilt fusion as RRF (rank-only, no normalization, k=60). BM25 fusion adds only +0.1ms against Dense at 39.5ms.

4

한국어 BM25 토크나이저

0 → 매칭
문제

공백 분리 토크나이저는 한국어에서 조사 때문에 매칭이 실패한다. '로봇은 무엇인가'와 '로봇이 작동한다'의 공통 토큰이 0개였다.

결정

konlpy(JVM 의존성)를 추가하는 대신, 문자 bigram과 어절 원형을 함께 색인해 조사 변형을 흡수. 추가 의존성 없이 rank-bm25만으로 동작.

성과

'로봇' bigram 공유로 매칭 성립. 고유 수치·모델명(5200mAh, Qwen3-1.7B) 검색도 정상.

English

Whitespace tokenization breaks on Korean particles (zero common tokens for two sentences sharing a noun). Indexed character bigrams alongside surface forms instead — no konlpy/JVM dependency, running on rank-bm25 alone.

5

Ring Buffer 세션 메모리

20턴 상한
문제

TTL 기반 컨텍스트는 부하 시 메모리가 무한히 증가하는 누수 위험이 있다.

결정

세션별 고정 길이 링버퍼(deque maxlen=20)로 메모리를 결정론적으로 상한.

성과

500턴 연속 주입 후에도 보관 턴 수 20 유지. leak-free 검증.

English

TTL-based context grows unbounded under load. A fixed-length ring buffer (deque maxlen=20) caps memory deterministically — 20 turns retained after injecting 500.

6

프로파일 기반 하드웨어 추상화

코드 무변경
문제

서버(transformers 직접 로드)와 엣지(llama.cpp HTTP)는 추론 계층이 달라, 코드 분기 시 두 경로를 각각 유지보수해야 한다.

결정

PROFILE 환경변수로 YAML 프로파일 전체를 교체. provider·모델·GPU 오프로딩·컨텍스트 길이·TTS·분석 주기까지 설정만으로 전환.

성과

provider가 바뀌어도 상위 파이프라인 코드는 무변경. 가장 제약이 강한 Jetson에서 먼저 설계해 서버 확장 시 설정 교체만으로 대응.

English

Server (transformers) and edge (llama.cpp/HTTP) differ at the inference layer. A PROFILE env var swaps the entire YAML profile — provider, model, GPU offload, context length, TTS, interval — so the upper pipeline never changes.

Stack

FastAPI (ASGI)Qwen3-1.7B (QLoRA fine-tuned, GGUF Q4_K_M)Qwen3-VL-4B / SmolVLM-500Mllama.cpp + CUDA (SM87 source build)ChromaDB + bge-m3 + BM25 (RRF fusion)Next.js 15 / React 19Jetson AGX OrinRTX 5080