LangChain 设计原理分析⁴ | BaseLanguageModel 接口解构:多模型适配的设计模式

本文聚焦 LangChain 对语言模型接口的核心抽象 ------ BaseLanguageModel,结合源码深入剖析其在多模型适配中的角色,并探讨工厂模式等设计思想如何提升模型集成能力。


一、什么是 BaseLanguageModel?

在 LangChain 中,BaseLanguageModel 是所有语言模型类的统一基类,位于模块 langchain_core.language_models.base 中。

它作为 LangChain 的 Runnable 抽象体系的一部分,定义了语言模型统一的配置接口、token 使用信息获取、流式生成支持等结构,但 并不直接实现文本生成或对话功能

python 复制代码
class BaseLanguageModel(
    RunnableSerializable[LanguageModelInput, LanguageModelOutputVar], ABC
):
    """Abstract base class for interfacing with language models.

    All language model wrappers inherited from BaseLanguageModel.
    """

该基类是为以下两类模型服务的抽象基类:

  • BaseLLM:传统的纯文本生成语言模型(如 text-davinci),位于 langchain_core.language_models.llms.py 模块中。
python 复制代码
class BaseLLM(BaseLanguageModel[str], ABC):
    """Base LLM abstract interface.

    It should take in a prompt and return a string.
    """

    callback_manager: Optional[BaseCallbackManager] = Field(default=None, exclude=True)
    """[DEPRECATED]"""

    model_config = ConfigDict(
        arbitrary_types_allowed=True,
    )
  • BaseChatModel:基于对话格式的语言模型(如 GPT-4、Claude、Mistral),位于 langchain_core.language_models.chat_models.py 模块中。
python 复制代码
class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
    """Base class for chat models.

    Key imperative methods:
        Methods that actually call the underlying model.
        +---------------------------+----------------------------------------------------------------+---------------------------------------------------------------------+--------------------------------------------------------------------------------------------------+
        | Method                    | Input                                                          | Output                                                              | Description                                                                                      |
        +===========================+================================================================+=====================================================================+==================================================================================================+
        | `invoke`                  | str | list[dict | tuple | BaseMessage] | PromptValue           | BaseMessage                                                         | A single chat model call.                                                                        |
        +---------------------------+----------------------------------------------------------------+---------------------------------------------------------------------+--------------------------------------------------------------------------------------------------+
        | `ainvoke`                 | '''                                                            | BaseMessage                                                         | Defaults to running invoke in an async executor.                                                 |
        +---------------------------+----------------------------------------------------------------+---------------------------------------------------------------------+--------------------------------------------------------------------------------------------------+
        | `stream`                  | '''                                                            | Iterator[BaseMessageChunk]                                          | Defaults to yielding output of invoke.                                                           |
        +---------------------------+----------------------------------------------------------------+---------------------------------------------------------------------+--------------------------------------------------------------------------------------------------+
        | `astream`                 | '''                                                            | AsyncIterator[BaseMessageChunk]                                     | Defaults to yielding output of ainvoke.                                                          |
        +---------------------------+----------------------------------------------------------------+---------------------------------------------------------------------+--------------------------------------------------------------------------------------------------+
        | `astream_events`          | '''                                                            | AsyncIterator[StreamEvent]                                          | Event types: 'on_chat_model_start', 'on_chat_model_stream', 'on_chat_model_end'.                 |
        +---------------------------+----------------------------------------------------------------+---------------------------------------------------------------------+--------------------------------------------------------------------------------------------------+
        | `batch`                   | list[''']                                                      | list[BaseMessage]                                                   | Defaults to running invoke in concurrent threads.                                                |
        +---------------------------+----------------------------------------------------------------+---------------------------------------------------------------------+--------------------------------------------------------------------------------------------------+
        | `abatch`                  | list[''']                                                      | list[BaseMessage]                                                   | Defaults to running ainvoke in concurrent threads.                                               |
        +---------------------------+----------------------------------------------------------------+---------------------------------------------------------------------+--------------------------------------------------------------------------------------------------+
        | `batch_as_completed`      | list[''']                                                      | Iterator[tuple[int, Union[BaseMessage, Exception]]]                 | Defaults to running invoke in concurrent threads.                                                |
        +---------------------------+----------------------------------------------------------------+---------------------------------------------------------------------+--------------------------------------------------------------------------------------------------+
        | `abatch_as_completed`     | list[''']                                                      | AsyncIterator[tuple[int, Union[BaseMessage, Exception]]]            | Defaults to running ainvoke in concurrent threads.                                               |
        +---------------------------+----------------------------------------------------------------+---------------------------------------------------------------------+--------------------------------------------------------------------------------------------------+
    ...
    """

