import os
from openai import OpenAI
from dotenv import load_dotenv
load_dotenv()
client = OpenAI(
base_url="https://openrouter.ai/api/v1",
api_key=os.getenv("OPENROUTER_API_KEY"),)
response = client.chat.completions.create(
model="openai/gpt-5-mini",
messages=[{"role":"user","content":"Write a haiku about debugging code at 2 AM"}])print(response.choices[0].message.content)技术支持
Night hum, coffee cooled
cursor blinks, bug hides somewhere
I chase ghosts 'til dawn技术支持
response = client.chat.completions.create(
model="openrouter/auto",
messages=[{"role":"user","content":"Debug this Python code in 3 sentences: def factorial(n): return n * factorial(n-1)"}])print(f"Auto router selected: {response.model}")print(response.choices[0].message.content)技术支持
Auto router selected: openai/chatgpt-4o-latest
The given code is missing a base case, which causes infinite recursion and eventually a RecursionError. To fix it, add a base case like `if n == 0: return 1` before the recursive call. Here's the corrected version:
\```python
def factorial(n):
if n == 0:
return 1
return n * factorial(n - 1)
\```
技术支持
response = client.chat.completions.create(
model="openai/gpt-5",
messages=[{"role":"user","content":"Write a detailed explanation of how neural networks learn"}],
stream=True)for chunk in response:if chunk.choices[0].delta.content isnotNone:print(chunk.choices[0].delta.content, end="")技术支持
defstream_response(model, messages, show_progress=True):
response = client.chat.completions.create(
model=model,
messages=messages,
stream=True)
complete_response =""for chunk in response:if chunk.choices[0].delta.content isnotNone:
content = chunk.choices[0].delta.content
complete_response += content
if show_progress:print(content, end="", flush=True)if show_progress:print()# Add final newlinereturn complete_response
# Use it with different models
result = stream_response("anthropic/claude-sonnet-4",[{"role":"user","content":"Explain quantum entanglement like I'm 12 years old"}])技术支持
该处理器在展示进度的同时捕获完整响应,既提供流式体验又保留最终文本,并包含适当的输出格式化。
有了流式输出,用户体验从“等待和祈祷”变成“看到过程在推进”。这会让您的 AI 应用更加灵敏和有吸引力。
在 OpenRouter 中处理推理令牌
一些 AI 模型可以在给出最终答案之前展示其“思考”过程。推理令牌能透明呈现模型解决复杂问题的思路,展示其得出结论的逐步逻辑。理解这一内部推理有助于您验证答案、调试模型行为,并构建更值得信任的应用。
response = client.chat.completions.create(
model="anthropic/claude-sonnet-4",
messages=[{"role":"user","content":"How many 'r's are in the word 'strrawberry'?"}],
max_tokens=2048,
extra_body={"reasoning":{"max_tokens":512}})print("Final answer:")print(response.choices[0].message.content)print("\nReasoning process:")print(response.choices[0].message.reasoning)技术支持
Final answer:
To count the 'r's in 'strrawberry', I'll go through each letter:
...
There are **4** 'r's in the word 'strrawberry'.
Reasoning process:
...技术支持
模型会同时给出最终答案和达到结论的内部推理。这种双重输出有助于判断模型是否采用了正确的方法。
控制推理强度
您可以用两种方式控制模型在回答中投入的推理力度。effort 参数适用于 OpenAI o 系列等模型,按级别对应 max_tokens 的特定百分比:
高努力(High effort):约使用 max_tokens 的 80% 用于推理
中等努力(Medium effort):约使用 max_tokens 的 50% 用于推理
低努力(Low effort):约使用 max_tokens 的 20% 用于推理
# High effort reasoning for complex problems
response = client.chat.completions.create(
model="deepseek/deepseek-r1",
messages=[{"role":"user","content":"Solve this step by step: If a train travels 240 miles in 3 hours, then speeds up by 20 mph for the next 2 hours, how far does it travel total?"}],
max_tokens=4000,# High effort will use ~3200 tokens for reasoning
extra_body={"reasoning":{"effort":"high"}})print("Problem solution:")print(response.choices[0].message.content)print("\nStep-by-step reasoning:")print(response.choices[0].message.reasoning)技术支持
# First message with reasoning
response = client.chat.completions.create(
model="anthropic/claude-sonnet-4",
messages=[{"role":"user","content":"Should I invest in renewable energy stocks? Consider both risks and opportunities."}],
extra_body={"reasoning":{"max_tokens":3000}})# Build conversation history with reasoning preserved
messages =[{"role":"user","content":"Should I invest in renewable energy stocks? Consider both risks and opportunities."},{"role":"assistant","content": response.choices[0].message.content,"reasoning_details": response.choices[0].message.reasoning_details # Preserve reasoning},{"role":"user","content":"What about solar energy specifically? How does that change your analysis?"}]# Continue conversation with reasoning context
follow_up = client.chat.completions.create(
model="anthropic/claude-sonnet-4",
messages=messages,
extra_body={"reasoning":{"max_tokens":2000}})print("Follow-up answer:")print(follow_up.choices[0].message.content)print("\nContinued reasoning:")print(follow_up.choices[0].message.reasoning)技术支持
使用方式与之前一致,只是额外加入 attachments 参数以包含视觉内容。文件附件可用于 OpenRouter 上的所有模型。即便某些模型原生不支持 PDF 或图像,OpenRouter 也会在内部解析这些文件,并将内容传递给模型。
处理图像
您可以通过 URL 或 base64 编码在请求中包含图像。如果图像已在线,使用 URL 更简单:
response = client.chat.completions.create(
model="openai/gpt-5-mini",
messages=[{"role":"user","content":"What's happening in this image? Describe the scene in detail."}],
extra_body={"attachments":[{"type":"image/jpeg","url":"https://example.com/photo.jpg"}]})print(response.choices[0].message.content)技术支持
对于本地图像,您可以使用 base64 编码:
import base64
defencode_image_to_base64(image_path):withopen(image_path,"rb")as image_file:
encoded_string = base64.b64encode(image_file.read()).decode('utf-8')return encoded_string
# Analyze a local screenshot
encoded_image = encode_image_to_base64("screenshot.png")
response = client.chat.completions.create(
model="openai/gpt-5-mini",
messages=[{"role":"user","content":"This is a screenshot of a data dashboard. What insights can you extract from the charts and metrics shown?"}],
extra_body={"attachments":[{"type":"image/png","data": encoded_image
}]})print(response.choices[0].message.content)技术支持
模型会基于实际图像给出具体洞见,而非泛泛而谈。
处理 PDF 文档
PDF 的处理方式相同,但用途扩展到文档分析。您可以就报告提问、分析表单,或从复杂文档中提取信息:
defencode_pdf_to_base64(pdf_path):withopen(pdf_path,"rb")as pdf_file:
encoded_string = base64.b64encode(pdf_file.read()).decode('utf-8')return encoded_string
# Analyze a research paper
encoded_pdf = encode_pdf_to_base64("research_paper.pdf")
response = client.chat.completions.create(
model="openai/gpt-5-mini",
messages=[{"role":"user","content":"Summarize the key findings from this research paper. What are the main conclusions and methodology used?"}],
extra_body={"attachments":[{"type":"application/pdf","data": encoded_pdf
}]})print(response.choices[0].message.content)技术支持
这对财报、学术论文、合同或任何需要基于实际内容进行 AI 分析的 PDF 都很实用。如果需要比较图像或联合分析多份文档,您也可以在一次请求中附加多个文件。
成本与模型选择
多模态请求通常比纯文本更贵,因为需要处理额外的数据类型。图像与 PDF 的处理更耗算力,这也会体现在价格上。您可以在 模型页面查看各模型的多模态定价。
"response_format":{"type":"json_schema", # Always this for structured outputs
"json_schema":{"name":"your_schema_name", # Name for your schema
"strict": True, # Enforce strict compliance
"schema":{
# Your actual JSON schema definition goes here
}}}技术支持
情感分析示例
下面通过一个完整示例从文本中提取情感,展示结构化输出在实践中的工作方式:
response = client.chat.completions.create(
model="openai/gpt-5-mini",
messages=[{"role":"user","content":"Analyze the sentiment: 'This movie was absolutely terrible!'"}],
extra_body={"response_format":{"type":"json_schema","json_schema":{"name":"sentiment_analysis","strict":True,"schema":{"type":"object","properties":{"sentiment":{"type":"string","enum":["positive","negative","neutral"]},"confidence":{"type":"number"}},"required":["sentiment","confidence"]}}}})import json
result = json.loads(response.choices[0].message.content)print(result)技术支持
管理多个 AI 供应商的 API 很快就会变得令人手足无措。每家供应商都有不同的身份验证方式、定价模式和 API 规范。开发者为了访问不同的模型,不得不在 OpenAI、Anthropic、Google 等平台之间来回切换,浪费了无数时间。
OpenRouter 通过提供统一的 API,连接数十家供应商的 400 多个模型,从而解决了这一复杂性。您可以用同一个 API 密钥和一致的接口访问 GPT-5、Claude 4、Gemini 2.5 Pro 等数百个模型。平台在后台处理自动回退、成本管理和供应商路由。
在本教程中,我将讲解关于 OpenRouter 您需要了解的一切:从发出第一个 API 调用,到实现结构化输出等高级功能。最后,您将学会如何构建不受单一供应商锁定、可靠的应用程序。
什么是 OpenRouter?
OpenRouter 是一个统一的 API 平台,通过单一端点让您访问来自数十家供应商的 400 多个 AI 模型。无需再为 OpenAI、Anthropic、Google、Meta 等分别维护不同的 API 密钥,使用一个密钥即可访问它们的全部模型目录。
该平台充当智能路由器,负责将您的请求发送到合适的供应商,并处理身份验证、计费和错误处理。这种方式解决了使用多个 AI 供应商时的诸多痛点。
OpenRouter 解决的问题
与多个 AI 供应商配合使用会很快变得混乱。每家都有自己的 API 格式、登录流程和计费系统。您最终不得不为每个服务维护独立代码,拖慢开发速度,让测试新模型变得十分痛苦。
当供应商宕机或触发速率限制时,情况更糟。您的应用会崩溃,除了等待别无他法。此外,要弄清哪家供应商为类似模型提供了最佳价格,意味着您得在不同平台间手动追踪成本。
最大的问题在于被单一供应商锁定。当您围绕他们的特定 API 构建一切后,后续若要切换到更好的模型或更便宜的选项,就会变成一项大工程。
OpenRouter 如何解决
OpenRouter 通过一组相互关联的功能来解决这些问题:
这些功能相互配合,让 AI 开发更顺畅、更可靠。
谁该使用 OpenRouter?
不同类型的用户都能从这种统一方案中获益:
既然已了解 OpenRouter 的优势,我们来完成您的第一个 API 调用。
前置条件
在开始使用 OpenRouter 之前,您需要在本机进行一些准备。本文假设您熟悉基础的 Python 编程,并有过使用 API 的经验。您不必是专家,但应理解如何发起 HTTP 请求和处理 JSON 响应等概念。
您的系统需安装 Python 3.7 或更高版本。我们将使用
openaiPython 包与 OpenRouter 的 API 交互,并配合python-dotenv安全管理环境变量。您可以通过以下命令安装:您还需要一个 OpenRouter 账号和 API 密钥。前往 openrouter.ai 创建免费账号——您会获得少量额度用于测试。登录后,在账户设置的 API Keys 部分生成新密钥。
拿到 API 密钥后,在项目目录创建
.env文件,并按如下方式添加您的密钥:这样可以将 API 密钥与代码分离,确保安全。如果计划在测试之外长期使用 OpenRouter,请在 Credits 页面为账户充值。
完成以上准备后,您就可以通过 OpenRouter 发起第一个 API 调用了。
在 OpenRouter 中发起首次 API 调用
如果您之前用过 OpenAI SDK,开始使用 OpenRouter 将会非常简单。您只需改一行代码,就能瞬间访问来自不同供应商的数百个模型。
首次请求与设置
让我们直接通过一个可运行示例来演示 OpenRouter 的用法:
关键变化在两处。其一,
base_url参数将请求重定向到 OpenRouter 的服务器,而非 OpenAI。其二,模型名称采用provider/model-name格式——使用openai/gpt-5-mini而不是仅gpt-5-mini。这既指定了供应商的版本,又保持了熟悉的接口。以下是一些可直接替换到上述示例中的常见模型:既然已经体会到切换不同模型有多容易,您可能会问:当所选模型不可用时会发生什么?如何确保在供应商出现问题时,应用仍保持可靠?这就要用到 OpenRouter 的路由与弹性特性。
为弹性而设计的模型路由
要构建可靠的 AI 应用,就必须为意外情况做准备。供应商会遇到停机,模型会触发速率限制,有时内容审核也会拦截请求。模型路由是 OpenRouter 的解决方案——它会在不同模型之间自动切换,确保您的应用平稳运行。
设置手动回退
最直接增强弹性的方法是指定备用模型。当主要模型失败时,OpenRouter 会按顺序尝试您的备选。由于 OpenAI SDK 原生不支持此功能,我们通过
extra_body参数将这些路由指令传给 OpenRouter API:OpenRouter 会优先尝试 Kimi-K2。如果其不可用、被限流或被拦截,则会自动尝试 Claude Sonnet 4,然后是 DeepSeek R1。
response.model字段会显示实际给出响应的模型。自动路由,省心之选
理解了手动回退后,自动路由就更有吸引力了。它自动处理模型选择与回退,由 NotDiamond 的评估系统提供支持:
自动路由会分析您的提示,选择最佳的可用模型,并在首选模型不可用时内置回退。无需任何配置即可获得弹性。不过,在敏感或高要求场景中请谨慎使用自动路由,它有时会低估问题复杂度,进而选择容量较低的模型。
构建有效的回退策略
并非所有模型都适合作为彼此的备份。同一供应商的宕机可能影响其所有模型,因此应选择来自不同供应商的回退。速率限制和成本差异巨大,因此也要将昂贵模型与更便宜的替代品搭配:
这样可以在可用时获得高端质量,作为备份则有稳健表现,最后还有几乎必定可用的免费选项。不同供应商的内容审核政策也不尽相同,因此多元化的回退链能在敏感话题上覆盖更全面。
为回退链查找模型
通过 模型页面,您可以按供应商与能力筛选,构建回退链。许多强大的模型如 DeepSeek R1 和 Kimi-K2 因为是开源而免费,非常适合作为回退。对于新用户,免费模型的调用上限为每天 50 次;若您购买了 10 个额度,限额可提升至每天 1000 次。
对于动态应用,您也可以以编程方式发现模型:
这种方式能让您的回退链随新模型的出现而不断适配与增强。
用于实时响应的流式输出
在处理 AI 模型时,尤其是较长的回答,用户通常希望能逐步看到输出,而不是一次性等待完整响应。流式输出通过在生成过程中分块发送内容来解决这一问题,带来类似 ChatGPT 界面的交互体验。
基础流式设置
在 OpenRouter 中启用流式输出,只需在请求中添加
stream=True。响应会变成一个迭代器,模型在生成时持续产出数据块:每个数据块包含响应的一小部分。
delta.content字段保存新增文本片段,我们立即打印且不加换行,从而形成流式效果。end=""参数可防止在数据块之间加入换行。构建更完善的流式处理器
在生产环境中,您需要对流式过程有更多控制。下面这个处理器能管理完整响应:
该处理器在展示进度的同时捕获完整响应,既提供流式体验又保留最终文本,并包含适当的输出格式化。
有了流式输出,用户体验从“等待和祈祷”变成“看到过程在推进”。这会让您的 AI 应用更加灵敏和有吸引力。
在 OpenRouter 中处理推理令牌
一些 AI 模型可以在给出最终答案之前展示其“思考”过程。推理令牌能透明呈现模型解决复杂问题的思路,展示其得出结论的逐步逻辑。理解这一内部推理有助于您验证答案、调试模型行为,并构建更值得信任的应用。
什么是推理令牌
推理令牌会出现在响应中独立的
reasoning字段,与主体内容分离。不同模型对推理的支持方式不同——有的使用“努力级别”,有的使用“令牌预算”。下面是一个展示推理过程的简单示例:
模型会同时给出最终答案和达到结论的内部推理。这种双重输出有助于判断模型是否采用了正确的方法。
控制推理强度
您可以用两种方式控制模型在回答中投入的推理力度。
effort参数适用于 OpenAI o 系列等模型,按级别对应max_tokens的特定百分比:max_tokens的 80% 用于推理max_tokens的 50% 用于推理max_tokens的 20% 用于推理对于支持直接令牌分配的模型(如 Anthropic 的模型),您可以指定精确的推理预算:
更高的令牌预算通常会产生更充分的推理;较低预算则会更快但细节较少。
在对话中保留推理
在构建多轮对话时,需要同时保留推理与最终答案以维持上下文。对于复杂讨论而言尤为重要,因为模型的思考过程会影响后续回答:
reasoning_details字段可保留完整的推理链,使模型在回答追问时能够基于先前分析继续推理,从而让对话更连贯、更具上下文意识。成本与计费考量
推理令牌按输出令牌计费,因此会增加使用成本。不过,它们通常能显著提升回答质量,尤其在复杂任务中,准确性往往比速度更重要。根据 OpenRouter 的文档,推理令牌能在具挑战性的问题上提升模型表现,并提供决策过程的透明度。
对于成本敏感的应用,您可以根据任务复杂度调整努力级别或令牌预算,以平衡推理质量与开销。简单问题可能无需推理,而复杂问题则受益于高强度推理。
在 OpenRouter 中使用多模态模型
到目前为止我们一直在处理文本,但当您需要分析图像或文档时怎么办?或许您想就一张图表提问、从 PDF 中提取信息,或描述照片中发生的事情。这就是多模态模型的用武之地——它们能在同一请求中理解文本和视觉内容。
理解多模态能力
与其用文字描述图像,不如直接发送图像并就其提问。这会让应用更直观,因为模型能“看到”您正在处理的内容。您也无需担心文字描述是否覆盖了所有重要细节。
使用方式与之前一致,只是额外加入
attachments参数以包含视觉内容。文件附件可用于 OpenRouter 上的所有模型。即便某些模型原生不支持 PDF 或图像,OpenRouter 也会在内部解析这些文件,并将内容传递给模型。处理图像
您可以通过 URL 或 base64 编码在请求中包含图像。如果图像已在线,使用 URL 更简单:
对于本地图像,您可以使用
base64编码:模型会基于实际图像给出具体洞见,而非泛泛而谈。
处理 PDF 文档
PDF 的处理方式相同,但用途扩展到文档分析。您可以就报告提问、分析表单,或从复杂文档中提取信息:
这对财报、学术论文、合同或任何需要基于实际内容进行 AI 分析的 PDF 都很实用。如果需要比较图像或联合分析多份文档,您也可以在一次请求中附加多个文件。
成本与模型选择
多模态请求通常比纯文本更贵,因为需要处理额外的数据类型。图像与 PDF 的处理更耗算力,这也会体现在价格上。您可以在 模型页面查看各模型的多模态定价。
不同模型在视觉内容方面的强项各不相同。有的擅长细致的图像分析,有的在文档理解上更出色。请根据您的具体需求与预算多做尝试,找出最合适的模型。
使用结构化输出
在构建真实应用时,您需要可预测的数据格式,便于代码稳定解析。自由文本适合聊天界面,但对于需要提取特定信息的应用则非常糟糕。与其接收不确定的文本并依赖正则解析或指望模型按格式输出,不如使用 结构化输出 强制模型返回符合要求的 JSON,包含精确的字段与数据类型。这样可消除解析错误,大幅简化应用代码。
结构化输出请求的组成
结构化输出通过
response_format参数实现,基本结构如下:情感分析示例
下面通过一个完整示例从文本中提取情感,展示结构化输出在实践中的工作方式:
该模式的要点如下:
sentiment:字符串字段,通过enum限定为三种取值。模型不能返回 "positive"、"negative"、"neutral" 之外的内容confidence:数值字段,表示模型的置信度required:两个字段都必须存在——模型不能跳过strict: True:强制严格遵循模式结构没有结构化输出时,您可能会收到诸如“情感非常负面,置信度很高”或“Negative(95% 确定)”之类的文本。有了模式后,您总能得到可解析的 JSON,立即用于代码中。
设置
strict: True会严格强制约束,模型不得偏离您的结构。required数组指定必须包含的字段。您可以用enum限定取值,用array表示列表,并通过嵌套object类型表达复杂数据。模型兼容性
并非所有模型都支持结构化输出,但大多数现代模型都支持。您可以查看 模型页面以了解兼容性。当某个模型原生不支持结构化输出时,OpenRouter 通常会在内部处理格式化。
结构化输出让 AI 响应从不可预测的文本变为可靠的数据,您的应用可以放心依赖。对于任何需要一致性数据抽取的生产场景,这都是必不可少的功能。
结语
我们学习了如何通过 OpenRouter 的统一 API 访问数百个 AI 模型,从首次请求到实现流式输出、推理令牌和结构化输出等功能。
平台的自动回退与模型路由让您的应用即使在个别供应商出问题时也能保持可靠。借助相同的代码模式,我们可以比较模型、切换供应商,为每项任务找到最合适的选择,而无需管理多个 API 密钥。
从简单请求开始尝试,随需求增长逐步启用更多功能。针对不同任务测试不同模型——有的更适合创意写作,有的更擅长数据分析或推理问题。
通过本文,您已具备构建不被任何单一供应商锁定的 AI 应用的能力,能够在新模型与新能力出现时灵活适配。