
Underclass thực hiện những gì
Underclass là một proxy cục bộ kết hợp nhiều gói đăng ký ChatGPT/Codex và GitHub Copilot thành một endpoint tương thích với OpenAI. Các ứng dụng khách giao tiếp qua những route quen thuộc /v1/*, trong khi Underclass chọn một gói đăng ký đủ điều kiện, theo dõi tình trạng hạn mức và chuyển tiếp yêu cầu đến dịch vụ upstream phù hợp.
Điều này hữu ích khi mỗi gói đăng ký có một hạn mức riêng. Thay vì phải chuyển đổi tài khoản thủ công mỗi khi một tài khoản đạt giới hạn, bạn có thể đưa các tài khoản vào một pool dùng chung. Tài khoản đã hết hạn mức sẽ chuyển sang trạng thái chờ và tự động trở lại vòng xoay sau khi cửa sổ hạn mức được đặt lại.
Underclass được thiết kế để hoạt động như một nhà cung cấp duy nhất với năng lực dồi dào, đồng thời duy trì tình trạng tài khoản, tính liên kết phiên và hiệu quả bộ nhớ đệm prompt.
Tính năng chính
- API tương thích với OpenAI: Ứng dụng khách có thể sử dụng
/v1/responses,/v1/chat/completionsvà/v1/models. - Nhiều backend: Pool có thể chứa tài khoản ChatGPT/Codex, tài khoản GitHub Copilot hoặc cả hai.
- Phiên cố định: Các yêu cầu có
prompt_cache_keyhoặcpromptCacheKeysẽ tiếp tục được gán cho cùng một gói đăng ký khi có thể. - Tự động tạm nghỉ: Các phản hồi về hạn mức sẽ đưa tài khoản ra khỏi vòng xoay cho đến khi thời gian chờ thử lại hết hạn.
- Phản hồi bão hòa nhanh: Nếu mọi tài khoản đủ điều kiện cho một model đều đang tạm nghỉ, Underclass trả về
429cùng giá trịRetry-Aftersớm nhất có thể, thay vì xếp hàng vô thời hạn. - Chọn tài khoản có ít yêu cầu đang xử lý nhất: Các tài khoản Codex và Copilot đủ điều kiện cạnh tranh trong một pool phẳng, được lọc theo danh mục model của từng backend.
- Trạng thái bền vững: Tài khoản, danh mục model, thông tin xác thực và liên kết cố định được giữ nguyên sau khi khởi động lại.
- Giao diện web quản trị: Thêm và quản lý tài khoản, chỉnh sửa danh mục model và kiểm tra 200 yêu cầu gần nhất.
- Khả năng quan sát có cấu trúc: Mỗi phản hồi có
x-request-id, và log xác định nhãn tài khoản đã phục vụ từng yêu cầu.
Cài đặt và khởi động Underclass
Tùy chọn 1: Chạy từ dự án Rust
Từ repository, tạo các secret riêng cho ứng dụng khách API và giao diện quản trị, sau đó khởi động máy chủ:
UNDERCLASS_PROXY_KEY="$(openssl rand -hex 32)" \
UNDERCLASS_UI_TOKEN="$(openssl rand -hex 32)" \
cargo run -- serve
Listener mặc định là http://127.0.0.1:8080, và giao diện web có tại URL gốc:
http://127.0.0.1:8080/
Lưu cả hai giá trị đã tạo trong trình quản lý mật khẩu hoặc tệp secret runtime. Proxy key cấp quyền cho các yêu cầu đến /v1/*, còn UI token cấp quyền cho các thao tác quản trị. Underclass không ghi các giá trị này vào thông tin chẩn đoán.
Tùy chọn 2: Chạy bằng Nix
Repository cũng là một Nix flake. Chạy proxy mà không cần cài đặt:
nix run github:ghuntley/underclass -- serve
Để cài gói vào profile Nix, sử dụng:
nix profile install github:ghuntley/underclass
Để phát triển, vào shell được cung cấp và chạy các bài kiểm thử:
nix develop --no-pure-eval
cargo test
Tùy chọn --no-pure-eval là bắt buộc đối với shell phát triển devenv. Tùy chọn này không cần thiết với nix run hoặc nix profile install.
Thêm tài khoản vào pool
- Mở
http://127.0.0.1:8080/. - Dán giá trị đã cấu hình cho
UNDERCLASS_UI_TOKEN. - Chọn Add account.
- Chọn ChatGPT / Codex hoặc GitHub Copilot.
- Mở URL xác thực thiết bị được hiển thị và nhập mã thiết bị.
- Chờ quá trình onboarding hoàn tất. Underclass gắn nhãn tài khoản bằng địa chỉ email hoặc tên người dùng.
- Lặp lại quy trình cho mọi gói đăng ký cần tham gia pool.
Giao diện cũng có thể bật, tắt, xóa hoặc đăng nhập lại tài khoản. Tài khoản bị tắt vẫn được cấu hình nhưng không được chọn cho các yêu cầu.
Kết nối OpenCode
Underclass có lệnh connect để thêm nhà cung cấp Underclass vào OpenCode:
cargo run -- connect
Theo mặc định, lệnh này cập nhật cấu hình OpenCode toàn cục trong ~/.config/opencode/. Lệnh hợp nhất khối nhà cung cấp vào opencode.json hoặc opencode.jsonc, ghi thông tin xác thực vào auth.json và tạo bản sao lưu. Thao tác này có tính lũy đẳng nên có thể chạy lại an toàn.
Khởi động OpenCode với một model được pool cung cấp:
opencode --provider underclass --model underclass/gpt-5.5
Để ghi cấu hình cục bộ của dự án, thêm --project:
cargo run -- connect --project
Xem trước cấu hình đã hợp nhất mà không ghi bất kỳ tệp nào:
cargo run -- connect --dry-run
Xóa phần tích hợp đã tạo khi không còn cần thiết:
cargo run -- connect --remove
Lệnh này cũng chấp nhận --base-url, --api-key, --model và --no-default-model khi bạn cần tùy chỉnh cấu hình nhà cung cấp được tạo.
Sử dụng API tương thích với OpenAI
Bất kỳ ứng dụng khách tương thích nào cũng có thể sử dụng proxy bằng cách đặt base URL thành listener của Underclass và cung cấp proxy key dưới dạng bearer token.
Liệt kê các model có sẵn
curl http://127.0.0.1:8080/v1/models \
-H "Authorization: Bearer $UNDERCLASS_PROXY_KEY"
Endpoint models trả về một danh mục hợp nhất với các giới hạn đã gộp. Danh mục được khởi tạo bằng các nhóm model Codex được ghi nhận và danh sách model trực tiếp của Copilot. Vì danh mục được lưu dưới dạng dữ liệu, bạn có thể chỉnh sửa danh mục thông qua giao diện người dùng hoặc API quản trị mà không cần thay đổi mã proxy.
Gửi yêu cầu hoàn tất trò chuyện
curl http://127.0.0.1:8080/v1/chat/completions \
-H "Authorization: Bearer $UNDERCLASS_PROXY_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.5",
"messages": [
{"role": "user", "content": "Explain sticky session routing."}
]
}'
Proxy cũng hỗ trợ POST /v1/responses. Cả hai tuyến yêu cầu đều truyền kết quả theo luồng thông qua tài khoản được chọn. Sau khi bắt đầu truyền, lỗi từ upstream sẽ được chuyển tiếp thay vì âm thầm gửi lại một yêu cầu đã hoàn tất một phần.
Tìm hiểu về định tuyến và chuyển đổi dự phòng
Hoạt động của phiên cố định
Khi một yêu cầu có prompt_cache_key hoặc promptCacheKey, Underclass liên kết giá trị đó với một gói đăng ký. OpenCode có thể gửi ID phiên khi được cấu hình với setCacheKey: true. Các liên kết có hiệu lực trong 24 giờ và vẫn tồn tại sau khi proxy khởi động lại.
Nếu tài khoản được chỉ định bắt đầu ở trạng thái làm nguội, Underclass ưu tiên liên kết lại phiên với một tài khoản khác trên cùng backend. Thiết kế này giúp duy trì lợi ích bộ nhớ đệm prompt của upstream, đồng thời vẫn cho phép khôi phục khi hạn ngạch cạn kiệt.
Xử lý hạn ngạch và xác thực
Phản hồi về hạn ngạch, bao gồm các phản hồi 429 hoặc giới hạn sử dụng liên quan, sẽ đưa tài khoản vào trạng thái làm nguội. Underclass sử dụng giá trị upstream Retry-After nếu có; nếu không, hệ thống dùng thời gian làm nguội riêng được cấu hình cho backend. Các tài khoản đang làm nguội sẽ tự động đủ điều kiện trở lại khi thời gian làm nguội kết thúc.
Đối với upstream trả về 401, Underclass thực hiện một lần làm mới token và thử lại một lần. Nếu xác thực vẫn thất bại, tài khoản cần đăng nhập lại.
Bão hòa pool
Nếu không có tài khoản đủ điều kiện nào cho model được yêu cầu, Underclass không giữ yêu cầu trong hàng đợi. Hệ thống lập tức trả về 429 và báo thời điểm đặt lại sớm nhất đã biết thông qua Retry-After.
Cấu hình proxy
Có thể đặt cấu hình bền vững tùy chọn trong ~/.config/underclass/config.toml:
bind = "127.0.0.1:8080"
proxy_key = "replace-with-a-client-secret"
ui_token = "replace-with-an-admin-secret"
codex_cooldown_secs = 1800
copilot_cooldown_secs = 1800
Thời gian làm nguội mặc định cho mỗi backend là 1.800 giây khi dịch vụ upstream không cung cấp khoảng thời gian thử lại.
Các biến môi trường sẽ ghi đè các giá trị cấu hình tương ứng:
UNDERCLASS_BINDUNDERCLASS_PROXY_KEYUNDERCLASS_UI_TOKEN
Để kiểm thử với các dịch vụ giả lập, hãy sử dụng UNDERCLASS_CODEX_UPSTREAM và UNDERCLASS_COPILOT_UPSTREAM. Giá trị mặc định của chúng trỏ đến các endpoint upstream thực.
Underclass lưu thông tin xác thực, các liên kết phiên cố định, các khóa đã tạo và danh mục model trong ~/.local/share/underclass/pool.db. Xóa cơ sở dữ liệu đó sẽ đặt lại hoàn toàn trạng thái cục bộ.
Mẹo vận hành nâng cao
Kiểm tra log và đối chiếu các yêu cầu
Mọi phản hồi được xử lý đều bao gồm x-request-id. Log có cấu trúc và có thể sử dụng định dạng JSON hoặc định dạng dễ đọc thông qua --log-format json hoặc --log-format pretty. Sử dụng RUST_LOG để kiểm soát bộ lọc.
Khi preflight được đặt phía trước Underclass, UUIDv4 hợp lệ được cung cấp dưới dạng x-request-id sẽ được giữ nguyên trong phản hồi, log định tuyến, log thử lại và lịch sử yêu cầu. Các mã định danh không hợp lệ, trùng lặp, nil, bị thiếu hoặc không phải v4 sẽ được thay thế. Mã định danh chỉ hỗ trợ việc đối chiếu; không chứng minh quyền cấp phép hoặc việc kiểm tra.
Quản lý danh mục model cẩn thận
Khả năng đủ điều kiện định tuyến và khối model OpenCode đều tuân theo danh mục đã lưu. Sử dụng giao diện web hoặc API danh mục quản trị để kiểm tra hay chỉnh sửa ánh xạ model của backend. Các mã định danh model chưa biết sẽ được chuyển tiếp đến Codex, cho phép các model Codex mới được giới thiệu hoạt động mà không cần phát hành proxy mới.
Mặc định giữ dịch vụ ở chế độ riêng tư
Underclass mặc định lắng nghe trên localhost. Hãy giữ mặc định này trừ khi bạn chủ động sử dụng và bảo vệ truy cập từ xa. Proxy không ghi log access token, refresh token, header authorization hoặc nội dung prompt, nhưng nhãn tài khoản hiển thị trong log và giao diện người dùng.
- Không commit
pool.dbhoặc các tệp*.bakđược tạo tự động. - Bảo vệ riêng UI token và khóa proxy của client.
- Giữ riêng
auth.jsonđược OpenCode tạo; Underclass ghi tệp này với quyền0600. - Sử dụng đường hầm bảo mật hoặc biện pháp bảo vệ tương tự được cân nhắc kỹ trước khi cho phép dịch vụ truy cập từ bên ngoài localhost.
Chạy Underclass trên NixOS
Flake cung cấp một module NixOS. Cấu hình dịch vụ có thể bật Underclass, liên kết dịch vụ cục bộ, tải bí mật từ tệp môi trường thời gian chạy và thiết lập thời gian làm nguội cho backend:
{
inputs.underclass.url = "github:ghuntley/underclass";
outputs = { nixpkgs, underclass, ... }: {
nixosConfigurations.my-host = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
underclass.nixosModules.default
{
services.underclass = {
enable = true;
bindAddress = "127.0.0.1:8080";
environmentFile = "/run/secrets/underclass.env";
settings = {
codex_cooldown_secs = 1800;
copilot_cooldown_secs = 1800;
};
};
}
];
};
};
}
Tệp môi trường thời gian chạy có thể lưu các bí mật mà không đưa chúng vào Nix store:
UNDERCLASS_PROXY_KEY=sk-underclass-replace-me
UNDERCLASS_UI_TOKEN=replace-me
Mô-đun sử dụng người dùng động, lưu dữ liệu bền vững trong /var/lib/underclass, mặc định liên kết với localhost và để đóng tường lửa. Chỉ bật services.underclass.openFirewall khi bạn chủ động liên kết dịch vụ ra ngoài localhost.
Phát triển và kiểm thử dự án
Xây dựng và chạy bộ kiểm thử bằng Cargo:
cargo build
cargo test
Dự án kết hợp các bài kiểm thử đơn vị, kiểm thử thuộc tính Hegel và kiểm thử đầu cuối. Phần lõi của pool và health hoạt động đồng bộ, sử dụng clock được truyền vào, còn hành vi bất đồng bộ được giữ ở các lớp bên ngoài. Nhà cung cấp mới triển khai trait provider::Backend và đăng ký với ứng dụng.
Để tìm hiểu bối cảnh kiến trúc, hãy xem các bản ghi quyết định kiến trúc của dự án. Mô hình định tuyến được giải thích trong ADR 0010, còn việc tương quan yêu cầu xuyên proxy được đề cập trong ADR 0012.
Kết luận
Underclass biến nhiều gói đăng ký ChatGPT/Codex và GitHub Copilot thành một dịch vụ tương thích với OpenAI cục bộ duy nhất. Định tuyến cố định, khả năng tự động khôi phục sau thời gian cooldown, lựa chọn tài khoản theo model và hành vi bão hòa dừng nhanh giúp giảm việc chuyển đổi tài khoản thủ công mà vẫn duy trì hành vi client có thể dự đoán. Hãy khởi động máy chủ, thêm tài khoản qua giao diện web, chạy underclass connect và trỏ OpenCode hoặc client tương thích khác đến endpoint tương ứng.
