MINERVA 아키텍처 개요

React + FastAPI + Agent 3-Layer 풀스택 AI Agent 플랫폼

MINERVA는 사내 AI Agent 플랫폼으로, React 프론트엔드, FastAPI 서빙 레이어, LangChain 기반 Agent 코어의 3계층 구조이다. 전체 아키텍처, 데이터 흐름, 기술 스택, 설계 원칙을 개괄한다.

Agent
저자

Kwangmin Kim

공개

2026년 05월 05일

1 MINERVA란

MINERVA는 사내 AI Agent 플랫폼이다. 사용자가 웹 UI에서 질문하면 RAG(Retrieval-Augmented Generation) 기반으로 사내 문서를 검색하여 답변을 생성한다.

플랫폼이 해결하는 문제:

  • 사내 문서(규정, 매뉴얼, 가이드라인)에서 정보를 빠르게 찾는다
  • 데이터 표준화처럼 반복적인 작업을 자동화한다
  • 에이전트 성능을 A/B 실험으로 측정하고 개선한다
  • 모니터링 대시보드로 사용 현황을 추적한다

2 3-Layer 아키텍처

MINERVA는 세 개의 독립적인 레이어로 구성된다. 각 레이어는 명확한 경계를 가지며, 교체하더라도 다른 레이어에 영향을 주지 않는다.

┌──────────────────────────────────────────────────────────┐
│  Layer 1: Frontend (React + TypeScript)                  │
│  ┌────────────────────────────────────────────────────┐  │
│  │  Pages: Home, QnaChatbot, DataStandardizer,       │  │
│  │         CodeStandardizer, Monitoring,              │  │
│  │         Records, Tests                             │  │
│  │  Components: FeedbackButtons, QnaChatEmbed,        │  │
│  │              TimeseriesChart                       │  │
│  │  lib/: citationMarking, referencePanel,            │  │
│  │        conversations, anonymousUser, ...           │  │
│  │  API Client: 타입 안전 fetch 래퍼                  │  │
│  └────────────────────────────────────────────────────┘  │
│                       │ fetch / SSE                       │
│                       ▼                                   │
│  Layer 2: Serving (FastAPI + Pydantic)                   │
│  ┌────────────────────────────────────────────────────┐  │
│  │  main.py: 앱 생성, CORS, lifespan                 │  │
│  │  schemas.py: 요청/응답 Pydantic 모델               │  │
│  │  routers/: 7개 모듈, 9개 엔드포인트 군             │  │
│  │           (qna, ds, monitoring, records,           │  │
│  │            feedback, ab, health)                   │  │
│  └────────────────────────────────────────────────────┘  │
│                       │ BaseAgent.run() / .stream()       │
│                       ▼                                   │
│  Layer 3: Agent Core (LangChain + LangGraph)             │
│  ┌────────────────────────────────────────────────────┐  │
│  │  contracts.py: BaseAgent ABC + Query/Response      │  │
│  │  agents/: QnA, DataStandardizer (Supervisor +      │  │
│  │          sub_agents), InsilicoAnalyzer (stub)      │  │
│  │  rag/: retriever, chunking, reranker, vectorstore, │  │
│  │        metadata_loader                             │  │
│  │  config.py: RAGConfig (7개 sub-settings)           │  │
│  │  experiments.py + stats.py: A/B + 통계 검정        │  │
│  │  metrics_logger / feedback_logger / pricing:       │  │
│  │        관측성·비용                                  │  │
│  │  prompts.py / answer_features.py / relevance.py:   │  │
│  │        프롬프트·답변 신호·점수 보정                 │  │
│  └────────────────────────────────────────────────────┘  │
└──────────────────────────────────────────────────────────┘

2.1 Layer 1: Frontend

사용자가 상호작용하는 웹 인터페이스이다.

구성 요소 기술 역할
프레임워크 React 19, TypeScript 6 컴포넌트 기반 UI
빌드 도구 Vite 8 개발 서버, 번들링, HMR
라우팅 React Router 7 SPA 페이지 전환
차트 Recharts 모니터링 시계열 시각화
API 통신 fetch + ReadableStream REST + SSE 스트리밍

7개 페이지가 각각 하나의 도메인을 담당한다:

