조달청 평가 시스템 — ai_engine 최적 활용 가이드

eval-system-premium BFF 가 ai_engine 의 capabilities 를 가장 잘 쓸 수 있는 방법 — endpoint + 모델 + 응답 schema 기반 매핑

작성: 2026-05-21 대상: jodal-eval-ai/pps-mono-repo ai_engine 위치: services/ai_engine/ BFF 측 어댑터: app/adapters/ai_engine_client.py

1TL;DR

핵심 결론: ai_engine 은 3-layer 풀스택 LLM 백엔드 — Online (실시간 chat) / Offline (배치 추출/매칭) / Embedding (벡터). 현재 BFF 는 /online/eval-chatbot/chat 1개만 사용. 9개 더 활용 가능. 특히 EvalChatResponse 의 ui_actions·citations·suggested_actions·tabular_data 가 데모 임팩트 핵심.
풀스택 capability
17 endpoints (online 7 + offline 9 + embedding 3) + 4 모델 슬롯 (qwen3-32B LLM ×2 + qwen3-VL vision + KURE-v1 embed)
현재 활용도
BFF 는 chat 1 endpoint 만. evidence drill-down + summary 만 추가했음. Offline / Embedding 0 활용
놓치고 있는 가치
tabular_data + ui_actions 활용 안 함. matcher 가 cross-DB write 로 BFF 가 못 읽음. embedding RAG 검색 미사용

2ai_engine 의 capability 전체 맵

Layer 1 — Online API 실시간 응답

Endpoint용도응답 스키마
POST /online/chatbot/generateRFP 요구사항 생성 (rfp-gen 전용)GenerateRequirementResponse
POST /online/chatbot/chatRFP 요구사항 편집 챗 (rfp-gen 전용)ChatResponse
POST /online/document-assistant/adapt-sectionsRFP 섹션 일괄 적응AdaptSectionsResponse
POST /online/document-assistant/edit-section단일 섹션 편집EditSectionResponse
POST /online/eval-chatbot/chat ★ 평가 핵심기술평가 챗봇 — proposal 분석 / 비교 / RAG / UI 제어EvalChatResponse (풍부함 ↓)
GET /online/eval-chatbot/healthhealth check{status}
GET /online/healthoverall health{status}

Layer 2 — Offline API 비동기 / 배치 / Celery

Endpoint용도활용 시점
POST /offline/rfp/structureRFP 전체 구조 분석 (목차 + 섹션 + 메타)RFP 업로드 직후 1회
POST /offline/rfp/extract-toc목차 추출RFP 구조 분석 단계
POST /offline/rfp/find-section-boundary섹션 시작/끝 위치 찾기목차 → 본문 매핑
POST /offline/rfp/find-section키워드 기반 섹션 검색특정 요구사항 위치 찾기
POST /offline/rfp/extract-value값 추출 (date/number/text)스칼라 필드 자동 채우기
POST /offline/rfp/analyze-image이미지 분석 (Qwen3-VL)시험성적서·도면 OCR/분석
POST /offline/rfp/extract-requirements일반 요구사항 추출RFP → requirement_items 시드
POST /offline/rfp/extract-tech-requirements주요기술 추출tech_match seed 의 item 마스터
POST /offline/rfp/extract-evaluation-items평가항목 추출eval_item_match seed 의 item 마스터
GET /offline/rfp/task/{task_id}비동기 작업 결과 폴링Celery task 결과 가져오기

Layer 3 — Embedding API 의미 검색

Endpoint용도모델
POST /embed단일 텍스트 → 벡터KURE-v1 (native 1024 dim)
POST /embed/batch다수 텍스트 → 벡터 배열KURE-v1
GET /embed/models사용 가능한 임베딩 모델 목록

3모델 슬롯 (.env.onprem)

