AI Chatbot / Tutor
LMS에 연동되는 범용 AI Chat과 과목별 AI Tutor입니다. 기술 검토와 prototype에서 출발해 질문 분석, RAG 운영, Claude multi-model까지 확장했습니다.
배경
대학 LMS에서는 교수와 조교가 학생의 수업 질문에 즉시 답하기 어렵습니다. AI Chat은 범용 대화를, AI Tutor는 과목별 강의자료를 검색한 답변을 제공합니다. 출시 후에는 자산·통계·다국어 운영과 모델 비용 정책까지 범위를 넓혔습니다.
프로젝트 근거 · 2026.09
26
기관별 운영 환경 · 내부용 3개 제외
15,232
캐시 정책에 사용한 운영 스레드
71.7%
전체 턴 간격 5분 이하인 스레드
−37%
Claude 4턴 입력 비용 실측
시제품에서 운영 정책까지
호출 기능을 만든 뒤, 자산·통계·비용의 경계를 운영했습니다.
- 2025.05—07
인증·소유권·스트림 계약을 먼저 고정
Bot·Thread·Message 스키마와 API를 정의하고, 60초 교환 토큰과 LTI 컨텍스트를 자체 세션으로 수렴했다. 응답·도구 상태는 공통 SSE 이벤트로 화면에 전달했다.
AICHAT-14 · 22 · 25 · 39 · 41 · 51 - 2025.07—08
과목 자료를 RAG 운영 흐름으로 연결
강의계획서·Commons 문서·자막·TransLive를 과목 Vector Store에 연결하고 업로드 이력, 지원 형식, 100MB 제한, 운영자 권한과 모바일 진입까지 제품 경계에 넣었다.
AICHAT-31—35 · 48—57 · 74 · 78 · 93—95 - 2025.08—11
기관별 배포와 외부 자산 migration을 표준화
26개 환경의 OS·노드·DB·NAS·제품 조합을 배포 설정으로 분리했습니다. Chat·Tutor의 provider project를 나누면서 이동할 수 없는 과거 첨부 대화는 read-only로 보존하고, remote asset 상태와 통계 recovery 절차를 문서화했습니다.
AICHAT-168 · 207 · 219—221 · 235—243 - 2026.06—07
Standalone Bot과 질문 분석을 다시 설계
리소스·접근 제어·스트리밍·E2E 경계를 분리하고 OpenAPI 타입을 단일 원천으로 만들었다. 질문 분석은 스펙 결정 기록을 SSOT로 두고, 집계 수치와 모델 해석의 책임을 분리했다.
AICHAT-491 · 493—510 · 532—540 - 2026.08
Claude를 세 번째 provider로 배포
무상태 history 재조립, stream·tool·usage 변환과 파일 만료 경계를 구현했다. 운영 대화 분포와 실측 비용으로 5분 prompt cache 정책을 정했다.
AICHAT-542 · 545 · 548—551 - 2026.09 · 진행 중
컨텍스트와 비용을 제품 정책으로
긴 thread의 token 편중과 long-context 요율을 관측하고 있다. provider 공통 임계점, 요약·절삭·새 대화 UX와 기관별 ACU 차감 정책을 설계하는 단계다.
AICHAT-546 · 552 · 556—560
배포와 운영
26개 환경을 하나의 서버처럼 다루지 않았습니다.
기관마다 활성 제품, 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와 분리한 독립 서비스
기존 LMS와 포트·DB를 분리한 독립 서비스입니다. LTI로 LMS에 삽입하고 JWT SSO로 추가 로그인 없이 사용자를 인증합니다.
과목별 맞춤 RAG
AI Tutor는 LTI가 전달한 과목 ID와 역할에 따라 해당 과목의 강의자료만 검색합니다. 과목마다 지식 베이스를 분리해 다른 수업의 자료가 섞이지 않게 했습니다.
운영자 권한 분리
학교 운영자와 시스템 운영자의 권한을 분리했습니다. 모델 선택, 도구 제한과 사용자 접근 권한은 사이트별 관리 화면에서 설정합니다.
구현한 내용
기능별 구현 범위상세 내용
기술 검토 및 선정
프로토타입 설계 및 개발
상용화 개발
운영 제품으로 확장
AICHAT-232 · 235 · 236 · 243질문 분석과 멀티 모델 고도화
2026.07 ~ 현재문제 해결 사례
독립 서비스의 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를 추가했습니다.
배포 문서도 제품의 일부였습니다. 설치 조건과 복구 절차를 남겨 다른 팀원이 기관별 배포를 이어갈 수 있게 했습니다. 다음에는 공개 평가셋과 점수까지 재현 가능한 형태로 남기려 합니다.