페이지 경로 기능
Home / 플랫폼 소개, 에이전트 목록
QnA Chatbot /agents/qna_chatbot 문서 기반 질의응답, 참조 패널
Data Standardizer /agents/data_standardizer 논리명→메타데이터 표준화 (data 모드)
Code Standardizer /agents/code_standardizer 코드 입력→표준화 코드/매핑/설명 (code 모드)
Monitoring /monitoring 사용량·지연·인용률 등 10개 지표 시계열
Records /records 호출 기록 드릴다운 (agent/status/metric 필터)
Tests /tests A/B 실험 대시보드 (arm별 메트릭 + SRM·검정)
노트

Data Standardizer와 Code Standardizer는 같은 백엔드 에이전트(data_standardizer)를 호출하지만 페이지가 분리된다. 입력이 표 형태인지 코드 블록인지에 따라 사용자 경험이 크게 다르므로 UI를 분리하고, 백엔드는 mode 파라미터(data/code)로 자동 감지한다.

2.2 Layer 2: Serving

FastAPI가 프론트엔드와 에이전트 코어를 연결한다.

# main.py — 핵심 구조
app = FastAPI(title="MINERVA API", lifespan=lifespan)

app.add_middleware(CORSMiddleware, ...)
app.include_router(qna_chatbot.router)
app.include_router(data_standardizer.router)
app.include_router(monitoring.router)
app.include_router(records.router)
app.include_router(feedback.router)
app.include_router(ab.router)
app.include_router(health.router)

서빙 레이어의 책임:

  • HTTP 요청/응답 처리 (Pydantic으로 검증)
  • CORS 미들웨어 (프론트엔드 통신 허용)
  • Lifespan 이벤트 (에이전트 초기화/정리)
  • SSE 스트리밍 (토큰 단위 응답)
  • 에이전트 캐싱 (실험 arm별 인스턴스 관리)

2.3 Layer 3: Agent Core

실제 AI 로직이 실행되는 레이어이다. LangChain과 LangGraph로 구현한다.

# contracts.py — 에이전트 계약
class BaseAgent(ABC):
    name: str

    @abstractmethod
    def run(self, query: Query) -> Response: ...

    @abstractmethod
    async def stream(self, query: Query) -> AsyncGenerator[StreamEvent, None]: ...

모든 에이전트는 BaseAgent를 상속하여 run(동기)과 stream(SSE) 인터페이스를 구현한다. 새 에이전트를 추가할 때 이 계약만 구현하면 서빙 레이어가 자동으로 인식한다.

3 데이터 흐름

사용자가 질문을 입력하고 답변을 받기까지의 전체 흐름이다.

3.1 동기 요청 (/run)

1. 사용자가 브라우저에서 질문 입력
2. React → fetch POST /agents/qna_chatbot/run
3. Vite Proxy → FastAPI(:8000)로 전달
4. FastAPI → Pydantic RunRequest 검증
5. FastAPI → QnaChatbotAgent.run(Query) 호출
6. Agent → 문서 검색 (Hybrid: BM25 + Vector)
7. Agent → Reranker로 관련성 재정렬
8. Agent → Parent Chunk 매핑 (전체 문맥 복원)
9. Agent → LLM에 컨텍스트 + 질문 전달
10. Agent → Response(text, citations, run_id) 반환
11. FastAPI → JSON 응답
12. React → 화면에 답변 + 인용 표시

3.2 스트리밍 요청 (/stream)

1~5. (동기 요청과 동일)
6~8. (검색/재정렬/매핑 동일)
9. Agent → LLM.astream()으로 토큰 단위 생성
10. Agent → yield StreamEvent(token) (각 토큰마다)
11. FastAPI → StreamingResponse(text/event-stream)
12. React → ReadableStream으로 토큰 수신, 즉시 화면 반영
13. Agent → yield StreamEvent(done) (완료 시)

스트리밍 방식에서는 사용자가 첫 토큰을 0.3초 내에 볼 수 있다. 전체 응답이 완성될 때까지 기다릴 필요가 없으므로 체감 응답 시간이 크게 줄어든다.

4 프로젝트 디렉토리 구조

