QuarkusMiddleTechnical
Что такое интеграция Quarkus с LangChain4j и как она поддерживает работу с AI-моделями?
quarkus-langchain4j интегрирует LangChain4j в CDI-контекст Quarkus: AI-сервисы объявляются через @RegisterAiService-интерфейсы, провайдер (OpenAI, Ollama и др.) настраивается в application.properties, поддерживаются ChatMemory, RAG и Tool Calling.
Quarkus LangChain4j
quarkus-langchain4j — расширение, интегрирующее библиотеку LangChain4j в Quarkus CDI-контекст. Позволяет декларативно описывать AI-сервисы как CDI-бины, абстрагируя провайдер (OpenAI, Azure OpenAI, Ollama, Hugging Face и др.).
Зависимости (Maven)
<dependency>
<groupId>io.quarkiverse.langchain4j</groupId>
<artifactId>quarkus-langchain4j-openai</artifactId>
<version>0.20.0</version>
</dependency>
<!-- Для Ollama (локально): -->
<dependency>
<groupId>io.quarkiverse.langchain4j</groupId>
<artifactId>quarkus-langchain4j-ollama</artifactId>
<version>0.20.0</version>
</dependency>
Конфигурация
# application.properties
quarkus.langchain4j.openai.api-key=${OPENAI_API_KEY}
quarkus.langchain4j.openai.chat-model.model-name=gpt-4o-mini
quarkus.langchain4j.openai.chat-model.temperature=0.3
quarkus.langchain4j.openai.timeout=30s
quarkus.langchain4j.log-requests=true
quarkus.langchain4j.log-responses=true
Декларативный AI-сервис
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
import io.quarkiverse.langchain4j.RegisterAiService;
@RegisterAiService
public interface ReviewAnalyzer {
@SystemMessage("Ты — аналитик отзывов клиентов. Отвечай кратко на русском языке.")
@UserMessage("Определи тональность отзыва: {review}. Ответь одним словом: positive/negative/neutral.")
String analyzeSentiment(String review);
}
// Использование в ресурсе:
@Path("/reviews")
public class ReviewResource {
@Inject ReviewAnalyzer analyzer;
@POST
public String analyze(String text) {
return analyzer.analyzeSentiment(text);
}
}
Чат с памятью (ChatMemory)
@RegisterAiService(chatMemoryProviderSupplier = RegisterAiService.BeanChatMemoryProviderSupplier.class)
public interface AssistantService {
String chat(@MemoryId String userId, @UserMessage String message);
}
// Bean для хранения истории:
@ApplicationScoped
public class MemoryProvider implements ChatMemoryProvider {
private final Map<String, ChatMemory> memories = new ConcurrentHashMap<>();
@Override
public ChatMemory get(Object memoryId) {
return memories.computeIfAbsent(
memoryId.toString(),
id -> MessageWindowChatMemory.withMaxMessages(20)
);
}
}
RAG (Retrieval-Augmented Generation)
// Встроенное хранилище (dev/test)
@Inject EmbeddingStore<TextSegment> store;
@Inject EmbeddingModel embeddingModel;
public void ingestDocument(String text) {
List<TextSegment> segments = new DocumentByParagraphSplitter(500, 50).split(
Document.from(text)
);
List<Embedding> embeddings = embeddingModel.embedAll(segments).content();
store.addAll(embeddings, segments);
}
// AI-сервис с retriever
@RegisterAiService(retrieverSupplier = RegisterAiService.BeanRetrieverSupplier.class)
public interface KnowledgeBot {
String answer(@UserMessage String question);
}
Инструменты (Tool Calling)
@ApplicationScoped
public class WeatherTool {
@Tool("Get current weather for a city")
public String getWeather(String city) {
return weatherApi.fetch(city); // реальный HTTP-вызов
}
}
// AI-сервис автоматически получает инструмент через CDI
@RegisterAiService(tools = WeatherTool.class)
public interface WeatherBot {
String chat(@UserMessage String message);
}
Подводные камни
- В native-сборке модели, использующие рефлексию для десериализации JSON (structured output), требуют
@RegisterForReflectionна DTO-классах. @RegisterAiServiceсоздаёт прокси через CDI — нельзя вызывать методы AI-сервиса из того же бина без инжекции (self-invocation не перехватывается).- Память (
ChatMemory) по умолчанию in-memory — при перезапуске теряется; для production нужен Redis или DB-бэкенд. - Стриминг ответов (
Multi<String>) требует Reactive-пути; смешивание с блокирующим кодом вызывает дедлок на IO-треде. - Токены тарифицируются за каждый запрос — без лимита
MessageWindowChatMemoryистория растёт бесконечно и увеличивает стоимость. quarkus.langchain4j.log-requests=trueв production логирует чувствительные данные — отключить перед деплоем.- Версии quarkus-langchain4j и LangChain4j-core должны совпадать: используйте BOM
quarkus-langchain4j-bomдля согласованности. - Dev Services для Ollama стартуют Docker-контейнер автоматически в dev-режиме — убедитесь, что Docker доступен в CI.
Common mistakes
- Путать термин «quarkus langchain4j» с соседним механизмом Quarkus.
- Не называть границу lifecycle, transaction, thread или request для «quarkus langchain4j».
- Игнорировать production-эффекты «quarkus langchain4j»: latency, SQL shape, memory, security или observability.
What the interviewer is testing
- Попросить объяснить механизм «quarkus langchain4j» на минимальном примере.
- Проверить, видит ли кандидат failure mode и диагностику для «quarkus langchain4j».
- Уточнить, какие настройки или API меняют «quarkus langchain4j» в реальном сервисе.