Home [Project] MDAI API - 기업용 AI 경영분석 챗봇 백엔드
Post
Cancel

[Project] MDAI API - 기업용 AI 경영분석 챗봇 백엔드

프로젝트 개요

9개 법인의 매출/원가/수익 데이터를 LLM 에이전트가 실시간으로 분석하고, 테이블/차트와 함께 스트리밍 응답을 제공하는 기업용 경영분석 챗봇 백엔드 시스템이다. 법인별 맞춤 프롬프트 주입, RAG 기반 데이터 검색, LangGraph 멀티 에이전트 파이프라인을 핵심 기술로 채택했다.

회사 프로젝트로 개인 레포지토리에는 소스 코드가 공개되어 있지 않습니다.


담당 역할

영역담당 내용핵심 성과
LLM Agent 설계LangGraph 상태 머신 기반 멀티 에이전트 파이프라인 구축, 조건부 라우팅 설계3종 에이전트(Sorting/MR/H2) + SSE 스트리밍
프롬프트 엔지니어링법인별 Prompt Provider 패턴 설계, 9개 법인 맞춤 시스템 프롬프트 구현동일 질문에 법인별 차별화된 분석 응답
RAG 파이프라인ChromaDB 기반 벡터 검색, 법인별 데이터 사전/분석 템플릿 임베딩분석 정확도 향상을 위한 컨텍스트 주입
백엔드 APIFastAPI async 전체 설계, Router-Service-Repository 레이어 구조30+ 엔드포인트, 비동기 SSE 스트리밍
인증/인가RBAC V2 설계, 법인별 접근 제어, 감사 로그세분화된 권한 코드 체계 + 토큰 로테이션
테스트/배포pytest 기반 테스트 코드, Docker 멀티스테이지 빌드1,400+ 케이스 (131 파일), 경량화 이미지

해결한 핵심 문제들

문제 1: 법인마다 분석 로직이 다르다

9개 법인은 사업 구조가 모두 다르다. 매출 항목 분류, 원가 계산 방식, 스프레드 산출 기준이 법인마다 다르기 때문에, 하나의 분석 로직으로는 대응할 수 없었다.

해결: Strategy Pattern + Factory 자동 등록

1
2
3
4
5
6
7
8
9
10
11
사용자 질문 → StrategyFactory.get_strategy(business_unit)
    │
    ├── RMOsanStrategy     (RM오산: 분류별 매출/스프레드)
    ├── HighOneStrategy    (하이원: 리조트 매출 구조)
    ├── SeoulEcoStrategy   (서울에코: 환경 사업)
    ├── ShinpoongStrategy  (신풍: 제조업 원가)
    ├── MijuStrategy       (미주: 유통 마진)
    ├── DaeyoungStrategy   (대영: 건설 공종별)
    ├── JSStrategy         (JS: 서비스업)
    ├── RMHwaseongStrategy (RM화성)
    └── H2Strategy         (H2: 수소 사업)

각 Strategy는 BaseStrategy를 상속하고, 데코레이터로 Factory에 자동 등록된다. 새 법인이 추가되면 Strategy 클래스 하나만 작성하면 된다.

법인별 Prompt Provider 패턴

법인별로 Prompt Injection Pattern을 적용했다. 추상 프롬프트 프로바이더를 정의하고, 각 법인이 자사 데이터 구조에 맞는 시스템 프롬프트를 제공한다. LLM이 동일한 “매출 분석해줘”라는 질문에 대해 법인마다 다른 테이블 구조와 지표를 반환하게 된다.

법인분석 특성프롬프트 핵심 지시
RM오산반입처별 매출/스프레드원재료 분류 체계(철/비철/특수), 수율 기반 원가 산출, 반입처별 스프레드 비교
하이원리조트 매출 구조시설별(콘도/스키/골프) 매출 지표, 객실 가동률, 시즌별 비교
신풍제조업 원가공정별 원가 항목 분류, 제조원가명세서 구조, 재공품 반영

