← 프로젝트 목록

AI Chatbot / Tutor

LMS에 연동되는 범용 AI Chat과 과목별 AI Tutor입니다. 기술 검토와 prototype에서 출발해 질문 분석, RAG 운영, Claude multi-model까지 확장했습니다.

2025.04 ~ 현재 · 기술 검토 · 기획 · 풀스택 · 운영 개발
NestJSReactMongoDBBullMQOpenAIClaudeLTI

배경

대학 LMS에서는 교수와 조교가 학생의 수업 질문에 즉시 답하기 어렵습니다. AI Chat은 범용 대화를, AI Tutor는 과목별 강의자료를 검색한 답변을 제공합니다. 출시 후에는 자산·통계·다국어 운영과 모델 비용 정책까지 범위를 넓혔습니다.


프로젝트 근거 · 2026.09

26

기관별 운영 환경 · 내부용 3개 제외

15,232

캐시 정책에 사용한 운영 스레드

71.7%

전체 턴 간격 5분 이하인 스레드

−37%

Claude 4턴 입력 비용 실측


시제품에서 운영 정책까지

호출 기능을 만든 뒤, 자산·통계·비용의 경계를 운영했습니다.

Jira + Confluence · 2025.05—2026.09
  1. 2025.05—07

    인증·소유권·스트림 계약을 먼저 고정

    Bot·Thread·Message 스키마와 API를 정의하고, 60초 교환 토큰과 LTI 컨텍스트를 자체 세션으로 수렴했다. 응답·도구 상태는 공통 SSE 이벤트로 화면에 전달했다.

    AICHAT-14 · 22 · 25 · 39 · 41 · 51
  2. 2025.07—08

    과목 자료를 RAG 운영 흐름으로 연결

    강의계획서·Commons 문서·자막·TransLive를 과목 Vector Store에 연결하고 업로드 이력, 지원 형식, 100MB 제한, 운영자 권한과 모바일 진입까지 제품 경계에 넣었다.

    AICHAT-31—35 · 48—57 · 74 · 78 · 93—95
  3. 2025.08—11

    기관별 배포와 외부 자산 migration을 표준화

    26개 환경의 OS·노드·DB·NAS·제품 조합을 배포 설정으로 분리했습니다. Chat·Tutor의 provider project를 나누면서 이동할 수 없는 과거 첨부 대화는 read-only로 보존하고, remote asset 상태와 통계 recovery 절차를 문서화했습니다.

    AICHAT-168 · 207 · 219—221 · 235—243
  4. 2026.06—07

    Standalone Bot과 질문 분석을 다시 설계

    리소스·접근 제어·스트리밍·E2E 경계를 분리하고 OpenAPI 타입을 단일 원천으로 만들었다. 질문 분석은 스펙 결정 기록을 SSOT로 두고, 집계 수치와 모델 해석의 책임을 분리했다.

    AICHAT-491 · 493—510 · 532—540
  5. 2026.08

    Claude를 세 번째 provider로 배포

    무상태 history 재조립, stream·tool·usage 변환과 파일 만료 경계를 구현했다. 운영 대화 분포와 실측 비용으로 5분 prompt cache 정책을 정했다.

    AICHAT-542 · 545 · 548—551
  6. 2026.09 · 진행 중

    컨텍스트와 비용을 제품 정책으로

    긴 thread의 token 편중과 long-context 요율을 관측하고 있다. provider 공통 임계점, 요약·절삭·새 대화 UX와 기관별 ACU 차감 정책을 설계하는 단계다.

    AICHAT-546 · 552 · 556—560

배포와 운영

26개 환경을 하나의 서버처럼 다루지 않았습니다.

AICHAT-168 · 207 · 219 · 235 · 241

기관마다 활성 제품, Ubuntu 버전, 웹 노드 수, MongoDB 구성, NAS 경로와 SSO가 달랐습니다. 차이를 예외 처리로 숨기지 않고 사이트 브랜치와 환경 설정, 실행 순서로 드러냈습니다.

Preflight

제품 조합 · OS · node · DB · NAS · SSO

→

All nodes

Apache · runtime · API · worker

→

Run once

