2025, Oct 16 11:16
Как исправить ошибку Cannot instantiate typing.Union в связке OpenAI Agents и Ollama
Почему OpenAI Agents + Ollama падает с TypeError Cannot instantiate typing.Union, и как исправить: зафиксировать openai<1.99.0. Пояснения и рабочий пример.
Вызов функций в OpenAI Agents SDK, подключённом к бэкенду Ollama, может неожиданно падать с TypeError из typing.Union. Если агент рушится на простом вызове инструмента, а трассировка завершается сообщением «TypeError: Cannot instantiate typing.Union», скорее всего, вы столкнулись с несовместимостью между кодом агентов и актуальной версией пакета openai.
Минимальная конфигурация, воспроизводящая сбой
Ниже приведён пример, который подключает OpenAIChatCompletionsModel к HTTP-эндпоинту Ollama и публикует простой инструмент-функцию. Логика проста: создать клиент, зарегистрировать инструмент и попросить агента посчитать 2 + 2.
from dotenv import load_dotenv
from agents import Agent, Runner, AsyncOpenAI, OpenAIChatCompletionsModel, function_tool
import asyncio
load_dotenv(override=True)
host_url = "http://localhost:11434/v1"
api_token = "dummy"
model_id = "qwen3:4b"
@function_tool
def add_numbers(x: int, y: int) -> int:
    """Send two integers and return their sum"""
    print(f"Adding {x} and {y}")
    return x + y
async def entrypoint():
    async_client = AsyncOpenAI(base_url=host_url, api_key=api_token)
    chat_model = OpenAIChatCompletionsModel(model=model_id, openai_client=async_client)
    base_agent = Agent(
        name="basic_worker",
        instructions="You are a simple agent that can perform basic tasks",
        tools=[add_numbers],
        model=chat_model,
    )
    result = await Runner.run(base_agent, "What is 2 + 2?")
    print(result)
asyncio.run(entrypoint())
На затронутых конфигурациях выполнение завершается трассировкой, оканчивающейся сообщением:
TypeError: Cannot instantiate typing.Union
Что именно ломается и почему
Проблема возникает из-за того, как код агентов пытается собрать сообщение вызова инструмента. Внутри он формирует ChatCompletionMessageToolCallParam примерно так:
new_tool_call = ChatCompletionMessageToolCallParam(
    id=fs["id"],
    type="function",
    function={
        "name": "file_search_call",
        "arguments": json.dumps({
            "queries": fs.get("queries", []),
            "status": fs.get("status"),
        }),
    },
)
Однако ChatCompletionMessageToolCallParam объявлён как псевдоним типа Union из двух моделей pydantic:
ChatCompletionMessageToolCallParam: TypeAlias = Union[
    ChatCompletionMessageFunctionToolCallParam,
    ChatCompletionMessageCustomToolCallParam,
]
Пытаться инстанцировать typing.Union как класс в Python недопустимо. Union — это не конкретный тип, который можно вызвать; это типовое выражение, указывающее, что значение может быть одним из нескольких типов. Чтобы увидеть, почему это падает, тот же некорректный приём в изоляции:
from typing import Union
UType = Union[int, str]
UType("hello")
Это стабильно приводит к:
TypeError: Cannot instantiate typing.Union
В итоге путь в модуле agents, который формирует ChatCompletionMessageToolCallParam таким образом, не подходит для последних версий пакета openai. Несовместимость подтверждена и связана с версиями.
Исправление
Зафиксируйте пакет openai на версии ниже 1.99.0. Согласно связанному обсуждению, даунгрейд устраняет ошибку.
Если вы управляете зависимостями через constraints-файл или аналогичный механизм, укажите ограничения так:
openai<1.99.0
openai-agents>=0.0.15
Изменений в прикладном коде не требуется; после фиксации версии openai путь вызова инструмента перестаёт пытаться инстанцировать Union, и выполнение идёт как ожидалось.
Рабочий код после фиксации зависимости
С установленным openai<1.99.0 тот же пример запускается успешно:
from dotenv import load_dotenv
from agents import Agent, Runner, AsyncOpenAI, OpenAIChatCompletionsModel, function_tool
import asyncio
load_dotenv(override=True)
host_url = "http://localhost:11434/v1"
api_token = "dummy"
model_id = "qwen3:4b"
@function_tool
def add_numbers(x: int, y: int) -> int:
    """Send two integers and return their sum"""
    print(f"Adding {x} and {y}")
    return x + y
async def entrypoint():
    async_client = AsyncOpenAI(base_url=host_url, api_key=api_token)
    chat_model = OpenAIChatCompletionsModel(model=model_id, openai_client=async_client)
    base_agent = Agent(
        name="basic_worker",
        instructions="You are a simple agent that can perform basic tasks",
        tools=[add_numbers],
        model=chat_model,
    )
    result = await Runner.run(base_agent, "What is 2 + 2?")
    print(result)
asyncio.run(entrypoint())
Почему это важно для вашего стека
Типизированные SDK быстро меняются, и небольшие правки в определениях моделей могут ломать предположения в более высокоуровневых обёртках. Здесь псевдоним типа на базе Union в пакете openai конфликтует с кодом, который обращается с ним как с конкретным классом. Контроль совместимости версий помогает избежать труднодиагностируемых сбоев в продакшене, особенно когда вы объединяете несколько слоёв — вроде OpenAI Agents SDK и бэкенда Ollama.
Выводы
Если интеграция OpenAI Agents + Ollama падает с «Cannot instantiate typing.Union», быстрым и подтверждённым решением будет зафиксировать openai на версии ниже 1.99.0 при использовании openai-agents≥0.0.15. Это убирает некорректную попытку инстанцировать Union и возвращает корректную обработку вызова функций. Следите за обсуждением апстрима и пересмотрите фиксацию, когда несовместимость будет устранена.
Материал основан на вопросе на StackOverflow от Dileep17 и ответе Tom McLean.