이 설계로 LLM이 각 법인의 도메인 전문가처럼 응답할 수 있게 되었다. 프롬프트와 분석 로직이 Strategy 단위로 캡슐화되어, 한 법인의 프롬프트를 수정해도 다른 법인에 영향이 없다.

문제 2: LLM 응답이 느리고 구조화되지 않는다

경영 분석은 텍스트뿐 아니라 테이블, 차트, 후속 질문이 함께 제공되어야 한다. 하지만 LLM의 단일 호출로는 이러한 복합 응답을 안정적으로 생성하기 어렵고, 응답 대기 시간이 길었다.

해결: LangGraph 멀티 에이전트 + SSE 스트리밍

LangGraph 상태 머신으로 분석 파이프라인을 다단계로 분리했다.

상태 스키마

LangGraph의 TypedDict 기반 상태 객체가 노드 간 데이터를 전달한다:

필드타입역할
querystr사용자 원본 질문
business_unitstr대상 법인 코드
parsed_intentstr분석 유형 (매출/원가/비교/추세 등)
retrieved_contextlistRAG로 검색된 데이터 사전/템플릿
structured_datadictDB에서 조회한 정형 데이터
analysis_resultstrLLM 분석 텍스트
tableslist생성된 테이블 데이터
chartslist차트 설정 (Plotly spec)
follow_upslist후속 추천 질문

조건부 라우팅

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[Query Parser]
     │
     ├── intent: 매출/원가/GP 분석 ──→ [RAG Retriever] ──→ [Sorting Agent]
     ├── intent: 원자재/재활용     ──→ [RAG Retriever] ──→ [MR Agent]
     └── intent: 수소 사업         ──→ [RAG Retriever] ──→ [H2 Agent]
                                                               │
                                                     [Response Composer]
                                                               │
                                                      SSE Stream 출력
                                                               │
                                                  ┌────────────┼────────────┐
                                                  │            │            │
                                              텍스트        테이블        차트
                                           (스트리밍)     (JSON 배열)   (Plotly)
  • Query Parser: 질문을 파싱하여 법인, 분석 유형, 기간을 추출. 이 결과로 어떤 Agent를 호출할지 조건부 라우팅
  • Sorting Agent: 9개 법인의 주요 경영분석(매출, 원가, GP, 추세) 담당. Strategy에서 제공하는 법인별 프롬프트와 데이터 구조 활용
  • MR Agent: 원자재 재활용 법인(RM오산, RM화성) 전용. 반입처/품목별 스프레드 분석에 특화
  • H2 Agent: 수소 사업(H2 법인) 전용. 수소 충전소 매출/원가 구조에 특화

SSE 스트리밍 프로토콜

각 노드가 독립적으로 작업을 완료하면 SSE(Server-Sent Events)로 즉시 프론트에 전달한다. 사용자는 텍스트가 먼저 스트리밍되고, 이어서 테이블과 차트가 렌더링되는 것을 경험한다.

응답 메시지의 content_type으로 렌더링 방식을 구분한다:

content_type구조렌더링
text단일 문자열 (스트리밍)마크다운 텍스트
multiJSON 배열 [{type, data}, ...]텍스트 + 테이블 + 차트 복합

multi 타입의 각 블록은 type: "text", type: "table" (헤더 + 행 데이터), type: "chart" (Plotly JSON spec), type: "follow_up" (추천 질문 리스트)로 구분된다.

문제 3: 비정형 경영 데이터를 정확히 검색해야 한다

LLM이 정확한 분석을 하려면 법인별 데이터 구조, 계정 체계, 분석 기준을 이해해야 한다. 9개 법인의 데이터 스키마가 모두 다르고, 같은 “매출”이라도 법인마다 의미와 항목 구성이 다르다. 이 정보를 모든 프롬프트에 하드코딩하면 토큰 비용과 유지보수 부담이 급증한다.

해결: ChromaDB RAG 파이프라인 + 법인별 필터링

