Bài viết này sẽ hướng dẫn bạn cách chạy đánh giá bằng cách sử dụng bất kỳ mô hình LLM nào mà không gặp phải ngoại lệ "Giới hạn tỷ lệ OpenAI" đáng sợ. Chúng ta sẽ bắt đầu bằng:
Cho đến nay, lời giải thích của Cloudflare là lời giải thích hay nhất mà tôi từng thấy: Giới hạn tốc độ là một chiến lược để hạn chế lưu lượng mạng. Nó đặt giới hạn về tần suất ai đó có thể lặp lại một hành động trong một khung thời gian nhất định - ví dụ: cố gắng đăng nhập vào tài khoản.
Nói một cách đơn giản, hãy tưởng tượng bạn là mẹ của bốn đứa trẻ đều yêu thích mật ong. Lần trước mật ong hết sớm hơn dự kiến. Bây giờ, bạn đã đặt đồng hồ đếm đến mười nghìn và cho mỗi đứa trẻ lần lượt uống một ít mật ong. Đồng hồ tính giờ thể hiện giới hạn tỷ lệ, vì nó thực thi một thời gian chờ cụ thể trước khi họ có thể lấy thêm mật ong.
Sau khi giải thích khái niệm này, hãy cùng tìm hiểu giới hạn tốc độ của OpenAI và thảo luận cách tôi triển khai logic giới hạn tốc độ để quản lý R/TPM (yêu cầu/mã thông báo mỗi phút) của OpenAI bằng Python.
OpenAI đã đặt ra một số hạn chế nhất định về số lượng yêu cầu mà một người có thể thực hiện cho các mô hình AI của mình trong vòng một phút. Những hạn chế này là khác nhau đối với từng mô hình AI do OpenAI cung cấp.
Đối với phiên bản miễn phí:
Đối với bậc 1:
Xem tài liệu để biết thêm thông tin về các giới hạn tỷ lệ cấp độ khác..
Lý do cho những hạn chế này bao gồm:
Những hạn chế này dự kiến sẽ vẫn nhất quán trong tương lai gần.
Quá trình này (xem hình ảnh bên dưới) bao gồm việc cho phép người dùng chạy các đánh giá LLM từ giao diện người dùng và định cấu hình các tham số giới hạn tỷ lệ cho ứng dụng LLM của họ mà không cần phải tự viết logic.
Điều này đạt được thông qua chức năng chuẩn bị và gọi lô. Mỗi lệnh gọi trong lô sẽ gọi hàm run_with_retry
, hàm này sẽ gọi hàm cuối cùng ( invoke_app
) bằng cơ chế thử lại.
Tôi tin rằng bạn có thể viết mã logic bằng bất kỳ ngôn ngữ nào bạn chọn sau khi xem quy trình trên. Dù sao đi nữa, tôi sẽ chỉ cho bạn cách tôi đã làm. Để biết thêm thông tin cơ bản và bối cảnh, tôi chủ yếu làm việc với tư cách là kỹ sư phần mềm phụ trợ tại Agenta.
Agenta là nền tảng nhà phát triển LLM end-to-end mã nguồn mở cung cấp cho bạn các công cụ để quản lý và kỹ thuật nhanh chóng, ⚖️ đánh giá, chú thích của con người và 🚀 triển khai. Tất cả đều không áp đặt bất kỳ hạn chế nào đối với việc bạn lựa chọn khung, thư viện hoặc mô hình. Agenta cho phép các nhà phát triển và nhóm sản phẩm cộng tác xây dựng các ứng dụng hỗ trợ LLM cấp sản xuất trong thời gian ngắn hơn.
Chúng tôi muốn cung cấp cho người dùng khả năng định cấu hình cấu hình giới hạn tỷ lệ đánh giá LLM của họ từ giao diện người dùng để họ có thể bỏ qua ngoại lệ giới hạn tỷ lệ của nhà cung cấp LLM.
Nhìn vào sơ đồ quy trình, điều đầu tiên cần triển khai là logic để chuẩn bị và gọi lô (các lệnh gọi LLM). Xác thực cấu hình giới hạn tốc độ và sử dụng mô hình xác thực dữ liệu để xác định giới hạn tốc độ chạy LLM là quan trọng. Mô hình bên dưới xử lý tham số rate_limit_config
cần thiết để lệnh gọi hàng loạt hoạt động.
from pydantic import BaseModel, Field class LLMRunRateLimit(BaseModel): batch_size: int = Field(default=10) max_retries: int = Field(default=3) retry_delay: int = Field(default=3) delay_between_batches: int = Field(default=5)
Hàm batch_invoke
có các tham số sau:
async def batch_invoke( uri: str, testset_data: List[Dict], parameters: Dict, rate_limit_config: Dict ) -> List[AppOutput]: """ Invokes the LLm app in batches, processing the testset data. Args: uri (str): The URI of the LLm app. testset_data (List[Dict]): The testset data to be processed. parameters (Dict): The parameters for the LLm app. rate_limit_config (Dict): The rate limit configuration. Returns: List[AppOutput]: The list of app outputs after running all batches. """ batch_size = rate_limit_config[ "batch_size" ] # Number of testset to make in each batch max_retries = rate_limit_config[ "max_retries" ] # Maximum number of times to retry the failed llm call retry_delay = rate_limit_config[ "retry_delay" ] # Delay before retrying the failed llm call (in seconds) delay_between_batches = rate_limit_config[ "delay_between_batches" ] # Delay between batches (in seconds) list_of_app_outputs: List[AppOutput] = [] # Outputs after running all batches openapi_parameters = await get_parameters_from_openapi(uri + "/openapi.json") async def run_batch(start_idx: int): print(f"Preparing {start_idx} batch...") end_idx = min(start_idx + batch_size, len(testset_data)) for index in range(start_idx, end_idx): try: batch_output: AppOutput = await run_with_retry( uri, testset_data[index], parameters, max_retries, retry_delay, openapi_parameters, ) list_of_app_outputs.append(batch_output) print(f"Adding outputs to batch {start_idx}") except Exception as exc: import traceback traceback.print_exc() print( f"Error processing batch[{start_idx}]:[{end_idx}] ==> {str(exc)}" ) # Schedule the next batch with a delay next_batch_start_idx = end_idx if next_batch_start_idx < len(testset_data): await asyncio.sleep(delay_between_batches) await run_batch(next_batch_start_idx) # Start the first batch await run_batch(0) return list_of_app_outputs
Sau khi chuẩn bị và gọi lô, bước tiếp theo liên quan đến việc thực thi logic run_with_retry
. Việc triển khai tùy chỉnh này bao gồm chức năng giới hạn tốc độ và quản lý lệnh gọi ứng dụng llm, thử lại sau khi đạt đến độ trễ đã đặt. Rút lui theo cấp số nhân, một kỹ thuật thử lại một thao tác với thời gian chờ tăng theo cấp số nhân, được sử dụng cho đến khi đạt được số lần thử lại tối đa.
async def run_with_retry( uri: str, input_data: Any, parameters: Dict, max_retry_count: int, retry_delay: int, openapi_parameters: List[Dict], ) -> AppOutput: """ Runs the specified app with retry mechanism. Args: uri (str): The URI of the app. input_data (Any): The input data for the app. parameters (Dict): The parameters for the app. max_retry_count (int): The maximum number of retries. retry_delay (int): The delay between retries in seconds. openapi_parameters (List[Dict]): The OpenAPI parameters for the app. Returns: AppOutput: The output of the app. """ retries = 0 last_exception = None while retries < max_retry_count: try: result = await invoke_app(uri, input_data, parameters, openapi_parameters) return result except (httpx.TimeoutException, httpx.ConnectTimeout, httpx.ConnectError) as e: last_exception = e print(f"Error in evaluation. Retrying in {retry_delay} seconds:", e) await asyncio.sleep(retry_delay) retries += 1 # If max retries reached, return the last exception return AppOutput(output=None, status=str(last_exception))
Việc sử dụng AppOutput : Điều quan trọng là phải xử lý một ngoại lệ ngay cả sau khi nó đã sử dụng hết số lần thử tối đa. Bằng cách này, bạn cho phép tất cả dữ liệu mà bạn đang cố xử lý chạy và sau đó bạn có thể xác định dữ liệu nào không thành công và dữ liệu nào đã đạt.
Bước cuối cùng là gọi ứng dụng, sử dụng openapi_parameters
của ứng dụng LLM để xác định cách gọi ứng dụng bằng một điểm dữ liệu duy nhất.
Bạn không cần phải lo lắng về hàm make_payload. Nó xây dựng tải trọng để gọi ứng dụng LLM dựa trên các tham số OpenAPI của nó.
async def invoke_app( uri: str, datapoint: Any, parameters: Dict, openapi_parameters: List[Dict] ) -> AppOutput: """ Invokes an app for one datapoint using the openapi_parameters to determine how to invoke the app. Args: uri (str): The URI of the app to invoke. datapoint (Any): The data to be sent to the app. parameters (Dict): The parameters required by the app taken from the db. openapi_parameters (List[Dict]): The OpenAPI parameters of the app. Returns: AppOutput: The output of the app. Raises: httpx.HTTPError: If the POST request fails. """ url = f"{uri}/generate" payload = await make_payload(datapoint, parameters, openapi_parameters) async with httpx.AsyncClient() as client: try: logger.debug(f"Invoking app {uri} with payload {payload}") response = await client.post( url, json=payload, timeout=httpx.Timeout(timeout=5, read=None, write=5) ) response.raise_for_status() llm_app_response = response.json() app_output = ( llm_app_response["message"] if isinstance(llm_app_response, dict) else llm_app_response ) return AppOutput(output=app_output, status="success") except: return AppOutput(output="Error", status="error")
Và điều đó làm tròn quá trình.
Chiến lược lũy thừa lùi lại trong mã hoạt động như sau:
Xử lý hàng loạt: Hàm batch_invoke chia dữ liệu tập kiểm tra thành các lô nhỏ hơn với kích thước có thể định cấu hình. Mỗi lô được xử lý tuần tự.
Lệnh gọi riêng lẻ với Thử lại: Trong mỗi lô, mỗi điểm dữ liệu được xử lý bởi hàm run_with_retry
. Hàm này cố gắng gọi ứng dụng cho điểm dữ liệu. Nếu lệnh gọi không thành công do lỗi mạng cụ thể (hết thời gian, sự cố kết nối), hàm sẽ thử lại với độ trễ. Độ trễ này ban đầu được đặt thành giá trị có thể định cấu hình ( retry_delay
) và được nhân đôi cho mỗi lần thử lại tiếp theo trong cùng một đợt.
Cách tiếp cận này giúp tránh làm quá tải máy chủ ứng dụng với các yêu cầu lặp đi lặp lại sau khi xảy ra lỗi. Nó cho phép máy chủ có thời gian để khôi phục và cho phép xóa hàng đợi yêu cầu đang chờ xử lý trước khi thử lại.
Chiến lược này cũng bao gồm số lần thử lại tối đa có thể định cấu hình cho mỗi điểm dữ liệu để ngăn chặn các vòng lặp vô hạn. Độ trễ giữa các đợt ( delay_between_batches
) cũng được đưa vào để tránh vượt quá giới hạn tốc độ do máy chủ ứng dụng đặt ra.
Tôi hy vọng điều này tóm tắt tất cả những gì bạn đã học được trong bài viết hôm nay. Hãy cho tôi biết nếu bạn có bất cứ thắc mắc nào!