Chuyển đến nội dung chính
Hướng dẫn AI

Xây dựng nhóm gói đăng ký AI bền vững với Underclass

Tìm hiểu cách chạy Underclass, thêm tài khoản đăng ký qua quy trình OAuth device, kết nối OpenCode, cấu hình định tuyến và thời gian chờ, kiểm tra các model, cũng như vận hành proxy an toàn bằng Cargo, Nix hoặc NixOS.

Xây dựng nhóm gói đăng ký AI bền vững với Underclass

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/completions và /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_key hoặc promptCacheKey sẽ 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ề 429 cùng giá trị Retry-After sớ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

  1. Mở http://127.0.0.1:8080/.
  2. Dán giá trị đã cấu hình cho UNDERCLASS_UI_TOKEN.
  3. Chọn Add account.
  4. Chọn ChatGPT / Codex hoặc GitHub Copilot.
  5. Mở URL xác thực thiết bị được hiển thị và nhập mã thiết bị.
  6. 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.
  7. 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_BIND
  • UNDERCLASS_PROXY_KEY
  • UNDERCLASS_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.db hoặ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ền 0600.
  • 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.