Использование SDK GitHub Copilot для Java
Java-разработчикам больше не нужно полагаться на подходы, привязанные к конкретным Java-фреймворкам, чтобы использовать ИИ в своих корпоративных приложениях.
Хотя Langchain4j действительно расширил возможности разработчиков, избавив их от посредничества конкретных ИИ-провайдеров, зависимость от самого Langchain4j всё равно оставалась. А в случае со Spring AI у вас, разумеется, возникала зависимость от архитектурных решений Spring (если не от самого Spring).
Теперь GitHub Copilot SDK для Java представляет собой первый по-настоящему независимый от фреймворков способ взаимодействия с ИИ из Java. А благодаря поддержке BYOK (Bring Your Own Key) GitHub Copilot SDK для Java также нейтрален по отношению к провайдерам ИИ.
Несмотря на название GitHub Copilot SDK, вы можете использовать его с любым прямым провайдером моделей (таким как OpenAI, Azure, Anthropic или эндпоинтами, совместимыми с OpenAI), передав provider/ProviderConfig со своим собственным baseUrl + apiKey (или токеном доступа). Подписка на Copilot не требуется. |
GitHub Copilot SDK для Java — это клиентская библиотека, которая позволяет вашему серверному коду на Java программно создавать сеансы агентов Copilot, регистрировать инструменты, отправлять промпты и получать структурированные ответы. Она работает в серверных средах, включая Jakarta EE и Spring. Если вы занимаетесь корпоративной разработкой на Java какое-то время, этот SDK покажется вам родным: CompletableFuture, аннотации, лямбды, виртуальные потоки — всё здесь.
В этой статье показано, как использовать SDK, разобран полноценный пример приложения на Jakarta EE 11, а также предложены конкретные следующие шаги для самостоятельного тестирования. Я выбрал Jakarta EE 11 для своей демо-версии, поскольку был ведущим координатором релизов этого выпуска. Я верю, что открытые стандарты — это лучший способ расширить возможности разработчиков. Подробнее о Jakarta EE 11 читайте в этой статье на InfoQ.
Данное демонстрационное приложение представляет собой обертку агента (agent harness) на базе Jakarta EE 11. Но разработчики, разумеется, могут создать собственную обертку, используя хорошо знакомые им Java-фреймворки и библиотеки.
Клонируйте демонстрационное приложение и попробуйте его в деле >
Где получить
SDK доступен в виде зависимости Maven:
com.github
copilot-sdk-java
1.0.7-preview.1
Предварительные требования:
- JDK 17 или 25 (рекомендуется 25 — открывает доступ к виртуальным потокам и другим современным функциям)
- Maven 3.9+
- Учетная запись GitHub с активной подпиской на Copilot
- Локально установленный Copilot CLI версии 1.0.71 или выше.
Разбор демо-приложения
Лучший способ увидеть SDK в действии — запустить это демонстрационное приложение.
Получение кода
git clone https://github.com/microsoft/Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk.git
cd Build26-BRK206-your-agent-anywhere-multiclient-multidevice-with-github-copilot-sdk/src/java-agent-orchestrator
mvn clean package liberty:run
# Open http://localhost:9080/index.xhtml
Демонстрационное приложение на Java создано на базе:
| Компонент | Технология |
|---|---|
| Среда выполнения | Open Liberty 26.0.0.5 |
| Платформа | Jakarta EE 11 (Faces 4.1, CDI 4.1, WebSocket 2.2, Data 1.0, Persistence 3.2) |
| Интерфейс (UI) | PrimeFaces 15.0.16 |
| Оркестрация ИИ | Copilot SDK для Java 1.0.7-preview.1 |
| База данных | H2 в памяти (10 предварительно добавленных объектов недвижимости) |
Что делает приложение
Приложение представляет собой конвейер обработки лидов в сфере недвижимости с помощью агента. Клиент отправляет запрос («Я ищу дом с 3 спальнями в Лондоне стоимостью до 800 000 фунтов стерлингов»), и система запускает изолированного агента Copilot на виртуальном потоке для его обработки через конвейер:
Архитектура использует Jakarta WebSocket для передачи обновлений статуса в реальном времени с сервера в браузер, что позволяет наблюдать за прохождением этапов агентами по мере вызова моделью инструментов:
Отправляйте несколько запросов одновременно, чтобы увидеть в действии параллельно работающих агентов на виртуальных потоках. Каждый из них обрабатывается независимо в рамках собственного сеанса Copilot.
Возможности SDK в действии
Давайте рассмотрим ключевые особенности SDK по мере их появления в коде примера.
Определение инструментов с помощью @CopilotTool
Это главная API-функция. Если вы когда-либо писали эндпоинт @GET в JAX-RS или бин @MessageDriven, это покажется вам мгновенно знакомым:
@CopilotTool(value = "Sets the current phase of the agent. Use this to report progress.",
name = "set_current_phase")
public String setCurrentPhase(
@CopilotToolParam("The phase to transition to (VALIDATING, SEARCHING, "
+ "WRITING_REPORT, REJECTED_GARBAGE, REJECTED_NO_MATCHES, or DONE)")
String phaseName) {
phase = Phase.valueOf(phaseName.trim().toUpperCase(Locale.ROOT));
notifyUi();
return "Phase set to " + phase.getLabel();
}
Аннотация @CopilotTool объявляет метод инструментом, который может вызывать модель. Аннотация @CopilotToolParam описывает каждый параметр, чтобы модель понимала, что именно нужно передать. SDK берет на себя всю генерацию JSON Schema, синтаксический анализ аргументов и диспетчеризацию. Вы просто пишете обычный метод на Java.
Два предварительных требования для сборки с @CopilotTool. API инструментов на основе аннотаций в настоящее время является экспериментальной функцией SDK, поэтому вам необходимо настроить две вещи в сборке Maven:
- Включение экспериментальных API: передайте
-Acopilot.experimental.allowed=trueкомпилятору. Без этого флага процессор аннотаций откажется генерировать метаданные инструмента. Более подробную информацию об экспериментальных API см. в документации Copilot SDK. - Регистрация процессора аннотаций: добавьте SDK в качестве
annotationProcessorPath, чтобы компилятор мог найти процессор@CopilotToolи сгенерировать классы$$CopilotToolMetaна этапе компиляции.
Оба параметра настраиваются в maven-compiler-plugin:
org.apache.maven.plugins
maven-compiler-plugin
3.15.0
-Acopilot.experimental.allowed=true
com.github
copilot-sdk-java
1.0.7-preview.1
Чтобы зарегистрировать все аннотированные инструменты из объекта:
List annotatedTools = ToolDefinition.fromObject(this);
Инлайн-инструменты на основе лямбд с помощью ToolDefinition.from(...)
Если вы хотите определить инструмент прямо в месте вызова без создания отдельного метода, используйте стиль с лямбдами:
ToolDefinition reportIntentTool = ToolDefinition
.from("report_intent",
"Reports the current intent of the agent",
Param.of(String.class, "intent", "Intent in max 4 words"),
(String intent) -> {
currentIntent = intent;
addEvent(Instant.now(), "intent", "Intent updated", intent);
notifyUi();
return "ok";
})
.overridesBuiltInTool(true);
Обратите внимание на .overridesBuiltInTool(true). Это указывает SDK, что наш инструмент report_intent намеренно заменяет встроенный инструмент с тем же именем. Это полезно, когда вам требуется кастомное поведение для инструмента, который модель уже знает.
Сканирование инструментов между классами
Инструментам не обязательно находиться в том же классе, что и логика вашего агента. Вот searchProperties, определенный в отдельном бине CDI:
@ApplicationScoped
public class PropertyDatabase {
@CopilotTool(value = "Searches the real estate listings database. "
+ "Returns up to 10 matching properties.",
name = "search_properties")
public List searchProperties(
@CopilotToolParam("Property type substring (e.g. 'flat', 'house')") String type,
@CopilotToolParam("City substring (e.g. 'London', 'Bristol')") String city,
@CopilotToolParam("Minimum number of bedrooms (0 for no minimum)") int minBedrooms,
@CopilotToolParam("Maximum price in GBP (0 for no maximum)") double maxPriceGbp) {
// ... filter and return matching properties ...
}
}
searchProperties (
@CopilotToolParam («Property type substring (e.g. 'flat', 'house')») String type,
@CopilotToolParam («City substring (e.g. 'London', 'Bristol')») String city,
@CopilotToolParam («Minimum number of bedrooms (0 for no minimum)») int minBedrooms,
@CopilotToolParam («Maximum price in GBP (0 for no maximum)») double maxPriceGbp) {
// … filter and return matching properties …
}
}»>Обычно вы регистрировали бы их с помощью ToolDefinition.fromObject(propertyDatabase). В демонстрационном приложении вместо этого мы используем обертку на основе лямбды, поскольку клиентские прокси-объекты CDI могут скрывать метаданные аннотаций.
Настройка системного сообщения
SDK предоставляет детальный контроль над системным сообщением. Используйте SystemMessageMode.CUSTOMIZE для замены определенных разделов с сохранением остальных:
SystemMessageConfig systemMessage = new SystemMessageConfig()
.setMode(SystemMessageMode.CUSTOMIZE)
.setSections(Map.of(SystemMessageSections.IDENTITY,
new SectionOverride()
.setAction(SectionOverrideAction.REPLACE)
.setContent("""
You are part of a real estate recommendation system.
You will receive enquiries from customers, and you must
carry out the following workflow...
""")));
Текстовый блок ("""...""") делает многострочные промпты читаемыми без конкатенации строк. Переопределение раздела IDENTITY заменяет только самоописание модели, оставляя защитные механизмы безопасности (guardrails) в неприкосновенности. Если вы предпочитаете более простой подход, SystemMessageMode.APPEND добавляет ваш контент после стандартного системного сообщения, ничего не заменяя.
Цикл агента: sendAndWait(...)
Одна строка запускает полный цикл работы агента:
session = client.createSession(sessionConfig).get();
// ...
AssistantMessageEvent result = session.sendAndWait(escapedEnquiry).get();
За вызовом .get() модель выполняет рассуждения, вызывает ваши инструменты (потенциально несколько раз) и возвращает окончательный ответ. При использовании виртуального потока .get() обходится недорого. Во время ожидания потоки ОС не расходуются. SDK автоматически передает вызовы инструментов вашим зарегистрированным обработчикам и возвращает результаты модели до завершения работы.
Обработка событий в реальном времени с помощью session.on(...)
Подпишитесь на события сеанса для создания отзывчивых пользовательских интерфейсов:
sessionSubscription = session.on(event -> {
captureSessionEvent(event);
uiUpdateSocket.pushDetailUpdate(id);
});
Каждый вызов инструмента, каждый результат, каждое сообщение ассистента генерируют событие. Демонстрационное приложение перехватывает эти события и передает их в браузер через Jakarta WebSocket, благодаря чему панель мониторинга конвейера обновляется в реальном времени. Вы можете использовать сопоставление с образцом (pattern matching) для обработки определенных типов событий:
if (event instanceof AssistantMessageEvent msg) {
finalReport = msg.getData().content();
} else if (event instanceof ToolExecutionStartEvent start) {
// Tool is being invoked...
}
Бездесктопный клиент и обработка разрешений
Клиент настроен для работы на стороне сервера:
copilotClient = new CopilotClient(
new CopilotClientOptions()
.setMode(CopilotClientMode.EMPTY)
.setCopilotHome(copilotHome)
.setExecutor(contextualVirtualThreadExecutor));
CopilotClientMode.EMPTY означает отсутствие интеграции с IDE — клиент взаимодействует напрямую с Copilot CLI. Пользовательский Executor (рассматривается ниже) гарантирует выполнение обратных вызовов (callback) инструментов в контексте контейнера.
Для обработки разрешений в примере используется:
sessionConfig.setOnPermissionRequest(PermissionHandler.APPROVE_ALL);
APPROVE_ALL подходит для демонстраций и разработки. В продакшене реализуйте настоящую политику разрешений, которая проверяет, какие инструменты модели разрешено вызывать.
Шаблоны интеграции с Jakarta EE
SDK не является изолированным фреймворком. Он естественным образом комбинируется с Jakarta EE, а также, разумеется, с проприетарными фреймворками, такими как Spring.
Параметр Executor является ключевой точкой интеграции. Спецификация Jakarta Concurrency (§5.2 в спецификации 3.1) требует, чтобы создаваемые приложением потоки получались из ManagedThreadFactory, чтобы контейнер мог:
- Отслеживать поток для завершения жизненного цикла (
@PreDestroy/ остановка сервера) - Применять ограничения и политики параллелизма
- Автоматически распространять контекст (без необходимости ручного использования
contextualRunnable)
Open Liberty 26.x поддерживает виртуальные потоки ManagedThreadFactory с помощью атрибута virtual в server.xml.