1
2
3
4
5
6
7
8
9
10
사용자 질문 → Embedding
                │
     ChromaDB 검색 (법인별 collection 필터)
                │
         Top-K 문서 검색
         ├── 데이터 사전: 테이블/컬럼 설명, 계정 체계
         ├── 분석 템플릿: 법인별 분석 절차, 지표 산출 방식
         └── FAQ: 자주 묻는 질문과 표준 응답 패턴
                │
     System Prompt + Retrieved Context → LLM Agent
  • 법인별 Collection 관리: 각 법인의 데이터 사전, 분석 템플릿, FAQ를 별도로 임베딩하여 관리. 검색 시 사용자의 법인 할당 정보로 자동 필터링
  • 임베딩 대상: 테이블 스키마 설명, 계정 코드 매핑, 분석 절차 문서, 이전 분석 리포트 요약
  • ChromaDB 선택 이유: 경량(별도 서버 불필요), Python 네이티브 임베딩, 메타데이터 필터링 지원으로 법인별 격리가 간단

이 구조로 각 질문에 가장 관련성 높은 컨텍스트만 주입하여 토큰 효율성과 분석 정확도를 동시에 확보했다.

문제 4: 세분화된 접근 제어가 필요하다

9개 법인의 사용자가 같은 시스템을 사용하지만, A법인 사용자가 B법인 데이터를 조회하면 안 된다. 또한 관리자/일반 사용자/뷰어 등 역할별 기능 제한이 필요했다.

해결: RBAC V2 (Role-Based Access Control)

기존 단순 역할 시스템을 세분화된 권한 코드 체계로 재설계했다:

1
2
3
4
5
6
7
User → Role → [Permission 1, Permission 2, ...]
               │
               ├── FEATURE_CHAT_USE        (챗봇 사용)
               ├── FEATURE_ADMIN_USER_MANAGE (사용자 관리)
               └── ...

+ BusinessUnitAssignment (법인 접근 제어)
  • FastAPI의 Depends로 모든 엔드포인트에 require_permission() 주입
  • 법인 필터링: 사용자의 BusinessUnitAssignment에 따라 조회 가능한 데이터 자동 제한
  • 감사 로그: RBAC 변경 이력을 rbac_audit_logs 테이블에 자동 기록
  • 토큰 갱신/무효화: refresh_tokens 테이블로 토큰 로테이션 관리

문제 5: HR 시스템과 사용자 정보 동기화

직원 입사/퇴사/부서 이동이 빈번한데, 챗봇의 사용자 정보를 수동으로 관리하면 불일치가 발생한다.

해결: APScheduler 기반 자동 동기화

  • HR/ERP 시스템에서 주기적으로 사용자/부서 데이터를 가져와 동기화
  • 사이트 코드 매핑으로 HR 시스템의 조직 코드를 플랫폼 식별자로 변환
  • 신규 사용자는 must_change_password 플래그와 함께 자동 생성
  • 조건부 기동: 환경 설정에 따라 스케줄러 활성화/비활성화

데이터 흐름 아키텍처

시스템 전체의 데이터 흐름을 정형 데이터(PostgreSQL)와 비정형 데이터(ChromaDB) 두 축으로 구성했다:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[ERP/HR 시스템]
      │
      ├── APScheduler 동기화 ──→ PostgreSQL (정형 데이터)
      │                          ├── 매출/원가/수익 실적
      │                          ├── 법인/부서/사용자 정보
      │                          └── 대화 이력/피드백
      │
      └── Embedding Pipeline ──→ ChromaDB (벡터 데이터)
                                  ├── 법인별 데이터 사전
                                  ├── 분석 템플릿
                                  └── FAQ/표준 응답
                                          │
사용자 질문 ─→ [Query Parser] ─→ [RAG Retriever] ─→ [LLM Agent] ─→ SSE Response
                    │                   │                  │
               intent 추출       ChromaDB 검색      PostgreSQL 조회
               법인 식별         context 주입        정형 데이터 분석
  • 정형 데이터: ERP에서 동기화된 매출/원가/수익 실적을 PostgreSQL에 저장. LLM Agent가 분석 시 SQL을 통해 직접 조회
  • 비정형 데이터: 법인별 데이터 사전, 분석 절차, FAQ를 ChromaDB에 임베딩. RAG로 검색하여 프롬프트에 컨텍스트 주입
  • 두 데이터의 결합: RAG가 “무엇을 어떻게 분석할지”를 안내하고, PostgreSQL이 “실제 분석할 데이터”를 제공

