使用GitHub Copilot SDK for Java
Using the GitHub Copilot SDK for Java
GitHub Copilot SDK for Java 1.0.7-preview.1 作为首个框架无关的 Java AI 驱动方案发布,支持 BYOK(自带密钥)以对接 OpenAI、Azure、Anthropic 等任意模型提供商,无需 Copilot 订阅。该 SDK 提供 `@CopilotTool` 注解、lambda 工具定义、`sendAndWait()` agent 循环及 `session.on()` 事件流,兼容 Jakarta EE 11 与 Spring。示例应用基于 Open Liberty 26.0.0.5 构建房地产线索管理流水线,使用虚拟线程、CDI 和 WebSocket 实现并发 agent 处理,Maven 坐标 `com.github:copilot-sdk-java:1.0.7-preview.1`。
Java 开发者不再需要依赖特定于 Java 框架的方法来从企业应用中驱动 AI。诚然,Langchain4j 通过去中介化特定 AI 供应商赋能了开发者,但你仍然依赖 Langchain4j。而使用 Spring AI,你当然会依赖 Spring 的设计选择,甚至可能依赖 Spring 本身。现在,GitHub Copilot SDK for Java 是第一个真正与框架无关的从 Java 驱动 AI 的方式。凭借其 BYOK 支持,GitHub Copilot SDK for Java 也实现了 AI 供应商中立。💡 尽管它名为 GitHub Copilot SDK,你可以通过传递带有自己的 baseUrl + apiKey(或 bearer token)的 provider / ProviderConfig,将其用于任何直接模型提供商,如 OpenAI、Azure、Anthropic 或兼容 OpenAI 的端点。无需 Copilot 订阅。
GitHub Copilot SDK for Java 是一个客户端库,它让你的服务器端 Java 代码能够以编程方式创建 Copilot agent 会话、注册工具、发送提示词并接收结构化响应。它适用于服务器环境,包括 Jakarta EE 和 Spring。如果你长期构建企业级 Java,这个 SDK 会让你感到熟悉:CompletableFuture、注解、lambda、虚拟线程,一应俱全。这篇文章将展示如何使用该 SDK,逐步讲解一个完整的 Jakarta EE 11 示例应用,并为你提供具体的后续步骤以便亲自尝试。
我选择 Jakarta EE 11 作为演示平台,因为我是该版本的发布协调负责人。我相信开放标准是赋能开发者的最佳方式。更多关于 Jakarta EE 11 的内容,请参阅这篇 InfoQ 文章。这个示例应用是一个使用 Jakarta EE 11 构建的 agent 框架(agent harness)。当然,开发者也可以使用自己熟悉的 Java 框架和库来构建自己的 agent 框架。
克隆示例应用并亲自尝试 >
如何获取
该 SDK 可作为 Maven 依赖使用:
<dependency>
<groupId>com.github</groupId>
<artifactId>copilot-sdk-java</artifactId>
<version>1.0.7-preview.1</version>
</dependency>
先决条件:
- JDK 17 或 25(推荐 25 —— 解锁虚拟线程和其他现代特性)
- Maven 3.9+
- 一个具有有效 Copilot 订阅的 GitHub 账户
- 本地安装的 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
# 打开 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 | |
| AI 编排 | Copilot SDK for Java 1.0.7-preview.1 | |
| 数据库 | H2 内存数据库(10 条种子房源数据) |
应用功能
该应用是一个房地产线索管理 agent 流水线。客户提交咨询(“我在伦敦寻找一套 3 居室、价格低于 80 万英镑的房子”),系统会在一个虚拟线程上启动一个隔离的 Copilot Agent 来处理该咨询,并经过一个流水线:
架构使用 Jakarta WebSocket 将实时状态更新从服务器推送到浏览器,因此你可以观察 agent 在模型调用工具时如何推进各个阶段:
同时提交多个咨询,以查看并发虚拟线程 agent 的运行情况。每个 agent 都使用自己的 Copilot 会话独立处理。
SDK 特性实战
让我们看看示例代码中出现的 SDK 关键特性。
使用 @CopilotTool 定义工具
这是核心 API。如果你曾经在 JAX-RS 中编写过 @GET 端点或 @MessageDriven bean,你会立刻感到熟悉:
@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 中配置:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.15.0</version>
<configuration>
<compilerArgs>
<arg>-Acopilot.experimental.allowed=true</arg>
</compilerArgs>
<annotationProcessorPaths>
<path>
<groupId>com.github</groupId>
<artifactId>copilot-sdk-java</artifactId>
<version>1.0.7-preview.1</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
要注册来自某个对象的所有注解工具:
List<ToolDefinition> annotatedTools = ToolDefinition.fromObject(this);
使用 ToolDefinition.from(...) 的内联 lambda 工具
当你想在调用点定义一个工具而不需要专门的方法时,可以使用 lambda 风格:
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 工具是有意替换同名内置工具的。当你需要为模型已知的工具提供自定义行为时,这很有用。
跨类工具扫描
工具不必与你的 agent 逻辑位于同一个类中。这是定义在独立 CDI bean 中的 searchProperties:
@ApplicationScoped
public class PropertyDatabase {
@CopilotTool(value = "Searches the real estate listings database. " +
"Returns up to 10 matching properties.", name = "search_properties")
public List<Property> 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) 注册这些工具。在示例应用中,我们改用 lambda 包装器,因为 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 部分覆盖仅替换模型的自我描述,同时保持安全护栏不变。如果你更喜欢简单的方法,SystemMessageMode.APPEND 会在默认系统消息之后添加你的内容,而不替换任何内容。
Agent 循环:sendAndWait(...)
一行代码即可启动完整的 agent 循环:
session = client.createSession(sessionConfig).get();
// ...
AssistantMessageEvent result = session.sendAndWait(escapedEnquiry).get();
在 .get() 背后,模型进行推理,调用你的工具(可能多次),并返回其最终响应。在虚拟线程上,.get() 开销很小。等待期间不会消耗平台线程。SDK 自动将工具调用分发给已注册的处理程序,并将结果反馈给模型,直到完成。
使用 session.on(...) 进行实时事件处理
订阅会话事件以构建响应式 UI:
sessionSubscription = session.on(event -> {
captureSessionEvent(event);
uiUpdateSocket.pushDetailUpdate(id);
});
每次工具调用、每个结果、每条助手消息都会触发事件。示例应用捕获这些事件并通过 Jakarta WebSocket 推送到浏览器,因此流水线仪表板会实时更新。你可以使用模式匹配来处理特定事件类型:
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(下文讨论)确保工具回调在容器上下文中运行。
对于权限处理,示例使用:
sessionConfig.setOnPermissionRequest(PermissionHandler.APPROVE_ALL);
APPROVE_ALL 适用于演示和开发。在生产环境中,应实现真实的权限策略,验证模型被允许调用哪些工具。
Jakarta EE 集成模式
SDK 不是一个框架孤岛。它可以自然地与 Jakarta EE 组合 —— 当然也可以与 Spring 等专有框架组合。Executor 参数是关键集成点。Jakarta Concurrency(3.1 规范中的 §5.2)要求应用程序创建的线程必须从 ManagedThreadFactory 获取,以便容器能够:
- 跟踪线程以进行生命周期关闭(
@PreDestroy/ 服务器停止) - 应用并发约束和策略
- 自动传播上下文(无需手动
contextualRunnable)
Open Liberty 26.x 通过 server.xml 中的 virtual 属性支持虚拟线程 ManagedThreadFactory。
然后,在 AppState.java 中我们注入工厂:
@Resource(lookup = "concurrent/virtualThreadFactory")
private ManagedThreadFactory virtualThreadFactory;
并使用它来创建传递给 Copilot SDK 的 Executor。
// ManagedThreadFactory (virtual=true) 创建容器管理的虚拟线程,
// 自动传播 CDI、JNDI 和事务上下文。
Executor managedVirtualExecutor = runnable -> virtualThreadFactory.newThread(runnable).start();
String copilotHome = Path.of(System.getProperty("user.home"), ".copilot").toString();
CopilotClientOptions copilotClientOptions = new CopilotClientOptions()
.setMode(CopilotClientMode.EMPTY)
.setCopilotHome(copilotHome)
.setExecutor(managedVirtualExecutor);
copilotClient = new CopilotClient(copilotClientOptions);
这会创建携带容器上下文的虚拟线程。当 SDK 将工具调用分发给 searchProperties() 时,该方法可以 @Inject 一个 JPA 仓库并查询数据库,因为回调线程上存在容器上下文。
示例中的其他集成模式:
- CDI
@ApplicationScoped用于单例CopilotClient(每个应用程序生命周期一个客户端)。 - Jakarta Faces
f:websocket通过PushContext推送实时浏览器更新。 - Jakarta Data
@Repository用于类型安全的数据库查询,无需原始 JPA 样板代码。
使用 ToolSet 进行细粒度工具访问控制
SessionConfig 允许你精确指定每个会话可以访问哪些工具:
sessionConfig.setAvailableTools(new ToolSet()
.addCustom("*") // 所有已注册的自定义工具
.addBuiltIn("web_fetch")); // 仅 web_fetch 内置工具
这是一个重要的生产关注点。与其暴露所有内置工具(文件系统访问、shell 执行等),不如显式选择 agent 仅需要的工具。在示例应用中,我们允许所有自定义工具加上 web_fetch,以便 agent 在搜索阶段可以查找实时房产信息。
总结
以下是我们涵盖的内容:
- Java 原生 API:
CompletableFuture、注解、lambda 和虚拟线程使 SDK 感觉像是地道的 Java,而不是从其他语言移植过来的事后想法。 - 三种工具定义风格:注解用于企业模式,lambda 用于内联便捷,JSON Schema 用于完全控制。
- 系统消息自定义:部分级别的覆盖让你精确控制 agent 行为。
- 一行代码的 Agent 循环:
sendAndWait(...)自动处理完整的工具调用循环。 - 实时事件流:
session.on(...)支持响应式 UI 和可观测性。 - 无头服务器端操作:无需 IDE;可在任何安装了 Copilot CLI 的地方运行。
- 与 Jakarta EE 的自然组合:CDI、JPA、WebSocket 和虚拟线程都通过
Executor集成点协同工作。
下一步尝试什么
- 探索 BYOK 支持:GitHub Copilot SDK 可以通过传递带有自己的 baseUrl + apiKey(或 bearer token)的 provider / ProviderConfig,直接用于模型提供商,例如 OpenAI、Azure、Anthropic 或兼容 OpenAI 的端点。无需 Copilot 订阅。
- 克隆示例应用并在本地运行。同时提交多个咨询以查看虚拟线程的运行情况。
- 更换模型。尝试
session.setModel(...)以试验不同的 Copilot 模型。 - 添加你自己的工具。定义一个新的
@CopilotTool方法(例如抵押贷款计算器、学区查询),观察 agent 发现并使用它。 - 部署到 Azure。Open Liberty 在 Azure App Service、AKS 或 Azure Container Apps 上运行良好。请参阅 https://aka.ms/java/ee 上的 Jakarta EE on Azure 指南。
Copilot SDK for Java 将 GitHub Copilot 的全部功能置于你的 Java 代码之后,无需 IDE,也无需框架锁定。克隆示例应用并亲自尝试 >
文章《Using the GitHub Copilot SDK for Java》最初出现在 The GitHub Blog 上。