Microsoft AutoGen: настройка на своём ключе вместо OpenAI

AutoGen строит своего клиента поверх openai-python, поэтому base_url и api_key меняются в config_list напрямую. Разбираем настройку и типичные грабли.

5 мин · · Обновлено · агенты

AutoGen строит своего клиента поверх обычного openai Python-пакета, поэтому весь механизм переключения провайдера — это base_url и api_key в config_list. Никакого отдельного адаптера под Anthropic или GLM не нужно: любая модель гейтвея отвечает в формате /v1/chat/completions, который AutoGen понимает из коробки.

Это удобно ровно потому, что AutoGen не пытается угадывать провайдера по имени модели, как делают некоторые обёртки поверх LiteLLM. Он просто собирает openai.OpenAI(base_url=..., api_key=...) и дальше вызывает chat.completions.create() с тем model, который вы указали в конфиге. Значит, весь опыт настройки одинаковый что для GPT, что для Claude, что для GLM — разница только в строке с именем модели, а не в архитектуре подключения.

Базовый config_list

bash
import autogen

config_list = [
    {
        "model": "gpt-5.6-sol",
        "base_url": "https://api.zukko.pro/v1",
        "api_key": "fetch_ваш_ключ",
        "default_headers": {"X-Zukko-Cache": "true"},
    }
]

assistant = autogen.AssistantAgent(
    name="assistant",
    llm_config={"config_list": config_list},
)

user_proxy = autogen.UserProxyAgent(name="user_proxy", code_execution_config=False)
user_proxy.initiate_chat(assistant, message="Составь план миграции базы данных")

Заголовок X-Zukko-Cache: true в default_headers включает на гейтвее кэш ответов — это основной способ, он работает для любой модели (в отличие от Idempotency-Key, этот заголовок как раз можно ставить на весь клиент). temperature: 0 тоже включает кэш, но только для моделей, которые принимают это значение: новые модели Claude (claude-sonnet-5, claude-opus-5-5) могут отклонить temperature, отличную от 1, с ошибкой 400, поэтому для них temperature в llm_config не задаём. Запрос, тело которого полностью совпадает с предыдущим, при повторе возвращается мгновенно и бесплатно, в ответе будет заголовок X-Zukko-Cache: hit. «Почти одинаковые» запросы (например, с дописанной репликой в истории) уже не совпадают; попадания в кэш не пишутся в /v1/usage. Для чата кэш живёт до 7 суток — удобно, когда во время отладки AutoGen-сценария один и тот же промпт прогоняется по десять раз подряд.

Разные агенты — разные модели

Ключ, который принимает config_list, ожидает поле api_key, а не apiKey — опечатка в регистре тихо ломает подключение и AutoGen падает с ошибкой авторизации, которая выглядит так, будто ключ неверный, хотя ключ на самом деле правильный. Второй частый паттерн — свой набор моделей под каждого агента в GroupChat:

bash
critic = autogen.AssistantAgent(
    name="critic",
    llm_config={
        "config_list": [{
            "model": "claude-opus-5-5",
            "base_url": "https://api.zukko.pro/v1",
            "api_key": "fetch_ваш_ключ",
        }],
    },
)

