[PitchCoach] AI 분석 파이프라인을 백엔드 서비스로 연결하기

현서·2026년 5월 23일

들어가며

POKI는 캡스톤디자인 프로젝트로 시작해, 스타트 학기와 그로쓰 학기 두 단계로 개발을 이어간 프로젝트입니다.

스타트 학기에는 공고문과 IR Deck을 분석하기 위한 AI 파이프라인 자체를 설계하는 데 집중했습니다. Google Document AI로 PDF를 구조화하고, Gemini를 활용해 공고문 평가 기준과 IR Deck의 부족한 지점을 찾아내는 구조였습니다.

이번 글은 그 다음 단계인 그로쓰 학기에서의 구현을 다룹니다.

스타트 학기에는 "AI가 PDF를 어떻게 읽고 분석할 것인가"가 중심이었다면, 그로쓰 학기에는 "이 분석 파이프라인을 실제 백엔드 서비스와 어떻게 연결하고 배포할 것인가"가 중심 과제였습니다.

당시에는 AI 파이프라인 자체가 가장 큰 문제라고 생각했습니다.

그런데 백엔드와 연결하기 시작하자 문제의 성격이 조금 달라졌습니다.

PDF를 넣고 JSON이 나오는 것까지는 파이프라인의 문제였습니다. 하지만 사용자가 실제 서비스에서 이 기능을 쓰려면 다음 질문에 답해야 했습니다.

  • 분석이 오래 걸리는 동안 프론트는 어떤 상태를 보여줄 것인가?
  • 분석 결과는 어디에 저장할 것인가?
  • 공고문에서 뽑은 평가 기준을 IR Deck 분석에 어떻게 다시 사용할 것인가?
  • AI 응답이 비어 있거나 일부 기준을 누락하면 어떻게 처리할 것인가?
  • Python 기반 AI 서버와 NestJS 백엔드는 어떻게 배포하고 연결할 것인가?

이번 글에서는 이 질문들을 해결하면서 AI 파이프라인을 실제 백엔드 서비스 흐름에 붙인 과정을 정리합니다.

구현 목표는 다음과 같이 정리할 수 있었습니다.

요구사항구현 방향
PDF 분석은 오래 걸리는 작업업로드 API는 202 Accepted만 반환하고 분석은 비동기로 처리
분석 상태를 화면에 노출해야 함IN_PROGRESS, COMPLETED, FAILED 상태를 DB에 저장
공고문 기준이 IR Deck 분석에 이어져야 함NoticeEvaluationCriteriastrategy_json으로 변환해 FastAPI에 전달
AI 응답이 일부 누락될 수 있음DB 기준을 source of truth로 두고 AI 결과를 merge
LayoutLM 기반 구조 태깅은 운영 부담이 큼Google 계열 embedding 기반 RAG 매칭으로 전환
Python AI 서버와 NestJS 백엔드를 함께 운영해야 함EC2 + Docker Compose 내부 네트워크로 연결

1. 백엔드와 AI 서버를 분리한 이유

POKI 서버는 두 개의 애플리케이션으로 나뉩니다.

  • Pitchcoach-BACK: NestJS 기반 백엔드
  • Pitchcoach-AI: FastAPI 기반 AI 서버

처음부터 이렇게 나눠야 한다고 확신했던 것은 아닙니다. 단순하게 생각하면 NestJS 백엔드 안에서 AI 분석 API를 호출하고 결과만 저장하면 될 것처럼 보입니다.

하지만 실제 파이프라인을 보면 Python 쪽 의존성이 적지 않았습니다.

  • Google Document AI SDK
  • PDF 처리 및 chunking 유틸리티
  • Gemini 기반 JSON 파싱
  • IR Deck RAG pipeline
  • 추후 음성 분석을 위한 ffmpeg, librosa 계열 의존성

반대로 NestJS 쪽은 인증, 권한 검사, Pitch 상태 관리, Prisma 기반 DB 저장, Swagger 문서화처럼 제품 API에 가까운 역할을 맡고 있었습니다.

