news 2026/9/15 21:56:37

GitHub Copilot SDK 的 BYOK 模式:使用自有 API Key 接入 OpenAI、Azure、Anthropic 与本地模型

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
GitHub Copilot SDK 的 BYOK 模式:使用自有 API Key 接入 OpenAI、Azure、Anthropic 与本地模型

GitHub Copilot SDK 的 BYOK 模式:使用自有 API Key 接入 OpenAI、Azure、Anthropic 与本地模型

【免费下载链接】copilot-sdkMulti-platform SDK for integrating GitHub Copilot Agent into apps and services项目地址: https://gitcode.com/GitHub_Trending/co/copilot-sdk

导读

BYOK(Bring Your Own Key,自带密钥)是 GitHub Copilot SDK 提供的一种认证模式:开发者跳过 GitHub Copilot 认证链路,直接使用自己从 OpenAI、Microsoft Foundry(Azure OpenAI)、Anthropic、Ollama 等模型提供商获取的 API Key 或 Bearer Token 来驱动会话。它特别适合企业私有化部署、自定义模型托管以及希望与模型提供商直接结算的场景。读完本文,你将掌握 BYOK 的完整配置模型(ProviderConfig)、五种语言的接入代码、wireApi传输协议选择、Bearer Token 动态获取回调,以及自定义模型列表与常见故障排查方法。

什么是 BYOK?

在默认的 GitHub Copilot 认证流程中,SDK 会使用 GitHub 账号凭证(如 Copilot CLI 存储的凭据、环境变量中的 GitHub Token 或显式传入的 SDK Token)访问 Copilot API。而 BYOK 模式完全绕开这条链路:应用在创建会话时显式传入一个provider配置,SDK 运行时直接向你所指定的模型提供商端点发起请求。

这意味着:

  • 计费归属:请求费用直接计入你的模型提供商账户,而非 GitHub Copilot 配额;
  • 模型自主权:可用模型完全由你的提供商决定,不受 GitHub Copilot 模型列表限制;
  • 部署灵活性:既可以对接云端 API,也可以对接本机运行的 Ollama、Microsoft Foundry Local 等本地推理服务。

从源码结构看,BYOK 的核心载体是各语言 SDK 中的ProviderConfig类型(如 Go 的 ProviderConfig、Node.js 的 ProviderConfig),它承载了端点地址、凭据、传输协议等一系列字段,是连接 SDK 与外部模型服务的"接线端子"。

支持的提供商一览

ProviderType Value说明
OpenAI"openai"OpenAI 官方 API 及任何 OpenAI 兼容端点
Microsoft Foundry / Azure OpenAI"openai""azure"/openai/v1/兼容路径用"openai";走 Azure 原生端点用"azure"
Anthropic"anthropic"Claude 系列模型
Ollama"openai"通过 OpenAI 兼容 API 访问本地模型
Microsoft Foundry Local"openai"在本机设备上通过 OpenAI 兼容 API 运行 AI 模型
其他 OpenAI 兼容服务"openai"vLLM、LiteLLM 等

可以看出"openai"是覆盖面最广的类型:只要端点对外暴露的是 OpenAI 兼容协议,都可以用它接入。

ProviderConfig 配置参考

创建 BYOK 会话时,provider参数接受以下字段:

