AI-大语言模型LLM-LangChainV1.0学习笔记-模型不同调用方式的差异

目的

  • 为避免一学就会、一用就废,这里做下笔记
  • 以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-openaiChatOpenAI

实现原理

  • ChatOpenAI 是 LangChain 为 OpenAI 聊天模型设计的包装类
  • 它将 LangChain 的消息格式转换为 OpenAI API 的请求格式
  • 处理响应时,只解析 OpenAI 官方定义的标准字段(contenttool_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-openaiOpenAI

实现原理

  • 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?")

为什么不推荐?

  1. 端点可能不匹配OpenAI 类请求的是 /v1/completions,但 DeepSeek-R1 作为聊天模型,可能只支持 /v1/chat/completions
  2. 格式转换丢失信息:消息角色(system/user/assistant)无法传递
  3. 响应解析错误 :返回的 JSON 格式与 OpenAI 类期望的不一致

实际效果:可能无法调用,或调用后只返回部分结果。

方式3:使用 langchain-deepseekChatDeepSeek 类(✅ 推荐)

实现原理

  • 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_contentcontent 可能交替返回。处理流式推理需要更复杂的逻辑,这也是自定义封装的主要难点。

选择建议

使用场景 推荐方式 原因
需要获取推理过程,希望开箱即用 方式3:ChatDeepSeek 官方支持,最简单可靠
不需要推理过程,只关心最终答案 方式1:ChatOpenAI 代码统一,可同时调用多家
已大量使用 ChatOpenAI,但又需要推理过程 方式4:自定义封装 保持代码一致性,但需要开发
任何需要工具调用/结构化输出的场景 改用 DeepSeek-V3 R1 本身不支持这些功能
简单的文本补全 方式2:OpenAI 基本不适用,不推荐

总结:为什么会有这些差异?

  1. 历史原因OpenAI 类是早期文本补全模型的遗留产物
  2. 通用 vs 专用ChatOpenAI 追求通用性,牺牲了对特殊字段的支持
  3. 官方集成ChatDeepSeek 是 LangChain 为 DeepSeek 专门优化的结果
  4. 自定义扩展:LangChain 的设计允许通过继承来扩展功能
相关推荐
flyyyya1 小时前
【AI学习从零至壹】AI agent自动化工作流
人工智能·学习·自动化
2501_918126911 小时前
stm32是用杜邦线母头接核心板和调试器吗
stm32·单片机·嵌入式硬件·学习·个人开发
Alsian2 小时前
Day32 神经网络
人工智能·深度学习·神经网络
梦帮科技2 小时前
【DREAMVFIA开源】量子互联网协议:节点通信与路由算法
人工智能·神经网络·算法·生成对抗网络·开源·量子计算
雨大王5122 小时前
为什么汽车零部件需要智能制造?关键技术与发展路径探讨
大数据·人工智能
海绵宝宝_2 小时前
Antigravity 配置VS Code 插件市场教程
人工智能·学习
2501_926978332 小时前
概率分形:智能涌现的统一机理
人工智能·经验分享·机器学习·ai写作·agi
迪巴拉15252 小时前
抗社交网络压缩的鲁棒对抗扰动生成研究
网络·人工智能·php
~kiss~2 小时前
月之暗面(Moonshot AI)的Kimi K2.5开源权重多模态旗舰大模型
人工智能·开源