그래서 책임을 다음처럼 나눴습니다.

NestJS  = 제품 API와 데이터의 소유자
FastAPI = AI 분석 실행자

이 구조에서는 프론트엔드가 FastAPI를 직접 호출하지 않습니다. 사용자는 NestJS API만 호출하고, NestJS가 내부적으로 FastAPI에 분석을 요청합니다.

Client
  ↓
NestJS API
  ├─ 인증/권한 검사
  ├─ DB 상태 생성
  ├─ FastAPI 분석 요청
  └─ 분석 결과 DB 동기화
        ↓
     FastAPI AI
        ├─ 공고문 분석
        └─ IR Deck 분석

FastAPI를 외부에 공개하지 않은 이유도 여기에 있습니다. AI 서버는 분석을 실행하는 내부 컴포넌트이고, 서비스의 상태와 권한은 NestJS가 통제하는 편이 더 단순했습니다.

2. 공고문 분석을 비동기 작업으로 만든 이유

공고문 분석 API는 다음 경로로 만들었습니다.

POST /api/pitches/{pitchId}/notices/analyze

사용자가 공고문 PDF를 업로드하면 NestJS는 먼저 기본적인 검증을 합니다.

  • 존재하는 Pitch인지 확인
  • 현재 사용자의 Pitch인지 확인
  • PDF 파일인지 확인
  • 파일 크기가 제한을 넘지 않는지 확인

검증이 끝나면 FastAPI 응답을 기다리지 않고 먼저 DB에 Notice 레코드를 만듭니다.

analysisStatus = IN_PROGRESS
pdfUploadStatus = PROCESSING
version = latestVersion + 1
isLatest = true

응답은 202 Accepted로 바로 내려줍니다.

{
  "notice_id": "notice-id",
  "pitch_id": "pitch-id",
  "analysis_status": "IN_PROGRESS",
  "message": "공고문 분석이 시작되었습니다."
}

이렇게 만든 이유는 PDF 분석이 네트워크 요청 하나 안에서 끝날 만큼 짧은 작업이 아니기 때문입니다. OCR, 표 추출, Gemini 분석까지 거치면 사용자가 요청이 끝날 때까지 기다리는 구조는 적절하지 않았습니다.

그래서 업로드 API는 "분석 결과를 반환하는 API"가 아니라 "분석 작업을 시작하는 API"로 설계했습니다.

프론트엔드는 이후 조회 API를 polling합니다.

GET /api/notices/{noticeId}

조회 시점에 분석이 끝났으면 결과를 보여주고, 아직이면 IN_PROGRESS 상태만 반환합니다.

3. 분석 전에도 기준이 필요했던 이유

공고문 분석을 붙이면서 가장 먼저 생긴 문제는 빈 화면이었습니다.

AI 분석이 끝나기 전까지는 공고문에서 추출한 평가 기준이 없습니다. 그런데 프론트 화면은 평가 기준 섹션을 중심으로 구성되어 있었습니다. 분석이 실패하거나 지연되면 사용자는 아무것도 볼 수 없게 됩니다.

그래서 공고문 레코드를 만들 때 기본 평가 기준을 함께 저장했습니다.

Pitch 유형에 따라 기본 기준을 다르게 두었습니다.

  • 정부지원형: 문제정의, 솔루션, 시장/비즈니스, 실적, 팀, 자금 계획
  • 경진대회형: 문제정의, 솔루션, 시장/비즈니스, 실적, 팀, 자금 계획
  • 기본형: VC Demo 기준의 문제정의, 솔루션, 시장/비즈니스, 실적, 팀, 자금 계획

예를 들면 이런 형태입니다.

{
  "criteria_name": "문제정의",
  "points": 20,
  "pitchcoach_interpretation": "사회·산업 문제의 구체성과 검증 근거를 평가합니다.",
  "ir_guide": "문제 규모와 수혜대상, 검증 데이터(인터뷰/설문)를 포함하세요."
}

이 기본 기준은 최종 결과가 아닙니다. 분석이 완료되면 FastAPI가 반환한 실제 공고문 기준으로 교체됩니다.

하지만 이 fallback이 있으면 두 가지가 좋아집니다.

첫째, 분석이 지연되어도 프론트가 빈 상태가 되지 않습니다.
둘째, Gemini가 기준을 일부 누락하더라도 서비스가 최소한의 기준을 유지할 수 있습니다.

AI 기능을 붙일 때 모델 응답을 전제로 화면을 만들면 작은 실패에도 UX가 크게 흔들립니다. 이 부분은 구현하면서 계속 신경 쓴 지점이었습니다.

4. FastAPI에서는 BackgroundTasks로 분석 넘기기

FastAPI의 공고문 분석 API도 같은 방식으로 동작합니다.

POST /api/pitches/{pitch_id}/notices/analyze

요청이 들어오면 파일을 저장하고, 실제 분석은 BackgroundTasks로 넘깁니다.

PDF 업로드
  ↓
파일 저장
  ↓
BackgroundTasks에 분석 작업 등록
  ↓
202 응답

공고문 분석 파이프라인은 두 단계로 구성했습니다.

[Stage 1] Document AI
  - 텍스트 추출
  - 표 구조 추출
  - metadata 정리

[Stage 2] Gemini
  - 공고명/주최기관/모집유형 추출
  - 평가 기준과 배점 추출
  - 추가 가점 항목 추출
  - IR Deck 작성 가이드 생성

FastAPI 내부에서는 분석 상태를 IN_PROGRESS, COMPLETED, FAILED로 관리합니다. NestJS는 이후 이 상태를 조회해서 자신의 DB에 동기화합니다.

여기서 Celery나 Redis Queue까지 도입하지는 않았습니다. 현재 요구사항에서는 분석 작업을 비동기로 넘기고 polling으로 확인하는 정도면 충분했습니다.

물론 동시에 많은 사용자가 대용량 IR Deck을 업로드하거나, 실패한 작업을 재시도해야 하는 요구사항이 생기면 큐 기반 구조가 더 적합합니다. 다만 초기 구현 단계에서는 큐를 먼저 도입하기보다, FastAPI의 BackgroundTasks와 NestJS의 polling 구조로 흐름을 검증하는 편이 낫다고 판단했습니다.

5. LayoutLM에서 Google Embedding 기반 RAG로 바꾼 이유

이전 글에서는 Document AI 결과를 LayoutLMv3 입력으로 변환하고, 슬라이드 블록의 구조를 태깅하는 방향을 설명했습니다.

초기 설계에서는 이 접근이 자연스러웠습니다. Document AI가 텍스트와 좌표를 추출하고, LayoutLM이 이 블록이 제목인지 본문인지, 혹은 캡션인지 판단하면 IR Deck의 구조를 더 잘 이해할 수 있을 것이라고 봤습니다.

하지만 백엔드와 배포까지 연결하면서 우선순위가 바뀌었습니다.

서비스에서 정말 필요했던 것은 "이 텍스트 블록이 제목인가?"보다 "이 슬라이드가 공고문의 어떤 평가 기준을 뒷받침하는가?"에 가까웠습니다.

예를 들어 공고문에 시장성 기준이 있고, IR Deck 5번 슬라이드에 TAM/SAM/SOM과 고객 규모가 들어 있다면, 시스템은 두 정보를 연결해야 합니다. 이 문제는 레이아웃 분류보다 의미 기반 검색에 더 가까웠습니다.

그래서 실제 구현에서는 LayoutLM 중심 구조 태깅을 핵심 경로에서 제외하고, Google 계열 embedding 기반 RAG 매칭으로 전환했습니다.

현재 AI 서버의 임베딩 클라이언트는 두 가지 경로를 지원합니다.

  • Gemini REST gemini-embedding-001
  • Vertex AI embedding 경로

코드에서는 우선 gemini-embedding-001을 사용하고, 실패하면 Vertex AI 또는 deterministic fallback embedding으로 내려가도록 구성했습니다.

IR Deck slide text
  ↓
Gemini / Google embedding
  ↓
Rubric item embedding
  ↓
Cosine similarity 기반 top-k slide retrieval
  ↓
기준별 점수와 근거 슬라이드 산출

이 전환에는 세 가지 이유가 있었습니다.

첫째, 배포 부담이 줄었습니다. LayoutLM을 운영 경로에 넣으면 torch, transformers, 이미지 텐서 처리 의존성이 커집니다. 반면 현재 배포용 FastAPI 컨테이너는 OCR, PDF 처리, Gemini 호출 중심으로 가볍게 유지할 수 있습니다.

둘째, 공고문 기준과 IR Deck 슬라이드의 연결에는 임베딩이 더 직접적이었습니다. LayoutLM은 문서 블록의 구조를 이해하는 데 강점이 있지만, POKI의 IR Deck 분석에서는 공고문 루브릭 항목과 슬라이드 내용을 의미적으로 매칭하는 것이 더 중요했습니다.

셋째, fallback 설계가 쉬웠습니다. embedding API가 실패해도 간단한 deterministic embedding으로 최소한의 유사도 계산을 유지할 수 있습니다. 모델 inference 자체가 실패하면 전체 분석이 멈추는 구조보다 운영상 다루기 쉬웠습니다.

정리하면, LayoutLM은 문서 구조 이해에는 좋은 선택이었지만 실제 서비스의 IR Deck 분석 문제는 "구조 태깅"보다 "평가 기준과 슬라이드의 의미 매칭"에 가까웠습니다. 그래서 백엔드 연동 이후의 구현에서는 Google Embedding 기반 RAG가 더 적합했습니다.

6. ai://로 NestJS 레코드와 FastAPI 작업 연결하기

NestJS와 FastAPI는 서로 다른 서버입니다. 따라서 NestJS의 notice.id와 FastAPI가 생성한 notice_id는 다릅니다.

처음에는 이 매핑을 위한 별도 컬럼을 둘까 고민했는데, 구현 단계에서는 pdfUrl 필드에 다음처럼 저장했습니다.

ai://notice-fastapi-id

예를 들어 NestJS가 FastAPI에 공고문 분석을 요청하고 FastAPI가 notice-abc를 반환하면, NestJS는 자신의 Notice 레코드에 ai://notice-abc를 저장합니다.

이 값이 있으면 이후 polling 때 어떤 FastAPI 작업을 조회해야 하는지 알 수 있습니다.

GET /api/notices/{noticeId}
  ↓
NestJS DB에서 Notice 조회
  ↓
pdfUrl이 ai://로 시작하는지 확인
  ↓
FastAPI /api/notices/{aiId} 조회
  ↓
COMPLETED면 NestJS DB에 결과 저장

완성된 모델링이라고 보기는 어렵지만, 초기 연동 단계에서는 이 방식이 단순했습니다.

  • NestJS DB ID와 FastAPI 작업 ID를 분리할 수 있습니다.
  • 외부 파일 URL을 노출하지 않아도 됩니다.
  • ai:// prefix만 보고 AI 작업 참조인지 구분할 수 있습니다.
  • 나중에 S3 URL과도 구분 가능합니다.

장기적으로는 aiJobId 같은 별도 컬럼을 두는 편이 더 명확합니다. 다만 초기 제품 구현에서는 이 방식으로도 NestJS와 FastAPI 사이의 연결 구조를 빠르게 검증할 수 있었습니다.

7. 공고문 기준을 사용자가 수정할 수 있게 만들기

공고문 분석 결과는 항상 완벽하지 않습니다.

특히 평가 기준명과 배점은 서비스에서 매우 중요한 데이터입니다. IR Deck 분석 점수의 기준이 되기 때문입니다. 그래서 사용자가 공고문 분석 결과를 확인하고 직접 수정할 수 있도록 했습니다.

수정 API는 다음과 같습니다.

PATCH /api/notices/{noticeId}

여기서 가장 중요한 검증은 배점 합계입니다.

evaluation_criteria.points 합계 = 100

합계가 100이 아니면 POINTS_SUM_INVALID를 반환합니다.

사용자가 기준을 수정하면 NestJS는 기존 NoticeEvaluationCriteria를 삭제하고 새 기준으로 교체합니다. 그리고 FastAPI에도 수정된 기준을 전달해 pitchcoach_interpretation, ir_guide를 다시 맞추도록 했습니다.

즉, 공고문 수정은 단순히 화면에 보이는 텍스트 수정이 아닙니다.

수정된 기준은 이후 IR Deck 분석에 그대로 들어갑니다. 그래서 이 데이터는 단순한 UI state가 아니라 분석 pipeline의 입력값으로 봐야 했습니다.

8. IR Deck 분석에 공고문 기준을 strategy_json으로 넘기기

IR Deck 분석에서 가장 신경 쓴 부분은 공고문 분석 결과와의 연결이었습니다.

공고문 없이 IR Deck만 분석하면 결국 일반적인 피드백이 나오기 쉽습니다. 하지만 POKI가 하려던 것은 특정 공고문 기준에 맞는 IR Deck 피드백이었습니다.

그래서 IR Deck 업로드 시 NestJS는 최신 공고문을 조회합니다.

Pitch
  └─ latest Notice
       └─ NoticeEvaluationCriteria[]

그 다음 평가 기준을 FastAPI가 이해할 수 있는 strategy_json으로 변환합니다.

{
  "type": "STARTUP_CONTEST",
  "focus_point": "시장성과 실행 가능성 중심 평가",
  "presentation_minutes": 8,
  "required_sections": ["problem", "solution", "market", "team"],
  "source": "notice_evaluation_criteria",
  "evaluation_criteria": [
    {
      "criteria_name": "시장성",
      "points": 30,
      "pitchcoach_interpretation": "시장 규모와 성장 가능성을 평가합니다.",
      "ir_guide": "TAM/SAM/SOM과 수익 모델을 명확히 제시하세요."
    }
  ]
}

그리고 IR Deck PDF와 함께 FastAPI로 보냅니다.

POST /api/pitches/{pitchId}/ir-decks/analyze
Content-Type: multipart/form-data

file = IR_Deck.pdf
strategy_json = {...}
pitch_type = STARTUP_CONTEST

이전 글에서 말했던 공고문과 IR Deck의 alignment는 실제 구현에서는 이 API 계약으로 연결되었습니다.

공고문 분석 결과가 NoticeEvaluationCriteria로 저장되고, IR Deck 분석 시 strategy_json으로 변환되어 들어갑니다. FastAPI는 이 기준을 바탕으로 IR Deck을 평가합니다.

9. IR Deck 결과를 summary와 slide detail로 나누기

FastAPI의 IR Deck 분석 결과는 크게 네 가지로 정리됩니다.

  • 전체 점수와 구조 요약
  • 공고문 기준별 점수
  • 발표 가이드
  • 슬라이드별 피드백

NestJS에서는 이 결과를 그대로 하나의 JSON으로 저장하지 않았습니다. 프론트에서 필요한 화면 단위에 맞춰 DB 모델을 나눴습니다.

DB 모델역할
IRDeck분석 상태, 버전, 총점, 발표 가이드
DeckScore전체 점수, 구조 요약, 강점, 개선점
CriteriaScore공고문 기준별 점수와 피드백
Slide슬라이드 번호, 카테고리, 점수
SlideFeedback슬라이드별 상세 피드백

API도 두 개로 나눴습니다.

GET /api/ir-decks/{deckId}
GET /api/ir-decks/{deckId}/slides

첫 번째 API는 분석 메인 화면에 필요한 데이터를 반환합니다.

  • 총점
  • 강점/개선점
  • 기준별 점수
  • 발표 가이드

두 번째 API는 슬라이드 상세 화면에 필요한 데이터를 반환합니다.

  • 슬라이드 번호
  • 카테고리
  • 슬라이드 점수
  • 상세 피드백
  • 슬라이드별 강점/개선점

이렇게 나눈 이유는 응답 크기와 화면 책임 때문입니다. 분석 메인 화면에서 매번 모든 슬라이드 상세 피드백까지 받을 필요는 없었습니다.

10. AI 결과를 그대로 저장하지 않고 DB 기준으로 merge하기

IR Deck 결과 저장에서 가장 신경 쓴 부분은 CriteriaScore였습니다.

FastAPI가 공고문 기준별 점수를 반환하더라도, 항상 모든 기준을 빠짐없이 반환한다고 가정할 수는 없습니다.

예를 들어 공고문 기준은 네 개인데 AI 결과가 두 개만 올 수 있습니다.

공고문 기준
  - 문제정의
  - 솔루션
  - 시장/비즈니스
  - 팀

AI 결과
  - 문제정의
  - 시장/비즈니스

이때 AI 결과만 저장하면 프론트에서는 솔루션, 기준이 사라진 것처럼 보입니다. 사용자는 공고문 분석 화면에서 봤던 기준이 IR Deck 분석 화면에도 그대로 있길 기대합니다.

그래서 저장 시점에 DB의 NoticeEvaluationCriteria를 기준으로 AI 결과를 merge했습니다.

최종 CriteriaScore
  - 문제정의: AI 점수 사용
  - 솔루션: fallback score 생성
  - 시장/비즈니스: AI 점수 사용
  - 팀: fallback score 생성

누락된 기준에는 0점과 함께 fallback 메시지를 넣었습니다.

AI 분석 결과에 해당 공고문 평가 기준의 개별 점수가 포함되지 않았습니다.

이 방식은 조금 보수적입니다. 하지만 서비스 입장에서는 AI 응답보다 사용자가 확인한 공고문 기준이 더 안정적인 기준점이었습니다.

이 구조에서는 NestJS DB가 source of truth이고, AI 결과는 그 위에 붙는 분석값으로 다뤘습니다.

11. 상태는 단순하게, timeout은 반드시

공고문과 IR Deck 모두 상태는 세 가지로 관리했습니다.

IN_PROGRESS
COMPLETED
FAILED

업로드 직후에는 IN_PROGRESS, 분석 성공 시 COMPLETED, 실패 시 FAILED로 바꿉니다.

IR Deck 분석은 상대적으로 오래 걸리기 때문에 NestJS 내부에서 일정 간격으로 FastAPI 결과를 확인하는 poller를 두었습니다.

IR_DECK_SYNC_INTERVAL_MS = 5000
IR_DECK_ANALYSIS_TIMEOUT_MINUTES = 15

timeout을 둔 이유는 단순합니다. AI 서버 작업이 중간에 멈췄을 때 사용자가 계속 분석 중 화면에 남아 있으면 안 되기 때문입니다.

이런 상태 관리는 기능 자체보다 덜 화려해 보이지만, 실제 서비스에서는 거의 필수에 가깝습니다. AI 분석은 실패 가능성이 있는 외부 작업이고, 실패했을 때도 사용자에게 설명 가능한 상태가 남아야 합니다.

12. 배포 구조: EC2 + Docker Compose

배포는 EC2 위에서 Docker Compose로 구성했습니다.

EC2
  ├─ Nginx
  │   └─ HTTPS 443 → NestJS 3000
  ├─ NestJS API
  ├─ FastAPI AI
  └─ PostgreSQL

NestJS는 환경변수로 FastAPI 내부 주소를 가집니다.

AI_SERVER_URL=http://fastapi-ai:8000

Docker Compose에서는 NestJS, FastAPI, PostgreSQL을 같은 네트워크에 올립니다.