agent/
├── src/
│   ├── core/                           # Layer 3: Agent Core (공통 부품)
│   │   ├── contracts.py                # BaseAgent ABC + Query/Response/StreamEvent
│   │   ├── config.py                   # RAGConfig (7개 sub-settings)
│   │   ├── llm.py                      # Azure/Ollama Provider 추상화
│   │   ├── experiments.py              # A/B 실험 (YAML + sticky_hash)
│   │   ├── stats.py                    # z-test, Welch t-test, SRM χ², lift
│   │   ├── metrics_logger.py           # runs.jsonl (29 필드)
│   │   ├── feedback_logger.py          # feedback.jsonl (run_id FK)
│   │   ├── pricing.py                  # 모델별 토큰 단가 + cost 계산
│   │   ├── prompts.py                  # 매니페스트 기반 프롬프트 로딩
│   │   ├── relevance.py                # display_score 보정 (한국어 코사인)
│   │   ├── answer_features.py          # 답변 신호 정규식 5패턴
│   │   └── rag/
│   │       ├── retriever.py            # Hybrid Search (BM25 + Vector)
│   │       ├── chunking.py             # Parent-Child Chunking
│   │       ├── reranker.py             # FlashRank → CrossEncoder → Azure → cosine
│   │       ├── vectorstore.py          # FAISS / Azure AI Search
│   │       └── metadata_loader.py      # 외부 참고문헌·표준 메타 병합
│   ├── agents/                         # 에이전트 구현체
│   │   ├── qna_chatbot/agent.py        # QnA 챗봇 (단일 LCEL 체인)
│   │   ├── data_standardizer/          # Supervisor 패턴
│   │   │   ├── agent.py                # mode 라우팅 + 후처리 조율
│   │   │   └── sub_agents/
│   │   │       ├── rag_recommender.py      # 표준화 RAG 추천
│   │   │       ├── domain_classifier.py    # ALBERT 도메인 분류
│   │   │       ├── domain_auditor.py       # 도메인 우선순위 감사
│   │   │       └── post_processing/{tables,code}.py
│   │   └── insilico_analyzer/          # stub (7월~ 본격 개발)
│   ├── domains/standardization/        # 도메인 공유 모듈 (계획 단계, 빈 디렉토리)
│   └── services/api/                   # Layer 2: Serving
│       ├── main.py                     # FastAPI 앱
│       ├── schemas.py                  # Pydantic 스키마
│       └── routers/                    # 7개 모듈 (9개 엔드포인트 군)
│           ├── health.py               # GET /, /health
│           ├── qna_chatbot.py          # /agents/qna_chatbot/{run,stream,documents,...}
│           ├── data_standardizer.py    # /agents/data_standardizer/{run,stream}
│           ├── monitoring.py           # /monitoring/metrics
│           ├── records.py              # /records (drill-down)
│           ├── feedback.py             # /feedback, /feedback/comment
│           └── ab.py                   # /monitoring/ab/{experiments,{name}}
├── frontend/                           # Layer 1: Frontend
│   ├── src/
│   │   ├── main.tsx
│   │   ├── App.tsx
│   │   ├── pages/                      # 7개 페이지 (Home, QnA, DS, Code, Mon, Rec, Tests)
│   │   ├── api/client.ts               # 타입 안전 fetch 래퍼
│   │   ├── components/                 # FeedbackButtons, QnaChatEmbed, TimeseriesChart
│   │   └── lib/                        # 11개 유틸 (citationMarking, referencePanel, ...)
│   └── vite.config.ts                  # Proxy 설정
├── data/
│   ├── configs/                        # RAG config YAML
│   ├── docs/                           # 지식베이스 문서
│   ├── experiments/                    # A/B 실험 정의 + logs/
│   └── runtime/                        # runs.jsonl, feedback.jsonl
├── docs/platform-refactor-handoff.md   # 정본 핸드오프 (Phase 진행 로그)
├── Dockerfile                          # 멀티스테이지 빌드
├── Makefile                            # install / dev-{backend,frontend,poc} / smoke-test
└── pyproject.toml                      # 47개 Python 의존성

5 기술 스택

레이어 기술 버전
Frontend React, TypeScript, Vite, React Router, Recharts 19, 6, 8, 7, -
Serving FastAPI, Uvicorn, Pydantic 0.136, -, 2.9
Agent LangChain, LangGraph 1.1, 1.0
LLM Azure OpenAI (GPT-4.1), Ollama (개발용) -
Vector DB FAISS (로컬), Azure AI Search (클라우드) -
Reranker FlashRank, CrossEncoder -
NLP (한국어) KiwiPyKP, KonLPy -
배포 Docker (멀티스테이지), Poetry -