coder = autogen.AssistantAgent(
    name="coder",
    llm_config={

Claude Opus 5.5, как и другие модели Claude и GPT в каталоге, держит контекст в 1,000,000 токенов и понимает изображения (у GLM — 128K и без vision, поэтому coder на glm-5.3 картинки не разберёт) — в критике кода это не нужно, но пригодится, если добавить агента, который разбирает скриншоты интерфейса:

bash
message = {
    "role": "user",
    "content": [
        {"type": "text", "text": "Что не так с этим макетом?"},
        {"type": "image_url", "image_url": {"url": screenshot_data_url}},
    ],
}
user_proxy.initiate_chat(ui_reviewer, message=message)

Формат сообщения с картинкой — стандартный OpenAI image_url, работает одинаково для любой модели с vision (Claude, GPT), которая стоит за config_list.

Модель выключили посреди прогона

Каталог обновляется, и модель может быть выключена, пока долгий сценарий с несколькими раундами уже запущен. В этом случае гейтвей отвечает 503 с кодом model_disabled (например, так было с gpt-5.4 — её замена gpt-5.6-sol):

bash
{"error": {
  "message": "...",
  "code": "model_disabled"
}}

В долгоживущих AutoGen-сценариях есть смысл ловить model_disabled отдельно от прочих ошибок и один раз автоматически подставлять заранее выбранную запасную модель, а не просто ронять весь GroupChat. Актуальный список моделей — GET https://api.zukko.pro/v1/models:

bash
try:
    user_proxy.initiate_chat(assistant, message=task)
except Exception as e:
    if "model_disabled" in str(e):
        assistant.llm_config["config_list"][0]["model"] = "gpt-5.6-sol"
        user_proxy.initiate_chat(assistant, message=task)
    else:
        raise

Свои ретраи поверх ретраев openai-python

У openai.OpenAI, на котором построен AutoGen, есть собственный механизм повторов — по умолчанию до двух дополнительных попыток на сетевые ошибки и 5xx, ещё до того, как об этом узнает ваш код. Idempotency-Key при этом задаётся на каждый логический запрос — через extra_headers в конкретном вызове chat.completions.create(). Тогда все внутренние повторы этого вызова пойдут с одним и тем же значением заголовка — это ровно то, что нужно: гейтвей увидит повтор и не спишет второй раз. А вот в default_headers клиента ключ класть нельзя: он уйдёт со всеми запросами, и уже второй запрос с другим телом получит 422 idempotency_conflict. Вторая ошибка — генерировать ключ заново при каждом ретрае: тогда каждая попытка получит новый ключ, и идемпотентность перестанет работать именно там, где она нужнее всего: при нестабильном соединении.

401 при формально верном ключе

Если config_list собирается из переменных окружения через autogen.config_list_from_json, легко забыть переименовать поле api_type — AutoGen по умолчанию ждёт "openai", и если в JSON остался "azure" от старого конфига под Azure OpenAI, запрос уйдёт с другой структурой заголовков, и гейтвей ответит 401 с Invalid API key, хотя сам ключ скопирован верно.

429 в групповом чате

На практике групповой чат с четырьмя-пятью агентами и max_round под двадцать легко выходит на десятки вызовов за одну сессию отладки, а если запускать такие сессии по кругу во время разработки промптов — счёт идёт на сотни в час.

GroupChatManager может за секунды сделать десяток последовательных вызовов, когда агенты передают ход по кругу. Если на ключе в кабинете выставлен дневной или месячный лимит расхода (это настраивается отдельно для каждого fetch_... на странице ключа), групповой чат с большим max_round быстрее упрётся в key_daily_limit_reached, чем одиночный агент. Дневной лимит сбрасывается в 00:00 UTC; своего rate limit у шлюза нет, поэтому 429 без кодов key_daily_limit_reached / key_monthly_limit_reached приходит от провайдера модели. При отладке удобно временно снять лимит с тестового ключа или завести отдельный ключ без ограничений только для разработки.

Идемпотентность при повторных попытках

UserProxyAgent с включённым code_execution_config иногда переотправляет один и тот же запрос при таймауте контейнера с кодом. Не пытайтесь защититься от этого через default_headers в config_list: этот параметр уходит в конструктор openai.OpenAI, то есть один и тот же Idempotency-Key получат все запросы агента, и второй же раунд с новым телом упадёт с 422 idempotency_conflict. Ключ нужен на каждый логический запрос — уникальный для запроса и одинаковый при его повторах. Для вызовов, которые важно не задвоить, делайте их напрямую через клиент и передавайте ключ в конкретном .create():

bash
import uuid
from openai import OpenAI

client = OpenAI(base_url="https://api.zukko.pro/v1", api_key="fetch_ваш_ключ")
idem_key = str(uuid.uuid4())  # один на логический запрос; при повторе — тот же

resp = client.chat.completions.create(
    model="gpt-5.6-sol",
    messages=[{"role": "user", "content": task}],
    extra_headers={"Idempotency-Key": idem_key},
)

Повтор с тем же значением Idempotency-Key в течение 24 часов вернёт прежний ответ без нового списания.

Проверка перед стартом сценария

bash
curl https://api.zukko.pro/v1/chat/completions \
  -H "Authorization: Bearer fetch_ваш_ключ" \
  -H "Content-Type: application/json" \
  -H "X-Zukko-Cache: true" \
  -d '{"model":"gpt-5.6-sol","messages":[{"role":"user","content":"ping"}]}'

Если в ответе есть X-Zukko-Cost-Micros — запрос дошёл до модели и списался; повторный такой же запрос вернёт X-Zukko-Cache: hit и ничего не спишет. Баланс и историю списаний видно через GET /v1/balance и GET /v1/usage, ключи выпускаются в кабинете https://zukko.pro/app/api-keys. Если конфиг собирается динамически из нескольких JSON-файлов и непонятно, на каком этапе потерялось поле, быстрее написать в поддержку в Telegram (https://t.me/zukkopro?direct), чем гадать по логам openai.OpenAI в одиночку.

zukko

Платите в 25×
меньше

за Claude Code & ChatGPT

Один ключ fetch_* и base URL api.zukko.pro — в Cursor, Claude Code, Codex и любом SDK.

AutoGen с Claude и GPT — настройка config_list на своём ключе · Zukko