워커 개요
워커 리포지토리에 있는 것
lumie-worker에는 백그라운드 처리와 AI 중심 작업을 위한 독립적인 FastAPI 서비스가 들어 있습니다. 오케스트레이션 레이어는 여전히 백엔드입니다. 백엔드는 RabbitMQ 작업을 발행하고, worker HTTP 엔드포인트를 호출하며, 제품 데이터를 기록하는 유일한 서비스로 남아 있습니다.
worker 리포지토리는 두 번째 백엔드가 아니라 제품 인접 프로세서 집합으로 읽어야 합니다. 워커는 이미지를 렌더링하거나, LLM을 호출하거나, 큐를 소비하거나, 기술적 체크포인트 상태를 저장할 수 있지만, academy 비즈니스 aggregate의 소유자가 되어서는 안 됩니다.
이 섹션의 현재 제품 문서 표면은 다음과 같습니다.
| 서비스 | 주요 인터페이스 | 주요 작업 | 핵심 통합 |
|---|---|---|---|
| Grading | RabbitMQ consumer와 백엔드 HTTP fallback | OMR 답안 이미지 채점 | aio-pika, MinIO, Redis, httpx, OpenTelemetry |
| Report | RabbitMQ consumer와 동기 fallback HTTP 라우트 | 학생별 시험 리포트 이미지 생성 | aio-pika, httpx, Jinja2, Playwright, OpenTelemetry |
| Analysis | HTTP API | 시험 코멘터리와 학생별 피드백 생성 | FastAPI, Pydantic, openai.AsyncOpenAI, OpenTelemetry |
| Chatbot | SSE를 사용하는 HTTP API | 도구 사용과 사용자 확인이 포함된 academy assistant 실행 | LangGraph, langchain-openai, httpx, Postgres checkpointer, OpenTelemetry |
소스 경로
| 경로 | 역할 |
|---|---|
lumie-worker/services/grading/ | 운영용 OMR 채점 워커 |
lumie-worker/services/report/ | 리포트 렌더링 워커 |
lumie-worker/services/analysis/ | LLM 기반 코멘터리 및 피드백 워커 |
lumie-worker/services/chatbot/ | LangGraph 기반 assistant 워커 |
lumie-worker/libs/common/mq.py | 공유 RabbitMQ 메시지 처리 기본 요소 |
lumie-worker/libs/common/callback_mq.py | worker-to-backend 콜백용 공유 publisher |
lumie-worker/libs/common/http_signing.py | 백엔드 /internal/** 호출용 HMAC 서명 헬퍼 |
lumie-worker/libs/common/observability.py | 공유 Prometheus 및 OpenTelemetry 헬퍼 |
lumie-worker/contracts/mq-schemas-v1.yaml | grading과 report용 수동 유지 MQ 계약 참고 자료 |
공유 런타임 패턴
대부분의 worker 서비스는 services/<name>/src/ 아래에서 같은 구조를 따릅니다.
config.py는 타입이 지정된BaseSettings를 정의하여 누락된 환경 변수가 시작 시 실패하도록 만듭니다.wiring.py는 adapter와 use case를 구성해main.py를 얇게 유지합니다.schema.py는 wire 계약을 정의하는 Pydantic 메시지 또는 요청 모델을 담습니다.usecase.py는 HTTP, RabbitMQ, 환경 처리의 소유권 없이 워크플로를 오케스트레이션합니다.adapters/에는 MinIO, Redis, 백엔드 HTTP 클라이언트, LLM 클라이언트 같은 구체적 통합이 들어갑니다.
모든 서비스는 FastAPI lifespan hook을 사용해 시작 시 장수명 리소스를 만들고 종료 시 닫습니다. Lumie는 여기에서 공유 httpx.AsyncClient를 열고, Playwright를 예열하고, callback publisher를 연결하고, 컴파일된 LangGraph를 부착합니다.
서비스 레이아웃 계약
새 worker 서비스는 구체적인 이유가 없는 한 다음 구조를 따라야 합니다.
| 파일 또는 디렉터리 | 기대 책임 |
|---|---|
main.py | FastAPI 앱, lifespan, 라우트 마운트, 얇은 handler |
src/config.py | Pydantic 설정과 fail-fast 환경 검증 |
src/schema.py | wire-facing Pydantic 요청, 명령, 콜백 모델 |
src/usecase.py | 프레임워크 소유권이 없는 애플리케이션 오케스트레이션 |
src/wiring.py | 구체적 adapter와 use case 구성 |
src/adapters/ | HTTP, MQ, 스토리지, LLM, 브라우저, 외부 시스템 adapter |
src/domain/ | 순수 데이터 가공, 프롬프트, 렌더링 모델, 도메인 변환 |
src/observability/ | 서비스 로컬 metric 이름과 tracing wrapper |
tests/ | 외부 서비스 없이 실행 가능한 use case 및 observability 테스트 |
통합 경계
worker 리포지토리는 의도적으로 두 가지 상호작용 스타일로 나뉩니다.
grading-svc와report-svc는 RabbitMQ consumer입니다. 명령 큐를 구독하고, 작업을 처리한 뒤, 콜백 payload를lumie.commandsexchange로 다시 발행합니다.grading-svc는 백엔드가 업로드된 OMR 이미지 1장을 직접 호출하는 경로를 위해 동기 HTTP 채점 엔드포인트도 유지합니다.analysis-svc와chatbot-svc는 HTTP 서비스입니다. 다른 애플리케이션 레이어가 직접 호출하며, 같은 요청 경로에서 응답을 반환합니다.
워커끼리는 서로 호출하지 않습니다. 공유 백엔드 호출은 X-Tenant-Slug, X-Signature, X-Timestamp로 모든 /internal/** 요청에 서명하는 httpx adapter를 통해 이뤄집니다.
백엔드 소유권 규칙
워커는 CPU 집약적일 수도, IO 집약적일 수도, AI 중심일 수도 있지만, 소유권의 중심은 백엔드에 남아 있습니다.
데이터베이스 경계는 백엔드 쪽에 머뭅니다. 워커 가 제품 데이터를 필요로 하면 백엔드 /internal/** API나 명령 payload에서 가져옵니다. 워커가 제품 상태를 만들어내면, 그 상태는 백엔드가 소유한 handler가 저장할 수 있도록 콜백 또는 응답으로 돌려줍니다.
신뢰성과 관측성
- 공유 MQ 작업은
libs/common/mq.py::universal_process를 사용하며, 이 함수가 JSON 파싱, 재시도, 콜백 처리, ack/nack 동작을 표준화합니다. - 공유 콜백 발행은
libs/common/callback_mq.py에 있으며, 서비스 수명 동안 하나의 견고한 RabbitMQ 연결을 열어 둡니다. - 문서화된 모든 워커는
/metrics에 Prometheus metric을 마운트합니다. - OpenTelemetry는 서비스별
OTEL_ENABLED,OTEL_ENDPOINT,OTEL_SERVICE_NAME으로 활성화됩니다.
grading-svc, report-svc, analysis-svc는 libs/common/observability.py의 공유 tracing 헬퍼를 사용합니다. chatbot-svc는 LangGraph와 LangChain span을 깨지지 않게 내보내기 위해 별도의 tracing 구성을 사용합니다.
실패 의미
| 표면 | 잘못된 입력 | 처리 실패 | 콜백 실패 |
|---|---|---|---|
| RabbitMQ worker | requeue 없이 거부하여 broker DLQ가 수집하도록 함 | universal_process를 통한 재시도 가능 실패는 nack(requeue=True) | 콜백 재시도 소진 후 nack(requeue=True) |
| 동기 HTTP 라우트 | FastAPI 검증 오류 또는 명시적 HTTPException | HTTP 오류 응답 | 해당 없음 |
| LLM HTTP 라우트 | FastAPI 검증 오류 | 상위 API 오류가 handler failure 경로로 전파 | 해당 없음 |
| SSE chatbot 라우트 | 스트림 시작 전 요청 검증 | 실패 시점에 따라 error event 또는 닫힌 스트림 | 해당 없음 |
즉, 큐 consumer는 콜백 발행을 작업 단위의 일부로 취급해야 합니다. 작업은 백엔드가 성공 또는 실패 payload를 받을 때까지 완료된 것이 아닙니다.
설정 패턴
모든 서비스는 Pydantic settings로 환경을 읽습니다. localhost RabbitMQ 같은 로컬 전용 값은 기본값을 둘 수 있지만, 운영에 중요한 secret과 base URL은 누락 시 시작 단계에서 실패해야 합니다. 예시는 다음과 같습니다.
analysis-svc는LLM_API_KEY가 필요합니다.report-svc는LUMIE_BACKEND_URL과LUMIE_INTERNAL_HMAC_SECRET이 필요합니다.- 큐 워커는 큐 이름과 prefetch 기본값을 코드에 두고, 배포 값으로 덮어씁니다.