슬롯모델KINDPURPOSEURL활용
qwen3_32bQwen/Qwen3-32Bllmrfp-structure:8305offline RFP 추출 일체
qwen3_vl_8b_instructQwen/Qwen3-VL-8B-Instructvisionrfp-image-analysis:8303시험성적서·도면 OCR/분석
qwen3_32b_evalQwen/Qwen3-32Bllmeval-chatbot:8305평가 챗봇 + evidence + summary
local_embeddingnlpai-lab/KURE-v1embeddingsemantic-search:8306제안서·RFP 벡터 검색 (RAG)
주의 — EMBEDDING_DIM 불일치 (5/22 D-1 결정 리스크): .env.onprem:58 = 1536, KURE-v1 native = 1024. 배포 직후 curl :8306/embed 응답 벡터 길이 확인 → 1024 면 env 수정 후 컨테이너 재기동.

4EvalChatResponse — ★ 데모 임팩트의 핵심

BFF 가 가장 많이 호출하는 /online/eval-chatbot/chat 의 응답에는 단순 텍스트가 아니라 UI 제어까지 포함됩니다. 현재 BFF 는 reply_message 만 사용 — 나머지 5개 필드 미활용.

필드타입현재 활용권장 활용
reply_messagestr✅ 사용그대로
ui_actionsList[UIAction]❌ 미사용navigate / highlight_element / filter 자동 발화 — "○○ 업체 강조" 클릭 한 번에 표 정렬·하이라이트
citationsList[{file_name, page_number, content_snippet}]⚠️ 일부 (evidence)모든 응답에 출처 인용 표시 — 평가위원 신뢰도 ↑
suggested_actionsList[{label, action_payload}]❌ 미사용후속 질문 버튼 자동 생성 — "다른 업체와 비교?", "특허 조회?"
tabular_dataDict (headers + rows)❌ 미사용비교 표를 LLM 이 직접 만들어 반환 — frontend 가 그대로 render
error_codeErrorCode enum⚠️ 일부OUT_OF_DOMAIN / MALICIOUS_INTENT_BLOCKED 등 가드레일 표시
input_tokens / output_tokensint✅ summary 에서 사용사용량 추적 / 비용 관리

UIAction 의 위력

LLM 이 답변하면서 동시에 frontend 에 "이것 하이라이트해", "이 탭으로 이동해", "이 필터 적용해" 명령 발행. 평가위원의 클릭 부담 0.

UIActionType:
  - NAVIGATE          // target_view 로 이동 (compare_overview / item_compare / company_detail_analysis)
  - HIGHLIGHT_ELEMENT // element_id 강조
  - FILTER            // metadata 의 조건으로 표 필터

UserIntentType — LLM 의 의도 분류

PROPOSAL_ANALYSIS  // 제안서 분석/검색
COMPARISON         // 복수 업체 비교
REQUIREMENT_CHECK  // RFP 요구사항 확인
NAVIGATION         // UI 네비게이션
GENERAL_QUESTION   // 일반 질의 (도메인 내)
OUT_OF_DOMAIN      // 도메인 외 → 가드레일

5BFF 기준 use case × ai_engine 매핑

Use case (BFF) ai_engine endpoint 모델
제안서 한 줄 요약 (POST /api/proposals/{id}/summary — 이미 구현) /online/eval-chatbot/chat + ui_context.intent=proposal_analysis qwen3_32b_eval 이미 ai_engine_client 통해 호출 중. citations 추가하면 신뢰도 ↑
업체간 비교 insight /online/eval-chatbot/chat + ui_context.intent=comparison + selected_companies[] qwen3_32b_eval tabular_data 응답을 그대로 표로 render. ui_actions 로 자동 하이라이트
evidence drill-down (이미 구현) /online/eval-chatbot/chat + ui_context.intent=evidence_drilldown qwen3_32b_eval 이미 사용 중. citations 활용 강화 권장
RFP 업로드 시 구조/요구사항 추출 /offline/rfp/extract-tech-requirements + /extract-requirements + /extract-evaluation-items qwen3_32b 현재 mock seed 로 채움 → 실제 RFP 파싱 결과로 교체. requirement_items 자동 시드
제안서 LLM 매칭 (tech / requirement / eval) Celery task → matcher → /online/eval-chatbot/chat 또는 별도 모듈 qwen3_32b_eval 현재 cross-DB 이슈로 fixture fallback. matcher 가 BFF DB 로도 dual write 필요
시험성적서·도면 분석 /offline/rfp/analyze-image qwen3_vl_8b_instruct ProductRecord.image_assets 의 시험성적서 이미지를 VLM 으로 분석 → 신뢰도 점수
제안서 RAG 검색 (의미 기반) /embed/batch + Qdrant 저장 → 검색 시 /embed KURE-v1 "이 제안서에 ○○ 기능 있나?" 같은 자연어 검색. evidence 자동 추출
평가 의견 자동 생성 (LLM이 평가 1차 초안) /online/eval-chatbot/chat + 전체 컨텍스트 qwen3_32b_eval 평가위원이 LLM 의 초안 수정만 하면 됨 → 시간 절감

6현재 BFF ai_engine_client 의 한계

한계현재 상태개선안
endpoint 1개만 호출POST /online/eval-chatbot/chat 만generic 메서드 추가 — post(path, payload) wrapper. offline + embedding 도 호출 가능
응답 schema 가 chat 전용AIEvalChatRequest / AIEvalChatResponse 만응답 타입을 generic 으로 — chat(req) -> EvalChatResponse 외에 offline_task(path, payload) -> dict 추가
cancel 메커니즘이 강제됨모든 호출이 cancel_store 폴링 (long-running chat 용)cancel 필요 없는 호출 (offline batch) 은 별도 메서드 — Redis 의존 0
ui_actions / suggested_actions 무시BFF 가 reply_message 만 frontend 로 전달Envelope.data 에 full EvalChatResponse 포함 → frontend 가 UI 자동 제어
citations 일부만evidence endpoint 에서만 활용summary / compare 등 모든 응답에 표시

7권장 BFF 보강 — 단계별 추가 endpoint

Phase 1 — 데모일 (5/22) D-1 안

BFF endpoint호출 대상예상 시간
POST /api/proposals/{id}/summary이미 구현 ✅0
POST /api/compare/{bid_id}/insight/online/eval-chatbot/chat (intent=comparison, selected_companies[])1시간
compare endpoint 응답에 tabular_data 추가위 insight 결과 가공30분
summary 응답에 citations 표시이미 받고는 있으나 frontend 가 표시 안 함30분 (FE)

Phase 2 — 데모 후 1주

BFF endpoint호출 대상가치
POST /api/rfp/structure/offline/rfp/structure → /extract-toc → /find-section-boundaryRFP 업로드 시 자동 구조화
POST /api/rfp/extract-items/offline/rfp/extract-tech-requirements + extract-requirements + extract-evaluation-itemsrequirement_items 자동 시드
POST /api/specs/{id}/embed/embed/batch (제안서 청크 → 벡터) + Qdrant upsertRAG 기반 검색 가능
POST /api/specs/{id}/analyze-images/offline/rfp/analyze-image (각 image_asset)시험성적서 자동 검증

Phase 3 — 데모 후 1개월

BFF endpoint호출 대상가치
POST /api/eval/{proposal_id}/auto-score모든 requirement_items × proposal RAG + LLM 매칭1차 자동 평가 초안
POST /api/eval/{proposal_id}/draft-opinionmatcher 결과 + 평가위원 의견 종합 → 평가 의견 초안평가위원은 검토만

8매처 (analysis_pipeline) cross-DB 이슈

현재 상태: services/analysis_pipeline/eval_system/result_saver.pyknowledge_hub.eval_db 로 write. BFF compare endpoint 는 자기 DB (eval_system_premium) 만 read → 매칭 결과 못 봄 → fixture fallback 활성

해결 옵션