index · migration · options · cron

Site topology

환경 차이를 배포 입력으로

Ubuntu 18.04는 내부 Node mirror, 20.04 이상은 nvm을 사용했습니다. 단일 Mongo와 replica set, 서로 다른 NAS mount를 사전 점검하고 대상 production branch만 내려받았습니다.

Product migration

자원 이동 불가를 상태로 보존

Chat과 Tutor의 OpenAI 프로젝트를 분리했습니다. Vector Store가 project 밖으로 이동하지 않는 제약 때문에 환경 설정을 먼저 적용하고, 과거 Chat 첨부 대화는 잘못된 자원을 찾지 않도록 읽기 전용으로 전환했습니다.

External state

요청 성공과 인덱싱 완료를 분리

provider 장애 때 업로드는 수락됐지만 파일이 in_progress에 남았습니다. 로컬 기록과 remote state를 함께 확인했고, 파일 하나의 404가 전체 cleanup job을 중단하지 않게 recovery boundary를 좁혔습니다.

Risk review

REVIEWED

서버가 살아 있어도 queue는 사라질 수 있습니다.

공유 cache Redis의 eviction이 BullMQ 상태만 조용히 지울 수 있음을 분석했습니다. 전용 noeviction Redis와 DB 기준 복구는 완료 성과가 아니라 다음 운영 개선으로 구분했습니다.


Question analysis / design rule

AICHAT-532 · POC HOLD

수치는 코드가 만들고, 모델은 구조와 해석만 맡습니다.

질문 분석 리포트에서 LLM이 숫자를 만들어내지 못하도록 책임을 분리했습니다. 일간 집계와 주간 스냅샷이 수치를 확정하고, 모델 출력의 숫자는 버린 뒤 승인된 주제 안에서 블록 구성과 해석만 사용합니다.

Deterministic
기간·주차·질문 유형·제외량은 증분 집계와 snapshot에서 계산
Generative
모델은 관점 선택, report block 구성과 교수자용 해석을 담당
Guardrail
질문 원문은 내부 검증에만 두고 화면에는 비식별 요약만 노출

구조

CONTEXT & INFERENCE PATH

인증 맥락부터 검색·생성·스트리밍까지 이어지는 경로

LMS에서 받은 사용자·과목 맥락을 세션에 보존하고, 모델·도구·검색 계층을 어댑터 뒤에 분리해 제품 변화에 대응합니다.

인증 맥락부터 검색·생성·스트리밍까지 이어지는 경로 LMS에서 받은 사용자·과목 맥락을 세션에 보존하고, 모델·도구·검색 계층을 어댑터 뒤에 분리해 제품 변화에 대응합니다. 01 CLIENT · REACT 02 SERVER · NESTJS 03 MEMORY & ASSETS LTI SESSION PROMPT TOOL CALL Canvas LMS LTI launch Chat / Tutor UI conversation · operator Streaming client REST · SSE · recovery Auth context JWT · role · course Product modules chat · bot · quiz · stats Adapter models Tool runtime search · code · image MongoDB conversation state File storage uploaded assets Retrieval index sources · citations Brain scoped memory
AI Chat과 Tutor는 같은 실행 기반을 공유하지만 봇·과목·퀴즈·통계 도메인의 책임은 서버 모듈로 분리했습니다.

LMS와 분리한 독립 서비스

기존 LMS와 포트·DB를 분리한 독립 서비스입니다. LTI로 LMS에 삽입하고 JWT SSO로 추가 로그인 없이 사용자를 인증합니다.

과목별 맞춤 RAG

AI Tutor는 LTI가 전달한 과목 ID와 역할에 따라 해당 과목의 강의자료만 검색합니다. 과목마다 지식 베이스를 분리해 다른 수업의 자료가 섞이지 않게 했습니다.

운영자 권한 분리

학교 운영자와 시스템 운영자의 권한을 분리했습니다. 모델 선택, 도구 제한과 사용자 접근 권한은 사이트별 관리 화면에서 설정합니다.


구현한 내용

기능별 구현 범위상세 내용

기술 검토 및 선정