5.1 LLM Provider 추상화

# llm.py
LLM_PROVIDER = os.getenv("LLM_PROVIDER", "azure")

if LLM_PROVIDER == "azure":
    llm = AzureChatOpenAI(
        azure_deployment="gpt-4.1",
        azure_endpoint=settings.azure_openai_endpoint,
    )
elif LLM_PROVIDER == "ollama":
    llm = ChatOllama(model="llama3.1")

환경 변수 하나로 Azure(프로덕션)와 Ollama(개발)를 전환한다. 코드 변경 없이 LLM 공급자를 교체할 수 있어 로컬 개발과 프로덕션 배포의 차이를 최소화한다.

6 설계 원칙

6.1 계약 기반 설계

모든 에이전트가 동일한 인터페이스(BaseAgent)를 구현한다. 서빙 레이어는 에이전트의 내부 구현을 몰라도 동일한 방식으로 호출할 수 있다.

# 새 에이전트 추가 시 필요한 것
class NewAgent(BaseAgent):
    name = "new_agent"

    def run(self, query: Query) -> Response:
        ...  # 구현

    async def stream(self, query: Query) -> AsyncGenerator[StreamEvent, None]:
        ...  # 구현

라우터, 스키마, 프론트엔드 페이지를 추가하면 완성이다.

6.2 설정 외부화

RAG 파이프라인의 모든 파라미터(청크 크기, top-k, 모델 이름 등)를 YAML 파일로 관리한다. 코드를 수정하지 않고 설정만 바꿔서 파이프라인을 조정할 수 있다.

# data/configs/qna_chatbot.yaml
chunking:
  chunk_size: 1500
  chunk_overlap: 400
retriever:
  top_k: 5
  search_type: hybrid
reranker:
  model: flashrank
  top_n: 3
llm:
  model: gpt-4.1
  temperature: 0.7

6.3 A/B 실험을 통한 개선

에이전트의 설정을 실험적으로 변경하고, 사용자 피드백과 메트릭으로 효과를 측정한다. YAML dotted-key override와 sticky hash를 사용하여 사용자를 일관되게 실험군에 할당한다.

7 시리즈 안내

이 포스트는 MINERVA 플랫폼 시리즈의 개요이다. Phase B(구현·배포) 7편에서 시스템을 직조한 뒤, Phase C(중간 점검·플랫폼 진화) 시리즈로 이어진다. Phase C는 바이브 코딩으로 만든 결과물을 정밀 분석한 다음 LangGraph·Agentic Mode·실험·관측성·스킬 생태계로 단계적 진화 설계를 수립한다.

7.1 Phase B: 구현과 배포

순서 주제 핵심 내용
1 아키텍처 개요 (이 포스트) 3-Layer 구조, 데이터 흐름, 기술 스택
2 BaseAgent 계약 패턴 ABC + Pydantic 스키마, 계약 기반 설계
3 RAG 파이프라인 Hybrid Search, Reranker, Parent-Child Chunking
4 FastAPI 서빙 레이어 라우터, 캐싱, warmup, SSE 스트리밍
5 React 프론트엔드 7개 페이지, 타입 안전 API 클라이언트
6 A/B 실험 프레임워크 YAML override, sticky hash, JSONL 메트릭, stats 검정
7 프로덕션 배포 Docker 멀티스테이지, uvicorn, Azure, Makefile

7.2 Phase C: 중간 점검과 플랫폼 진화

서브페이즈 범위 상태
C-1 현재 상태 분석 데이터 흐름·상태·에러·Config·테스트 (5편) 작성 완료
C-2 LangGraph 전환 StateGraph·노드 분해·State·Checkpointing·계약 v2 (5편) 작성 완료
C-3 Agentic Mode Tool Binding·ReAct·Plan-and-Execute·위임 (4편) 진행 중
C-4 ~ C-10 실험·발화 분석·하네싱·스킬·지식·관측성·스케일링 계획

8 관련 주제

선행 지식 (Phase A)

후속 주제 (이 시리즈)

다른 카테고리 연결

Subscribe

Enjoy this blog? Get notified of new posts by email: