AIREITER

Настройка LangChain с OpenRouter: базовые URL, инструменты и исправление ошибок

Последнее обновление: 2026-08-28 04:13:05

В новых приложениях на LangChain используйте ChatOpenRouter. Для старых проектов, которые пока нельзя обновить, оставьте ChatOpenAI с адресом https://openrouter.ai/api/v1. URL https://openrouter.ai/api нужен только в сценарии с Anthropic Agent SDK.

Короткий ответ: в новых проектах LangChain выбирайте нативную интеграцию

OpenRouter отвечает за доступ к моделям и маршрутизацию запросов, а LangChain — за абстракции чат-моделей, цепочек и агентов. Актуальная связка для Python — пакет langchain-openrouter и класс ChatOpenRouter. В JavaScript и TypeScript используются @langchain/openrouter и тот же ChatOpenRouter.

Ваша ситуацияПодходГлавная причина
Новое приложение LangChain на Pythonlangchain-openrouter + ChatOpenRouterИнтеграция предоставляет маршрутизацию по провайдерам, метаданные, reasoning, инструменты и управление структурированным выводом
Новое приложение LangChain на JS/TS@langchain/openrouter + ChatOpenRouterСохраняются нативные интерфейсы LangChain для сообщений, потоковой генерации и инструментов
Существующее старое приложение LangChainChatOpenAI + base_url="https://openrouter.ai/api/v1"Минимальные изменения для совместимости
Существующее приложение с init_chat_modelLangChain v1 и langchain-openrouterВключает маршрут диспетчеризации через model_provider="openrouter"
Приложение на Claude/Anthropic Agent SDKANTHROPIC_BASE_URL="https://openrouter.ai/api"Agent SDK использует совместимый с Anthropic маршрут
Приложение Next.js на Vercel AI SDK@openrouter/ai-sdk-provider + createOpenRouter()Используются streamText() и абстракции инструментов из AI SDK

В анонсе официального пакета LangChain говорится, что нативная интеграция сохраняет специфичные для OpenRouter возможности, которые могут потеряться при использовании универсальной обертки ChatOpenAI. Речь идет о reasoning-контенте, структурированном выводе, метаданных маршрутизации и идентификации трассировок. Если эти поля вам не нужны, существующую совместимую конфигурацию необязательно срочно переписывать.

Сначала разберитесь с картой эндпоинтов, а уже потом выбирайте фреймворк

Разные SDK добавляют к базовому URL разные пути запросов. Совместимый с OpenAI маршрут — https://openrouter.ai/api/v1; именно это значение указано в руководстве OpenRouter для OpenAI SDK. В руководстве по Anthropic Agent SDK базовая переменная ANTHROPIC_BASE_URL вместо этого указывает на https://openrouter.ai/api.

Клиент или SDKНастраиваемый базовый URLТипичный набор запросов
OpenAI Python/JS SDKhttps://openrouter.ai/api/v1OpenAI Chat Completions
Устаревший LangChain ChatOpenAIhttps://openrouter.ai/api/v1Совместимые с OpenAI вызовы чата
LangChain ChatOpenRouterОбычно собственный базовый URL не нуженНативная интеграция с OpenRouter
Провайдер OpenRouter для Vercel AI SDKОбычно собственный базовый URL не нуженПакет провайдера сам управляет эндпоинтом
Anthropic Agent SDKhttps://openrouter.ai/apiВызовы Agent SDK, совместимые с Anthropic

Не добавляйте /chat/completions в базовый URL SDK, если он явно не требует полный адрес эндпоинта. Ошибка с продублированным путем вроде /v1/v1 означает, что нужно проверить, как SDK объединяет базовый URL с путем запроса.

Для старого клиента LangChain, работающего через OpenAI-совместимый API, минимальная рабочая конфигурация выглядит так:

import os
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(
    model="openai/gpt-4o",
    api_key=os.environ["OPENROUTER_API_KEY"],
    base_url="https://openrouter.ai/api/v1",
)

response = llm.invoke("Explain provider routing in one sentence.")
print(response.content)

Используйте API-ключ OpenRouter, а не ключ OpenAI. В Quickstart OpenRouter указана аутентификация через Bearer-токен. Заголовки HTTP-Referer и X-OpenRouter-Title нужны для атрибуции приложения и не заменяют Authorization.

LangChain + OpenRouter: актуальная настройка

Python: langchain-openrouter