▸GPT-4o, GPT-4.1 mini, Claude를 내부 PoC 기준으로 비교해 품질·비용·운영 복잡도의 균형이 가장 나은 GPT-4.1 mini 선정
▸벡터DB 비교(Pinecone, Qdrant, OpenAI Vector Store) → 별도 인프라 불필요한 OpenAI Vector Store 선정
▸문서 파서 비교(Unstructured, Docling) → OpenAI File Search가 파싱까지 처리하므로 별도 파서 불필요로 결론
▸검토 결과를 Confluence에 비교 테이블로 문서화하여 팀 의사결정 지원

프로토타입 설계 및 개발

▸MongoDB 스키마 설계 — Bot, Thread, Message, CourseBot 등 핵심 컬렉션 정의
▸RESTful API 명세 — 챗봇 CRUD, 대화 관리, 메시지 스트리밍, 파일 업로드
▸JWT 기반 SSO 인증 흐름 설계 — LMS → 임시 토큰 → AI Chat 세션의 3단계 인증
▸NestJS + React로 핵심 기능 프로토타입 구현

상용화 개발

▸OpenAI의 Tool 개념을 기반으로 서비스 전체를 설계. 웹 검색(web_search), 파일 검색(file_search), 이미지 생성(image_generation), 코드 인터프리터(code_interpreter) 등 각 Tool별로 SSE 스트리밍 이벤트, 서버 처리 로직, 클라이언트 UI를 일관된 구조로 정의
▸ChatGPT의 UX를 참고하여, 도구 호출 시 진행 상태(added → processing → done)를 실시간으로 표시하고, 각 도구의 결과를 인라인으로 렌더링하는 채팅 UI 구현
▸Canvas LTI로 각 과목에 AI 튜터를 삽입하여 과목별 맞춤 답변 제공
▸운영자 관리 인터페이스 — 학교/시스템 운영자 권한 분리, AI 모델 및 도구별 활성화/비활성화 설정
▸모바일 반응형 대응, 설치/운영 가이드 문서화

운영 제품으로 확장

AICHAT-232 · 235 · 236 · 243
▸코드 인터프리터 결과 파일의 다운로드 링크, 한글 파일명, 서버 경로 변환 문제를 해결해 생성 결과를 실제 산출물로 연결
▸Vector Store 만료 설정과 실패 자산 정리, 원격 파일 삭제 스케줄러로 외부 AI 자산의 수명주기 관리
▸코드 인터프리터·이미지 생성 사용량 통계, 대학별 튜터 명칭, AI Chat/Tutor 자원 분리로 멀티테넌트 운영 요구 반영
▸LMS 사용자 언어를 따르는 AI Chat·Tutor 영문화와 핵심 UI 로컬라이징

질문 분석과 멀티 모델 고도화

2026.07 ~ 현재
▸AICHAT-532 질문 분석 재설계 PoC에서 스펙 결정 기록을 SSOT로 만들고, 누락 기능의 원인을 역추적해 요구사항·목업 검증 절차를 보강
▸기존 provider abstraction에 Claude를 추가하고 prompt caching·recent context 정책을 적용해 2026.08 고객사 배포
▸AI Tutor 2 교수자 설정과 LMS 수업 일정 연동을 설계하고, BullMQ·Redis 장애 영향과 주간 통계 내보내기 흐름을 점검

문제 해결 사례

독립 서비스의 SSO 인증 설계

문제 상황 AI Chat은 LMS와 포트·DB가 분리된 서비스지만, 이미 로그인한 사용자가 추가 인증 없이 들어와야 했다. 과목 튜터는 LTI로 진입해 URL 리다이렉트와 다른 인증 입력을 사용했다.

문제 정의 두 진입 경로를 지원하면서 LMS 세션과 AI Chat 세션의 수명·책임을 분리해야 했다.

가설 LMS가 짧게 유효한 교환용 토큰을 발급하고 AI Chat이 자체 세션으로 바꾸면, 서비스 결합을 줄이면서 두 경로를 하나의 인증 흐름으로 합칠 수 있다고 봤다.

행동 TTL 60초의 LMS 임시 토큰 검증과 AI Chat JWT 발급을 구현하고, LTI의 사용자·과목·역할 정보도 같은 세션 생성 단계에 연결했다.

