LangChain 是 Python 生态里最常用的 LLM 应用框架,Agent、RAG、LangGraph 工作流大多基于它的 Chat Model 抽象来写。好消息是:LangChain 的 OpenAI 和 Anthropic 集成都支持自定义 base_url,所以接入 Zivv 这类 OpenAI 兼容中转,业务代码几乎不用动,只改初始化参数。这篇从安装开始,依次讲 ChatOpenAI、ChatAnthropic、流式输出、工具调用、结构化输出和多模型切换,最后给出常见报错的排查清单。
先选对集成包
LangChain 把各家模型拆成了独立的集成包,接中转时用到的主要是两个:
| 集成包 | 类 | 协议 | Zivv Base URL |
|---|---|---|---|
| langchain-openai | ChatOpenAI | OpenAI 兼容 | https://zivv.pro/v1 |
| langchain-anthropic | ChatAnthropic | Anthropic 原生 | https://zivv.pro |
选择很简单:大多数场景用 ChatOpenAI 就够了,一个类就能调用 Claude、GPT、Gemini 全部模型;如果你需要 Claude 的原生特性(例如 Anthropic 风格的扩展思考参数、原生的 content block 结构),再用 ChatAnthropic。两种方式用的是同一个 Zivv Key。
安装依赖:
pip install -U langchain langchain-openai langchain-anthropic建议把 Key 放在环境变量里,不要写进代码:
export ZIVV_API_KEY=sk-your-key-here还没有 Key 的话,先 注册 Zivv 并在控制台创建一个,模型 id 到 模型广场 核对,或者调用 GET /v1/models 获取。
ChatOpenAI:一个类调用全部模型
import os
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="gpt-5.5",
base_url="https://zivv.pro/v1",
api_key=os.environ["ZIVV_API_KEY"],
temperature=0.3,
timeout=120,
max_retries=2,
)
resp = llm.invoke("用一句话解释什么是向量数据库")
print(resp.content)把 model 换成 claude-sonnet-5 或 gemini-3.5-flash,同一段代码就会调用对应模型,其余参数不变。这就是 OpenAI 兼容中转最大的价值:不用为每家厂商维护一套 SDK。
base_url 的规则和其他客户端一致:填到 /v1 为止,不要只写域名,也不要补全 /chat/completions。
用环境变量代替参数
如果你的项目已经大量使用 ChatOpenAI,不想逐处改参数,可以只设置环境变量,底层 OpenAI 客户端会读取它们:
export OPENAI_API_KEY=sk-your-key-here
export OPENAI_BASE_URL=https://zivv.pro/v1不过显式传 base_url 更直观,排查问题时也不会被某个遗留的环境变量误导,新项目推荐显式写。
ChatAnthropic:走 Anthropic 原生协议
import os
from langchain_anthropic import ChatAnthropic
claude = ChatAnthropic(
model="claude-sonnet-5",
base_url="https://zivv.pro",
api_key=os.environ["ZIVV_API_KEY"],
max_tokens=4096,
)
print(claude.invoke("列出三个 Python 异步编程的常见坑").content)注意两点:Anthropic 原生入口的 base_url 是 https://zivv.pro,不带 /v1,因为 SDK 会自己拼接 /v1/messages;Anthropic 协议要求设置 max_tokens,按任务需要的输出长度给一个合理值。这和 Claude Code 的配置思路一致(Claude Code 用 ANTHROPIC_BASE_URL=https://zivv.pro),详见 Claude Code 配置文档。
流式输出
聊天界面、长文生成都需要流式返回。LangChain 所有 Chat Model 都支持 stream 方法:
for chunk in llm.stream("写一段 200 字的产品介绍"):
print(chunk.content, end="", flush=True)异步场景用 astream,写法相同。如果想在流式模式下拿到 token 用量(方便自己做成本统计),ChatOpenAI 可以开启 stream_usage=True,最后一个分块会带上 usage_metadata。成本估算方法可参考 Token 成本计算指南。
工具调用
工具调用是 Agent 的基础。LangChain 用 bind_tools 把函数定义交给模型,ChatOpenAI 和 ChatAnthropic 写法一致:
from langchain_core.tools import tool
@tool
def get_weather(city: str) -> str:
"""查询指定城市的天气"""
return f"{city}:晴,22°C"
llm_with_tools = llm.bind_tools([get_weather])
msg = llm_with_tools.invoke("上海今天天气怎么样?")
for call in msg.tool_calls:
print(call["name"], call["args"])模型返回的 tool_calls 是标准化后的结构,与具体厂商无关。拿到结果后把 ToolMessage 追加进消息列表再调用一次,就完成了一轮工具循环;用 LangGraph 的预置 Agent 可以把这个循环自动化。
一个经验:工具调用对模型能力要求较高,复杂多工具场景优先用 claude-sonnet-5 或 gpt-5.5,简单单工具场景中档模型就够了。
结构化输出
with_structured_output 能让模型直接返回 Pydantic 对象,适合信息抽取:
from pydantic import BaseModel
class Ticket(BaseModel):
category: str
priority: int
summary: str
extractor = llm.with_structured_output(Ticket, method="function_calling")
print(extractor.invoke("用户反馈:支付页面一直转圈,很着急"))这里显式指定了 method="function_calling"。较新版本的 langchain-openai 对部分模型默认使用 JSON Schema 模式,而通过兼容接口调用非 OpenAI 模型时,不是每个模型都支持这种模式;用函数调用方式实现结构化输出兼容性最好。如果遇到结构化输出报错,第一步就是改这个参数。
多模型切换
用中转的一大好处是可以按任务分配模型。最简单的做法是写一个工厂函数:
MODELS = {
"fast": "gemini-3.5-flash",
"default": "claude-sonnet-5",
"hard": "claude-opus-4-8",
}
def get_llm(tier: str = "default", **kwargs) -> ChatOpenAI:
return ChatOpenAI(
model=MODELS[tier],
base_url="https://zivv.pro/v1",
api_key=os.environ["ZIVV_API_KEY"],
**kwargs,
)
classifier = get_llm("fast", temperature=0)
writer = get_llm("default")LangChain 还提供 init_chat_model 和 with_fallbacks:前者可以用字符串动态初始化模型,后者可以在主模型报错时自动切到备用模型,例如主用 claude-sonnet-5,失败时回退到 gpt-5.5。因为都走同一个 base_url 和 Key,回退不需要额外配置账号。更多路由思路见 多模型路由策略。
生产环境的几条建议
示例代码跑通之后,真正上线前还有几件事值得做。
按环境拆分 Key。 开发、测试、生产各用一个 Key,在控制台按 Key 查看用量,哪个环境消耗异常一眼可见。某个 Key 泄露时也只需停掉这一个,不影响线上服务。团队协作时可以配合 团队模式,给每个成员和项目单独发 Key、设置日 / 月 / 总额预算,避免某个 Agent 死循环把余额跑空。
把模型名收拢到配置。 不要在几十个文件里散落写死模型 id。像上面的工厂函数那样集中管理,以后升级模型或调整档位只改一处。模型目录会随时间更新,上线前用 /v1/models 核对一次当前可用的 id。
超时和重试要分层设置。 LangChain 的 max_retries 处理的是瞬时错误,不要设得太大,否则在上游拥堵时会放大请求量。对用户实时等待的接口,建议「短超时 + 少量重试 + with_fallbacks 换模型」;对后台批处理,则可以给更长的超时。
监控 token 而不只是请求数。 Zivv 按实际 token 用量计费,计费方式是分组倍率乘以模型官方价格。Agent 和长对话场景里,单次请求的输入 token 会随历史增长,定期看用量页的模型维度统计,能及时发现上下文膨胀的问题。
Embeddings 需要另选服务商
RAG 项目里通常还会用 OpenAIEmbeddings 生成向量。Zivv 目前不提供 Embeddings 接口,所以不要把 OpenAIEmbeddings 的 base_url 指向 Zivv,否则会直接报错。推荐的组合是:
- 对话、推理、工具调用的 Chat Model 走 Zivv
- 向量化使用其他提供 Embeddings 的服务商,或本地部署的开源向量模型
LangChain 的 Embeddings 和 Chat Model 本来就是独立对象,两边分别初始化即可,互不影响。
常见报错排查
| 报错 | 常见原因 | 处理 |
|---|---|---|
| 401 Unauthorized | Key 错误、带空格或已停用 | 检查环境变量,见 401 排查 |
| 404 Not Found | ChatOpenAI 的 base_url 缺 /v1,或 ChatAnthropic 多写了 /v1 | 按上文规则修正 |
| model not found | 模型 id 写错或不在分组内 | 调 /v1/models 核对,见 404 排查 |
| 结构化输出报错 | 模型不支持 JSON Schema 模式 | 改用 method="function_calling" |
| 429 / 529 | 单模型高峰拥堵 | 配置 max_retries 与 with_fallbacks |
| 请求超时 | 长输出超过默认超时 | 调大 timeout,或改用流式 |
定位问题时,先用 curl 直接请求一次,把「LangChain 配置问题」和「Key / 模型问题」分开:
curl https://zivv.pro/v1/chat/completions \
-H "Authorization: Bearer sk-your-key-here" \
-H "Content-Type: application/json" \
-d '{"model": "gpt-5.5", "messages": [{"role": "user", "content": "回复 OK"}]}'常见问题 FAQ
Q:LangChain 的 ChatOpenAI 能调用 Claude 模型吗? A:可以。通过 Zivv 的 OpenAI 兼容入口 https://zivv.pro/v1,ChatOpenAI 只需把 model 设为 claude-sonnet-5 等 Claude 模型 id 即可。
Q:ChatAnthropic 的 base_url 要不要加 /v1? A:不要。Anthropic 原生入口填 https://zivv.pro,SDK 会自动拼接 /v1/messages。加了 /v1 反而会 404。
Q:LangChain 用中转支持流式和工具调用吗? A:支持。stream / astream 流式输出和 bind_tools 工具调用都能正常使用,写法与直连官方完全一致。
Q:OpenAIEmbeddings 可以用 Zivv 吗? A:不可以,Zivv 目前不提供 Embeddings 接口。向量化请使用其他服务商或本地模型,Chat Model 继续走 Zivv。
Q:LangGraph 项目怎么接入? A:LangGraph 使用的就是 LangChain 的 Chat Model 对象,把节点里的模型按本文方式初始化即可,不需要额外配置。
Q:数据会被中转记录吗? A:Zivv 不存储请求与响应内容,只记录 token 用量用于计费。中转原理和安全边界见 AI API 中转站是什么。
写在最后
LangChain 接中转只需要记住两个地址:ChatOpenAI 用 https://zivv.pro/v1,ChatAnthropic 用 https://zivv.pro。剩下的流式、工具调用、结构化输出、多模型回退都和直连官方一样写。已有项目的迁移清单可以参考 从官方 API 迁移到 OpenAI 兼容中转,SDK 细节见 OpenAI SDK 接入文档。现在就 注册 Zivv,用一个 Key 跑通你的 LangChain 应用。