services:
  nestjs:
    build: ./Pitchcoach-BACK
    ports:
      - "3000:3000"
    environment:
      DATABASE_URL: postgresql://postgres:postgres@postgres:5432/poki?schema=public
      AI_SERVER_URL: http://fastapi-ai:8000

  fastapi-ai:
    build: ./Pitchcoach-AI
    env_file:
      - ./Pitchcoach-AI/.env
    volumes:
      - ./Pitchcoach-AI/credentials:/app/credentials:ro

  postgres:
    image: postgres:15

FastAPI 컨테이너에는 PDF/음성 처리를 위한 시스템 패키지를 함께 설치했습니다.

FROM python:3.11-slim

RUN apt-get update && apt-get install -y --no-install-recommends \
    poppler-utils \
    ffmpeg \
    && rm -rf /var/lib/apt/lists/*

poppler-utils는 PDF 렌더링과 페이지 처리에 필요했고, ffmpeg는 음성 분석 기능까지 고려해 포함했습니다.

NestJS 컨테이너는 시작 시 Prisma migration을 적용하도록 했습니다.

CMD ["sh", "-c", "npx prisma migrate deploy && node dist/src/main.js"]

AI 분석 결과는 DB 스키마와 강하게 연결되어 있기 때문에, migration이 빠진 상태로 서버만 뜨면 결과 저장 단계에서 문제가 생길 수 있습니다. 그래서 컨테이너 시작 시점에 migration을 적용하도록 구성했습니다.

13. 배포 자동화

CI/CD는 GitHub Actions에서 EC2에 SSH로 접속해 Docker Compose를 재실행하는 방식으로 구성했습니다.

main branch push
  ↓
GitHub Actions
  ↓
EC2 SSH 접속
  ↓
git pull
  ↓
docker compose down
  ↓
docker compose up -d --build

아직 복잡한 무중단 배포까지는 적용하지 않았습니다. 프로젝트 규모와 일정상 우선은 단순한 compose 재빌드가 적절했습니다.

다만 백엔드와 AI 서버가 각각 배포될 수 있기 때문에 동시에 compose를 재시작하면 충돌할 수 있습니다. 이 부분은 이후 배포 파이프라인을 분리하거나, 서비스 단위 재시작으로 바꿀 필요가 있습니다.

14. 구현하고 나서 바뀐 생각

처음에는 AI 파이프라인의 핵심이 모델 선택과 프롬프트 설계라고 생각했습니다.

물론 그 부분도 중요합니다. 하지만 백엔드에 붙이고 나니 다른 문제가 더 크게 보였습니다.

  • 분석 작업은 오래 걸립니다.
  • 사용자는 중간 상태를 봐야 합니다.
  • 결과는 다시 조회 가능해야 합니다.
  • AI 응답은 누락될 수 있습니다.
  • 공고문 기준은 IR Deck 분석까지 이어져야 합니다.
  • 프론트는 FastAPI와 NestJS의 차이를 몰라도 되어야 합니다.

결국 제품에서 AI는 단일 API 호출이 아니라 상태를 가진 워크플로우에 가깝습니다.

이번 구현을 통해 공고문 분석 결과는 단독 리포트로 끝나지 않게 되었습니다. 공고문 기준은 IR Deck 분석의 입력이 되고, IR Deck 분석 결과는 다시 음성 분석과 Q&A 훈련의 컨텍스트로 이어집니다.

이전 글에서 만든 파이프라인이 비정형 PDF를 구조화하는 작업이었다면, 이번 작업은 그 구조화된 데이터를 서비스의 흐름 안에 연결하는 작업이었습니다.

POKI에서 AI 서버는 단순히 답변을 생성하는 서버가 아니라, 발표 평가에 필요한 기준과 근거를 만들어 백엔드에 전달하는 내부 분석 컴포넌트가 되었습니다.

profile
LLM부터 풀스택까지, 만들면서 기록합니다

0개의 댓글