// src/main/kotlin/network/RagService.kt import dev.langchain4j.data.segment.TextSegment import dev.langchain4j.model.openai.OpenAiChatModel import dev.langchain4j.model.openai.OpenAiEmbeddingModel import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.SqlExpressionBuilder.plus import org.jetbrains.exposed.sql.transactions.transaction import java.time.Duration object RagService { // 임베딩 모델 (8081) 및 채팅 모델 (8080) 설정 private val embeddingModel = OpenAiEmbeddingModel.builder() .baseUrl("http://127.0.0.1:8081/v1") .apiKey("unused") .build() private val chatModel = OpenAiChatModel.builder() .baseUrl("http://127.0.0.1:8080/v1") .apiKey("unused") .timeout(Duration.ofSeconds(60)) .build() /** * 텍스트를 임베딩하여 H2 DB에 저장합니다. */ fun ingest(text: String, meta: String = "") { val embedding = embeddingModel.embed(text).content().vector() transaction { VectorStoreTable.insert { it[content] = text it[metadata] = meta // 벡터 데이터를 문자열 형태로 저장 (H2 포맷) it[VectorStoreTable.embedding] = embedding.joinToString(",", "[", "]") } } println("💾 H2 벡터 저장 완료: ${text.take(15)}...") } /** * 질문과 가장 유사한 정보를 H2에서 검색하여 AI 답변을 생성합니다. */ fun ask(question: String): String { val queryVector = embeddingModel.embed(question).content().vector() val vectorStr = queryVector.joinToString(",", "[", "]") // H2의 VECTOR_COSINE_SIMILARITY 함수를 사용하여 검색 val context = transaction { val query = "SELECT content FROM VECTOR_STORE " + "ORDER BY VECTOR_COSINE_SIMILARITY(embedding, '$vectorStr') DESC " + "LIMIT 3" val results = mutableListOf() exec(query) { rs -> while (rs.next()) { results.add(rs.getString("content")) } } results.joinToString("\n\n") } val prompt = """ [참고 정보] $context [질문] $question 위 정보를 참고하여 분석 결과를 말해주세요. """.trimIndent() return chatModel.generate(prompt) } }