
IBM Quarkus LangChain4j 입문: AI Service를 통해 LLM 호출하기
요약
Java 생태계에서 생성형 AI 기능을 통합할 수 있는 Quarkus LangChain4j의 기초를 다룹니다. AI Service, Prompt, Chat Model의 역할을 정리하고, IBM Quarkus를 활용해 LLM을 호출하여 문의 내용을 구조화된 데이터로 분석하는 실습을 소개합니다.
핵심 포인트
- Quarkus LangChain4j를 통한 Java 기반 AI 애플리케이션 개발 방법
- AI Service, Prompt, Chat Model의 핵심 역할 및 개념 정리
- LLM을 활용한 AI Ticket Triage(문의 분석) 구현 사례
- LangChain4j의 주요 기능(RAG, Tool Calling 등) 및 MCP 프로토콜 소개
생성형 AI (Generative AI) 분야에서는 Python이 앞서 나가고 있다는 인상이 있지만, Java 생태계에서도 AI 애플리케이션 개발을 지원하는 라이브러리와 프레임워크가 충실해지고 있습니다.
특히 Quarkus에는 LLM과의 연계를 용이하게 하는 Quarkus LangChain4j가 준비되어 있어, Java다운 개발 스타일을 유지하면서 생성형 AI 기능을 애플리케이션에 통합할 수 있습니다.
Quarkus의 AI 관련 기능에는 LangChain4j, AI Service, Tool Calling, RAG, MCP 등 다양한 기능과 용어가 있습니다.
이번에는 이 중에서도 기본이 되는 AI Service, Prompt, Chat Model의 역할을 정리합니다.
또한, 오픈 소스인 Quarkus를 기반으로 한 IBM Enterprise Build of Quarkus를 사용하여 REST API로부터 LLM을 호출하는 것까지 확인합니다.
참고로, 본 프로덕션 운영을 위해서는 IBM에 의한 지원과 장기 라이프사이클이 제공되는 제품판도 준비되어 있습니다.
이 기사는 Quarkus 및 생성형 AI 관련 기술에 대해 학습한 내용을 스스로의 이해를 정리할 목적도 겸하여 정리한 것입니다.
이번에는 다음 기능들은 다루지 않으며, 후속 기사에서 다룰 예정입니다.
- Tool Calling
- Chat Memory
- RAG
- MCP Server
- MCP Client
- Agentic Workflow
참고로, Native Image에 대해서는 과거 기사에서 소개했습니다. 관심이 있으신 분은 이쪽도 참조해 주시기 바랍니다.
AI Ticket Triage
문의 내용을 LLM으로 분석하는 「AI Ticket Triage」를 작성합니다.
사용자가 문의 내용을 입력하면, LLM이 다음 정보를 반환합니다.
| 필드 | 내용 |
|---|---|
summary | 문의 요약 |
category | 카테고리 |
priority | 우선순위 |
recommendedAction | 권장하는 초동 대응 |
assignedTeam | 담당 팀 |
예를 들어, 다음과 같은 요청을 전송하면,
POST /tickets/analyze
Content-Type: text/plain
비밀번호를 잊어버려 로그인할 수 없습니다.
LLM이 문의 내용을 분석하여 업무에서 다루기 쉬운 구조화된 데이터(Structured Data)로 반환합니다.
{
"summary": "비밀번호를 잊어버려 로그인할 수 없음.", "category": "APPLICATION", "priority": "LOW", "recommendedAction": "비밀번호 재설정 절차를 안내한다.", "assignedTeam": "APPLICATION"
}
처리 흐름은 다음과 같습니다.
사용자
↓ HTTP 요청
REST Resource (TicketResource)
...
소스 코드는 이곳에서 공개하고 있습니다.
LangChain4j는 Java 애플리케이션에서 LLM을 이용하기 위한 라이브러리입니다. 주로 다음 기능을 제공합니다.
- Chat Model
- AI Service
- Prompt Template
- Chat Memory
- Tool Calling
- RAG
- Embedding Model
- Embedding Store
Quarkus LangChain4j는 LangChain4j를 Quarkus에 통합하는 Extension입니다. Quarkus의 CDI나 설정 관리 등을 이용하면서, Java 인터페이스와 어노테이션(Annotation)을 통해 AI Service를 선언적으로 정의할 수 있습니다.
MCP (Model Context Protocol)는 AI 애플리케이션과 검색, 파일 조작, 업무 API 등의 외부 기능을 공통된 방법으로 연결하기 위한 프로토콜입니다.
AI 애플리케이션
↓ MCP Client
MCP Server
...
AI Service는 LLM을 이용하는 처리를 Java 인터페이스로 정의하는 메커니즘입니다.
예를 들어, 저녁 식사를 제안하는 AI Service는 다음과 같이 표현할 수 있습니다.
public interface FoodExpert {
List<String> recommendMeals(String mood);
}
일반적인 Java 인터페이스처럼 보이지만, Quarkus LangChain4j가 런타임(Runtime)에 구현체를 제공하며 다음과 같은 처리를 수행합니다.
- 메서드 인수를 프롬프트 (Prompt)에 삽입
- 채팅 모델 (Chat Model) 호출
- LLM으로부터 답변 획득
- 답변을 Java 반환 값으로 변환
AI Service는 LLM과의 상호작용을 Java 서비스 계층(Service Layer)으로 다루기 위한 창구입니다.
채팅 메모리 (Chat Memory, 대화 이력 유지), 툴 콜링 (Tool Calling, Java 메서드 등 외부 기능 호출), RAG (Retrieval-Augmented Generation, 외부 데이터를 검색하여 답변에 활용하는 메커니즘) 등도 조합할 수 있습니다.
프롬프트 (Prompt)는 LLM에 전달하는 지시 사항입니다.
Quarkus LangChain4j에서는 주로 다음과 같은 어노테이션과 변수 표기법을 사용하여 프롬프트를 정의합니다.
| 기술 | 용도 |
|---|---|
@SystemMessage | AI의 역할이나 전체적인 규칙을 지정 |
@UserMessage | 사용자의 요구사항을 지정 |
{{변수명}} | Java 메서드의 인수를 프롬프트에 삽입 |
@SystemMessage("""
당신은 저녁 식사 메뉴를 제안하는 어시스턴트입니다.
간결하고 실용적인 제안을 해주세요.
...""")
채팅 모델 (Chat Model)은 실제 LLM 제공업체 (Provider)로 요청을 보내고 답변을 받는 컴포넌트입니다.
이번에는 OpenAI용 quarkus-langchain4j-openai Extension을 사용합니다.
의존성(Dependency)과 application.properties를 설정하면, Quarkus에 의해 OpenAI에 연결하는 Chat Model이 구성됩니다.
| 명칭 | 역할 |
|---|---|
| AI Service | 애플리케이션으로서 어떤 AI 기능을 제공할지 |
| ... | |
| 항목 | 버전 |
| --- | --- |
| OS | macOS 26.5.2 |
| ... |
IBM Enterprise Build of Quarkus의 Application Configurator에서 다음 의존성을 선택하여 프로젝트를 생성합니다.
quarkus-rest-jackson
quarkus-langchain4j-openai
생성된 pom.xml의 주요 의존성은 다음과 같습니다.
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-jackson</artifactId>
...
</dependency>
이번에는 OpenAI를 사용합니다.
OpenAI Quickstart에 따라 API 키를 취득하고 환경 변수에 설정합니다.
export OPENAI_API_KEY="your_api_key_here"
application.properties에 다음 내용을 추가합니다.
quarkus.langchain4j.openai.api-key=${OPENAI_API_KEY}
quarkus.langchain4j.log-requests=true
quarkus.langchain4j.log-responses=true
${OPENAI_API_KEY}는 방금 설정한 환경 변수를 참조합니다.
log-requests와 log-responses는 개발 중 디버깅에 유용하므로 활성화했습니다.
이번 앱에 등장하는 클래스는 3개입니다.
| 클래스 | 역할 |
|---|---|
TicketAnalyzer | AI Service 인터페이스 |
TicketAnalysis | LLM의 답변을 받는 Java Record |
TicketResource | REST 엔드포인트 (Endpoint) |
package dev.autonomura.ticket;
public record TicketAnalysis(
String summary,
...
) {}
Java Record로 출력 형식을 정의합니다.
Quarkus LangChain4j는 LLM의 답변을 이 Record로 변환합니다.
package dev.autonomura.ticket;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
...
각 어노테이션 (Annotation)의 역할은 다음과 같습니다.
@RegisterAiService
TicketAnalyzer를 Quarkus의 AI Service로 등록합니다.
개발자가 구현 클래스 (Implementation class)를 직접 작성하지 않아도, Quarkus LangChain4j가 구현체를 자동으로 생성합니다.
@SystemMessage
LLM의 역할과 답변 전체에 적용할 규칙을 정의합니다.
이번 예제에서는 우선순위(LOW / MEDIUM / HIGH / CRITICAL)와 카테고리(APPLICATION / DATABASE / ...) 선택지를 명시함으로써 출력을 안정화했습니다.
@UserMessage
사용자의 요청을 프롬프트 (Prompt)로 정의합니다.
{{ticket}}는 메서드 인자(Method argument)인 ticket을 프롬프트에 삽입합니다.
TicketAnalysis (반환값)
LLM의 답변을 단순한 문자열이 아닌 Java Record로 받습니다.
Quarkus LangChain4j가 JSON 파싱 (Parsing)과 타입 변환 (Type conversion)을 자동으로 수행합니다.
package dev.autonomura.ticket;
import jakarta.inject.Inject;
import jakarta.ws.rs.Consumes;
...
TicketResource에는 LLM 연결 처리나 프롬프트를 직접 기술하지 않습니다.
AI 관련 처리를 TicketAnalyzer로 분리함으로써, REST 계층 (Layer)과 AI 서비스 계층의 역할이 명확해집니다.
처리 흐름을 다시 정리하면 다음과 같습니다.
- 클라이언트가
POST /tickets/analyze를 호출합니다. TicketResource가 요청 본문 (Request body)의 문자열을 받습니다.- CDI로 주입된
TicketAnalyzer를 호출합니다. - AI Service가 프롬프트를 구성합니다.
- Chat Model이 LLM Provider로 요청을 보냅니다.
- AI Service가 답변을
TicketAnalysis로 변환합니다. - REST API가 JSON 형식으로 반환합니다.
mvn quarkus:dev
정상적으로 실행되면, 다른 터미널에서 호출합니다.
curl -s -X POST \
-H "Content-Type: text/plain" \
--data-binary "Web 애플리케이션에 로그인할 수 없습니다. 오늘 아침부터 여러 사용자에게 동일한 문제가 발생하고 있으며, 화면에는 'Database connection timeout'이라고 표시됩니다." \
...
응답 예시 (LLM의 답변이므로 실행할 때마다 표현이 달라질 수 있습니다):
{
"summary": "여러 사용자가 Web 애플리케이션에 로그인할 수 없으며, Database connection timeout 에러 메시지가 표시되고 있다.",
"category": "DATABASE",
...
LLM이 생성하는 답변은 사용하는 모델, 프롬프트, 모델 설정, 실행 시점 등에 따라 달라질 수 있습니다. 따라서 위 내용과 완전히 동일한 내용이 반환되지 않을 수도 있습니다.
TicketResource
↓ ticket (문의 문자열)을 전달
TicketAnalyzer
...
| 클래스 | 역할 |
|---|---|
TicketResource | HTTP 요청 수신 및 응답 반환 |
TicketAnalyzer | AI 기능 및 프롬프트 정의 |
TicketAnalysis | LLM 출력을 받는 데이터 모델 |
| Chat Model | LLM Provider와의 통신 |
| Quarkus LangChain4j | AI Service 생성, 프롬프트 처리, 출력 변환 |
이번 애플리케이션에서는 AI Service를 통해 Chat Model을 거쳐 LLM을 호출했습니다.
Tool Calling이나 MCP는 사용하지 않았습니다.
Tool Calling을 이용하면 LLM의 판단에 따라 Java 메서드와 같은 외부 기능을 호출할 수 있습니다.
또한, MCP를 이용하면 다른 애플리케이션이 제공하는 Tool이나 데이터 소스에 공통된 방식으로 연결할 수 있습니다.
이번에는 Quarkus LangChain4j를 사용하여 Java 인터페이스로 정의한 AI Service로부터 LLM을 호출했습니다.
@RegisterAiService를 사용하면 LLM과의 통신 처리를 직접 구현하지 않고, 일반적인 Java 서비스와 유사한 형태로 AI 기능을 정의할 수 있습니다 -
@SystemMessage와 @UserMessage를 사용하면 AI의 역할과 사용자로부터의 요구사항을 분리하여 관리할 수 있습니다 -
Java Record를 반환값으로 지정하면 LLM의 답변을 구조화된 Java 객체로 받을 수 있습니다
이번 구현에서는 LLM을 통한 질의 내용의 분석과 구조화까지 확인했습니다.
외부 Java 메서드나 업무 시스템과의 연동은 수행하지 않았습니다.
다음 회차에서는 Tool Calling을 추가하여, LLM이 사용자의 요구에 따라 Java 메서드를 선택하고 실행하는 메커니즘을 확인하겠습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기