Установите интеграцию, а ключ храните за пределами исходного файла:

pip install -U langchain-openrouter
export OPENROUTER_API_KEY="sk-or-..."

После этого создайте нативную чат-модель. В документации интеграции LangChain для Python этот конструктор поддерживает параметры model, temperature, max_tokens и max_retries:

from langchain_openrouter import ChatOpenRouter

model = ChatOpenRouter(
    model="anthropic/claude-sonnet-4.5",
    temperature=0,
    max_tokens=1024,
    max_retries=2,
)

response = model.invoke([
    ("system", "You are a concise technical assistant."),
    ("human", "What does OpenRouter add to a LangChain application?"),
])

print(response.content)

Названия моделей OpenRouter обычно записываются в формате provider/model: например, anthropic/claude-sonnet-4.5, openai/gpt-4o или deepseek/deepseek-r1. Перед развертыванием проверьте OpenRouter Models API: идентификаторы, алиасы, варианты, доступность и поддерживаемые параметры могут меняться.

JavaScript и TypeScript: @langchain/openrouter

Установите интеграцию и LangChain core:

npm install @langchain/openrouter @langchain/core

Конструктор с объектом параметров из документации LangChain для JavaScript удобно сопоставить с примером на Python:

import { ChatOpenRouter } from "@langchain/openrouter";

const model = new ChatOpenRouter({
  model: "anthropic/claude-sonnet-4.5",
  temperature: 0,
  maxTokens: 1024,
});

const response = await model.invoke([
  { role: "system", content: "You are a concise technical assistant." },
  { role: "user", content: "Explain the provider/model format." },
]);

console.log(response.content);

В Python используется max_tokens, а в JavaScript-примере — maxTokens. Обе интеграции по умолчанию читают переменную OPENROUTER_API_KEY. Если модель есть в каталоге, но вызов из кода завершается ошибкой, проверьте точный slug, суффикс варианта и поддерживаемые параметры запроса.

init_chat_model и ловушка с версиями

Частая ошибка выглядит так:

ValueError: Unsupported model_provider='openrouter'.

В обсуждении на форуме LangChain этот случай связывают с установкой LangChain до версии v1 при использовании документации для v1. В качестве решения рекомендуется обновить сам фреймворк и партнерский пакет одновременно:

pip install -U "langchain>=1" langchain-openrouter

Затем используйте универсальный инициализатор:

import os
from langchain.chat_models import init_chat_model

os.environ["OPENROUTER_API_KEY"] = "sk-or-..."

model = init_chat_model(
    "anthropic/claude-sonnet-4.5",
    model_provider="openrouter",
)

print(model.invoke("Say hello in one sentence.").content)

Если проект нельзя перевести на LangChain v1, установите langchain-openrouter и создавайте ChatOpenRouter напрямую. Так вы обходите универсальный реестр провайдеров и явно фиксируете зависимость от OpenRouter.

Переход с ChatOpenAI на ChatOpenRouter

Изменений в коде немного, но сама граница интеграции меняется: вместо универсального адаптера OpenAI используется специализированный класс провайдера:

# Before: OpenAI-compatible workaround
from langchain_openai import ChatOpenAI

model = ChatOpenAI(
    model="anthropic/claude-sonnet-4.5",
    base_url="https://openrouter.ai/api/v1",
    api_key=os.environ["OPENROUTER_API_KEY"],
)

# After: native OpenRouter integration
from langchain_openrouter import ChatOpenRouter

model = ChatOpenRouter(model="anthropic/claude-sonnet-4.5")

Сначала оставьте без изменений промпты и места вызова цепочек. Когда базовый нативный вызов заработает, добавляйте маршрутизацию провайдеров, reasoning, структурированный вывод и обработку метаданных.

Потоковая генерация, callbacks, инструменты и структурированный вывод в LangChain

model.stream() возвращает части сообщений — этого достаточно для простой обработки текста. stream_events(..., version="v3") предоставляет события жизненного цикла и лучше подходит для логирования через callbacks, обновления состояния интерфейса и трассировки. Оба интерфейса описаны в документации интеграции LangChain для Python.

model = ChatOpenRouter(model="openai/gpt-4o")

for event in model.stream_events(
    "Explain why OpenRouter has provider fallbacks.",
    version="v3",
):
    if event["event"] == "on_chat_model_stream":
        chunk = event["data"]["chunk"]
        print(chunk.text, end="", flush=True)

