嵌入 Copilot SDK¶
本导读说明 API 如何嵌入 GitHub Copilot SDK,并将 Copilot 会话封装为应用程序服务。你将了解应用如何启动 SDK 客户端、创建流式会话、监听会话事件,以及避免使用过时的模型目录。
SDK 的使用位置¶
主要集成点是
CopilotChatService。它通过以下语句导入 SDK:
该命名空间是迁移时常见的易错点。在 SDK 1.0.0 发布之前,命名空间为 GitHub.Copilot.SDK;在此处使用的 v1.x 代码中,命名空间为
GitHub.Copilot ,尽管包引用仍名为
GitHub.Copilot.SDK。
单个长生命周期客户端¶
Program.cs
将 CopilotChatService 注册为单例:
在 API 进程的整个生命周期内,该服务持有一个 CopilotClient 字段。这样可避免为每个 HTTP 请求反复启动和停止 Copilot 连接,并将模型列表获取和聊天流式传输统一置于一个托管服务之后。
CopilotChatService 实现了 IAsyncDisposable。当主机释放该单例时, CopilotChatService.DisposeAsync 调用
CopilotClient.StopAsync() ,从而正常关闭 SDK 连接。
启动和恢复客户端¶
CopilotChatService.EnsureStartedAsync 是列出模型或开始聊天前的入口检查:
if (_isStarted && _client != null) return;
_isStarted = false;
if (_client != null)
{
try { await _client.StopAsync(); } catch { }
}
_client = new CopilotClient();
await _client.StartAsync();
_isStarted = true;
该方法将 _isStarted 和 _client 作为真实状态来源。如果客户端尚未启动,或之前的故障已将其标记为不健康,服务会停止旧客户端,创建新的 CopilotClient,启动它并记录新状态。
恢复逻辑在 CopilotChatService.ChatStreamAsync中完成。后台会话捕获 IOException,记录 Copilot 连接已丢失,将
_isStarted = false,并以该异常结束输出通道。下一个请求会再次调用 EnsureStartedAsync 并重新创建客户端。
创建流式会话¶
CopilotChatService.ChatStreamAsync 会为每个提示构建一个 SessionConfig :
SessionConfig config = new()
{
Model = model,
Streaming = true,
SystemMessage = systemMessage != null ? new SystemMessageConfig
{
Mode = SystemMessageMode.Append,
Content = systemMessage
} : null
};
所选模型来自请求,服务默认使用 claude-haiku-4.5 。 Streaming = true 会指示 SDK 发出响应增量。提供系统消息时,该消息会作为 SystemMessageConfig 以 Append
模式发送,因此应用上下文会追加到会话中,而不是替换会话的基础系统行为。
会话使用 await using创建,因此该提示处理完毕后会异步释放会话:
会话事件¶
该服务使用显式泛型类型参数订阅 SDK 事件:
在 v1.x 中,必须显式指定 <SessionEvent> 。旧的非泛型形式已无法可靠推断事件类型,因此升级时遗漏该类型参数是常见故障。
CopilotChatService 处理四种事件:
AssistantMessageDeltaEvent将DeltaContent写入输出流。AssistantMessageEvent记录助手响应已完成。SessionIdleEvent完成用于等待本轮结束的TaskCompletionSource。SessionErrorEvent记录 SDK 错误,并以异常结束等待任务。
完成订阅后,通过以下调用发送提示:
将 SDK 事件桥接到 IAsyncEnumerable¶
SDK 会话在后台 Task中运行。增量会写入
Channel<string>,外层异步迭代器则读取该通道:
var outputChannel = Channel.CreateUnbounded<string>();
_ = Task.Run(async () =>
{
// create session, handle events, write chunks
}, cancellationToken);
await foreach (var chunk in outputChannel.Reader.ReadAllAsync(cancellationToken))
{
yield return chunk;
}
这种结构是有意设计的。C# 异步迭代器无法在拥有 SDK 会话的 try/catch 代码块内部执行 yield return。通道使会话能够在内部处理异常和完成状态,而外层方法则向控制器公开简洁的 IAsyncEnumerable<string>。
列出实时可用模型¶
CopilotChatService.ListModelsAsync 调用 CopilotClient.ListModelsAsync() ,并返回已连接 Copilot CLI 实际提供的模型 ID 和显示名称:
硬编码模型 ID 很容易引发问题。模型可用性会因账户、发布批次和提供商而变化;此演示中过时的硬编码列表曾导致六个模型中只有一个可用。API 在
ChatController.AvailableModels中仍保留静态目录,但它仅用于元数据和回退。正常流程会从 SDK 获取实时模型列表。