二、invoke 方法

虽然 BaseLanguageModel 没有显式实现 invoke(),但它继承自 RunnableSerializableRunnable,后者为 LangChain 中所有模块提供了统一的执行入口:

python 复制代码
# langchain_core.runnables.base
class Runnable(Protocol):
    def invoke(self, input: InputType, config: RunnableConfig = None) -> OutputType: ...

因此,任何继承自 BaseLanguageModel 的模型类 自动拥有了 invoke() 方法 ,可用于链式组合执行。 BaseLLMBaseChatModel 都在各自子类中重写了 invoke() 方法,它们的输入接口保持一致(通常为 LanguageModelInput),但输出类型不同:BaseLLM 输出纯文本结果,而 BaseChatModel 输出结构化的消息对象(BaseMessage)。

  • BaseLLM.invoke()
python 复制代码
@override
def invoke(
    self,
    input: LanguageModelInput,
    config: Optional[RunnableConfig] = None,
    *,
    stop: Optional[list[str]] = None,
    **kwargs: Any,
) -> str:
    config = ensure_config(config)
    return (
        self.generate_prompt(
            [self._convert_input(input)],
            stop=stop,
            callbacks=config.get("callbacks"),
            tags=config.get("tags"),
            metadata=config.get("metadata"),
            run_name=config.get("run_name"),
            run_id=config.pop("run_id", None),
            **kwargs,
        )
        .generations[0][0]
        .text
    )
  • BaseChatModel.invoke()
python 复制代码
@override
def invoke(
    self,
    input: LanguageModelInput,
    config: Optional[RunnableConfig] = None,
    *,
    stop: Optional[list[str]] = None,
    **kwargs: Any,
) -> BaseMessage:
    config = ensure_config(config)
    return cast(
        "ChatGeneration",
        self.generate_prompt(
            [self._convert_input(input)],
            stop=stop,
            callbacks=config.get("callbacks"),
            tags=config.get("tags"),
            metadata=config.get("metadata"),
            run_name=config.get("run_name"),
            run_id=config.pop("run_id", None),
            **kwargs,
        ).generations[0][0],
    ).message

👀 cast() 是 Python 类型系统中的一个函数,来自标准库模块 typing。它的作用主要是给类型系统看的注释,不会改变实际数据类型,所以请确保你的 cast 是合理的,不然只是自欺欺人。


三、工厂模式与多模型适配结构

LangChain 在早期版本中将主流模型厂商(如 OpenAI、Anthropic、Alibaba 等)的封装类直接集中维护在主包中,并统一继承自 BaseChatModel。然而在最新版本中,LangChain 已将这类具体实现迁移到子项目 langchain-community,形成"核心逻辑"和"社区接入"的分离架构。

langchain-community 包中,你可以找到如下模型的适配器类:

  • langchain_community.chat_models.openai.ChatOpenAI
python 复制代码
class ChatOpenAI(BaseChatModel):
    """`OpenAI` Chat large language models API.

    To use, you should have the ``openai`` python package installed, and the
    environment variable ``OPENAI_API_KEY`` set with your API key.

    Any parameters that are valid to be passed to the openai.create call can be passed
    in, even if not explicitly saved on this class.

    Example:
        .. code-block:: python

            from langchain_community.chat_models import ChatOpenAI
            openai = ChatOpenAI(model="gpt-3.5-turbo")
    """
  • langchain_community.chat_models.anthropic.ChatAnthropic
python 复制代码
class ChatAnthropic(BaseChatModel, _AnthropicCommon):
    """`Anthropic` chat large language models.

    To use, you should have the ``anthropic`` python package installed, and the
    environment variable ``ANTHROPIC_API_KEY`` set with your API key, or pass
    it as a named parameter to the constructor.

    Example:
        .. code-block:: python

            import anthropic
            from langchain_community.chat_models import ChatAnthropic
            model = ChatAnthropic(model="<model_name>", anthropic_api_key="my-api-key")
    """
  • langchain_community.chat_models.tongyi.ChatTongyi
python 复制代码
class ChatTongyi(BaseChatModel):
    """Alibaba Tongyi Qwen chat model integration.
    ...
    """

这些类通过重写 invoke/_generate 方法,实现:

  • 模型输入转换(参数、消息格式)
  • 模型 API 调用(REST / SDK)
  • 模型输出统一封装(BaseMessage
  • 错误重试 / 输出解析 / 日志跟踪

ChatOpenAI._generate()

python 复制代码
def _generate(
    self,
    messages: List[BaseMessage],
    stop: Optional[List[str]] = None,
    run_manager: Optional[CallbackManagerForLLMRun] = None,
    stream: Optional[bool] = None,
    **kwargs: Any,
) -> ChatResult:
    should_stream = stream if stream is not None else self.streaming
    if should_stream:
        stream_iter = self._stream(
            messages, stop=stop, run_manager=run_manager, **kwargs
        )
        return generate_from_stream(stream_iter)
    message_dicts, params = self._create_message_dicts(messages, stop)
    params = {
        **params,
        **({"stream": stream} if stream is not None else {}),
        **kwargs,
    }
    response = self.completion_with_retry(
        messages=message_dicts, run_manager=run_manager, **params
    )
    return self._create_chat_result(response)

ChatTongyi._generate()

python 复制代码
def _generate(
    self,
    messages: List[BaseMessage],
    stop: Optional[List[str]] = None,
    run_manager: Optional[CallbackManagerForLLMRun] = None,
    **kwargs: Any,
) -> ChatResult:
    generations = []
    if self.streaming:
        generation_chunk: Optional[ChatGenerationChunk] = None
        for chunk in self._stream(
            messages, stop=stop, run_manager=run_manager, **kwargs
        ):
            if generation_chunk is None:
                generation_chunk = chunk
            else:
                generation_chunk += chunk
        assert generation_chunk is not None
        generations.append(self._chunk_to_generation(generation_chunk))
    else:
        params: Dict[str, Any] = self._invocation_params(
            messages=messages, stop=stop, **kwargs
        )
        resp = self.completion_with_retry(**params)
        generations.append(
            ChatGeneration(**self._chat_generation_from_qwen_resp(resp))
        )
    return ChatResult(
        generations=generations,
        llm_output={
            "model_name": self.model_name,
        },
    )

示例:OpenAI 聊天模型

python 复制代码
import os

from langchain_community.chat_models import ChatOpenAI
from langchain_core.messages import HumanMessage

model = ChatOpenAI(
    model='deepseek-chat',
    openai_api_key=os.getenv('DEEPSEEK_API_KEY'),
    openai_api_base='https://api.deepseek.com',
    max_tokens=1024
)

response = model.invoke(
    [HumanMessage(content="一句话回答这个问题:AI会成为第一生产力吗?")])
print(response.content)

我没有 OpenAI 的 API Key,DeepSeek API 使用与 OpenAI 兼容的 API 格式,所以替代一下。

输出:


四、BaseLLM 与 BaseChatModel 的区别

在 LangChain 的语言模型抽象体系中,BaseLLMBaseChatModel 是两个并列的核心基类,它们都继承自 BaseLanguageModel,并作为 Runnable 可组合模块用于构建链、代理和应用框架。

它们的区别主要体现在输入输出格式、生成逻辑、使用场景等方面:

特性 BaseLLM BaseChatModel
输入类型 str(纯文本) List[BaseMessage](结构化消息列表)
输出类型 strLLMResult BaseMessageChatResult
使用场景 文本生成、摘要、续写、补全 多轮对话、工具调用、代理决策、意图识别等
是否重写 invoke() ✅ 重写 ✅ 重写
内部核心方法 _generate(prompt: PromptValue) _generate(messages: list[BaseMessage])
是否支持流式输出 ✅ 支持(token 级流式输出) ✅ 支持(message 内容流式分块)

📌 典型子类包括:

  • BaseLLMOpenAI(text-davinci)HuggingFaceHub
  • BaseChatModelChatOpenAIChatAnthropicChatBaichuan

🎯 使用接口一致性

尽管输入输出有所不同,二者都遵循 Runnable 接口标准,统一支持:

  • .invoke():执行一次推理
  • .stream():流式输出(用于实时响应)
  • .batch():批量请求
  • .with_config() / .with_retry() 等链式组合方式

五、自定义一个 Chat 模型类(继承 BaseChatModel)

自定义一个对话模型 EchoChatModel,用于模拟回显响应。

python 复制代码
from langchain_core.language_models.chat_models import BaseChatModel
from langchain_core.messages import BaseMessage, AIMessage
from langchain_core.outputs import ChatResult, ChatGeneration


class EchoChatModel(BaseChatModel):
    @property
    def _llm_type(self) -> str:
        return "echo-chat"

    def _generate(
        self,
        messages: list[BaseMessage],
        stop: list[str] = None,
        **kwargs
    ) -> ChatResult:
        content = f"Echo: {messages[-1].content}"
        return ChatResult(
            generations=[
                ChatGeneration(
                    message=AIMessage(content=content)
                )
            ]
        )

这在测试代理行为或调试链路时非常有用。

python 复制代码
from langchain_core.messages import HumanMessage

model = EchoChatModel()
response = model.invoke([HumanMessage(content="我向你提出了一个问题!")])
print(response.content)

输出:


六、设计模式与解耦价值

LangChain 采用"抽象基类 + 工厂方法 +统一协议"组合的方式封装 LLM:

  • 所有语言模型遵循统一的 Runnable 调用接口(invoke / stream / batch
  • 不同模型类型拆分为 BaseLLMBaseChatModel 两个方向
  • 各厂商通过继承封装模型差异,实现极高的复用性

这让我们在构建 Chain、Agent、工具执行器等复杂流程时,可以自由切换底层模型而不影响业务逻辑。


🔚 小结

  • BaseLanguageModel 是 LangChain 中抽象所有语言模型的基础
  • invoke() 来自 Runnable 接口体系,支持链式组合
  • BaseLLM / BaseChatModel 提供了具体模型的适配规范
  • 工厂模式实现解耦、多模型统一适配、可测性与可扩展性强

接下来我们将深入解析 LangChain 的 PromptTemplate 模板系统,掌握变量绑定、FewShot 示例注入与格式化机制,构建高可控的提示生成链路。

相关推荐
AI Echoes3 小时前
ChatGPT、Playground手动模拟Agent摘要缓冲混合记忆功能
人工智能·python·langchain
AIGC包拥它5 小时前
检索召回率优化探究二:基于 LangChain 0.3集成 Milvus 2.5向量数据库构建的智能问答系统
人工智能·python·langchain·软件工程·个人开发·milvus
王国强20098 小时前
LangChain 设计原理分析³ | 从零实现一个自定义 Runnable 模块
langchain
喵王叭9 小时前
【大模型核心技术】Agent 理论与实战
人工智能·langchain
Nero9 小时前
SpringBoot对接LangChain4J四件套
langchain
TechCampus10 小时前
Langchain4j + Flux 实现高可用 LLM 流式对话系统 教你如何接入AI
langchain
都叫我大帅哥11 小时前
幽默深度指南:LangChain中的RunnableParallel - 让AI任务像交响乐团般协同工作
python·langchain·ai编程
GetcharZp1 天前
别再只知道 ChatGPT 了!用 LangChain 撸了个“AI 智能体”,自动化工作不是梦!
langchain·agent