Callback нужен, когда приложению важно получать уведомления о начале, токенах, завершении или ошибке. Если нужны только части ответа модели, используйте stream(). Информацию об использовании токенов и метаданные ответа берите из завершенного ответа или агрегированного результата потока, как показано в документации интеграции, а не из произвольного первого чанка.

Вызов инструментов и маршрутизация по провайдерам

OpenRouter описывает нормализованный сценарий вызова инструментов: модель предлагает вызов, приложение выполняет инструмент, после чего отправляет результат обратно модели. Метод ChatOpenRouter.bind_tools() принимает инструменты LangChain, функции Python, классы Pydantic и схемы в виде словарей.

from pydantic import BaseModel, Field

class GetWeather(BaseModel):
    """Get current weather for a city."""

    location: str = Field(
        description="City and state, for example San Francisco, CA"
    )

model_with_tools = model.bind_tools(
    [GetWeather],
    strict=True,
)

result = model_with_tools.invoke("What is the weather in San Francisco?")
print(result.tool_calls)

Первый вызов возвращает только предложенные моделью вызовы инструментов. В полном ручном цикле для каждого вызова нужно создать ToolMessage, а затем снова обратиться к модели за финальным ответом:

from langchain_core.messages import HumanMessage, ToolMessage
from langchain_core.tools import tool

@tool
def get_weather(location: str) -> str:
    """Get the current weather in a location."""
    return f"Sunny in {location}."

model_with_tools = model.bind_tools([get_weather])
messages = [HumanMessage("What is the weather in San Francisco?")]
ai_msg = model_with_tools.invoke(messages)
messages.append(ai_msg)

for call in ai_msg.tool_calls:
    tool_output = get_weather.invoke(call["args"])
    messages.append(
        ToolMessage(
            content=tool_output,
            tool_call_id=call["id"],
        )
    )

final_msg = model_with_tools.invoke(messages)
print(final_msg.content)

Настройки маршрутизации провайдеров задаются в конфигурации модели OpenRouter, описанной в документации интеграции LangChain и руководстве OpenRouter по выбору провайдера:

routed_model = ChatOpenRouter(
    model="anthropic/claude-sonnet-4.5",
    openrouter_provider={
        "order": ["Anthropic", "Google"],
        "allow_fallbacks": True,
        "require_parameters": True,
        "data_collection": "deny",
    },
)

order задает предпочтительный порядок. Параметр only ограничивает список доступных провайдеров, а ignore исключает отдельных провайдеров. При allow_fallbacks=True запрос может уйти за пределы указанного приоритета. require_parameters=True не позволяет отправлять запрос провайдерам, которые не поддерживают хотя бы один параметр из тела запроса. Эти настройки позволяют выбирать между доступностью и контролем над провайдером.

В старых клиентах ChatOpenAI специфичные для OpenRouter поля, возможно, придется передавать через extra_body или model_kwargs — это зависит от версии клиента. Один из пользователей задал в обсуждении LangChain/OpenRouter на Reddit вполне логичный вопрос:

«Разве OpenRouter не поддерживает спецификацию OpenAI API, чтобы клиент OpenAI работал?» — u/tuxedo0.

Отдельный пакет делает границу интеграции с OpenRouter явной: универсальной обертке OpenAI больше не нужно самостоятельно предоставлять каждое специфичное для провайдера поле.

Структурированный вывод: проверяйте эндпоинт, а не только название модели

Нативная интеграция предоставляет типизированный вывод через with_structured_output(). В документации LangChain для Python показан нативный метод JSON Schema:

from pydantic import BaseModel, Field

class Movie(BaseModel):
    title: str
    year: int
    director: str
    rating: float = Field(description="Rating from 0 to 10")

structured_model = ChatOpenRouter(
    model="openai/gpt-5.5",
).with_structured_output(
    Movie,
    method="json_schema",
    strict=True,
)

movie = structured_model.invoke("Give details about the movie Inception.")
print(movie)

В интеграции также описан режим function_calling; параметр strict не поддерживается вместе с json_mode. Поведение структурированного вывода OpenRouter может зависеть от конкретного сервинг-эндпоинта, поэтому одного названия модели недостаточно для гарантии. В руководстве по структурированному выводу OpenRouter объясняется, почему важны проверка возможностей провайдера, require_parameters и валидация на стороне клиента.

