새로 LangChain 앱을 만든다면 ChatOpenRouter를 사용하는 것이 좋습니다. 업그레이드가 어려운 기존 프로젝트는 https://openrouter.ai/api/v1을 지정한 ChatOpenAI를 유지하면 되고, Anthropic Agent SDK를 사용할 때만 https://openrouter.ai/api를 사용하세요.
1분 요약: 새 LangChain 프로젝트라면 네이티브 연동을 선택하세요
OpenRouter는 모델 엔드포인트와 라우팅 계층을 제공하고, LangChain은 채팅 모델·체인·에이전트 추상화를 제공합니다. 현재 가장 자연스러운 조합은 Python에서 langchain-openrouter와 ChatOpenRouter를 사용하는 방식이며, JavaScript와 TypeScript에서는 @langchain/openrouter와 ChatOpenRouter를 사용합니다.
| 상황 | 권장 경로 | 주된 이유 |
|---|---|---|
| 새 LangChain Python 앱 | langchain-openrouter + ChatOpenRouter | 프로바이더 라우팅, 메타데이터, 추론, 도구, 구조화된 출력 제어 기능을 연동에서 직접 제공 |
| 새 LangChain JS/TS 앱 | @langchain/openrouter + ChatOpenRouter | LangChain의 네이티브 메시지·스트리밍·도구 인터페이스를 그대로 유지 |
| 기존 레거시 LangChain 앱 | ChatOpenAI + base_url="https://openrouter.ai/api/v1" | 호환성을 유지하면서 변경 범위를 최소화 |
기존 앱에서 init_chat_model을 사용하는 경우 | LangChain v1 + langchain-openrouter | model_provider="openrouter" 디스패치 경로를 활성화 |
| Claude/Anthropic Agent SDK 앱 | ANTHROPIC_BASE_URL="https://openrouter.ai/api" | Agent SDK가 Anthropic 호환 경로를 사용하기 때문 |
| Vercel AI SDK를 사용하는 Next.js 앱 | @openrouter/ai-sdk-provider + createOpenRouter() | AI SDK의 streamText()와 도구 추상화를 활용 |
LangChain의 퍼스트파티 패키지 발표에 따르면 네이티브 패키지는 일반적인 ChatOpenAI 래퍼에서 누락될 수 있는 OpenRouter 고유 기능을 보존합니다. 여기에는 추론 콘텐츠, 구조화된 출력, 라우팅 메타데이터, 트레이싱 식별자가 포함됩니다. 이런 필드가 필요하지 않다면 기존 호환성 설정을 당장 다시 작성할 필요는 없습니다.
프레임워크 이름보다 먼저 엔드포인트 구조를 확인하세요
SDK마다 base URL 뒤에 붙이는 요청 경로가 다릅니다. OpenAI 호환 경로는 https://openrouter.ai/api/v1이며, OpenRouter OpenAI SDK 가이드도 이 값을 사용합니다. 반면 Anthropic Agent SDK 가이드에서는 ANTHROPIC_BASE_URL을 https://openrouter.ai/api로 지정합니다.
| 클라이언트 또는 SDK | 설정할 Base URL | 일반적인 요청 계열 |
|---|---|---|
| OpenAI Python/JS SDK | https://openrouter.ai/api/v1 | OpenAI Chat Completions |
레거시 LangChain ChatOpenAI | https://openrouter.ai/api/v1 | OpenAI 호환 채팅 호출 |
LangChain ChatOpenRouter | 일반적으로 커스텀 base URL 불필요 | OpenRouter 네이티브 연동 |
| Vercel AI SDK OpenRouter 프로바이더 | 일반적으로 커스텀 base URL 불필요 | 프로바이더 패키지가 엔드포인트를 관리 |
| Anthropic Agent SDK | https://openrouter.ai/api | Anthropic 호환 Agent SDK 호출 |
SDK가 완전한 엔드포인트를 요구한다고 명시한 경우가 아니라면 base URL에 /chat/completions를 넣지 마세요. /v1/v1처럼 경로가 중복된 오류가 발생한다면 SDK가 base URL과 엔드포인트 경로를 어떻게 결합하는지 확인해야 합니다.
레거시 OpenAI 호환 LangChain 클라이언트의 최소 동작 형태는 다음과 같습니다.
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)
OpenAI 키가 아니라 OpenRouter API 키를 사용해야 합니다. OpenRouter Quickstart는 Bearer 토큰 인증을 사용합니다. HTTP-Referer와 X-OpenRouter-Title은 선택 사항인 앱 식별 헤더일 뿐이며, Authorization 헤더를 대신하지 않습니다.
LangChain + OpenRouter 현재 설정법
Python: langchain-openrouter
연동 패키지를 설치하고 API 키는 소스 파일 밖에서 관리하세요.
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를 확인하세요. 모델 ID, 별칭, 변형, 사용 가능 여부, 지원 파라미터는 바뀔 수 있습니다.
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를 읽습니다. 모델 카탈로그에는 모델이 보이는데 코드에서 실패한다면 정확한 슬러그, 변형 접미사, 지원 요청 파라미터를 확인하세요.
init_chat_model과 버전 문제
자주 발생하는 오류는 다음과 같습니다.
ValueError: Unsupported model_provider='openrouter'.
LangChain 포럼의 문제 해결 스레드에 따르면 이 오류는 v1 문서를 보면서 v1 이전 버전의 LangChain을 설치한 경우 발생할 수 있습니다. 프레임워크와 파트너 패키지를 함께 업그레이드하는 것이 권장됩니다.
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")
우선 기존 프롬프트와 체인 호출 지점을 그대로 유지한 채 전환하세요. 기본 네이티브 호출이 정상적으로 동작한 다음에 프로바이더 라우팅, 추론, 구조화된 출력, 메타데이터 처리를 추가하는 편이 안전합니다.
LangChain에서 스트리밍·콜백·도구·구조화된 출력 사용하기
간단한 텍스트 처리에는 model.stream()을 사용하면 메시지 청크를 받을 수 있습니다. 콜백 기반 로깅, UI 상태 관리, 트레이싱이 필요하다면 생명주기 이벤트를 제공하는 stream_events(..., version="v3")가 더 적합합니다. 두 방식 모두 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)
시작·토큰·종료·오류 알림이 필요하면 콜백을 사용하세요. 모델 청크만 필요하다면 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)
프로바이더 라우팅은 LangChain 연동 문서와 OpenRouter 프로바이더 라우팅 가이드에 설명된 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 스레드에서 다음과 같이 질문했습니다.
“Wondering, doesn't openrouter support the openai API spec such that the openai client works?” — u/tuxedo0.
전용 패키지를 사용하면 모든 프로바이더 전용 필드를 범용 OpenAI 래퍼가 노출해야 하는 구조를 피하고, OpenRouter 연동 경계를 코드에 명확하게 드러낼 수 있습니다.
구조화된 출력: 모델 이름만 보지 말고 엔드포인트를 확인하세요
네이티브 연동에서는 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도 지원합니다. 단, json_mode에서는 strict를 사용할 수 없습니다. OpenRouter의 구조화된 출력 동작은 서비스 엔드포인트에 따라 달라질 수 있으므로, 모델 이름만으로 지원을 보장할 수는 없습니다. OpenRouter 구조화된 출력 가이드는 프로바이더 기능 확인, require_parameters, 클라이언트 측 검증이 중요한 이유를 설명합니다.
Vercel AI SDK + OpenRouter
Vercel AI SDK를 사용하는 Next.js 또는 TypeScript 애플리케이션이라면 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은 텍스트 델타를 제공합니다. 추론, 도구 호출, 도구 결과, 단계 경계, 종료 이벤트가 필요하다면 fullStream을 사용하세요. 이러한 결과 인터페이스는 AI SDK streamText() 레퍼런스에 설명되어 있습니다. 위 예제의 날씨 실행기는 실제 API를 호출하지 않는 목업입니다.
공개된 OpenRouter 프로바이더 이슈에는 @openrouter/ai-sdk-provider 2.2.3, AI SDK 6.0.81, Node.js 24, openai/gpt-5.2 조합에서 여러 청크로 구성된 도구 호출 경로가 버퍼링되는 것처럼 보였다는 보고가 있습니다. 해당 보고에서는 tool-input-end 이벤트가 누락됐다고 설명합니다. 이는 버전별 이슈로 보아야 하며, 모든 환경에서 발생하는 일반적인 동작으로 단정해서는 안 됩니다. 같은 문제가 재현된다면 이슈에서는 다음 우회 방법을 제시합니다.
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에 넣어야 하며, 이 Agent SDK 구성에서는 /api 경로를 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 | Base 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, 데이터 정책 설정을 확인 |
top_k, min_p, 도구 또는 JSON Schema에서 발생하는 400 | 기능 호환성 불일치 | 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 객체와 오류 동작을 확인할 수 있습니다.
개발 중에는 키를 가린 뒤 실제로 생성된 요청을 비교하세요. 확인할 항목은 base URL, 최종 경로, Authorization 헤더 존재 여부, 정확한 모델 ID, 프로바이더 전용 본문 필드입니다. 인증 문제를 집중적으로 확인하려면 AIReiter의 잘못된 API 키 및 401/403 문제 해결 가이드를 참고하세요.
프로덕션 전환 체크리스트
- 패키지 세대를 맞추세요. 오래된 튜토리얼의 버전을 그대로 복사하지 말고
langchain,langchain-core,langchain-openrouter또는@langchain/openrouter가 서로 호환되는지 확인하세요. - 정식 모델 ID를 확인하세요.
/api/v1/models나 OpenRouter 모델 카탈로그를 사용하고, 별칭은 의도적인 동작 변경으로 취급하세요. - 폴백의 경계를 정하세요. 프로바이더 폴백은 가용성을 높여 줍니다. 반면 모델 폴백은 품질, 지연 시간, 기능, 비용을 바꿀 수 있습니다. OpenRouter가 최종 반환한
model을 기록하세요. - 필요한 기능을 명시적으로 요구하세요. 도구 호출과 구조화된 출력에는 프로바이더 기능 필터를 사용하세요. 도구 지원이 곧 엄격한 JSON Schema 지원을 의미하는 것은 아닙니다.
- 애플리케이션 출력을 검증하세요. Pydantic, Zod 또는 다른 검증기를 사용해 도구 인자와 구조화된 응답을 확인한 뒤 후속 코드가 처리하도록 하세요.
- 유용한 메타데이터를 기록하세요. 개인정보 보호 정책이 허용하는 범위에서 요청 ID, 모델, 사용량, 종료 이유, 서빙 프로바이더 정보를 보관하세요. LangChain 연동은 요청을 그룹화하는
session_id와 요청별 메타데이터를 위한trace도 문서화하고 있습니다. - 두 가지 스트리밍 방식을 모두 테스트하세요. 스트리밍하지 않은 호출이 성공했다고 해서 도구 호출 청크나 UI 스트리밍까지 정상이라고 볼 수는 없습니다.
- 프로바이더 제한 상황을 재현하세요.
only,ignore, 데이터 수집 필터, 가격 상한을 사용한다면 적합한 프로바이더가 하나도 없을 때의 실패 경로도 테스트하세요. - 인증 정보는 서버에만 두세요.
OPENROUTER_API_KEY는 서버 환경이나 시크릿 매니저에 저장하고, 브라우저 번들이나 커밋된.env파일에는 절대 넣지 마세요.
가장 안정적인 순서는 키 확인, 모델 ID 확인, 일반 텍스트 호출 성공 확인, 그다음 라우팅·도구·구조화된 출력·다단계 스트리밍을 차례로 추가하는 것입니다.