본문으로 건너뛰기

AI 서비스

이 페이지는 conversation state를 저장하고, chat request를 chatbot-svc로 프록시하며, read-only SQL을 검증하고, monolith 내부에서 확인된 tool을 실행하는 tenant 범위 모듈 lumie-backend/modules/ai의 레퍼런스입니다.

소스 경로

Path역할
lumie-backend/modules/ai/src/main/java/com/lumie/ai/adapter/in/web/{ChatController,ConversationController,InternalChatbotController}.java공개 chat endpoint, conversation 조회, worker callback surface
lumie-backend/modules/ai/src/main/java/com/lumie/ai/adapter/out/external/ChatbotClient.javaSSE passthrough를 포함한 monolith에서 chatbot-svc로의 HTTP 프록시
lumie-backend/modules/ai/src/main/java/com/lumie/ai/application/service/{ChatService,ConversationQueryService,SchemaDiscoveryService,SqlValidator,ScheduledTaskExecutor}.javapersistence, schema discovery, SQL safety, scheduled-task execution
lumie-backend/modules/ai/src/main/java/com/lumie/ai/domain/entity/{Conversation,ChatMessage,ScheduledTask}.javaAI conversation, message, scheduled-task aggregate
lumie-backend/app/src/main/java/com/lumie/app/config/internal/InternalHmacAuthFilter.java/internal/chatbot/**용 HMAC 보호
lumie-backend/app/src/main/resources/db/migration/public/{V18__rls_baseline,V27__langgraph_schema}.sql기본 AI table과 LangGraph 관련 schema 추가

공개 인터페이스

Endpoint목적
POST /v1/chatchatbot-svc로 프록시되는 비스트리밍 chat 요청
POST /v1/chat/streamchatbot-svc로 프록시되는 SSE chat stream
POST /v1/chat/confirm사용자 확인 후 pending tool/action 재개
GET /v1/conversations, GET /v1/conversations/{id}, DELETE /v1/conversations/{id}staff 전용 conversation 목록, 상세, soft-delete 작업
POST /internal/chatbot/querymonolith 내부에서 read-only SQL을 실행하기 위한 worker callback
POST /internal/chatbot/tools/{toolName}확인된 tool 실행을 위한 worker callback
GET /internal/chatbot/schemaschema 설명을 가져오기 위한 worker callback
GET /internal/chatbot/history최근 conversation history를 가져오기 위한 worker callback
POST /internal/chatbot/pending-action, POST /internal/chatbot/save-message, POST /internal/chatbot/conversationspending action, message, 신규 conversation을 저장하기 위한 worker callback endpoint

모든 공개 chat 및 conversation endpoint는 Role.STUDENT를 거부합니다. 즉, 이 surface는 staff 전용입니다.

내부 인터페이스와 의존성

Surface역할
chatbot-svcgraph orchestration과 model interaction을 소유하며, monolith는 더 이상 local LLM fallback을 실행하지 않음
/internal/chatbot/**SQL, write, persistence를 monolith 안에 유지하는 HMAC-protected callback surface
SqlValidatornon-SELECT SQL, 세미콜론, DDL/DML 키워드, catalog 접근을 거부
ToolRegistryworker를 대신해 실행되는 confirmed write tool의 런타임 registry
TenantService.listActiveTenants()ScheduledTaskExecutor가 기한이 된 task를 실행할 때 쓰는 cross-tenant source

집계와 테이블

Aggregate참고
Conversation소유 사용자와 active flag를 가진 conversation shell
ChatMessage저장된 assistant, user, tool-call, pending-action message record
ScheduledTasktenant 범위 scheduled tool execution 상태

런타임 흐름

계약 참고 사항

확인된 write tool은 내부 컨트롤러 경계에서 명시적인 allowlist로 제한됩니다.

// lumie-backend/modules/ai/src/main/java/com/lumie/ai/adapter/in/web/InternalChatbotController.java
private static final Set<String> ALLOWED_TOOLS = Set.of(
"create_announcement", "send_sms", "send_telegram", "schedule_task");

HMAC 인증에 성공한 worker 요청이라도, 요청한 tool이 이 집합 밖에 있거나 ToolRegistry에 등록되어 있지 않으면 거부됩니다.

예시 계약

이 예시는 ChatController, ChatReplyResponse, ChatbotClient, InternalChatbotController, chatbot-svc/src/graph/tools.py에서 직접 가져왔습니다.

대기 중인 채팅 작업 확인

POST /v1/chat/confirm
Idempotency-Key: confirm-99
Content-Type: application/json

{
"messageId": 99,
"confirmed": true
}
HTTP/1.1 200 OK

{
"conversationId": 10,
"message": "Announcement created.",
"pendingAction": null
}

놓치기 쉬운 세부사항 하나가 있습니다. ChatReplyResponse.pendingActionString입니다. worker가 후속 pending action을 반환하면 ChatbotClient.toReply(...)는 그 worker 객체를 중첩 객체가 아니라 JSON 문자열로 직렬화합니다.

내부 도구 실행 페이로드

worker tool schema는 camelCase argument key를 사용하며, InternalChatbotController.executeTool(...)arguments 안에서 그 key가 바뀌지 않은 상태로 들어오기를 기대합니다.

POST /internal/chatbot/tools/send_sms
Content-Type: application/json

{
"tenantSlug": "acme",
"tenantId": 15,
"userId": 7,
"arguments": {
"phoneNumber": "01012345678",
"message": "Tomorrow's class starts at 10:00."
}
}
HTTP/1.1 200 OK

{
"success": true,
"data": "<tool-result-json>",
"error": null
}

실패, 재시도, 관측성

  • ChatbotClient는 worker가 기본 JDK HTTP/2 협상 경로와 호환되지 않기 때문에 backend-to-worker HTTP를 HTTP/1.1로 고정합니다.
  • chat/confirm은 선택적 Idempotency-Key를 지원하지만, 일반 chatchat/stream은 idempotency layer를 사용하지 않습니다.
  • SqlValidator는 어떤 query도 데이터베이스에 도달하기 전에 write SQL, 다중 statement, pg_*, information_schema 접근을 차단합니다.
  • InternalChatbotController는 모든 worker callback마다 tenant와 user context를 다시 세팅하여, RLS와 사용자 인지형 tool 로직이 monolith 내부에서 실행되도록 합니다.
  • ScheduledTaskExecutor는 활성 tenant를 순회하며, 각 due task를 tenant context 안에서 실행하고 실패한 task는 FAILED로 표시합니다.
  • ChatService는 persistence 전용입니다. 코드 주석은 예전의 monolith 내부 Spring AI 경로가 Phase 5 전환에서 제거되었다고 명시합니다.

검증

cd lumie-backend
./gradlew :modules:ai:test
./gradlew :modules:ai:test --tests '*Chat*'
./gradlew :modules:ai:test --tests '*ScheduledTask*'

예상 성공 신호:

  • Gradle이 BUILD SUCCESSFUL로 종료되고, AI 모듈 테스트가 여전히 chat, confirm, scheduled-task 흐름을 다룹니다.
  • InternalChatbotController가 여전히 create_announcement, send_sms, send_telegram, schedule_task만 허용하고, worker tool schema가 phoneNumber, toolName 같은 camelCase key를 계속 사용합니다.

관련 페이지