Vercel AI SDK + OpenRouter

Для приложения на Next.js или TypeScript, использующего AI SDK от Vercel, установите провайдер OpenRouter:

npm install @openrouter/ai-sdk-provider ai zod

В этом компактном примере показаны потоковая генерация текста и локальный инструмент на базе Zod. В руководстве OpenRouter для Vercel AI SDK используется та же схема с createOpenRouter() и openrouter("provider/model").

import { createOpenRouter } from "@openrouter/ai-sdk-provider";
import { isStepCount, streamText, tool } from "ai";
import { z } from "zod";

const openrouter = createOpenRouter({
  apiKey: process.env.OPENROUTER_API_KEY,
});

const result = streamText({
  model: openrouter("openai/gpt-4o"),
  tools: {
    getWeather: tool({
      description: "Get the current weather in a location",
      inputSchema: z.object({
        location: z.string().describe("City and state"),
      }),
      execute: async ({ location }) => ({
        location,
        temperature: 18,
        unit: "celsius",
      }),
    }),
  },
  stopWhen: isStepCount(3),
  prompt: "What is the weather in San Francisco?",
});

for await (const textPart of result.textStream) {
  process.stdout.write(textPart);
}

textStream передает дельты текста. Если приложению нужны reasoning, вызовы инструментов, результаты инструментов, границы шагов или события завершения, используйте fullStream. Эти поверхности результата описаны в справочнике AI SDK по streamText(). Приведенный выше исполнитель погоды — заглушка, а не реальный вызов API.

В публичном issue провайдера OpenRouter сообщалось о заметной буферизации в одном сценарии многочастного вызова инструмента при использовании @openrouter/ai-sdk-provider 2.2.3, AI SDK 6.0.81, Node.js 24 и openai/gpt-5.2. В описании упоминалось отсутствующее событие tool-input-end. Считайте это проблемой конкретных версий, а не универсальным свойством интеграции. Если ошибка воспроизводится, в issue предлагается такой обходной путь:

import { createOpenAI } from "@ai-sdk/openai";

const openrouter = createOpenAI({
  apiKey: process.env.OPENROUTER_API_KEY,
  baseURL: "https://openrouter.ai/api/v1",
});

const model = openrouter.chat("openai/gpt-5.2");

Пакет @ai-sdk/openai нужно установить отдельно, а версии зависимостей — зафиксировать после проверки. Этот обходной путь меняет адаптер провайдера, поэтому проверьте, какие специфичные для OpenRouter параметры при этом остаются доступными.

Anthropic Agent SDK + OpenRouter

Anthropic Agent SDK — отдельная граница интеграции. В официальном руководстве OpenRouter для Agent SDK сказано, что SDK использует Claude Code в качестве среды выполнения и принимает такую конфигурацию окружения:

export ANTHROPIC_BASE_URL="https://openrouter.ai/api"
export ANTHROPIC_AUTH_TOKEN="$OPENROUTER_API_KEY"
export ANTHROPIC_API_KEY=""

Пустое значение ANTHROPIC_API_KEY задано намеренно. Ключ OpenRouter передается через ANTHROPIC_AUTH_TOKEN, а путь /api в этой конфигурации Agent SDK нельзя заменять на совместимый с OpenAI путь /api/v1.

TypeScript-пример из руководства выглядит так:

npm install @anthropic-ai/claude-agent-sdk
import { query } from "@anthropic-ai/claude-agent-sdk";

for await (const message of query({
  prompt: "Find and explain the bug in auth.py.",
  options: {
    allowedTools: ["Read"],
  },
})) {
  if (message.type === "assistant") {
    console.log(message.message.content);
  }
}

OpenRouter предоставляет эндпоинт модели, а Agent SDK сохраняет цикл работы агента и среду выполнения инструментов. Проверяйте инструменты, потоковую генерацию, поведение контекста и специфичные параметры модели именно на том маршруте, который будет использоваться в продакшене: совместимость с нативными возможностями Anthropic не возникает автоматически.

401, 404 и ситуация «через curl работает, а через SDK — нет»

Прежде чем менять модель, определите, на каком уровне возникает ошибка. Для аутентификации, построения URL, поиска модели, фильтрации провайдеров и проверки параметров нужны разные решения.

