BYOK no Copilot SDK: como usar seu próprio modelo em C#

O BYOK no Copilot SDK permite que uma aplicação C# use o runtime de agentes do GitHub Copilot enquanto envia cada chamada de inferência para um modelo que você controla. Neste guia, você monta um console no .NET 10 que conversa com uma implantação do Microsoft Foundry, autenticada com Microsoft Entra ID ou, quando necessário, com uma API key.
O que é BYOK no Copilot SDK
BYOK é a sigla de Bring Your Own Key, ou seja, traga a sua própria chave. No entanto, a ideia vai além da chave: você traz o seu próprio provedor de modelo.
O GitHub Copilot SDK entrega para a sua aplicação o mesmo motor que roda por trás do Copilot CLI. Com isso, você ganha cliente, sessão, mensagem de sistema e toda a configuração de agente sem escrever essa orquestração do zero.
Por outro lado, o modelo que responde às perguntas não precisa ser um modelo hospedado pelo GitHub. Com o BYOK no Copilot SDK, a classe ProviderConfig muda o destino e a forma de autenticação da inferência.
Na prática, o restante do código continua igual. Você mantém o runtime e troca apenas o endereço para onde as requisições ao modelo são enviadas.
Além disso, a documentação do GitHub informa que o uso com BYOK não exige autenticação no GitHub. Portanto, o consumo de tokens passa a ser cobrado e controlado pelo seu provedor, e não pela assinatura do Copilot.
BYOK não é chave de criptografia
Em outros serviços de nuvem, a mesma sigla costuma se referir a chaves de criptografia gerenciadas pelo cliente. Nesse caso, porém, o termo não tem relação com criptografia de armazenamento.
Aqui, BYOK significa usar o seu próprio endpoint de modelo e as suas próprias credenciais para gerar respostas. Por isso, vale deixar essa diferença clara antes de levar o assunto para uma conversa com o time de segurança.
Runtime e provedor são peças diferentes
O ponto central do exemplo é separar duas responsabilidades. De um lado está o runtime do Copilot, que orquestra a sessão; do outro, está o provedor, que executa o modelo.
O runtime é um processo local do Copilot CLI. A sua aplicação conversa com ele por meio do cliente do SDK, enquanto o runtime decide como montar e enviar cada requisição ao modelo.
O provedor, por sua vez, é qualquer endpoint compatível que você configurar no BYOK do Copilot SDK. Neste artigo, ele é uma implantação do Microsoft Foundry exposta pela API v1 compatível com OpenAI.
Assim que essa separação fica clara, o restante do código se torna previsível. Afinal, quase tudo o que você configura pertence a uma das duas peças.
Provedores compatíveis com BYOK no Copilot SDK
O BYOK no Copilot SDK aceita oficialmente vários tipos de provedor. A tabela abaixo resume as opções e o valor usado na propriedade Type.
| Provedor | Valor de Type |
Observação |
|---|---|---|
| OpenAI | openai |
API da OpenAI e endpoints compatíveis |
| Microsoft Foundry ou Azure OpenAI | openai ou azure |
openai para a rota /openai/v1/, azure para endpoints nativos |
| Anthropic | anthropic |
Modelos Claude pela Messages API |
| Ollama | openai |
Modelos locais por API compatível |
| Microsoft Foundry Local | openai |
Modelos locais no próprio dispositivo |
| vLLM, LiteLLM e similares | openai |
Qualquer endpoint compatível com OpenAI |
Neste exemplo de BYOK no Copilot SDK, o tipo escolhido é openai. Isso acontece porque o Foundry expõe a rota /openai/v1/, que segue o formato da API da OpenAI.
Já para endpoints nativos do Azure OpenAI sem essa rota, a documentação do GitHub recomenda o tipo azure. Nesse cenário, você também pode informar uma versão específica da API.
Pré-requisitos do projeto
Antes de escrever código, instale o SDK do .NET 10 e o GitHub Copilot CLI. Em seguida, confirme que os comandos dotnet --version e copilot --version funcionam no terminal.
Para o caminho padrão de autenticação com Entra ID, você também precisa do Azure CLI. Por outro lado, quem usar apenas API key pode dispensar essa ferramenta.
Além disso, você precisa de um recurso do Microsoft Foundry ou do Azure OpenAI com um modelo implantado. Escolha uma implantação que suporte a Responses API, porque o exemplo usa esse formato de comunicação.
Pacotes do projeto
O projeto é um console comum do .NET 10. Ele usa quatro pacotes NuGet e define um identificador de User Secrets.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<UserSecretsId>copilot-sdk-byok-sample</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<!-- Credenciais do Azure para usar o login do az CLI -->
<PackageReference Include="Azure.Identity" Version="1.21.0" />
<!-- Cliente .NET para o runtime do Copilot -->
<PackageReference Include="GitHub.Copilot.SDK" Version="1.0.11" />
<!-- Leitura de configuração e User Secrets -->
<PackageReference Include="Microsoft.Extensions.Configuration" Version="10.0.3" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="10.0.3" />
</ItemGroup>
</Project>
O pacote GitHub.Copilot.SDK fornece o cliente .NET do runtime. Já o Azure.Identity traz a classe AzureCliCredential, que reaproveita a sessão aberta com az login.
Os pacotes de configuração, por fim, carregam segredos de desenvolvimento sem colocar endpoint ou credencial no controle de versão. As versões acima são as do exemplo original, então confira se há versões mais novas antes de começar.
Lendo a configuração com User Secrets
O primeiro passo do programa é ler o nome da implantação, o endpoint e a chave opcional. Para isso, o exemplo de BYOK no Copilot SDK usa o provedor de User Secrets do .NET.
using Azure.Core;
using Azure.Identity;
using GitHub.Copilot;
using Microsoft.Extensions.Configuration;
// O User Secrets mantém endpoint e credenciais fora do repositório
var configuration = new ConfigurationBuilder()
.AddUserSecrets<Program>(optional: true)
.Build();
var modelName = GetRequiredValue(configuration, "AzureOpenAI:Deployment");
var endpoint = GetRequiredValue(configuration, "AzureOpenAI:Endpoint");
var apiKey = configuration["AzureOpenAI:ApiKey"]?.Trim();
// A pergunta pode vir pela linha de comando
var question = args.Length > 0
? string.Join(' ', args)
: "What is Microsoft Foundry, and how does it relate to the GitHub Copilot SDK?";
using var cancellation = new CancellationTokenSource();
Console.CancelKeyPress += (_, eventArgs) =>
{
eventArgs.Cancel = true;
cancellation.Cancel();
};
static string GetRequiredValue(IConfiguration configuration, string key)
{
var value = configuration[key]?.Trim();
if (string.IsNullOrWhiteSpace(value))
{
throw new InvalidOperationException($"Missing required configuration value: {key}");
}
return value;
}
Existem dois valores obrigatórios. O AzureOpenAI:Deployment é o nome da implantação do modelo, enquanto o AzureOpenAI:Endpoint é o endereço do recurso no Foundry ou no Azure OpenAI.
O AzureOpenAI:ApiKey, por outro lado, é opcional. Se ele não existir, o programa usa Entra ID; se existir, o programa envia a chave no cabeçalho api-key.
Se você ainda tem dúvidas sobre como isolar configurações por ambiente, o artigo sobre Directory.Build.props no .NET mostra como centralizar propriedades comuns entre projetos.
Configurando o ProviderConfig do BYOK no Copilot SDK
O coração do BYOK no Copilot SDK é a instância de ProviderConfig. É ela que diz ao runtime como falar com o provedor escolhido.
// O Microsoft Foundry expõe uma API compatível com a Responses API da OpenAI
var provider = new ProviderConfig
{
Type = "openai",
BaseUrl = NormalizeEndpoint(endpoint),
WireApi = "responses",
ModelId = modelName,
WireModel = modelName,
MaxPromptTokens = 8000,
MaxOutputTokens = 512
};
O valor openai em Type seleciona o formato compatível com OpenAI. Em seguida, BaseUrl aponta para a superfície da API, e WireApi define o uso da Responses API.
A propriedade WireApi aceita dois valores. O completions usa a Chat Completions API e oferece compatibilidade ampla, enquanto o responses traz estado entre turnos, namespaces de ferramentas e suporte a raciocínio.
Os limites de tokens mantêm o exemplo pequeno e previsível. Contudo, eles merecem atenção quando a implantação usa um modelo de raciocínio.
Nesse tipo de modelo, MaxOutputTokens também conta os tokens de raciocínio, e não só a resposta visível. Dessa forma, o limite de 512 tokens pode ser consumido antes de o modelo escrever qualquer resposta.
Se isso acontecer, aumente o valor ou remova a propriedade. Depois, ajuste o limite de acordo com a carga real da sua aplicação.
O papel de ModelId e WireModel
O ModelId identifica o modelo para o runtime do Copilot. Já o WireModel é o nome que segue na requisição enviada ao provedor.
No exemplo, as duas propriedades recebem o mesmo valor. Ainda assim, elas existem separadas porque o identificador interno e o nome do modelo no provedor nem sempre coincidem em outras integrações.
Normalizando o endpoint
O método auxiliar aceita tanto a URL do recurso quanto uma URL que já termina em /openai/v1/. Além disso, ele exige HTTPS antes de qualquer chamada.
static string NormalizeEndpoint(string endpoint)
{
var candidate = endpoint.Trim();
if (!Uri.TryCreate(candidate, UriKind.Absolute, out var uri) ||
uri.Scheme != Uri.UriSchemeHttps ||
string.IsNullOrWhiteSpace(uri.Host))
{
throw new ArgumentException("The configured AzureOpenAI:Endpoint must be an HTTPS URL.");
}
// Aceita a URL do recurso ou uma URL que já termina em /openai/v1
var trimmed = candidate.TrimEnd('/');
if (trimmed.EndsWith("/openai/v1", StringComparison.OrdinalIgnoreCase))
{
return trimmed + "/";
}
// Monta a rota v1 a partir do host do recurso
return $"{uri.GetLeftPart(UriPartial.Authority).TrimEnd('/')}/openai/v1/";
}
Rejeitar um endpoint malformado logo no início é melhor do que descobrir o erro na primeira chamada ao modelo. Afinal, uma mensagem clara na inicialização economiza tempo de depuração.
Vale lembrar que a API v1 do Azure OpenAI no Microsoft Foundry aceita tanto o domínio openai.azure.com quanto o services.ai.azure.com. Por isso, o método preserva o host original e acrescenta apenas a rota.
Autenticação com Entra ID por padrão
Quando nenhuma chave está configurada, o BYOK no Copilot SDK usa o login do Azure CLI. Assim, a aplicação não guarda nenhum segredo de longa duração.
if (string.IsNullOrWhiteSpace(apiKey))
{
// AzureCliCredential reaproveita a sessão criada com az login
var credential = new AzureCliCredential();
var scope = "https://cognitiveservices.azure.com/.default";
#pragma warning disable GHCP001 // Callbacks de autenticação BYOK ainda são experimentais
provider.BearerTokenProvider = async _ =>
{
var token = await credential.GetTokenAsync(
new TokenRequestContext([scope]),
cancellation.Token);
return token.Token;
};
#pragma warning restore GHCP001
}
A propriedade BearerTokenProvider recebe um callback, e não um token fixo. Dessa forma, o SDK pede um token novo sempre que precisa, e a aplicação não precisa controlar a expiração.
Repare também no aviso GHCP001. Ele indica que os callbacks de autenticação do BYOK ainda são experimentais, então a API pode mudar em versões futuras do pacote.
Antes de rodar, garanta que a sua identidade tem o papel Cognitive Services OpenAI User no recurso. Em seguida, execute az login no terminal.
Segundo a documentação do GitHub, o BearerTokenProvider tem prioridade sobre chave estática e token fixo. Portanto, se os dois caminhos forem configurados por engano, o callback vence.
Usando API key quando necessário
Alguns ambientes ainda exigem API key no BYOK do Copilot SDK. Nesse caso, o exemplo envia a chave em um cabeçalho, sem mudar o restante da configuração do provedor.
else
{
// O Azure OpenAI aceita a chave no cabeçalho api-key
provider.Headers = new Dictionary<string, string>
{
["api-key"] = apiKey
};
}
Para ativar esse caminho, basta gravar o segredo opcional com o comando abaixo. A partir daí, o programa deixa de exigir o az login.
dotnet user-secrets set "AzureOpenAI:ApiKey" "<your-api-key>"
Em nenhum dos dois caminhos o console imprime o endpoint ou a credencial. Ele mostra apenas o modo de autenticação escolhido, o que evita vazamentos em logs e gravações de tela.
A regra de ouro continua valendo também aqui. Use User Secrets no desenvolvimento, configuração gerenciada pelo ambiente em produção e nunca cole uma chave no código ou no README.
Iniciando o runtime do Copilot SDK com BYOK
Mesmo com o provedor configurado, o runtime do Copilot continua obrigatório. É ele que orquestra a sessão e monta as requisições enviadas ao modelo.
await using var client = new CopilotClient(new CopilotClientOptions
{
// Inicia o runtime local do Copilot CLI por entrada e saída padrão
Connection = RuntimeConnection.ForStdio("copilot"),
WorkingDirectory = AppContext.BaseDirectory
});
await client.StartAsync(cancellation.Token);
Essa distinção é importante. O BYOK no Copilot SDK não significa usar o SDK sem o Copilot, porque o processo local do CLI continua fazendo a orquestração.
Em outras palavras, o BYOK muda apenas o destino da requisição ao modelo. Toda a lógica de agente continua vindo do runtime que o GitHub mantém.
A conexão RuntimeConnection.ForStdio("copilot") inicia o executável do CLI e conversa com ele pela entrada e saída padrão. Por isso, o comando copilot precisa estar no PATH da máquina.
Criando uma sessão mínima
No BYOK do Copilot SDK, a sessão conecta o nome do modelo ao provedor. Além disso, ela desliga de propósito tudo o que não faz parte deste primeiro teste.
// Desliga ferramentas, skills e operações Git para isolar a chamada ao modelo
await using var session = await client.CreateSessionAsync(new SessionConfig
{
ClientName = "github-copilot-sdk-byok",
Model = modelName,
Provider = provider,
SystemMessage = new SystemMessageConfig
{
Mode = SystemMessageMode.Replace,
Content = "Answer every user question in English. Be concise and factual."
},
AvailableTools = [],
Tools = [],
EnableConfigDiscovery = false,
EnableSkills = false,
EnableHostGitOperations = false
}, cancellation.Token);
As coleções vazias de ferramentas e as opções desligadas são intencionais. Com isso, o exemplo fica mais fácil de entender e mais seguro de executar enquanto você aprende a configurar o provedor.
O modo SystemMessageMode.Replace substitui a mensagem de sistema padrão, em vez de apenas acrescentar uma instrução. Assim, a saída fica previsível mesmo em demonstrações com público de vários idiomas.
Note também que a propriedade Model é obrigatória quando existe um provedor personalizado. Sem ela, o runtime não sabe qual modelo pedir e devolve um erro de modelo não especificado.
Enviando a pergunta e lendo a resposta
Por fim, o programa envia uma única pergunta e espera a resposta final. O tempo limite de três minutos evita que o console fique preso indefinidamente.
Console.WriteLine($"Model: {modelName}");
Console.WriteLine($"Authentication: {(string.IsNullOrWhiteSpace(apiKey) ? "AzureCliCredential" : "ApiKey")}");
Console.WriteLine($"Question: {question}");
var response = await session.SendAndWaitAsync(
new MessageOptions { Prompt = question },
TimeSpan.FromMinutes(3),
cancellation.Token);
var content = response?.Data.Content;
if (string.IsNullOrWhiteSpace(content))
{
throw new InvalidOperationException("The model returned an empty response.");
}
Console.WriteLine();
Console.WriteLine("Answer:");
Console.WriteLine(content);
O método SendAndWaitAsync envia o prompt e só retorna quando o runtime recebe a resposta completa. Depois disso, o código verifica se o conteúdo veio vazio antes de imprimir o resultado.
Essa verificação é útil justamente no caso dos modelos de raciocínio. Afinal, um limite de saída baixo demais costuma aparecer como uma resposta em branco.
Executando o exemplo do BYOK no Copilot SDK
Para testar o BYOK no Copilot SDK, configure os dois valores obrigatórios a partir da pasta do projeto. Em seguida, faça o login no Azure e rode a aplicação.
dotnet user-secrets set "AzureOpenAI:Endpoint" "https://<resource>.services.ai.azure.com"
dotnet user-secrets set "AzureOpenAI:Deployment" "<your-deployment-name>"
az login
dotnet run
A saída mostra o modelo, o modo de autenticação, a pergunta e a resposta. Quando tudo dá certo, o console exibe algo parecido com o bloco abaixo.
Model: <your-deployment-name>
Authentication: AzureCliCredential
Question: What is Microsoft Foundry, and how does it relate to the GitHub Copilot SDK?
Answer:
<model response>
Você também pode trocar a pergunta padrão pela linha de comando. Por exemplo, dotnet run -- "Explain BYOK in one sentence." envia outra pergunta para o mesmo modelo.
Se a chamada falhar com erro de autorização, confira primeiro o papel atribuído à sua identidade. Por outro lado, um erro de recurso não encontrado costuma indicar um nome de implantação incorreto.
Cuidados e limitações do BYOK no Copilot SDK
O BYOK no Copilot SDK traz flexibilidade, mas também transfere responsabilidades para você. A primeira delas é que só os modelos do seu provedor ficam disponíveis.
Além disso, os limites de taxa passam a ser os do provedor, e não os do Copilot. Da mesma forma, o acompanhamento de uso e a cobrança acontecem na conta do provedor.
Por outro lado, as requisições com BYOK no Copilot SDK não consomem a cota de requisições premium do Copilot. Para quem já paga uma implantação no Foundry, isso pode simplificar o controle de custos.
Outro cuidado envolve os dados enviados. Com BYOK, o código e o contexto da sessão vão para o endpoint configurado, então confirme que o provedor atende às exigências de segurança, conformidade e residência de dados da sua empresa.
Também vale acompanhar a evolução do pacote. Como os callbacks de autenticação ainda são experimentais, algumas propriedades podem mudar de nome ou comportamento em versões futuras.
Para quem trabalha com o GitHub Copilot em outras frentes, o artigo sobre o WinDbg MCP com o GitHub Copilot mostra outra forma de estender o agente com ferramentas externas.
Próximos passos com BYOK no Copilot SDK
A maior vantagem do exemplo é a quantidade pequena de código. Depois que a separação entre runtime e provedor fica clara, o resto é configuração.
Você mantém o cliente, a sessão, a mensagem de sistema e a infraestrutura de agente que o GitHub entrega. Então, basta apontar a inferência para outro lugar, seja o Foundry, uma chave da OpenAI ou um Ollama local.
O Entra ID como caminho padrão também é uma boa escolha. Não há chave para rotacionar, vazar ou enviar sem querer para o repositório, porque um az login resolve a autenticação.
A sessão propositalmente simples tem uma única missão. Ela prova que o seu modelo responde, e essa resposta no console vira a sua linha de base.
A partir dela, você pode acrescentar recursos um de cada vez, como ferramentas, servidores MCP, skills, aprovações e streaming. Assim, quando algo quebrar, você saberá exatamente qual peça causou o problema.
Em resumo, o BYOK no Copilot SDK une o melhor dos dois mundos. Você aproveita o runtime de agentes do GitHub e mantém o controle total sobre o modelo, a conta e a cobrança.
FAQ
Preciso de assinatura do GitHub Copilot para usar BYOK?
Não. Segundo a documentação do GitHub, o SDK com BYOK funciona sem autenticação no GitHub, e o uso é cobrado pelo seu provedor de modelo.
O Copilot CLI precisa estar instalado mesmo com BYOK?
Sim. O CLI fornece o runtime que orquestra a sessão, e o BYOK muda apenas o destino das chamadas ao modelo.
Qual tipo de provedor devo usar com o Microsoft Foundry?
Use openai quando o endpoint expõe a rota /openai/v1/. Para endpoints nativos do Azure OpenAI sem essa rota, use azure.
Posso usar modelos locais com BYOK?
Sim. O Ollama e o Microsoft Foundry Local funcionam com o tipo openai, apontando o BaseUrl para o endereço local e sem precisar de chave.
Por que a resposta veio vazia com um modelo de raciocínio?
Provavelmente o MaxOutputTokens foi consumido pelos tokens de raciocínio. Aumente ou remova esse limite e teste novamente.
As chamadas com BYOK consomem requisições premium do Copilot?
Não. Com BYOK, o uso é controlado e cobrado pelo provedor, sem descontar da cota de requisições premium.