옵션방법장단점
A. matcher dual writeresult_saver 가 두 DB 모두 write일관성 보장. 코드 변경 ~50 lines
B. BFF 가 양쪽 readBFF 가 knowledge_hub.eval_db 직접 queryBFF 가 두 DB 의존 — 헥사고날 깨짐
C. matcher 가 BFF DB 만 writeknowledge_hub 측 제거knowledge_hub 의 다른 소비자 깨짐
D. Celery → BFF webhookmatcher 결과를 BFF webhook 으로 push (현재 hwpx 패턴과 동일)일관된 패턴. 비동기 안정성 ↑
권장: D 옵션 — hwpx /internal/done webhook 패턴 재사용. matcher 가 BFF 의 /internal/match-done 로 결과 push → BFF DB upsert. 헥사고날 유지 + 비동기.

9실전 호출 예시

Use case A — 제안서 요약 (이미 구현)

# BFF → ai_engine
POST http://ai-engine:8200/online/eval-chatbot/chat
Content-Type: application/json
{
  "message": "다음 우수제품 규격서 추출 결과를 평가위원이 빠르게 파악할 수 있도록 3~4문장으로 요약...",
  "conversation_history": [],
  "ui_context": {"intent": "proposal_summary", "spec_id": "spec-abc123"},
  "bid_id": "00000000-0000-0000-0000-000000000001"
}

# Response
{
  "success": true,
  "reply_message": "본 제품은 ...",
  "citations": [
    {"file_name": "spec.hwpx", "page_number": 3, "content_snippet": "..."}
  ],
  "suggested_actions": [
    {"label": "특허 자세히 보기", "action_payload": "특허 목록"},
    {"label": "다른 업체와 비교", "action_payload": "비교"}
  ],
  "input_tokens": 1240,
  "output_tokens": 142
}

Use case B — 비교 insight (권장 신규)

# BFF → ai_engine
POST http://ai-engine:8200/online/eval-chatbot/chat
{
  "message": "선택한 업체들을 주요기술 / 요구사항 / 평가항목 3 축으로 비교해서 표로 정리",
  "ui_context": {"intent": "comparison", "current_view": "compare_overview"},
  "selected_companies": [
    {"id": "spec-abc", "name": "(주)JK알에스티"},
    {"id": "spec-def", "name": "(주)한국산전"}
  ],
  "bid_id": "0000...0001"
}

# Response
{
  "success": true,
  "reply_message": "...",
  "tabular_data": {
    "headers": ["항목", "(주)JK알에스티", "(주)한국산전"],
    "rows": [
      ["방열성", "95점 (KS C 3604)", "88점"],
      ["A/S 기간", "5년", "3년"]
    ]
  },
  "ui_actions": [
    {"action_type": "navigate", "target_view": "compare_overview"}
  ]
}

Use case C — RFP 자동 구조화 (Phase 2)

# BFF → ai_engine (async)
POST http://ai-engine:8200/offline/rfp/extract-tech-requirements
{ "rfp_document": "...", "bid_id": "..." }

# Response (즉시)
{ "task_id": "celery-xxx", "status": "queued" }

# 폴링 또는 webhook
GET /offline/rfp/task/celery-xxx
{ "status": "done", "result": { "tech_requirements": [...20개...] } }

# → BFF: requirement_items 테이블에 bulk insert

10결론 — ai_engine 최적 활용 우선순위

우선순위활용가치
1 (D-1)EvalChatResponse 의 ui_actions·tabular_data·citations·suggested_actions 풀 활용데모 임팩트 ↑ — 단순 텍스트가 아닌 인터랙티브 UI 응답
2 (D-1)POST /api/compare/{bid_id}/insight 추가 — eval-chatbot/chat 호출비교 표 자동 생성 + 평가 보조
3 (데모 후)matcher cross-DB write 보강 (옵션 D — webhook)compare endpoint 가 fixture 없이 진짜 LLM 결과 반환
4 (데모 후)Offline API 통합 — RFP 자동 구조화 + extract-* 시리즈requirement_items 자동 시드 — mock 졸업
5 (데모 후)Embedding 통합 — KURE-v1 + Qdrant RAG의미 검색 — "○○ 기능 있는 업체" 자연어 쿼리
6 (1개월)Vision (Qwen3-VL) 통합 — 시험성적서·도면 분석이미지 기반 신뢰도 점수 → 평가위원 시간 절감