使用 GitHub Copilot SDK 在 .NET 中构建 Copilot Agent 扩展:包引入、六大护栏与会话生命周期实战
使用 GitHub Copilot SDK 在 .NET 中构建 Copilot Agent 扩展包引入、六大护栏与会话生命周期实战【免费下载链接】skillsRepository for skills to assist AI coding agents with .NET and C#项目地址: https://gitcode.com/GitHub_Trending/skills17/skills本篇技术指南围绕 plugins/dotnet-ai/skills/technology-selection 技能库中的 Copilot 分支参考文档展开讲解如何在 .NET 应用中引入GitHub.Copilot.SDK、将自定义开发工作流接入 GitHub Copilot Agent 运行时并给出从包版本管理、六大使用护栏到最小可运行代码的完整实战路径。读完本文你将掌握CopilotClient的创建与会话管理、权限决策、事件订阅、超时与 Token 统计的正确姿势并能对照本仓库 skill-validator 中真实的生产级用法进行验证与落地。适用边界它不是一个通用 LLM 客户端参考文档 copilot.md 开篇即给出了一条明确的红线仅用于必须通过 GitHub Copilot Agent 运行时运行的自定义开发工作流。不要把它当作通用 LLM 客户端。也就是说GitHub.Copilot.SDK的定位是Copilot 平台扩展而不是又一层模型调用封装。本仓库的技术选型决策树对此给出了更完整的横向对照见 SKILL.md任务类型技术选型单轮 prompt → response无需工具调用Microsoft.Extensions.AIMEAI的IChatClient多步工具调用、Agent 循环、多 AgentMicrosoft Agent FrameworkMicrosoft.Agents.AIGitHub Copilot 扩展 / 自定义开发工作流 AgentGitHub Copilot SDKGitHub.Copilot.SDK生产环境运行预训练模型ONNX Runtime选型库分层建议中同样强调SKILL.mdGitHub.Copilot.SDK只用于构建 Copilot 平台扩展这一场景常规 LLM 集成应当以 MEAI 为抽象层、把具体 ProviderAzure.AI.OpenAI/OpenAI/OllamaSharp等通过AddChatClient放在其后。因此先确认你的需求是否真的需要经过 Copilot Agent 运行时再决定是否引入本 SDK。包引入与版本策略Pre-1.0 必须精确锁定参考文档给出的最小引入方式PackageReference IncludeGitHub.Copilot.SDK Version0.3.0 /文档特别强调该 SDK 处于 Pre-1.0 阶段必须固定精确版本Pin an exact version并在每次升级前审阅发布说明release notes。这是因为 Pre-1.0 的 API 可能发生破坏性变更浮动版本号会让构建在无人察觉的情况下失效。这一策略在本仓库的工程实践中得到了印证当前仓库的 SkillValidator.csproj 已将GitHub.Copilot.SDK精确锁定到1.0.11高于参考文档撰写时的0.3.0并且针对该 SDK 1.x 标记的[Experimental]诊断码GHCP001涉及权限决策与 Session FS 的 RPC 类型做了显式抑制!-- GitHub.Copilot.SDK 1.x marks its permission-decision and session-fs RPC types ([Experimental] GHCP001). ... Suppress the experimental diagnostic until the SDK promotes them to stable. -- NoWarn$(NoWarn);GHCP001/NoWarn可见升级版本时不仅要改版本号还可能需要同步处理新版本引入的编译诊断或 API 形态变化——这正是升级前审阅 release notes的现实意义。你应当以 NuGet 上当前最新的稳定版为准并在升级后跑一遍完整构建与测试。六大 Guardrails生产级 Copilot 扩展的行为准则参考文档给出了六条使用护栏下面逐条展开并结合仓库源码说明其落地方式。1. 启动并复用唯一的CopilotClient应用关闭时停止不要每次请求都new一个客户端。CopilotClient是有状态、有成本连接/握手的资源应当进程内单例。本仓库的 AgentRunner.cs 是教科书级实现用ConcurrentDictionarystring, CopilotClient按插件根目录缓存客户端配合SemaphoreSlim双检锁保证并发下只初始化一次private static readonly ConcurrentDictionarystring, CopilotClient _pluginClients new(StringComparer.OrdinalIgnoreCase); private static readonly SemaphoreSlim _clientLock new(1, 1); public static async TaskCopilotClient GetPluginClient(string? pluginRoot, bool verbose) { var key pluginRoot ?? ; if (_pluginClients.TryGetValue(key, out var existing)) return existing; await _clientLock.WaitAsync(); try { if (_pluginClients.TryGetValue(key, out existing)) return existing; var options new CopilotClientOptions { ... }; var client new CopilotClient(options); await client.StartAsync(); _pluginClients[key] client; return client; } finally { _clientLock.Release(); } }对应的关闭逻辑AgentRunner.cs在应用退出时遍历缓存逐个StopAsync且对单个失败做容错而不是中断整体清理public static async Task StopAllClients() { foreach (var (key, client) in _pluginClients) { try { await client.StopAsync(); } catch (Exception ex) { Console.Error.WriteLine($Warning: failed to stop client {key}: {ex.Message}); } } _pluginClients.Clear(); }一个容易被忽略的细节仓库在启动时用CaptureGitHubToken()AgentRunner.cs一次性捕获GITHUB_TOKEN随后立刻从环境变量中清除避免凭据泄漏到子进程捕获到的 Token 通过CopilotClientOptions.GitHubToken注入。这也是凭据安全在实际工程中的落地点。2. 每个工作流创建有界 Session用后销毁CopilotClient是长生命周期的而Session是短生命周期的。每个独立工作流一次任务/一次对话对应一个 session处理完必须释放。参考文档的最小形态用await using var session ...显式依赖IAsyncDisposable仓库的 LlmSession.cs 同样在await using中创建会话并在finally中清理会话产生的临时配置目录LlmSession.cs确保不残留磁盘垃圾。3. 显式设置工作目录、模型、系统消息与权限处理器Session 的四个关键配置项必须显式给出不要依赖默认值。仓库的SessionConfig构造AgentRunner.cs展示了完整形态return new SessionConfig { Model model, // 显式指定模型标识 Streaming true, WorkingDirectory workDir, // 显式工作目录 SkillDirectories [..skillDirs, ..noiseDirs], ConfigDirectory configDir, McpServers sdkMcp, CustomAgents customAgents, InfiniteSessions new InfiniteSessionConfig { Enabled false }, // 禁用无限会话 CreateSessionFsProvider _ new LocalSessionFsHandler(configDir), OnPermissionRequest ... // 显式权限处理器 };系统消息System Message在 LlmSession.cs 中通过SystemMessageConfig显式设置并明确Mode SystemMessageMode.Replace替换而非追加Content传入系统提示词SystemMessage new SystemMessageConfig { Mode SystemMessageMode.Replace, Content systemPrompt, },客户端层面的CopilotClientOptions同样需要显式配置仓库设置了LogLevelverbose 时CopilotLogLevel.Info否则None、SessionFs初始工作目录、会话状态路径、按操作系统选择 Windows/Posix 约定见 AgentRunner.cs。4. 无用户可用时权限请求默认拒绝这是安全底线当没有真实用户在场例如批处理、评测、CI 场景时任何权限请求都应默认拒绝而不是默认放行。参考文档的原始表述是 Default permission requests to deny when no user is available. 仓库给出了两个实现样例默认拒绝处理器LlmSession.csOnPermissionRequest onPermissionRequest ?? ((_, _) Task.FromResult(PermissionDecision.UserNotAvailable())),沙箱化放行AgentRunner.csShell 命令类权限必须通过路径安全检查CheckShellPermission仅允许工作目录与技能路径内的路径才PermissionDecision.ApproveOnce()否则PermissionDecision.Reject(...)并附拒绝原因OnPermissionRequest (request, _) { if (request is PermissionRequestShell shellRequest) { var allowed CheckShellPermission(shellRequest, workDir, effectiveSkillPath, ...); return Task.FromResult( allowed ? PermissionDecision.ApproveOnce() : PermissionDecision.Reject(Path outside allowed directories)); } return Task.FromResult(PermissionDecision.ApproveOnce()); },同时它还通过Hooks.OnPreToolUse对工具调用做前置拦截AgentRunner.cs把权限校验推进到工具执行之前——这就是默认拒绝 最小授权的完整工程形态。5. 发送 Prompt 前先订阅事件GitHub.Copilot.SDK是事件驱动模型会话的状态、增量输出、Token 用量、错误都会以事件形式推送。必须在SendAsync之前完成事件订阅否则会错过关键事件尤其是SessionIdleEvent/SessionErrorEvent这类用于判定结束的事件。仓库 LlmSession.cs 展示了完整的事件处理骨架session.OnSessionEvent(evt { switch (evt) { case AssistantMessageEvent msg: responseContent msg.Data.Content ?? ; break; case AssistantUsageEvent usage: inputTokens (int)(usage.Data.InputTokens ?? 0); outputTokens (int)(usage.Data.OutputTokens ?? 0); cacheReadTokens (int)(usage.Data.CacheReadTokens ?? 0); cacheWriteTokens (int)(usage.Data.CacheWriteTokens ?? 0); break; case SessionIdleEvent: done.TrySetResult(responseContent); break; case SessionErrorEvent err: done.TrySetException(new InvalidOperationException(err.Data.Message ?? Session error)); break; } }); await session.SendAsync(new MessageOptions { Prompt userPrompt }); var content await done.Task.WaitAsync(cts.Token);四类核心事件各司其职AssistantMessageEvent收最终消息、AssistantUsageEvent收 Token 统计、SessionIdleEvent表示会话结束驱动TaskCompletionSource完成、SessionErrorEvent把错误转化为异常。仓库在评测场景还会记录AssistantMessageDeltaEvent流式增量、ToolExecutionStartEvent/ToolExecutionCompleteEvent等用于回放见 AgentRunner.cs。6. 强制超时与取消Token 用量记录不落敏感内容LLM 调用可能挂起或失控必须同时具备超时与可取消能力且记录 Token 时绝不写入 prompt 等敏感内容。仓库的双层超时设计LlmSession.csusing var cts CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); cts.CancelAfter(timeoutMs); // 每次尝试的硬超时并将该 token 传给done.Task.WaitAsync(cts.Token)超时即抛TimeoutExceptionAgentRunner.cs 中还有一整套按场景计算超时、CancelAfter(effectiveTimeout * 1000)并在超时后done.TrySetException的机制。Token 统计方面仓库只记录inputTokens / outputTokens / cacheReadTokens / cacheWriteTokens四类数字LlmSession.cs从不记录请求或响应正文——这正是记录 Token 用量但不记录敏感内容的落地实现。最小可用形态Minimal Shape参考文档给出的最小骨架是理解整个 SDK 生命周期的最佳起点var client new CopilotClient(new CopilotClientOptions()); await client.StartAsync(); await using var session await client.CreateSessionAsync(sessionConfig); await session.SendAsync(new MessageOptions { Prompt prompt }); await client.StopAsync();三步走StartAsync启动客户端 →CreateSessionAsync建会话传入第 3 条中的SessionConfig→SendAsync发 Prompt会话用await using保证释放客户端在应用关闭时统一StopAsync。真实代码中还需要补上session.OnSessionEvent事件订阅、OnPermissionRequest权限处理器、以及CancellationTokenSource.CancelAfter超时控制均可参照上一节仓库实现。与 MEAI / Agent Framework 的协作边界最后回到选型语境。参考文档的定位非常克制仓库决策树SKILL.md也给出了明确的不要用场景单轮 prompt → response摘要、推理、文本生成用 Microsoft.Extensions.AI 参考 的IChatClient不要引入 Copilot SDK多步工具调用 / Agent 循环用 Microsoft Agent Framework 参考Microsoft.Agents.AI构建在 MEAI 之上不要手写循环只有在必须经过 GitHub Copilot Agent 运行时的自定义开发工作流例如本仓库 skill-validator 这种驱动 Copilot Agent 执行技能评测的工具中GitHub.Copilot.SDK才是正确选择。反模式对照SKILL.md中与本文相关的两条不要用HttpClient直连 OpenAI 与 MEAI 混用不要把 Copilot SDK 当作通用 LLM 客户端——守住这条边界你的架构才不会被一个 Pre-1.0 的扩展 SDK 绑架。实战自检清单完成 Copilot 扩展集成后建议对照以下清单逐项核验对应 SKILL.md 的验证环节场景确认工作流确实需要经过 Copilot Agent 运行时而非普通 LLM 调用包版本已精确锁定Pre-1.0 不浮动升级前审阅过 release notesCopilotClient全局复用、仅启动一次应用关闭时StopAsync每个工作流使用独立await using会话用完即释放SessionConfig中 Model、WorkingDirectory、SystemMessage、OnPermissionRequest 均已显式设置无用户场景权限默认拒绝PermissionDecision.Reject/UserNotAvailable事件订阅含错误、用量、完成事件发生在SendAsync之前存在超时与取消机制CancelAfterWaitAsyncToken 记录不含敏感正文集成后完成构建并运行既有测试可参考 SkillValidator.Tests 的用例组织方式。【免费下载链接】skillsRepository for skills to assist AI coding agents with .NET and C#项目地址: https://gitcode.com/GitHub_Trending/skills17/skills创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考