# LangChain 自定义 Base URL：兼容 API 接入指南

> 正确设置 ChatOpenAI 的 base_url，分开验证普通调用、流式输出与工具调用，并识别更换端点时可能丢失的供应商扩展字段。

_发布 2026-09-29 · 更新 2026-09-29 · [KeepRouter Editorial](https://keeprouter.com/editorial-policy#editorial-team) · 5 分钟阅读_

![从请求契约到回滚的 OpenAI 兼容 API 字段级迁移清单](https://keeprouter.com/editorial/blog/openai-compatible-api-migration-checklist.png)

_迁移接入时，同时验证请求约定与应用结果。概念示意图。_

针对标准 Chat Completions 端点，在 LangChain 的 `ChatOpenAI` 中明确填写 `base_url`、目标服务的 Key 和模型 ID。先使用最小参数；兼容请求格式并不代表框架会保留供应商返回的全部扩展字段。

如果现有 Python 应用已经使用 LangChain，可以先换客户端配置，再逐项恢复复杂链路，无需把整个 Agent 一次性迁移。

## 明确客户端，而不是依赖环境猜测

[ChatOpenAI 官方说明](https://docs.langchain.com/oss/python/integrations/chat/openai)记录了自定义地址，并指出这个封装面向标准 OpenAI 规范。非标准推理字段可能在转换中丢失。

在独立环境安装 `langchain-openai`，记录锁定版本，在脚本外设置 `KEEPROUTER_API_KEY`。以下示例会发送一条真实免费文本请求；关闭自动重试，便于观察首次结果。

```python
import os
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(
    model="free",
    base_url="https://keeprouter.com/v1",
    api_key=os.environ["KEEPROUTER_API_KEY"],
    use_responses_api=False,
    max_retries=0,
    timeout=30,
)
reply = llm.invoke([
    ("system", "仅根据给出的事实回答。"),
    ("human", "事实：本次发布标签是 amber。标签是什么？"),
])
print(reply.content)
print(reply.usage_metadata)
```

答案应包含 amber，不要求标点完全一致。没有用量元数据与没有文本是两种问题，不要把缺失 token 计数填成零。

## 基础地址和网络代理是两层配置

`base_url` 指定模型 API 的目标。HTTP 代理则决定机器怎样访问网络。显式填写客户端配置，也能避免本地和线上环境中的 `OPENAI_BASE_URL`、`OPENAI_API_BASE` 把请求指向不同服务。

| 字段 | 作用 | 常见错误 |
| --- | --- | --- |
| `base_url` | API 域名与前缀 | 附加完整 `/chat/completions` |
| `api_key` | 目标服务认证 | 把 OpenAI Key 用于其他服务 |
| `model` | 该目录中的型号 | 沿用其他 Router 的前缀 |
| 网络代理 | 到目标地址的网络路径 | 当成模型 API 地址 |

框架外成功、框架内失败时，比较实际序列化的字段，别同时更换模型、检索和 Agent 实现。

## 一次恢复一种行为

先用同一个短问题测试 `llm.stream()`，观察内容增量和正常结束。只有线路支持时再开启流式用量选项，客户端参数不会让上游自动拥有缺失的字段。

之后使用一个无副作用工具，例如查询虚构店铺营业时间。检查工具名、参数、调用 ID，以及工具结果返回后是否产生最终回答。描述“我要调用工具”的文字不能算真正的工具调用。框架侧接口参考 [LangChain tools](https://docs.langchain.com/oss/python/langchain/tools)，模型侧能力仍按具体线路确认。

最后恢复实际输出解析器。内容块、字符串和结构化对象不是同一种结果；不要为了让断言通过而把所有对象强制转成字符串。

## 找到字段丢失的层

如果应用依赖推理字段、引用、原生搜索或缓存参数，应比较脱敏后的原始 API 响应与框架生成的 `AIMessage`。字段可能在上游、网关或框架任一层消失。

扩展协议不可缺少时，使用相应供应商集成；普通聊天仍可保留通用兼容路径。泛化封装的优势是统一接口，但不能替代所有原生能力。

## 把重试计入完整任务

三个模型调用、每个最多重试两次的示例，最多会产生九次尝试；任务层再次执行还会继续放大。明确哪一层负责重试，并为整个任务设置截止时间，可参考[故障切换指南](/zh/blog/llm-failover-design-guide)。

用一组代表性案例记录模型、结束状态、时间、用量是否完整及答案是否合格。不要在日志中输出 Key 或私有提示词。小样本适合调试，不能包装成性能排名。

确定基本协议后，再从[目录](/models)选择付费候选。[迁移检查器](/tools/api-migration-checker)可在不接收密钥的情况下检查配置；链路输出与合格任务成本明确前，保留旧客户端配置。

## 常见问题

### base_url 要写到 chat/completions 吗？

不用。本文配置以 /v1 结尾，由客户端补齐操作路径。填写完整操作地址可能导致路径重复。

### ChatOpenAI 会保留全部推理字段吗？

不会。它主要针对标准 OpenAI 协议。需要扩展字段时要单独检查，必要时使用相应供应商集成。

### 首次测试为什么关闭重试？

为了直接看到首次错误，并准确解释调用次数。请求验证完成且明确重试责任后，再加入有上限的重试。

## 参考的一手资料

_本文最近复核 2026-09-29_

1. [LangChain ChatOpenAI](https://docs.langchain.com/oss/python/integrations/chat/openai)
2. [LangChain tools](https://docs.langchain.com/oss/python/langchain/tools)

## 继续阅读

- [LlamaIndex OpenAILike：更换 LLM，保留 RAG 索引](https://keeprouter.com/zh/blog/llamaindex-openai-like-rag.md)
- [LLM 工具调用：实现完整的请求与结果循环](https://keeprouter.com/zh/blog/llm-tool-calling-loop.md)
- [api migration checker](https://keeprouter.com/tools/api-migration-checker)

## 迁移前，先检查你的请求

在浏览器中检查型号 ID 与请求字段，无需 API Key，也不发送推理请求。

[检查示例配置](https://keeprouter.com/tools/api-migration-checker)

[创建 Key，测试免费模型](https://keeprouter.com/login?returnTo=%2Fconsole%2Fkeys%3Fmodel%3Dfree)

免费测试使用 free 模型；其他付费型号需要足够预付额度。

[全部文章](https://keeprouter.com/zh/blog.md) · [模型与价格](https://keeprouter.com/models.md)