성과 두 경로에서 추가 로그인을 요구하지 않고 세션을 연결했으며, 같은 패턴을 AI Commons 인증에도 재사용했다.

회고 재사용 가능한 경계를 만들었지만 인증 실패율이나 로그인 소요 시간은 측정하지 않았다. 토큰 재사용·만료·잘못된 역할 입력에 대한 보안 테스트 결과를 함께 제시하면 더 강한 사례가 된다.

기술 스택 비교 평가 및 의사결정

문제 상황 팀의 첫 AI 챗봇 제품이라 모델, 벡터 저장소, 임베딩과 문서 파서를 동시에 선택해야 했다.

문제 정의 최고 품질만 고르는 문제가 아니라 4개월 상용화 일정 안에서 품질, 지연시간, 비용과 운영 복잡도의 균형을 찾아야 했다.

가설 초기에는 managed OpenAI stack으로 운영 요소를 줄이되 provider interface를 분리하면, 출시 속도와 이후 교체 가능성을 함께 확보할 수 있다고 봤습니다.

행동 범주별 후보 3~5개를 같은 기준으로 PoC하고, GPT-4.1 mini와 OpenAI Vector Store를 선택했다. 비교 결과와 선택 근거는 팀 문서로 남겼다.

성과 별도 vector DB 없이 4개월 만에 상용화했고, 이후 같은 provider layer에 Gemini와 Claude를 추가했습니다.

회고 선택 과정은 남아 있지만 평가셋과 점수는 공개할 수 있는 형태로 정리되지 않았다. 다음 비교에서는 질문 유형별 품질·지연·비용을 재현 가능한 표로 남겨야 한다.

무상태 Claude를 기존 제품 계약 안에 넣기

문제 상황 OpenAI는 previous_response_id로 대화를 이어가지만 Claude Messages API는 무상태다. 스트림 이벤트, 서버 도구, usage와 파일 수명도 기존 공급자와 달랐다.

문제 정의 프론트엔드의 채팅·도구 UI와 통계 계약을 바꾸지 않으면서, 히스토리 재전송이 만드는 컨텍스트 크기와 비용을 함께 통제해야 했다.

가설 provider별 차이를 BaseStreamService 아래에서 흡수하고, 안정적인 prefix만 cache하면 제품 호환성과 비용 효율을 동시에 지킬 수 있다고 봤습니다.

행동 최근 20개 메시지를 서버에서 재조립하고, Claude 응답·도구·사용량을 자체 SSE와 공통 타입으로 변환했다. 15,232개 운영 대화의 길이·첨부·턴 간격 분포를 분석해 시스템 프롬프트와 대화 기록 끝에 조건부 캐시 경계를 두고 TTL은 5분으로 정했다.

성과 기존 client contract를 유지한 채 세 번째 provider를 배포했습니다. 4턴 대화에서 input cost가 5,348에서 3,364로 37% 줄었고, 250KB 첨부 파일도 후속 턴에서 cache read로 전환됐습니다.

회고 최근 20개 message 이후에는 history cache를 끄는 안전한 선택을 했습니다. 다음 단계는 context 임계점·요약/truncation 정책과 provider·model별 통계를 하나의 비용 정책으로 연결하는 일입니다.


회고

초기 계약이 이후 확장 비용을 결정했습니다. DB 스키마, API 명세와 인증 흐름을 프로토타입에서 먼저 고정했습니다. 그 결과 상용화 단계에서도 핵심 구조를 유지할 수 있었습니다.

기술 비교는 출시 일정을 지키기 위한 일이었습니다. LLM, 벡터 DB와 문서 파서를 같은 기준으로 PoC했습니다. 관리형 구성으로 시작하되 공급자 경계를 분리해 이후 Gemini와 Claude를 추가했습니다.

배포 문서도 제품의 일부였습니다. 설치 조건과 복구 절차를 남겨 다른 팀원이 기관별 배포를 이어갈 수 있게 했습니다. 다음에는 공개 평가셋과 점수까지 재현 가능한 형태로 남기려 합니다.