Привет, Хабр! Сегодня поговорим о проекте, который наделал много шума в сообществе — g4f (GPT4Free). Многие видели его на GitHub, но большинство, наверное, и не задумывались, как он работает.
Как использовать g4f в Python-коде
Сам API g4f выглядит как у настоящего openai. Это сделано специально для того, чтобы программистам не пришлось учить новые команды и методы вызова кода в g4f. Достаточно просто заменить import openai на import g4f. Пример кода:
С использованием официальной библиотеки openai:
from openai import OpenAIclient = OpenAI(api_key="sk-...")response = client.chat.completions.create( model="gpt-4", messages=[{"role": "user", "content": "ВАШ_ЗАПРОС"}])print(response.choices[0].message.content)
С использованием официальной библиотеки g4f:
from g4f.client import Clientclient = Client()response = client.chat.completions.create( model="gpt-4", messages=[{"role": "user", "content": "ВАШ_ЗАПРОС"}])print(response.choices[0].message.content)
Оба кода отправляют запросы к GPT4, однако первый код требует API-ключ с официального сайта OpenAI, а второй — не требует. Это и есть главная причина существования g4f.
Установка g4f и подготовка к работе
Чтобы установить на свой компьютер g4f, нужно написать вот такую команду в терминале IDE или в командной строке:
python.exe -m pip install g4f
После этого репозиторий проекта установится на ваш компьютер, и его можно сразу использовать в Python-скриптах. Также можно клонировать репозиторий с помощью git. Это наиболее подходящий способ. Для этого в командной строке напишите команду:
git clone https://github.com/xtekky/gpt4free.git
После клонирования репозитория на жёстком диске появится папка «gpt4free».
Что находится внутри g4f
Теперь самое интересное — как работает этот проект. В папке «gpt4free» лежит множество файлов. Нас интересует папка с файлами «g4f». Внутри лежит много различных файлов, но нас интересует папка «Provider». Далее мы видит python-скрипты, где для каждого сайта-провайдера настроена имитация пользовательского взаимодействия с интерфейсом сайта. Посмотрим на примере кода EasyChat.py:
from __future__ import annotations # Улучшает работу с аннотацией типовimport os # Работа с файлами и переменными окруженияimport asyncio # Позволяет выполнять несколько задач одновременно (асинхронно)import requests # Отправка обычных HTTP-запросов к сайтамimport json # Чтение и запись данных в формате JSONtry: import zendriver as nodriver # Библиотека для управления настоящим браузером Chromium без окнаexcept ImportError: pass # Если библиотека не установлена – ничего страшного, но часть функций будет недоступнаfrom ..typing import AsyncResult, Messages # Вспомогательные описания типов данныхfrom ..config import DEFAULT_MODEL # Имя модели, которая используется по умолчаниюfrom ..requests import get_args_from_nodriver, raise_for_status # Функции: собрать данные из браузера и проверить успешность запросаfrom ..providers.base_provider import AuthFileMixin # Добавляет возможность сохранять/загружать авторизационные данные в файлfrom .template import OpenaiTemplate # Базовый класс, который копирует поведение API OpenAIfrom .helper import get_last_user_message # Достаёт последнее сообщение пользователя из всей перепискиfrom .. import debug # Простой вывод отладочных сообщенийclass EasyChat(OpenaiTemplate, AuthFileMixin): # Этот класс отвечает за доступ к сервису EasyChat, повторяя стиль OpenAI url = "https://chat3.eqing.tech" # Адрес сайта EasyChat base_url = f"{url}/api/openai/v1" # Основа для всех запросов к его API api_endpoint = f"{base_url}/chat/completions" # Конкретный путь, куда отправляется запрос на генерацию ответа working = False # Говорит, что сейчас этот способ доступа не работает (может меняться) active_by_default = True # По умолчанию включён в список доступных способов use_model_names = True # Использовать имена моделей без изменений default_model = DEFAULT_MODEL.split("/")[-1] # Из полного названия берём только короткое имя модели model_aliases = { DEFAULT_MODEL: f"{default_model}-free", # Можно обращаться по короткому имени, внутри добавится "-free" } captchaToken: str = None # Сюда попадёт код-пропуск после прохождения проверки «Я не робот» share_url: str = None # Ссылка для обмена готовой сессией (если хотим поделиться) looked: bool = False # Флаг, чтобы не пытаться бесконечно загружать сессию по share_url guestId: str = None # Уникальный номер гостя, который сайт присваивает посетителю @classmethod def get_models(cls, **kwargs) -> list[str]: # Возвращает список моделей, которые предлагает сайт if not cls.models: # Если ещё не спрашивали models = super().get_models(**kwargs) # Берем список от родительского класса (из API сайта) models = {m.replace("-free", ""): m for m in models if m.endswith("-free")} # Оставляем только бесплатные модели cls.model_aliases.update(models) # Запоминаем, что короткое имя соответствует полному cls.models = list(models) # Сохраняем список моделей для быстрого доступа return cls.models @classmethod async def create_async_generator( # Основной метод: отправляет запрос и отдаёт ответ по частям в реальном времени cls, model: str, # Какую модель просим messages: Messages, # История переписки stream: bool = True, # Отдавать ли ответ постепенно (обычно да) proxy: str = None, # Прокси-сервер, если нужно скрыть свой IP extra_body: dict = None, # Дополнительные данные, которые пойдут вместе с запросом **kwargs # Любые другие параметры ) -> AsyncResult: # Возвращает генератор кусочков ответа cls.share_url = os.getenv("G4F_SHARE_URL") # Проверяем, есть ли в настройках ссылка для обмена сессией model = cls.get_model(model.replace("-free", "")) # Превращаем короткое имя в полное, если нужно args = None # Здесь будут храниться все технические настройки для запроса (заголовки, куки и т.п.) cache_file = cls.get_cache_file() # Файл, в который мы спрячем данные сессии, чтобы не проходить капчу каждый раз async def callback(page): # Эта функция выполнится в браузере сразу после открытия страницы cls.captchaToken = None # Очищаем старый пропуск def on_request(event: nodriver.cdp.network.RequestWillBeSent, page=None): # Следим за всеми запросами, которые отправляет сайт if event.request.url != cls.api_endpoint: # Нас интересует только запрос к API return if not event.request.post_data: # Если запрос пустой — пропускаем return cls.captchaToken = json.loads(event.request.post_data).get("captchaToken") # Достаём из запроса код-пропуск await page.send(nodriver.cdp.network.enable()) # Включаем отслеживание сетевых запросов page.add_handler(nodriver.cdp.network.RequestWillBeSent, on_request) # Прикрепляем перехватчик запросов button = await page.find("我已知晓") # Ищем на странице кнопку согласия if button: await button.click() # Нажимаем её else: debug.error("No 'Agree' button found.") # Печатаем ошибку, если кнопка не найдена for _ in range(3): # Пробуем до трёх раз активировать сессию await asyncio.sleep(1) for _ in range(300): # Ждём, пока идёт проверка «Verifying...» modal = await page.find("Verifying...") if not modal: break debug.log("EasyChat: Waiting for captcha verification...") await asyncio.sleep(1) if cls.captchaToken: # Если код-пропуск уже получен debug.log("EasyChat: Captcha token found, proceeding.") break textarea = await page.select("[contenteditable=\"true\"]", 180) # Ищем поле ввода if textarea is not None: await textarea.send_keys("Hello") # Печатаем тестовое сообщение await asyncio.sleep(1) button = await page.select("button[class*='chat_chat-input-send']") # Ищем кнопку отправки if button: await button.click() # Отправляем тестовое сообщение for _ in range(300): # Ждём появления кода-пропуска, если он ещё не пришёл await asyncio.sleep(1) if cls.captchaToken: break cls.guestId = await page.evaluate( # Забираем из памяти браузера ID гостя '"" + JSON.parse(localStorage.getItem("user-info") || "{}")?.state?.guestId' ) await asyncio.sleep(3) # Небольшая пауза, чтобы всё сохранилось if cache_file.exists(): # Если сохранённые данные сессии уже есть на диске with cache_file.open("r") as f: args = json.load(f) # Читаем их cls.captchaToken = args.pop("captchaToken") # Извлекаем код-пропуск cls.guestId = args.pop("guestId", None) # Извлекаем ID гостя if cls.captchaToken: debug.log("EasyChat: Using cached captchaToken.") # Используем сохранённые данные elif not cls.looked and cls.share_url: # Если сохранённой сессии нет и задан URL обмена cls.looked = True try: debug.log("No cache file found, trying to fetch from share URL.") response = requests.get(cls.share_url, params={ # Обращаемся по ссылке обмена "prompt": get_last_user_message(messages), "model": model, "provider": cls.__name__ }) raise_for_status(response) # Проверяем, что ответ успешный text, *sub = response.text.split("\n" * 10 + "<!--", 1) # Отделяем текст ответа от спрятанных технических данных if sub: # Если есть технические данные debug.log("Save args to cache file:", str(cache_file)) with cache_file.open("w") as f: f.write(sub[0].strip()) # Сохраняем их для будущих запросов yield text # Отдаём полученный текст как результат finally: cls.looked = False return for _ in range(2): # Делаем до двух попыток на случай, если первая не удалась if not args: # Если технических настроек ещё нет args = await get_args_from_nodriver( # Открываем сайт в браузере и собираем все необходимые данные cls.url, proxy=proxy, callback=callback, user_data_dir=None ) if extra_body is None: extra_body = {} extra_body.setdefault("captchaToken", cls.captchaToken) # Добавляем код-пропуск в тело запроса try: last_chunk = None async for chunk in super().create_async_generator( # Отправляем запрос через родительский класс (шаблон OpenAI) model=model, messages=messages, stream=True, extra_body=extra_body, **{ **args, "headers": { "X-Guest-Id": cls.guestId, # Добавляем ID гостя в заголовки **args.get("headers", {}) } }, **kwargs ): if last_chunk == "\n" and chunk == "\n": # Убираем рекламную строку в конце («Предоставлено сервисом...») break last_chunk = chunk yield chunk # Отдаём кусочек ответа except Exception as e: if "CLEAR-CAPTCHA-TOKEN" in str(e): # Сервер сказал, что наш код-пропуск устарел debug.log("EasyChat: Captcha token expired, clearing cache file.") cache_file.unlink(missing_ok=True) # Удаляем файл с устаревшими данными args = None # Готовимся собрать новые данные через браузер continue raise e break if not args: # Если после всех попыток данные не добыты raise ValueError("Failed to retrieve arguments for EasyChat.") if os.getenv("G4F_SHARE_AUTH"): # Если разрешено делиться техническими данными сессии yield "\n" * 10 yield "<!--" yield json.dumps({**args, "captchaToken": cls.captchaToken, "guestId": cls.guestId}) with cache_file.open("w") as f: # Сохраняем технические данные в файл для быстрого входа в следующий раз json.dump({**args, "captchaToken": cls.captchaToken, "guestId": cls.guestId}, f)
Здесь я наглядно показал, как работает этот кусочек кода в g4f. Основной принцип работы — реверс-инжиниринг. Это процесс анализа готового продукта для понимания того, как он устроен внутри, без доступа к его исходному коду. Программа от имени пользователя взаимодействует с AI-сервисами, проходит капчи, сама разбирается с интерфейсом сайта, структурирует ответ под заданную модель. Пользователь лишь запускает код, а он отправляет автоматические запросы, и, обходя защиту сервисов от ботов, выводит ответ.
Насколько безопасно этим пользоваться
Самый опасный миф — «раз я не плачу, значит, никто не знает, что я спрашиваю». На деле всё наоборот. Если при использовании обычного API OpenAI цепочка такая:
Ваш код → Сервер OpenAI → Ответ от GPT-модели высокого качества
то при использовании g4f цепочка будет примерно такая:
Ваш код → g4f Client → Сайт 1 (ошибка: обнаружен бот) → Сайт 2 (долгое ожидание) → Сайт 3 (доступ запрещён) → Использование прокси → Сайт 3 → Сервер неизвестной LLM → Ответ сомнительного качества
При этом g4f, пока вы даже не знаете, может подключаться через случайные прокси-серверы, подменять заголовки и притворяться браузером, чтобы обойти большинство стандартных проверок на робота. Если сайт не доступен — g4f может проделать длительную цепочку перебора множества сайтов, отправляя ваш запрос на первый попавшийся рабочий. А если сайт и вовсе запретит доступ из-за слишком частых запросов — g4f замаскирует ваш IP без вашего ведома.
А самое опасное — это огромный риск утечки данных. Ваш запрос проходит через множество случайных серверов, которые g4f подбирает на лету. Любой из этих сайтов может логировать всё, что через него проходит: исходный код, коммерческую информацию, личные данные. В результате вы теряете контроль над тем, где окажется ваша информация. И самое страшное: доказать, что утечка произошла именно через g4f, будет невозможно — библиотека уже замела следы, и никто никогда не узнает, в чьём распоряжении оказались ваши данные и когда они сработают против вас.
Наглядное сравнение g4f с официальным OpenAI API
|
Критерий |
OpenAI (официальный API) |
g4f (GPT4Free) |
|---|---|---|
|
Модель |
Вы получаете именно ту модель, которую запросили |
Модель в большинстве случаев никогда не будет именно той, которую вы указали в коде |
|
Конфиденциальность |
Прямой защищённый канал, данные не покидают сервер OpenAI |
Запрос гуляет через десятки чужих серверов |
|
Стабильность |
Гарантированная, 99% |
Может упасть в любой момент из-за изменения алгоритмов безопасности сайтов, к которым обращается g4f |
|
Юридическая чистота |
Полностью легально, не нарушает Пользовательское Соглашение OpenAI |
Нарушает условия использования как самого OpenAI, так и участвующих в цепочке перебора сайтов, через которые идёт запрос |
|
Контроль над IP |
Вы работаете со своего IP |
Трафик идёт через случайные прокси без вашего ведома |
|
Качество ответа |
Предсказуемое и воспроизводимое. Если ответ вас не устраивает, достаточно просто поменять модель в коде и получить соответствующее качество ответа |
Хаотичное, может прийти ответ от неизвестной LLM. G4F просто изменит стиль ответа под выбранную модель. Менять модель в коде бессмысленно — качество ответа от этого не изменится |
|
Хранение сессий |
API-ключ — единственный идентификатор |
Кэшируются токены капч, чужие куки |
|
Риск утечки |
Минимален, трафик зашифрован |
Критический: данные могут осесть на любом узле |
|
Цена |
Можно найти на официальном сайте OpenAI |
«Бесплатно» ценой вашей приватности и безопасности |
|
Скорость ответа |
Почти мгновенная, 1-5 секунд |
Может занимать несколько минут. Всё зависит от того, как долго g4f будет перебирать сайты и решать капчи |
|
Техподдержка |
Официальная документация, есть техподдержка, статус-страница, отчёты об инцидентах |
Полностью отсутствует. Можно написать issue на GitHub, однако гарантия того, что оно останется замеченным отсутствует |
Как итог, вы видите сами: официальный API OpenAI даёт вам предсказуемость, безопасность и полный контроль над данными за цену, которая для большинства задач измеряется копейками. G4F же предлагает «бесплатный сыр», который в любой момент может обернуться утечкой, блокировкой или просто неработающим кодом — и без единой возможности получить помощь. Выбор между этими подходами сводится не к экономии денег, а к простому вопросу: готовы ли вы доверить свои личные данные и стабильность продукта непрозрачной цепочке случайных серверов и ничьей ответственности?
Надеюсь, это статья поможет вам в дальнейшем сделать правильный выбор между g4f и OpenAI API. Спасибо, что дочитали до конца, и пусть ваш код всегда работает предсказуемо, а данные остаются только в ваших руках!
ссылка на оригинал статьи https://habr.com/ru/articles/1031390/