字段类型说明
type"openai"|"azure"|"anthropic"提供商类型(默认"openai"
baseUrl/base_urlstring必填。API 端点 URL
apiKey/api_keystringAPI Key(本地提供商如 Ollama 可省略)
bearerToken/bearer_tokenstringBearer Token 认证(优先级高于apiKey
bearerTokenProvider/bearer_token_providercallback按需返回 Bearer Token 的回调(优先级高于apiKeybearerToken
wireApi/wire_api"completions"|"responses"选择 Chat Completions API 以获得广泛模型兼容性;或选择 Responses API 以获得多轮状态管理、工具命名空间与推理支持。Anthropic 模型不受此字段影响,始终走 Messages API
azure.apiVersion/azure.api_versionstringAzure API 版本。设置后运行时使用带版本的部署路由;省略时使用 GA 的v1无版本路由

在 SDK 源码中,这一配置模型还有若干未写入文档的扩展字段,理解它们有助于深度调优:

  • transport"http"/"websockets":仅对 OpenAI 兼容提供商且wireApi: "responses"生效。设为"websockets"后,Responses API 请求通过持久 WebSocket 连接传输,适合长时间运行、工具调用密集且依赖previous_response_id增量续接的会话(见 Node.js 类型定义 与 Go 类型定义),默认值为"http"
  • headers:附加到所有出站提供商请求的自定义 HTTP 头;
  • modelId/wire_modelmodelId是运行时用于查找 Agent 配置(工具、提示词、推理行为)与默认 token 上限的"已知模型名";wireModel是实际发送给提供商进行推理的模型名,适合提供商模型名(如 Azure 部署名、自定义微调名)与标准模型名不一致的场景,缺省时依次回退到modelIdSessionConfig.model
  • maxPromptTokens/maxOutputTokens:覆盖模型默认的提示词/输出 token 上限,前者触发会话压缩的阈值,后者决定生成被截断的边界;
  • transport之外,SDK 各语言会对字段名做 snake_case / camelCase 的自动映射,例如 Python 的base_urlwire_api会转换成线上的baseUrlwireApi(见 Python 客户端实现)。

wireApi:选择 Chat Completions 还是 Responses?

wireApi决定 SDK 以何种 OpenAI 协议格式与提供商通信:

  • "completions"(默认):使用 Chat Completions API(/chat/completions),兼容面最广,几乎任何 OpenAI 兼容服务都支持;
  • "responses":使用 Responses API,提供多轮状态管理、工具命名空间和推理支持,适合 GPT-5 系列等较新模型。

Anthropic 模型则不受此设置影响:只要type: "anthropic",SDK 始终使用 Anthropic Messages API。

快速开始:接入 Microsoft Foundry

Microsoft Foundry 是企业 BYOK 部署的常见目标。下面给出完整示例(以gpt-5.2-codex为部署名,FOUNDRY_API_KEY从环境变量读取):

Python
import asyncio import os from copilot import CopilotClient from copilot.session import PermissionHandler FOUNDRY_MODEL_URL = "https://<resource-name>.openai.azure.com/openai/v1/" # 设置 FOUNDRY_API_KEY 环境变量 async def main(): client = CopilotClient() await client.start() session = await client.create_session(on_permission_request=PermissionHandler.approve_all, model="gpt-5.2-codex", provider={ "type": "openai", "base_url": FOUNDRY_MODEL_URL, "wire_api": "responses", # 旧模型使用 "completions" "api_key": os.environ["FOUNDRY_API_KEY"], }) done = asyncio.Event() def on_event(event): if event.type.value == "assistant.message": print(event.data.content) elif event.type.value == "session.idle": done.set() session.on(on_event) await session.send("What is 2+2?") await done.wait() await session.disconnect() await client.stop() asyncio.run(main())
Node.js / TypeScript
import { CopilotClient } from "@github/copilot-sdk"; const FOUNDRY_MODEL_URL = "https://<resource-name>.openai.azure.com/openai/v1/"; const client = new CopilotClient(); const session = await client.createSession({ model: "gpt-5.2-codex", // 你的部署名 provider: { type: "openai", baseUrl: FOUNDRY_MODEL_URL, wireApi: "responses", // 旧模型使用 "completions" apiKey: process.env.FOUNDRY_API_KEY, }, }); session.on("assistant.message", (event) => { console.log(event.data.content); }); await session.sendAndWait({ prompt: "What is 2+2?" }); await client.stop();
Go
package main import ( "context" "fmt" "os" copilot "github.com/github/copilot-sdk/go" ) func main() { ctx := context.Background() client := copilot.NewClient(nil) if err := client.Start(ctx); err != nil { panic(err) } defer client.Stop() session, err := client.CreateSession(ctx, &copilot.SessionConfig{ Model: "gpt-5.2-codex", // 你的部署名 Provider: &copilot.ProviderConfig{ Type: "openai", BaseURL: "https://<resource-name>.openai.azure.com/openai/v1/", WireAPI: "responses", // 旧模型使用 "completions" APIKey: os.Getenv("FOUNDRY_API_KEY"), }, }) if err != nil { panic(err) } response, err := session.SendAndWait(ctx, copilot.MessageOptions{ Prompt: "What is 2+2?", }) if err != nil { panic(err) } if d, ok := response.Data.(*copilot.AssistantMessageData); ok { fmt.Println(d.Content) } }
.NET
using GitHub.Copilot; await using var client = new CopilotClient(); await using var session = await client.CreateSessionAsync(new SessionConfig { Model = "gpt-5.2-codex", // 你的部署名 Provider = new ProviderConfig { Type = "openai", BaseUrl = "https://<resource-name>.openai.azure.com/openai/v1/", WireApi = "responses", // 旧模型使用 "completions" ApiKey = Environment.GetEnvironmentVariable("FOUNDRY_API_KEY"), }, }); var response = await session.SendAndWaitAsync(new MessageOptions { Prompt = "What is 2+2?", }); Console.WriteLine(response?.Data.Content);
Java
import com.github.copilot.CopilotClient; import com.github.copilot.rpc.*; var client = new CopilotClient(); client.start().get(); var session = client.createSession(new SessionConfig() .setModel("gpt-5.2-codex") // 你的部署名 .setOnPermissionRequest(PermissionHandler.APPROVE_ALL) .setProvider(new ProviderConfig() .setType("openai") .setBaseUrl("https://<resource-name>.openai.azure.com/openai/v1/") .setWireApi("responses") // 旧模型使用 "completions" .setApiKey(System.getenv("FOUNDRY_API_KEY"))) ).get(); var response = session.sendAndWait(new MessageOptions() .setPrompt("What is 2+2?")).get(); System.out.println(response.getData().content()); client.stop().get();

五种语言的调用骨架高度一致:启动客户端 → 以provider配置创建会话 → 发送消息并消费事件/响应 → 停止客户端。唯一差异在于事件模型的表达方式:Python 与 TypeScript 使用事件回调(assistant.messagesession.idle),Go、.NET、Java 使用同步的SendAndWait风格调用。

分类型配置示例

OpenAI 直连

provider: { type: "openai", baseUrl: "https://api.openai.com/v1", apiKey: process.env.OPENAI_API_KEY, }

注意baseUrl需要包含完整路径(含/v1)。

Azure OpenAI(Azure 原生端点)

*.openai.azure.com端点使用type: "azure"

provider: { type: "azure", baseUrl: "https://my-resource.openai.azure.com", // 仅主机名 apiKey: process.env.AZURE_OPENAI_KEY, azure: { apiVersion: "2024-10-21", }, }

关键区别:baseUrl只写主机名,不要包含/openai/v1路径——SDK 会根据apiVersion自动构造请求路由(设置azure.apiVersion时走带版本的部署路由,省略时走 GA 的v1无版本路由)。

Microsoft Foundry(OpenAI 兼容端点)

如果 Foundry 部署暴露的是/openai/v1/兼容路径,则改用type: "openai"

provider: { type: "openai", baseUrl: "https://<resource-name>.openai.azure.com/openai/v1/", apiKey: process.env.FOUNDRY_API_KEY, wireApi: "responses", // GPT-5 系列模型 }

Ollama(本地)

provider: { type: "openai", baseUrl: "http://localhost:11434/v1", // 本地 Ollama 无需 apiKey }

Microsoft Foundry Local

Foundry Local 让你在自己设备上本地运行 AI 模型,同样暴露 OpenAI 兼容 API。通过 Foundry Local CLI 安装后,把 SDK 指向本地端点:

provider: { type: "openai", baseUrl: "http://localhost:<PORT>/v1", // 本地 Foundry Local 无需 apiKey }

[!NOTE] Foundry Local 启动在动态端口上——端口号不固定。使用foundry service status确认服务当前监听的端口,再将其填入baseUrl

快速上手 Foundry Local:

# Windows:安装 Foundry Local CLI(需要 winget) winget install Microsoft.FoundryLocal # macOS / Linux:参见 foundrylocal.ai 的安装说明 # 列出可用模型 foundry model list # 运行模型(会自动启动本地服务) foundry model run phi-4-mini # 查看服务运行端口 foundry service status

Anthropic

provider: { type: "anthropic", baseUrl: "https://api.anthropic.com", apiKey: process.env.ANTHROPIC_API_KEY, }

Anthropic 模型始终使用 Claude 专属的 Messages API 格式,不受wireApi影响。

认证方式:静态 API Key 与 Bearer Token

部分提供商要求 Bearer Token 认证而非 API Key。SDK 提供两种方式:

静态 Bearer Token(bearerToken

适用于应用已经持有 token 的场景:

provider: { type: "openai", baseUrl: "https://<resource-name>.openai.azure.com/openai/v1/", bearerToken: process.env.MY_BEARER_TOKEN, // 设置 Authorization 头 }

[!NOTE]bearerToken只接受静态 token 字符串。SDK 不会自动刷新该 token。如果 token 过期,请求会失败,你需要用新 token 重新创建会话。

动态 Bearer Token 回调(bearerTokenProvider

对于需要按需获取、自动续期的场景(典型如 Microsoft Entra ID / Azure Managed Identity),使用回调:

provider: { type: "openai", baseUrl: "https://my-custom-endpoint.example.com/v1", bearerTokenProvider: async () => { return await acquireBearerToken(); }, }

从源码看,这是一个"回调留在客户端、运行时按需回拨"的设计:SDK 不会把回调本身序列化进 RPC 配置,而是发送hasBearerTokenProvider: true标志;运行时在每次出站模型请求前,通过会话级的providerToken.getTokenRPC 回调到客户端获取 token,并将其作为Authorization: Bearer头应用(见 Go 实现 与 Node.js 类型定义)。回调还接收providerNamesessionId上下文参数,便于按提供商或按会话区分 token 的作用域与缓存。SDK 自身不做 token 缓存,缓存与刷新逻辑由回调或其所包裹的身份库(如DefaultAzureCredential)负责。

关于使用 Microsoft Entra Bearer Token 获取与刷新的详细流程,参见 Azure Managed Identity with BYOK。

自定义模型列表(onListModels)

使用 BYOK 时,CLI 服务端并不知道你的提供商支持哪些模型。此时可以在客户端级别提供onListModels处理器,让client.listModels()返回你的提供商模型(标准ModelInfo格式),下游消费者无需查询 CLI 即可发现可用模型。

Node.js / TypeScript
import { CopilotClient } from "@github/copilot-sdk"; import type { ModelInfo } from "@github/copilot-sdk"; const client = new CopilotClient({ onListModels: () => [ { id: "my-custom-model", name: "My Custom Model", capabilities: { supports: { vision: false, reasoningEffort: false }, limits: { max_context_window_tokens: 128000 }, }, }, ], });
Python
from copilot import CopilotClient from copilot.client import ModelInfo, ModelCapabilities, ModelSupports, ModelLimits client = CopilotClient( on_list_models=lambda: [ ModelInfo( id="my-custom-model", name="My Custom Model", capabilities=ModelCapabilities( supports=ModelSupports(vision=False, reasoning_effort=False), limits=ModelLimits(max_context_window_tokens=128000), ), ) ], )
Go
package main import ( "context" copilot "github.com/github/copilot-sdk/go" ) func main() { client := copilot.NewClient(&copilot.ClientOptions{ OnListModels: func(ctx context.Context) ([]copilot.ModelInfo, error) { return []copilot.ModelInfo{ { ID: "my-custom-model", Name: "My Custom Model", Capabilities: copilot.ModelCapabilities{ Supports: copilot.ModelSupports{Vision: false, ReasoningEffort: false}, Limits: copilot.ModelLimits{MaxContextWindowTokens: copilot.Int(128000)}, }, }, }, nil }, }) _ = client }
.NET
using GitHub.Copilot; var client = new CopilotClient(new CopilotClientOptions { OnListModels = (ct) => Task.FromResult<IList<ModelInfo>>(new List<ModelInfo> { new() { Id = "my-custom-model", Name = "My Custom Model", Capabilities = new ModelCapabilities { Supports = new ModelSupports { Vision = false, ReasoningEffort = false }, Limits = new ModelLimits { MaxContextWindowTokens = 128000 } } } }) });
Java
import com.github.copilot.CopilotClient; import com.github.copilot.rpc.*; import java.util.List; import java.util.concurrent.CompletableFuture; var client = new CopilotClient(new CopilotClientOptions() .setOnListModels(() -> CompletableFuture.completedFuture(List.of( new ModelInfo() .setId("my-custom-model") .setName("My Custom Model") .setCapabilities(new ModelCapabilities() .setSupports(new ModelSupports().setVision(false).setReasoningEffort(false)) .setLimits(new ModelLimits().setMaxContextWindowTokens(128000))) ))) );

需要注意两点行为语义:

  • 结果缓存:首次调用后结果即被缓存,与默认行为一致;
  • 完全接管:该处理器会完整替换 CLI 的models.listRPC——不会回退到服务端。从 Node.js 客户端实现 可以看到,listModels()检测到自定义处理器存在时,直接调用它并返回结果。

限制与注意事项

功能层面的差异

  • 模型可用性:只有你的提供商支持的模型可用;
  • 限流策略:受你的提供商限流约束,而非 GitHub Copilot 的限流;
  • 用量统计:用量由你的提供商统计,而非 GitHub Copilot;
  • Premium 请求:BYOK 请求不计入 Copilot premium 请求配额。

各提供商特定限制

Provider限制
Microsoft Foundry Local仅限本地;模型可用性取决于设备硬件;无需 API Key
Ollama无 API Key;仅限本地;模型支持程度不一
OpenAI受 OpenAI 限流与配额约束

故障排查

"Model not specified" 错误

使用 BYOK 时,model参数是必填的:

// ❌ 错误:使用自定义 provider 时必须指定模型 const session = await client.createSession({ provider: { type: "openai", baseUrl: "..." }, }); // ✅ 正确:指定模型 const session = await client.createSession({ model: "gpt-4", // 必填! provider: { type: "openai", baseUrl: "..." }, });

Azure 端点类型混淆

对 Azure OpenAI 端点(*.openai.azure.com)要使用正确的类型:

// ❌ 错误:Azure 原生端点使用 "openai" 类型 provider: { type: "openai", // 无法正常工作 baseUrl: "https://my-resource.openai.azure.com", } // ✅ 正确:使用 "azure" 类型 provider: { type: "azure", baseUrl: "https://my-resource.openai.azure.com", }

但如果你的 Microsoft Foundry 部署提供 OpenAI 兼容端点路径(例如/openai/v1/),则应使用type: "openai"

// ✅ 正确:OpenAI 兼容的 Microsoft Foundry 端点 provider: { type: "openai", baseUrl: "https://your-resource.openai.azure.com/openai/v1/", }

连接被拒绝(Ollama)

确认 Ollama 正在运行且可访问:

# 检查 Ollama 是否运行 curl http://localhost:11434/v1/models # 未运行时启动 ollama serve

连接被拒绝(Foundry Local)

Foundry Local 使用动态端口,重启后可能变化。确认当前端口:

# 查看服务状态与端口 foundry service status

根据输出更新baseUrl中的端口。如果服务未运行,启动一个模型即可拉起服务:

foundry model run phi-4-mini

认证失败

  1. 确认 API Key 正确且未过期;
  2. 检查baseUrl是否符合你的提供商预期格式(尤其注意 Azure 原生端点不要带/openai/v1);
  3. 对于 Bearer Token,确保提供完整的 token(而非仅前缀)。

进一步探索

BYOK 只是 GitHub Copilot SDK 的认证方式之一,SDK 还支持显式 Token、环境变量 GitHub Token、Copilot CLI 凭据等认证路径,完整对比见 认证总览 与 Authenticate Copilot SDK;若你的场景是在 GitHub Actions 或 GitHub App 中以组织身份运行自动化,可参考 服务端到服务端认证。入门构建第一个 Copilot 应用,参见 Getting Started 指南。

【免费下载链接】copilot-sdkMulti-platform SDK for integrating GitHub Copilot Agent into apps and services项目地址: https://gitcode.com/GitHub_Trending/co/copilot-sdk

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 21:55:44

恒捷家电商城SpringBoot毕业设计项目完整拆解与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/15 21:55:03

GD32H759 + RT-Thread 以太网驱动移植实战:从 DMA 描述符到 PHY 调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/15 21:53:23

设计团队文件存储方案:三类场景下的NAS与云盘选型指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/15 21:53:22

FckSignups 项目概览:200+ 浏览器免登录工具的终极收藏

FckSignups 项目概览&#xff1a;200 浏览器免登录工具的终极收藏 【免费下载链接】FckSignups A list of tools that are open-source, in-browser, and require no-signups! 项目地址: https://gitcode.com/GitHub_Trending/fc/FckSignups FckSignups&#xff08;现已…

作者头像 李华
网站建设 2026/9/15 21:52:18

AI生成测试用例实战:从PRD解析到自动化脚本的提示词工程

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/15 21:47:59

C++和标准库速成(七)——类、作用域解析、统一初始化和指派初始化

目录1. 类1.1 定义类1.2 使用类2. 作用域解析3. 统一初始化(高度建议)4. 指派初始化参考1. 类 1.1 定义类 类定义了对象的特征。在C中&#xff0c;类通常在模块接口文件中定义和被导出&#xff0c;然而类的方法定义既可以在相同的模块接口文件中&#xff0c;也可以在对应的模块…

作者头像 李华