目的
- 为避免一学就会、一用就废,这里做下笔记
- 以DeepSeek-R1模型的调用为例,说明模型在不同调用方式上的差异
内容
在LangChain 1.0版本中,调用DeepSeek-R1模型的四种方式各有特点。下面我将从使用方式、实现原理、功能支持三个维度进行深入对比,并解释为什么它们之间存在差异。
四种调用方式核心差异总览
| 对比维度 | 方式1:ChatOpenAI |
方式2:OpenAI |
方式3:ChatDeepSeek |
方式4:自定义封装 |
|---|---|---|---|---|
| 对应API端点 | /v1/chat/completions |
/v1/completions |
/v1/chat/completions |
/v1/chat/completions |
| 输入格式 | 消息列表 | 字符串 | 消息列表 | 消息列表 |
| 输出格式 | AIMessage对象 |
字符串 | AIMessage对象 |
AIMessage对象 |
| 获取推理过程 | ❌ 不支持 | ❌ 不支持 | ✅ 原生支持 | ✅ 可支持 |
| 实现复杂度 | 低 | 低 | 低 | 高 |
| 代码统一性 | 高(可同时调用多家) | 高 | 低(专用于DeepSeek) | 中 |
| 推荐指数 | ⭐⭐⭐(无推理需求) | ⭐(不推荐) | ⭐⭐⭐⭐⭐(有推理需求) | ⭐⭐(特殊场景) |
四种方式详细解析
方式1:使用 langchain-openai 的 ChatOpenAI 类
实现原理:
ChatOpenAI是 LangChain 为 OpenAI 聊天模型设计的包装类- 它将 LangChain 的消息格式转换为 OpenAI API 的请求格式
- 处理响应时,只解析 OpenAI 官方定义的标准字段(
content、tool_calls等)
python
from langchain_openai import ChatOpenAI
from langchain.schema import HumanMessage
llm = ChatOpenAI(
model="deepseek-reasoner",
api_key="your-api-key",
base_url="https://api.deepseek.com/v1",
temperature=0.6
)
response = llm.invoke([HumanMessage(content="9.11 and 9.8, which is greater?")])
# 只能获取最终答案
print(response.content) # "9.11 is greater than 9.8"
# ❌ reasoning_content 不存在
print(response.additional_kwargs.get('reasoning_content')) # None
为什么无法获取推理过程?
查看 langchain-openai 源码可知,ChatOpenAI 在解析响应时:
python
# 简化版源码逻辑
def _create_chat_result(self, response):
message = AIMessage(
content=choice['message'].get('content', ''),
additional_kwargs={}, # 只添加已知字段
# reasoning_content 未被识别,被丢弃
)
DeepSeek-R1 返回的 reasoning_content 是非标准扩展字段,通用库默认不会将其纳入响应对象。
langchain-openai官方文档中,声称支持推理模型的推理输出(reasoning参数),但实测报错,猜测可能只支持openai公司内部的推理模型(因其使用了独有的标识是推理结果的字段)
方式2:使用 langchain-openai 的 OpenAI 类
实现原理:
OpenAI对应的是 OpenAI 的文本补全接口 (/v1/completions)- 输入是纯文本字符串,输出也是纯文本
- 这是 GPT-3 时代的遗留接口
python
from langchain_openai import OpenAI
llm = OpenAI(
model="deepseek-reasoner", # ❌ 模型类型不匹配
api_key="your-api-key",
base_url="https://api.deepseek.com/v1"
)
# 输入是字符串,而非消息列表
response = llm.invoke("9.11 and 9.8, which is greater?")
为什么不推荐?
- 端点可能不匹配 :
OpenAI类请求的是/v1/completions,但 DeepSeek-R1 作为聊天模型,可能只支持/v1/chat/completions - 格式转换丢失信息:消息角色(system/user/assistant)无法传递
- 响应解析错误 :返回的 JSON 格式与
OpenAI类期望的不一致
实际效果:可能无法调用,或调用后只返回部分结果。
方式3:使用 langchain-deepseek 的 ChatDeepSeek 类(✅ 推荐)
实现原理:
ChatDeepSeek是 LangChain 官方为 DeepSeek 专门开发的集成类- 它继承自
BaseChatModel,专为 DeepSeek API 优化 - 内部处理了
reasoning_content字段 ,将其映射到additional_kwargs
python
from langchain_deepseek import ChatDeepSeek
from langchain.schema import HumanMessage
llm = ChatDeepSeek(
model="deepseek-reasoner",
temperature=0.6,
api_key="your-api-key"
)
response = llm.invoke([HumanMessage(content="9.11 and 9.8, which is greater?")])
# ✅ 直接获取推理过程
reasoning = response.additional_kwargs.get('reasoning_content')
print("推理过程:", reasoning)
print("最终答案:", response.content)
源码层面做了什么?
查看 langchain-deepseek 的简化实现:
python
class ChatDeepSeek(BaseChatModel):
def _create_chat_result(self, response):
# 获取原始响应
choice = response['choices'][0]
message = choice['message']
# 专门处理 reasoning_content
reasoning = message.get('reasoning_content', '')
# 创建 AIMessage 时,将 reasoning_content 放入 additional_kwargs
return AIMessage(
content=message.get('content', ''),
additional_kwargs={'reasoning_content': reasoning}
)
这正是为什么方式3能原生支持推理过程。
方式4:参照 ChatOpenAI 类封装自定义类
实现原理:
- 继承
ChatOpenAI,重写响应解析方法 - 拦截原始响应,提取
reasoning_content - 将其注入到返回的
AIMessage中
完整实现代码:
python
from langchain_openai import ChatOpenAI
from langchain_core.messages import AIMessage
from langchain_core.outputs import ChatResult, ChatGeneration
from typing import Any, Dict, List, Optional
class DeepSeekR1ChatOpenAI(ChatOpenAI):
"""
支持获取推理过程的 ChatOpenAI 子类
专为 DeepSeek-R1 模型设计
"""
def __init__(self, **kwargs):
# 自动添加必要的 extra_body 参数
if 'model_kwargs' not in kwargs:
kwargs['model_kwargs'] = {}
kwargs['model_kwargs']['extra_body'] = {
'return_reasoning': True
}
super().__init__(**kwargs)
def _create_chat_result(self, response: Dict[str, Any]) -> ChatResult:
"""重写响应解析方法,捕获 reasoning_content"""
# 1. 先调用父类方法获取标准结果
result = super()._create_chat_result(response)
# 2. 从原始响应中提取 reasoning_content
if 'choices' in response and len(response['choices']) > 0:
choice = response['choices'][0]
# DeepSeek 返回的 reasoning_content 可能在 message 的 model_extra 中
message_data = choice.get('message', {})
# 尝试从不同位置获取 reasoning_content
reasoning = None
if 'model_extra' in message_data:
reasoning = message_data['model_extra'].get('reasoning_content')
elif 'reasoning_content' in message_data:
reasoning = message_data['reasoning_content']
# 3. 将 reasoning_content 注入到 result 中
if reasoning is not None and result.generations:
for generation in result.generations[0]:
if isinstance(generation, ChatGeneration):
# 添加到 additional_kwargs
generation.message.additional_kwargs['reasoning_content'] = reasoning
return result
def _stream_response_to_generation(self, chunk):
"""处理流式响应中的推理内容"""
generation = super()._stream_response_to_generation(chunk)
# 从 chunk 中提取 reasoning_content
if (hasattr(chunk, 'choices') and len(chunk.choices) > 0 and
hasattr(chunk.choices[0], 'delta')):
delta = chunk.choices[0].delta
if hasattr(delta, 'reasoning_content') and delta.reasoning_content:
# 这里需要根据实际流式响应结构调整
pass
return generation
# 使用示例
llm = DeepSeekR1ChatOpenAI(
model="deepseek-reasoner",
api_key="your-api-key",
base_url="https://api.deepseek.com/v1",
temperature=0.6
)
response = llm.invoke([
HumanMessage(content="9.11 and 9.8, which is greater?")
])
# 获取推理过程
reasoning = response.additional_kwargs.get('reasoning_content')
print("推理过程:", reasoning)
print("最终答案:", response.content)
# 流式输出示例
for chunk in llm.stream([HumanMessage(content="解释一下相对论")]):
if chunk.content:
print(chunk.content, end="")
# 流式推理过程的处理需要更复杂的逻辑
流式输出的挑战 :
在流式模式下,reasoning_content 和 content 可能交替返回。处理流式推理需要更复杂的逻辑,这也是自定义封装的主要难点。
选择建议
| 使用场景 | 推荐方式 | 原因 |
|---|---|---|
| 需要获取推理过程,希望开箱即用 | 方式3:ChatDeepSeek |
官方支持,最简单可靠 |
| 不需要推理过程,只关心最终答案 | 方式1:ChatOpenAI |
代码统一,可同时调用多家 |
已大量使用 ChatOpenAI,但又需要推理过程 |
方式4:自定义封装 | 保持代码一致性,但需要开发 |
| 任何需要工具调用/结构化输出的场景 | 改用 DeepSeek-V3 | R1 本身不支持这些功能 |
| 简单的文本补全 | 方式2:OpenAI 类 |
基本不适用,不推荐 |
总结:为什么会有这些差异?
- 历史原因 :
OpenAI类是早期文本补全模型的遗留产物 - 通用 vs 专用 :
ChatOpenAI追求通用性,牺牲了对特殊字段的支持 - 官方集成 :
ChatDeepSeek是 LangChain 为 DeepSeek 专门优化的结果 - 自定义扩展:LangChain 的设计允许通过继承来扩展功能