아키텍처

레이어 구조

1
2
3
4
5
6
7
8
9
10
Router (API 엔드포인트)
   │
   ▼
Service (비즈니스 로직)
   │
   ▼
Repository (데이터 접근)
   │
   ▼
Database (PostgreSQL + ChromaDB)

모든 레이어는 FastAPI의 의존성 주입으로 연결되며, @transactional 데코레이터로 트랜잭션을 관리한다.

API 엔드포인트 (30+)

도메인주요 엔드포인트기능
AuthPOST /token, POST /login, POST /refreshJWT 인증, 토큰 갱신
ConversationsPOST /{id}/messages/streamSSE 스트리밍 분석 응답
ConversationsPOST /{id}/regenerate답변 재생성
ConversationsPOST /{id}/messages/{msg_id}/feedback피드백 (like/dislike)
Admin/admin/users/*, /admin/roles/*, /admin/rbac/*사용자/역할/권한 관리
HR SyncPOST /sync, GET /statusHR 동기화 트리거/상태

데이터베이스 (15+ 테이블)

핵심 테이블: users, roles, permissions, role_permissions, conversations, conversation_messages, refresh_tokens, departments, user_affiliates, rbac_audit_logs, suggested_questions

Alembic으로 18개 마이그레이션 버전을 관리한다.


기술적 의사결정

결정이유
FastAPI (async)9개 법인 동시 요청 처리, SSE 스트리밍에 async 필수
LangGraph다단계 분석을 상태 머신으로 명확히 분리, 노드별 독립 실행 및 스트리밍 가능
LangGraph vs LangChain AgentAgent는 자율 도구 호출 방식이라 디버깅이 어렵고, 상태 머신은 흐름이 명시적이어서 기업 환경에 적합
Strategy Pattern법인 추가 시 기존 코드 수정 없이 확장 (OCP 준수)
ChromaDB경량 벡터 DB, 별도 서버 불필요, 메타데이터 필터로 법인 격리 간단
SSE vs WebSocket단방향 스트리밍에 SSE가 적합, 브라우저 자동 재연결 내장, 인프라 단순
PostgreSQL (asyncpg)트랜잭션 안정성 + 비동기 성능
Docker 멀티스테이지빌드 이미지(gcc 포함) vs 런타임 이미지(slim) 분리로 최종 이미지 경량화

기술 스택

분류기술선택 이유
FrameworkFastAPI 0.121, Uvicornasync 네이티브, OpenAPI 자동 생성
DB (정형)PostgreSQL (asyncpg)트랜잭션 안정성 + 비동기 성능
DB (벡터)ChromaDB경량 벡터 DB, 서버리스 임베딩
ORMSQLAlchemy 2.0 (async) + Alembic타입 안전 쿼리 + 마이그레이션 관리
AuthJWT (python-jose) + bcrypt토큰 기반 인증 + 안전한 패스워드 해싱
LLMLangChain + LangGraph체인 구성 + 상태 머신 기반 에이전트
LLM ProviderAnthropic Claude, OpenAI기업 분석 품질 확보
StreamingSSE (Server-Sent Events)단방향 스트리밍, 자동 재연결
SchedulingAPSchedulerHR/ERP 동기화 스케줄링
Testingpytest (1,400+ 케이스)131 파일, 레이어별 단위/통합 테스트
InfraDocker (multi-stage), uv경량 이미지, 빠른 의존성 해결

성과 지표

항목수치
소스 파일200+
테스트 케이스1,400+ (131 파일)
API 엔드포인트30+
DB 테이블15+
마이그레이션18개
지원 법인9개
에이전트 유형3종 (Sorting / MR / H2)
법인별 Strategy9개 (신규 법인 추가 시 Strategy 1개 작성)