Темы



Использование 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 на виртуальном потоке для его обработки через конвейер:

Application flow diagram showing the pipeline stages: Customer Enquiry flows to QUEUED, then VALIDATING, which branches to either SEARCHING (if genuine) or REJECTED (if spam/off-topic). SEARCHING leads to WRITING_REPORT (if matches found) or NO MATCHES. WRITING_REPORT completes at DONE.

Архитектура использует Jakarta WebSocket для передачи обновлений статуса в реальном времени с сервера в браузер, что позволяет наблюдать за прохождением этапов агентами по мере вызова моделью инструментов:

Application architecture diagram showing Browser with Pipeline Dashboard connecting to Open Liberty server containing AppState, CopilotClient in EMPTY mode, virtual thread agents, and WebSocket push for real-time UI updates.

Отправляйте несколько запросов одновременно, чтобы увидеть в действии параллельно работающих агентов на виртуальных потоках. Каждый из них обрабатывается независимо в рамках собственного сеанса Copilot.

Screenshot of the sample application showing the pipeline dashboard with multiple enquiries being processed concurrently. Screenshot of the sample application showing detailed agent event log and property search results.

Возможности 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:

  1. Включение экспериментальных API: передайте -Acopilot.experimental.allowed=true компилятору. Без этого флага процессор аннотаций откажется генерировать метаданные инструмента. Более подробную информацию об экспериментальных API см. в документации Copilot SDK.
  2. Регистрация процессора аннотаций: добавьте 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, чтобы контейнер мог:

  1. Отслеживать поток для завершения жизненного цикла (@PreDestroy / остановка сервера)
  2. Применять ограничения и политики параллелизма
  3. Автоматически распространять контекст (без необходимости ручного использования contextualRunnable)

Open Liberty 26.x поддерживает виртуальные потоки ManagedThreadFactory с помощью атрибута virtual в server.xml.

© GitHub Engineering