自建 Copilot Cli 代理:让 GitHub Copilot 真正"Bring Your Own Key"

自建 Copilot Cli 代理:让 GitHub Copilot 真正"Bring Your Own Key"

Github: https://github.com/wosledon/copilot-auto-byok

一个基于 .NET 10 的轻量级模型代理,解决 Copilot Cli 只支持单个固定 BYOK 的问题,附带完整的指标监控与动态路由能力。


三大核心设计

1. 双协议代理,不强行转换

很多代理项目的一个误区是"协议互转"------把 OpenAI 的请求格式转成 Anthropic 的,反之亦然。这在实际使用中极其脆弱:

  • 字段映射永远滞后于官方 API 的更新
  • 工具调用(Function Calling)、系统提示等高级特性很难对齐
  • 流式响应的 SSE 格式差异巨大

我们的做法更简单也更可靠:分别暴露两套原生端点。

端点 协议 用途
POST /v1/chat/completions OpenAI Copilot 客户端或任何 OpenAI SDK
POST /v1/messages Anthropic Claude Code、Claude Desktop 等
GET /v1/models OpenAI 列出可用模型(含 AutoCopilot)

请求到达后,代理只做三件事:

  1. 校验 API Key
  2. 根据模型名找到对应的 Provider 和 Key
  3. HttpCompletionOption.ResponseHeadersRead 直接透传流式响应

没有格式转换,没有中间缓冲,延迟几乎为零。

csharp 复制代码
// ProxyService.cs 核心片段
var requestMessage = CreateProxyRequest(context, provider, actualModel);
var response = await _httpClient.SendAsync(
    requestMessage,
    HttpCompletionOption.ResponseHeadersRead,  // 关键:不缓冲响应体
    cancellationToken);

// 直接 pipe SSE 流
await response.Content.CopyToAsync(context.Response.Body);

2. AutoCopilot 影子模型

这是项目最具特色的设计。

传统代理需要在客户端指定确切的模型名,比如 gpt-4oclaude-3-5-sonnet。但 Copilot 客户端通常不会暴露模型选择,或者你希望在不修改客户端配置的情况下动态切换底层模型。

AutoCopilot 是一个虚拟模型名。你可以在管理后台随时把它绑定到任意一个已配置的模型:

json 复制代码
{
  "autoCopilot": {
    "currentModel": "claude-3-5-sonnet",
    "currentProvider": "anthropic"
  }
}

切换即时生效,无需重启服务。所有请求 model: "AutoCopilot" 的调用都会被透明转发到当前绑定的真实模型。

这在实际工作中非常有用:

  • 早上用 gpt-5.5 写代码,随时切到 deepseekv4-flash 写文档
  • 某个模型临时故障,秒级切换到备用模型
  • A/B 测试不同模型在相同提示下的表现

3. 请求级指标监控

BYOK 的一个核心诉求是"成本可控"。如果只是转发请求,你根本不知道这个月花了多少钱、哪个模型最费 token、响应慢不慢。

我们在代理层拦截了每一次请求,记录了 14 个字段的详细指标:

指标 说明
timestamp 请求时间
requested_model 客户端请求的模型名
actual_model 实际转发的模型名
provider openai / anthropic
protocol chat.completions / messages
is_streaming 是否流式
prompt_tokens 输入 token 数
completion_tokens 输出 token 数
latency_ms 首字节延迟(TTFT)
total_duration_ms 总耗时
tokens_per_second 生成速度
is_cache_hit 是否命中缓存
status_code HTTP 状态码
error 错误信息

基于这些数据,管理后台提供了:

  • 概览卡片:总请求数、成功率、总 token、预估成本
  • 趋势图表:请求量、token 消耗、延迟分布、模型占比(基于 Chart.js)
  • 请求日志:可筛选、可分页、可导出 CSV

成本估算是内置的------我们在代码中维护了一张各模型的价格表,根据 prompt_tokenscompletion_tokens 自动计算。虽然不是 100% 精确(不含缓存折扣等),但足够做用量预警。


关键实现细节

流式响应的零拷贝转发

