如果是新建 LangChain 应用,直接使用 ChatOpenRouter。对于暂时无法升级的旧项目,继续让 ChatOpenAI 配合 https://openrouter.ai/api/v1 使用;只有在 Anthropic Agent SDK 这条路径中,才应配置 https://openrouter.ai/api。
先说结论:新 LangChain 项目优先使用原生集成
OpenRouter 负责模型接入与供应商路由,LangChain 则提供聊天模型、链和 Agent 抽象。目前推荐的原生组合是: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 会在基础 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 SDK | https://openrouter.ai/api/v1 | OpenAI Chat Completions |
旧版 LangChain ChatOpenAI | https://openrouter.ai/api/v1 | 兼容 OpenAI 的聊天请求 |
LangChain ChatOpenRouter | 通常无需自定义基础 URL | OpenRouter 原生集成 |
| Vercel AI SDK OpenRouter provider | 通常无需自定义基础 URL | 由 Provider 包管理端点 |
| Anthropic Agent SDK | https://openrouter.ai/api | 兼容 Anthropic 的 Agent SDK 请求 |
除非 SDK 明确要求传入完整端点,否则不要把 /chat/completions 也写进基础 URL。如果错误信息里出现 /v1/v1 这类重复路径,先检查 SDK 是如何将基础 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)
这里必须使用 OpenRouter API key,而不是 OpenAI key。OpenRouter 的Quickstart规定使用 Bearer Token 认证。HTTP-Referer 和 X-OpenRouter-Title 只是可选的应用归属标识请求头,不能代替 Authorization。
LangChain + OpenRouter:当前推荐配置
Python:langchain-openrouter
先安装集成包,并把 API key 放在源码之外:
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。如果模型目录里能看到某个模型,但代码调用失败,请核对完整 slug、变体后缀以及该模型支持的请求参数。
init_chat_model 与版本陷阱
常见报错如下:
ValueError: Unsupported model_provider='openrouter'.
根据LangChain Forum 的排错讨论,这通常是因为项目安装的是 v1 之前的 LangChain,却参考了 v1 文档。讨论中的建议是同时升级框架和 Partner 包:
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。这样会绕过通用 Provider 注册表,并明确声明对 OpenRouter 的依赖。
从 ChatOpenAI 迁移到 ChatOpenRouter
代码改动并不大,但集成边界会从通用的 OpenAI 适配器,变成针对具体 Provider 的模型类:
# 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")
第一步先保持 Prompt 和链调用位置不变。等原生调用确认正常后,再逐步加入 Provider 路由、推理、结构化输出或元数据处理。
LangChain 中的流式输出、回调、工具和结构化输出
model.stream() 会产出消息片段,适合简单的文本消费。stream_events(..., version="v3") 则会暴露完整的生命周期事件,更适合回调式日志、UI 状态更新和追踪;这两种方式都记录在 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)
如果应用需要接收开始、Token、结束或错误通知,就使用 callback;如果只需要模型片段,使用 stream() 即可。用量和响应元数据应从完整响应或聚合后的流结果中读取,正如集成文档示例所示,不要从某个随意的首个片段中读取。
工具调用与 Provider 路由
OpenRouter文档说明了标准化的工具调用流程:模型先提出工具调用,应用执行工具,再把工具结果发回模型。ChatOpenRouter.bind_tools() 支持 LangChain 工具、Python 函数、Pydantic 类和字典 Schema。
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)
Provider 路由应通过 OpenRouter 专属模型配置来完成,具体可参考 LangChain 集成文档和 OpenRouter Provider 路由指南:
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 用于限制可选 Provider,ignore 用于排除 Provider。allow_fallbacks=True 允许请求在首选顺序之外继续寻找可用 Provider。require_parameters=True 会避开无法支持请求中全部参数的 Provider。这些设置本质上是在可用性和 Provider 控制力之间做取舍。
对于旧版 ChatOpenAI 客户端,OpenRouter 专属字段可能需要通过 extra_body 或 model_kwargs 传递,具体取决于客户端版本。一位用户在 LangChain/OpenRouter Reddit 讨论中提出了这个问题:
“想确认一下,OpenRouter 难道不支持 OpenAI API 规范,以至于 OpenAI 客户端无法工作吗?”—— u/tuxedo0。
专用集成包明确了 OpenRouter 的集成边界,不再要求通用 OpenAI 包装器暴露所有 Provider 专属字段。
结构化输出:检查端点,而不只是模型名称
原生集成通过 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 结构化输出指南解释了为什么 Provider 能力检查、require_parameters 和客户端验证都很重要。
Vercel AI SDK + OpenRouter
如果你是在 Next.js 或 TypeScript 应用中使用 Vercel AI SDK,可以安装 OpenRouter Provider:
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 Provider issue 报告称,在 @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,并固定你实际测试过的版本。这个方案更换了 Provider 适配器,因此要重新确认哪些 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 key 应放在 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 则保留 Agent 循环和工具运行时。由于 Anthropic 原生能力并不会自动实现完全一致,务必在最终部署所用的准确路由上测试工具、流式输出、上下文行为和模型专属选项。
401、404,以及“curl 能用但 SDK 不行”
先定位失败发生在哪一层,再考虑更换模型。认证、URL 拼接、模型查询、Provider 过滤和参数校验,对应的是完全不同的修复方式。
| 现象 | 可能出错的层 | 首先检查什么 |
|---|---|---|
401 Missing Authentication header | 请求头或环境变量加载 | 确认存在 Authorization: Bearer ...;测试 /api/v1/key |
带有 key 仍返回 401 Unauthorized | Key 错误、已撤销或为空 | 确认进程能读取 OPENROUTER_API_KEY;使用 OpenRouter key |
路径格式异常的 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 | Provider 过滤或端点可用性 | 检查 only、ignore、max_price、require_parameters 和数据策略设置 |
top_k、min_p、工具或 JSON Schema 返回 400 | 能力不匹配 | 检查 supported_parameters;进行路由时要求 Provider 具备兼容能力 |
curl 能用,但框架代码失败 | SDK 路径、大小写、序列化或请求头丢失 | 逐项对比最终 URL、认证请求头、模型 ID 和 JSON 请求体 |
OpenRouter 将 GET /api/v1/key 定义为需要认证的 API key 检查接口。在排查 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 也可能意味着模型本身存在,但请求过滤条件执行后已经没有符合要求的 Provider。要查看路由决策,可以启用可选的 X-OpenRouter-Metadata: enabled 请求头;Router Metadata 文档介绍了返回的 openrouter_metadata 对象及其错误行为。
开发阶段可以在脱敏 API key 后,对比实际生成的请求:基础 URL、最终路径、是否存在 Authorization 请求头、准确的模型 ID,以及请求体中的 Provider 专属字段。若要集中排查认证问题,可以参考 AIReiter 的无效 API key 与 401/403 错误排查指南。
上线前的迁移检查清单
- 统一依赖版本。 确保
langchain、langchain-core与langchain-openrouter或@langchain/openrouter彼此兼容,不要直接照抄旧教程中的版本号。 - 确认规范模型 ID。 使用
/api/v1/models或 OpenRouter 模型目录;如果改用别名,应把它视为一次有意的行为变更。 - 明确回退边界。 Provider 回退可以提升可用性,但模型回退可能改变质量、延迟、能力和成本。记录 OpenRouter 最终返回的
model。 - 在能力重要时强制校验。 对工具调用和结构化输出请求使用 Provider 能力过滤;支持工具调用并不等于支持严格 JSON Schema。
- 校验应用输出。 在下游代码使用工具参数和结构化响应之前,应通过 Pydantic、Zod 或其他校验器进行检查。
- 记录有用的元数据。 在隐私政策允许的范围内保留请求 ID、模型、用量、结束原因和服务 Provider 信息。LangChain 集成还记录了用于请求分组的
session_id以及用于单次请求元数据的trace。 - 测试两种流式模式。 非流式调用成功,并不能证明工具调用片段或 UI 流式输出一定正常。
- 演练 Provider 限制场景。 如果使用了
only、ignore、数据收集过滤器或价格上限,务必测试没有可用 Provider 时的失败路径。 - 凭据只放在服务端。 将
OPENROUTER_API_KEY存放在服务端环境或密钥管理器中,绝不要打包进浏览器 Bundle,也不要提交包含它的.env文件。
最稳妥的顺序是:先验证 key,再验证模型 ID,先完成一次普通文本调用,最后逐步加入路由、工具、结构化输出和多步骤流式处理。