СимптомВероятный уровеньЧто проверить в первую очередь
401 Missing Authentication headerЗаголовок или загрузка переменной окруженияПроверьте Authorization: Bearer ... и запрос к /api/v1/key
401 Unauthorized при наличии ключаНеверный, отозванный или пустой ключУбедитесь, что процесс видит OPENROUTER_API_KEY, и используйте ключ OpenRouter
404 с некорректным путемСборка базового URLДля OpenAI-совместимых клиентов нужен /api/v1, для Agent SDK — /api; проверьте дублирование /v1
404 Invalid model или model_not_foundИдентификатор моделиЗапросите /api/v1/models и используйте возвращенный id или документированный алиас
404 No allowed providers are availableФильтры провайдеров или доступность эндпоинтаПроверьте only, ignore, max_price, require_parameters и настройки политики данных
400 для top_k, min_p, инструментов или JSON SchemaНесовместимость возможностейПроверьте supported_parameters и при маршрутизации требуйте совместимых провайдеров
Через curl работает, а код фреймворка — нетПуть SDK, регистр, сериализация или потерянные заголовкиСравните итоговый URL, заголовок авторизации, ID модели и JSON-тело

OpenRouter документирует GET /api/v1/key как аутентифицированную проверку API-ключа. Выполните ее до того, как начнете отлаживать LangChain:

curl -sS https://openrouter.ai/api/v1/key \
  -H "Authorization: Bearer $OPENROUTER_API_KEY"

Затем проверьте обнаружение моделей:

curl -sS https://openrouter.ai/api/v1/models \
  -H "Authorization: Bearer $OPENROUTER_API_KEY"

Через Models API можно отфильтровать модели, поддерживающие инструменты, еще до их подключения:

curl -sS \
  "https://openrouter.ai/api/v1/models?supported_parameters=tools" \
  -H "Authorization: Bearer $OPENROUTER_API_KEY"

Ошибка 404 также может означать, что модель существует, но после применения фильтров запроса не осталось подходящих провайдеров. Чтобы увидеть решения маршрутизатора, включите необязательный заголовок X-OpenRouter-Metadata: enabled. В документации Router Metadata описаны объект openrouter_metadata и поведение при ошибках.

В разработке сравнивайте сформированный запрос, предварительно скрыв ключ: базовый URL, итоговый путь, наличие Authorization, точный ID модели и специфичные поля тела запроса. Для отдельного разбора проблем с аутентификацией смотрите руководство AIReiter по ошибкам неверного API-ключа и 401/403.

Чек-лист миграции в продакшен

  1. Согласуйте поколения пакетов. Версии langchain, langchain-core и langchain-openrouter или @langchain/openrouter должны быть совместимы. Не копируйте версии из старого руководства без проверки.
  2. Используйте канонические ID моделей. Берите их из /api/v1/models или каталога OpenRouter; считайте алиасы осознанным изменением поведения.
  3. Определите границу fallback. Запасной провайдер повышает доступность. Запасная модель может изменить качество, задержку, возможности и стоимость. Сохраняйте финальное значение model, которое вернул OpenRouter.
  4. Требуйте нужные возможности. Для запросов с инструментами и структурированным выводом используйте фильтры возможностей провайдера. Поддержка инструментов не означает автоматическую поддержку строгой JSON Schema.
  5. Проверяйте результаты приложения. Перед передачей данных в следующий участок кода валидируйте аргументы инструментов и структурированные ответы с помощью Pydantic, Zod или другого валидатора.
  6. Логируйте полезные метаданные. Если это допускает политика конфиденциальности, сохраняйте ID запроса, модель, использование токенов, причину завершения и информацию о провайдере. В интеграции LangChain также документируются session_id для группировки запросов и trace для метаданных конкретного запроса.
  7. Тестируйте оба режима потоковой генерации. Успешный запрос без потоковой передачи не гарантирует корректную работу чанков вызовов инструментов или потокового UI.
  8. Проверяйте ограничения провайдеров. Если используются only, ignore, фильтры сбора данных или ограничение цены, протестируйте сценарий, в котором подходящих провайдеров не остается.
  9. Храните учетные данные на сервере. Размещайте OPENROUTER_API_KEY в серверном окружении или менеджере секретов — никогда в браузерном бандле или закоммиченном файле .env.

Надежный порядок действий такой: проверить ключ, проверить ID модели, выполнить простой текстовый вызов и только потом добавлять маршрутизацию, инструменты, структурированный вывод и многошаговую потоковую генерацию.