对于 SSE 流式响应,很多初学者会犯一个错误:把响应完整读进内存字符串,再逐行 WriteAsync。这在高并发下既耗内存又增加延迟。

正确的做法是让 HTTP 管道直接对接:

csharp 复制代码
// 流式路径
if (isStreaming)
{
    await response.Content.CopyToAsync(originalResponse.Body);
}

CopyToAsync 内部使用缓冲区循环读写,数据从 Provider 的 TCP 连接直接流向客户端,不经过完整的字符串解析。我们在旁边再开一个任务读取同样的流来解析 usage 数据,互不阻塞。

配置热重载

代理服务理论上要 7×24 运行,重启来加载新配置是不可接受的。

ConfigService 使用了一个简单的原子替换模式:

csharp 复制代码
private AppConfiguration _config = new();
private readonly ReaderWriterLockSlim _lock = new();

public void UpdateConfiguration(AppConfiguration config)
{
    _lock.EnterWriteLock();
    try
    {
        _config = config;  // 引用替换,瞬间完成
        PersistToSqlite(config);
    }
    finally
    {
        _lock.ExitWriteLock();
    }
}

读配置用读锁,更新配置用写锁,保证并发安全的同时切换是毫秒级的。

JSON 到 SQLite 的平滑迁移

项目早期使用 JSON 文件存储配置(Data/models.json)。随着功能增加,配置和指标需要更结构化的查询能力,我们迁移到了 SQLite。

但在 Program.cs 启动时,会检查是否存在旧的 JSON 文件:

csharp 复制代码
if (File.Exists(configPath))
{
    MigrateJsonToSqlite(configPath, configService);
    File.Move(configPath, configPath + ".backup", overwrite: true);
}

适用场景与局限性

适合用

  • 个人开发者:已有 OpenAI/Anthropic 额度,想把 Copilot 客户端接过来
  • 小团队:统一管理模型访问和用量,避免每个人自己存 API Key
  • 模型评测:快速切换不同模型,在相同代码库上对比效果

不适合用

  • 需要多轮对话状态管理的场景(本项目是无状态代理)
  • 需要请求内容审查/过滤的企业环境(本项目不做内容安全扫描)
  • 超高并发生产环境(SQLite 单文件在极高写入下会瓶颈)

总结

copilot-auto-byok 的定位非常明确:不做太多,把 BYOK 代理这件事做到极致

它没有复杂的协议转换,没有沉重的依赖,没有需要编译的前端框架。有的只是:

  • 双协议原生代理
  • 一个可以随时切换的影子模型
  • 详尽的请求级指标

如果你也在寻找一种方式,让 Copilot 客户端用你自己的 API Key,同时还想看清每一分钱花在哪里------这个项目值得一试。


GitHub : copilot-auto-byok

技术栈: .NET 10 · ASP.NET Core · EF Core · SQLite · Chart.js

项目展示

相关推荐
Z-D-K2 小时前
考验AI的“自我”、记忆和逻辑-AI对《红楼梦》后40回的改写(11)
人工智能·ai·aigc·交互·agi
专注搞钱2 小时前
AI大模型在工业领域的落地实践——从概念到生产的真实案例
人工智能·ai·智能制造·工业4.0
Icarus_2 小时前
什么是向量数据库?
数据库·ai
亚林瓜子3 小时前
Claude Code + DS + superpowers(纯前端TODO系统)
ai·ds·cc·skill·deepseek·claude code·superpowers
jiayong233 小时前
Claude Code 常见操作实战指南
linux·服务器·网络·ai·claude·claude code
FuckPatience3 小时前
C# new List<T>(IEnumerable<T> collection),链表初始化时传入已存在链表
链表·c#·list
xiami_world3 小时前
从prompt到产品:AI 生成 UI 的三条技术路径对比与工程实践
人工智能·ui·ai·prompt·aigc·ai编程
兔老大RabbitMQ4 小时前
不知道以前在学什么
ai
阿昌喜欢吃黄桃4 小时前
Java优质开源AI项目
java·ai·langchain·开源·rag·springai·langchain4j