LangChain 深度解析:协议标准与生态包的分工
本文最后更新于28 天前,其中的信息可能已经过时,如有错误请发送邮件到big_fw@foxmail.com

Langchain是什么?

简单来说,LangChain 是一个专门为构建大语言模型(LLM)应用而设计的开源框架。

如果把大模型(如 GPT-4)比作一个功能强大的“大脑”,那么 LangChain 就是这个大脑的“神经系统”和“手脚”。它负责连接外部数据源、记忆对话历史、并编排复杂的业务逻辑。

在 LangChain 出现前,LLM 应用开发极其混乱:每个模型(OpenAI、Anthropic、Llama、Qwen)的 API 格式、参数、返回值都不一样、每个向量库(Pinecone、Chroma、FAISS)的查询、写入接口完全不同,记忆、工具、链的写法五花八门,换一个组件就要重写大量代码,今天用 GPT,明天想换 Claude,几乎要重构整个项目。

LangChain 的核心哲学是抽象,它试图通过统一的接口来包装一切:模型、向量数据库、内存管理等。

LangChain 能做到这些核心靠的就是统一抽象层和标准化接口,所有底层组件都遵循同一套调用规范,不管内部实现多不一样,对外暴露的方法完全相同。

它先定义好基础抽象类,比如基础语言模型、基础向量库、基础检索器、基础记忆、基础工具这些父类,规定好必须实现的方法,比如调用、生成、查询、保存上下文等,上层应用代码永远只面向这些统一的抽象接口,编程不直接调用第三方原生 API,所以切换模型或向量库时,只需要替换实例化的对象,业务逻辑完全不用改,调用方式始终一致。

不过LangChain 早期为了追求“大而全”,将过多的集成直接放进了核心库。所以在过去,Langchain被诟病是一个“臃肿的黑盒”,所有的模型集成、工具和逻辑都混在一起。

为了解决这个问题,它进化成了基于 ABC(抽象基类)构建的多态系统

Langchain的三层架构

1.Core 层(协议层)

包名: langchain-core

2.Partner/Community 层(实现层)

本质: 各种第三方插件。

实现: Partner: 官方维护的高质量集成(如 langchain-openai, langchain-anthropic)。

3.LangChain / LangGraph 层(应用层)

利用 Core 定义的协议,把 Partner 提供的具体工具串联起来。

在理解了 LangChain 的三层宏观架构后,你可能会产生一个疑问:既然有了统一的协议,为什么我们在安装和使用时,会看到这么多前缀不同的 Python 包?

这其实是 LangChain 为了解决底层协议稳定性上层集成多样性之间矛盾的工程方案。为了实现这种“即插即用”的灵活性,LangChain 将其功能拆分成了四个定位鲜明的核心包:

Langchain的四个主要核心包

一.Langchain_core

要介绍langchain_core,我们应该从它的四大核心组件说起

(1)Runnable 接口

要讲好runnable,首先我们要知道,我们为什么需要runnable?

正如我们所说,在 Runnable 协议出现之前,LangChain 的组件调用逻辑其实是一片混乱的,在开发流程中,我们可以把AI回答问题的程序想成三步。

第一步:PromptTemplate:将提前写好的问话模板,比如用户问题、填入一个固定的格式里,就要用.format()方法

第二步:LLM / ChatModel 就是真正的大模型,它拿到填好的问题去生成回答,它要用 .predict ()或 .generate() 才能跑

第三步:OutputParser:把模型返回的乱七八糟的文字提炼成你想要的干净结果,比如只提取答案,去掉多余话,它必须用 .parse() 才能生效

如果你想把这三个步骤连起来,必须手动写大量的“胶水代码”来处理中间变量。一旦中间某个环节想从“同步”改成“异步”,或者想增加“流式输出”支持,就必须把整段逻辑重写一遍。

langchain-core 引入 Runnable 的核心目的,就是通过接口抽象实现多态。它强制要求所有核心组件(提示词、模型、索引器、解析器)都必须继承自 Runnable 类,并实现统一的暴露方法。这意味着:

在编程里,如果一个组件“继承”了某个类,就代表它承诺了:“我会这个类所有能干的事”,之所以能做出这种“承诺”,是因为继承在编程里构建了一种 “父子关系”,当你写下 class Model(Runnable): 时,你实际上是在告诉计算机:

“把 Runnable 拥有的所有代码(方法、属性),原封不动地复制一份给 Model。”

由于 Model 拿到了 Runnable 的所有代码,它自然就具备了处理 .invoke().stream() 的能力。

强制要求提示词(Prompt)、模型(Model)、解析器(Parser)等所有组件都必须具备以下这几种统一的方法:

.invoke():同步调用。

.ainvoke():异步调用。

.stream():流式输出。

.batch():批量处理。

强制继承最核心的目的是为了实现 LCEL (LangChain Expression Language)。 当大家都共用了同样的接口(输入/输出规范),框架就可以用管道符号 | 把它们像乐高积木一样串联起来

Prompt | Model | Parser

当没有继承时,可能需要手动写代码:parser(model(prompt(input))),且每个函数的调用方式可能都不一样。但有了runnable继承后,框架不需要关心这个组件是“提示词”还是“模型”,它只知道这是一个 Runnable。既然是 Runnable,它就一定能接收上一个组件的输出,并把自己的结果传给下一个。

Standard Interface

runnable由四个核心组件组成

1.invoke

这是最基础的单元。在计算机科学中,invoke 指的是激活一个程序实体的过程。它不仅仅是简单的“跳转”到某行代码,通常还涉及上下文准备、参数传递和控制权转移,

在高级编程语言中,invoke 往往与反射(Reflection)相关。当你不知道某个类具体的函数名,但在运行时动态获取并执行它时,就会使用 invoke

在没有 Runnable 之前,不同的库调用方式五花八门(有的叫 .predict(),有的叫 .generate(),有的直接用 ()),invoke 的最大价值在于确定性。编写脚本、单次调试或处理严密的顺序逻辑时,invoke 保证了“A 完才能 B”的确定路径,符合人类处理任务的原始直觉——“我把这件事交给你,我等着你做完,然后我再继续”

2.ainvoke

在 AI 应用的实际运行中,90% 的执行时间都消耗在“等待网络传输”上。无论是调用 OpenAI 的 API,还是访问向量数据库,这本质上都是 I/O 密集型任务。如果你使用传统的 invoke 顺序执行 10 个请求,程序会产生阻塞排队,总耗时将等于这 10 次请求时间的物理叠加。

ainvoke 的底层依托于 Python 的 asyncio 库。它的执行逻辑不再是“等待返回再执行下一行”,而是状态通知机制,当程序执行到 await ainvoke() 时,它向底层操作系统发出 I/O 请求。它会立即告诉 CPU:“请求已发出,在数据返回之前,你可以去处理内存中的其他协程任务。”一旦网络数据包返回,事件循环会唤醒该任务,继续执行后续逻辑。

我们可以通过对比来看看两者的不同之处

维度invoke (同步)ainvoke (异步)
并发能力受限于线程池大小,容易因长文本生成导致线程耗尽。可以在单线程内维持成千上万个并发连接。
服务器响应整个进程/线程被卡死,新请求无法进入。服务器保持活跃,能持续接收并调度新请求。
硬件效率CPU 在等待网络时处于空转状态。CPU 利用率更均衡,能见缝插针处理其他任务。

我们以调用一个大语言模型(LLM)为例,来看看invoke和ainvoke的区别

无论是同步还是异步,定义模型和 Prompt 的方式是一样的:

from langchain_openai import ChatOpenAI 


from langchain_core.prompts import ChatPromptTemplate 


# 初始化模型 
model = ChatOpenAI(model="gpt-3.5-turbo") 

prompt = ChatPromptTemplate.from_template("请用一句话简述什么是 {topic}") 

# 组合成一个简单的链 (Chain) 

chain = prompt | model
def run_sync_task():
    print("--- 正在执行同步调用 ---")
    
    # 直接调用,代码运行到这里会阻塞(卡住),直到模型返回结果
    response = chain.invoke({"topic": "量子力学"})
    
    print(f"模型回复: {response.content}")
    print("--- 任务结束 ---")

run_sync_task()

而在 Web 异步框架中使用 ainvoke

import asyncio 
async def run_async_task(): 
    print("--- 正在发起异步请求 ---") 
# 使用 await 挂起当前协程,CPU 可以去处理其他并发进来的用户请求 
# 此时服务器并不会卡死 
response = await chain.ainvoke({"topic": "深度学习"}) 
print(f"异步模型回复: {response.content}") 
# 模拟并发环境运行 
asyncio.run(run_async_task())
3.stream / astream

这是目前大模型应用中最关键的用户体验优化手段,传统的 invoke 必须等待模型完成所有的推理计算(例如生成 500 个 Token),然后一次性将完整的字符串返回。

stream 返回的是一个生成器对象,客户端不再等待 HTTP 请求结束,而是持续监听数据流。虽然模型生成完整回答的总时间(Total Latency)没有改变,但 TTFT(首字延迟) 得到了质的提升。

在网络条件良好的情况下,用户通常在 500 毫秒内就能接收到第一批字符,长时间的同步 invoke 请求容易触发 Nginx 或浏览器端的 60 秒请求超时(Timeout),而流式传输通过持续的数据传输保持连接活跃。

4. batch / abatch

在完成了同步、异步、流式的演进后,batchabatch 解决了“规模化处理”的问题。

如果你有 100 篇文档需要总结,使用 for 循环配合 invoke 是极低效的:程序会等待第一篇完成,再开始第二篇,总耗时是单次时间的累加。batch 系列功能则将这种串行逻辑重构为并发分发

batch 并不是简单的循环,它在底层实现了一套任务调度机制,接收一个输入列表(List),并为每个输入项创建一个独立的执行单元。

batch (同步版):内部自动利用线程池(ThreadPoolExecutor),在多个线程中同时发起多个同步请求。

abatch (异步版):利用 asyncio.gather,在单个事件循环中并发发出所有异步请求。

既然我们已经理解了 Runnable 是如何给每个组件发了一张“通行证”,也就是:这些互不相识的组件,是怎么通过一个竖线 | 就瞬间“看对眼”并合体的?

LCEL 的“管道符”原理

关于管道符“|“,我在上一篇文章也有所提及

管道符并不是简单的执行,而是将多个 Runnable 对象封装进了一个名为 RunnableSequence 的容器中。

由于所有组件都遵循相同的 invoke 协议,RunnableSequence 只需要按顺序把上一个组件的输出丢给下一个组件的 invoke 即可。

Runnable 协议的引入,标志着 LangChain 从一个“库”进化成了一套“开发框架”,需要一步步指导计算机“先格式化、再调用模型、最后解析结果”变为只需要定义“数据流的走向”。

在工程实践中,Runnable 解决了调用接口(方法名、异步、流式)的统一,但 AI 领域存在一个核心矛盾:数据结构的代差

旧时代的 LLM(如 GPT-3 Davinci)接受的是纯文本(String),而现代的 Chat Model(如 GPT-4, Claude)接受的是结构化的消息列表(List of Messages)。

而这就要引出Langchain_core第二个核心体系:

(2)Prompt 体系

BasePromptTemplate 通过了技术手段,利用多态性抹平了这种差异

但想要理解promptvalue如何作为中间人抹平差异,我们必须深入底层看一看:

这些提示词模板内部,究竟是如何通过不同的多态逻辑,产出完全不同形态的数据的?

在 LangChain 的世界里,提示词模板被分成了两大技术流派。虽然它们都继承自 BasePromptTemplate 并遵循 Runnable 协议,但内部的“装配逻辑”截然不同。

A. 字符串派系:StringPromptTemplate(文本驱动)

这一派系的历史最悠久,核心逻辑是文本拼接。它的目标是生成一串连续的 Raw String,目标非常明确,就是将结构化的输入数据,通过预定义的逻辑,最终渲染为一段纯文本(Raw String)

它定义了一个核心契约(Interface):无论内部逻辑多复杂,外部调用者只需传入变量,它必须返回一个字符串。子类通过重写 .format(**kwargs) 方法来实现不同的拼接逻辑。

使用 Python 的 str.format 机制,通过正则表达式或简单的占位符替换(如 {variable}),将输入映射到模板字符串。当它运行程序时,它并不自己处理每一个示例,而是通过一个循环,多态地调用内部子模板的 format 方法。

这就是组合模式 (Composition)。父模板负责外层框架(前缀、后缀、分隔符),子模板负责具体示例的格式。无论子模板多复杂,只要它能返回字符串,父模板就能把它拼进最终的文本大蛋糕里。

B. 消息派系:BaseChatPromptTemplate(角色驱动)

在理解了文本驱动的 StringPromptTemplate 后,进入 BaseChatPromptTemplate 派系(消息派系),逻辑从“字符串拼接”转向了“对象转换与结构化管理”。

这一派系是为现代对话模型(如 GPT-4、Claude)设计的,它不再追求产出一段长文本,而是产出一个消息对象列表(List[BaseMessage])

在消息派系中,输入变量(如 {question})的处理逻辑不再是简单的占位符替换,而是一种类型包装

比如,当你输入{“user_input”: “你好”}
ChatPromptTemplate 内部的子模板会将这些数据识别并封装进具有“角色属性”的容器中。
输出的不再是String,而是[SystemMessage(...), HumanMessage(...)] 这种结构化的对象数组。

ChatPromptTemplate 实际上是一个消息模板的管理器。它内部维护着一个 List[BaseMessagePromptTemplate]。这种设计的精妙之处在于:不同的子类负责不同的角色映射逻辑。

每个组件都实现了统一的接口,但产出的对象类型不同:

  • SystemMessagePromptTemplate:重写逻辑,确保产出的是 SystemMessage(通常用于设定 AI 的人格或背景)。
  • HumanMessagePromptTemplate:重写逻辑,产出 HumanMessage(代表用户的提问)。
  • MessagesPlaceholder:这是一个特殊的占位符组件,它不产出单一消息,而是动态地将一段已有的对话历史(List)插入到当前位置。

但是在实际应用中,经常会出现很多问题,比如如果下游模型只收字符串,而我用了 ChatPromptTemplate,程序会崩溃吗?

其实答案是不会的,因为所有模板类都会返回一个多态容器力————PromptValue

在 LangChain 的工程架构中,PromptValue 的存在并不是为了增加复杂度,而是为了解决软件工程中一个经典的难题:多对多适配的爆炸性增长

如果没有这个中间层,当你有 5 种不同的 Prompt 表达方式和 5 种不同的模型接口时,你需要编写 5 * 5 = 25种适配逻辑。而 PromptValue 的出现,将这个问题简化为了 5 + 5 = 10

在设计 langchain-core 时,架构师遵循了一个核心原则:Prompt 模板不应该知道它后面接的是什么模型。

如果 Prompt 直接输出字符串,那么 Chat Model(如 GPT-4)就得被迫写一段逻辑把字符串拆成消息;如果 Prompt 直接输出消息列表,那么老牌 LLM(如 Llama-1)就得想办法把列表拼回字符串。

PromptValue 就像一个“万能转换插头”: 无论前端输入的是“英标”的纯文本,还是“美标”的角色消息,它都会被封装进 PromptValue 这个标准盒子里。这个盒子不预设立场,它只是静静地待在那里,等待下游模型来“取电”。

PromptValue 本质上是一个具备自我转换能力的抽象类。它强制要求所有实现类必须提供两个“插孔”:

  1. .to_string():不管内部存的是什么,强行挤压成一串纯文本。
  2. .to_messages():不管内部存的是什么,强行包装成一个 BaseMessage 列表。
场景 A:当它遇到文本模型(LLM)

PromptValue 被传递给一个 BaseLLM 对象时,模型会调用 .to_string()

  1. 如果是 StringPromptValue:直接返回那串字符。
  2. 如果是 ChatPromptValue:它会按照特定的格式(如 Human: ... \nAI: ...)自动把消息列表串起来。
场景 B:当它遇到对话模型(ChatModel)

当同样的 PromptValue 被传递给 BaseChatModel 时,模型会调用 .to_messages()

  1. 如果是 ChatPromptValue:直接返回消息列表。
  2. 如果是 StringPromptValue:它会自动把那一串纯文本包进一个 HumanMessage 对象里。
两大核心方法

1.StringPromptValue

当你使用传统的 PromptTemplate 时,产出的是 StringPromptValue。它内部存储的是一个简单的 text: str

  • to_string() 逻辑:极其简单,直接返回内部的 text
  • to_messages() 逻辑(自动包装): 为了兼容现代对话模型,它必须把一段孤零零的文本变成一个“对话”。底层逻辑是:将这段文本直接封装进一个 HumanMessage 对象中
    转换结果[HumanMessage(content="你的文本内容")]这体现了多态的默认行为:如果只有一段话,那它默认就是用户(Human)说的话。

2.ChatPromptValue

ChatPromptValue 内部存储的是 messages: List[BaseMessage]

to_messages() 逻辑:直接返回内部的消息对象列表。

to_string() 逻辑(核心拆解): 为了兼容那些只认字符串的老模型,它必须把带有角色(System, Human, AI)的消息列表“拍扁”。 它的底层逻辑通常是角色标签化拼接

  1. 遍历消息列表中的每一个 BaseMessage
  2. 识别消息类型:如果是 SystemMessage,前缀加 System: ;如果是 HumanMessage,前缀加 Human:
  3. 用换行符 \n 将它们连接起来。

转换结果System: 你是一个助手\nHuman: 你好\nAI: 你好!有什么可以帮您?

模型是如何选择的?我们在这模拟一个 RunnableSequence 的运行逻辑:

模拟runnablesequence运行逻辑

当你在代码里写下 chain = prompt | model 时,你实际上创建了一个 RunnableSequence。其实这并不仅仅是把数据 从左边传到右边,而是经过了三个阶段:

一.Prompt 的“标准化输出”,数据存储状态:结构化保留

在调用 prompt.invoke(input) 后,系统并没有执行最终的格式化字符串操作,也没有直接实例化消息列表。它返回的是一个 PromptValue 抽象类的实现实例(如 StringPromptValueChatPromptValue)。

这个阶段的核心意义在于:延迟数据格式的最终确定

PromptValue 并不是一个简单的数据持有者(Data Holder),它是一个行为持有者。它强制要求其实例必须具备两种转换能力

prompt.invoke 执行完毕,数据被锁定在 PromptValue 实例的内部属性中。

如果是 ChatPromptValue:它内部存储的是一个 List[BaseMessage]。这意味着系统在这一阶段完整保留了消息的角色(System, Human, AI)和元数据。

如果是 StringPromptValue:它内部存储的是一个经过变量填充后的 str

此时,数据被封装在一个“集装箱”里,它既保留了消息的结构化信息,也具备了随时变身的能力

二.Model 的“类型感知”

Model.invoke(input) 执行的初期,基类会检查 input 的类型。由于 PromptValue 遵循了统一的协议,模型不需要知道这个输入具体是 ChatPromptValue(来自对话模板)还是 StringPromptValue(来自普通文本模板)。

模型只识别一个事实:输入对象是否拥有 to_stringto_messages 方法

根据模型的不同派系,转换逻辑分为两条路径:

A. LLM 派系(纯文本模型,如早期 GPT-3)

这类模型(继承自 BaseLLM)底层 API 只接受单一字符串。

  • 动作:模型主动调用 input.to_string()
  • 多态表现
    • 如果输入是 ChatPromptValue:它会在内部启动“拉平”逻辑,将 SystemMessageHumanMessage 等对象依次转换,拼接成类似 System: ...\nHuman: ... 的长字符串。
    • 结果:结构化的对话历史被降维成一段纯文本,以适配老旧模型的输入限制。

B. ChatModel 派系(聊天模型,如 GPT-4, Claude)

这类模型(继承自 BaseChatModel)底层 API 预期的是结构化的角色消息序列。

  • 动作:模型主动调用 input.to_messages()
  • 多态表现
    • 如果输入是 ChatPromptValue:它直接返回内部维护的 List[BaseMessage],保持数据的原始结构(包括 Role 和 Content)。
    • 如果输入是 StringPromptValue:它会将那段纯文本自动包装进一个 HumanMessage 对象中,返回一个单元素的列表。
    • 结果:确保发送给 API 的数据始终是一个符合规格的消息数组。

它不仅解决了“消息转文本”的问题,也解决了“文本转消息”的问题。如果你用一个简单的 PromptTemplate(产出 StringPromptValue)去对接 ChatModelto_messages() 会自动把那段文本包装成一条 HumanMessage

既然 langchain-core 通过 PromptValue 抹平了输入的差异,那么当模型执行完 invoke 后,它返回给我们的又是什么呢?

这里体现了 LangChain 接口抽象的另一个极致:输出的标准化

(3) Model 体系

langchain-core 中,模型被分为两大阵营:BaseLLM(纯文本)和 BaseChatModel(对话)。虽然我们讲了它们如何“收货”,但真正体现 core 层价值的是它们如何“发货”。

无论你调用的是 OpenAI 的 gpt-4o 还是本地的 Llama-3,经过 langchain-core 的封装,它们在 invoke 之后的返回值都被强制统一了。

对于 ChatModel:返回的是一个 BaseMessage 对象(通常是 AIMessage)。它不只是一串字符:它是一个包含 .content(文本)、.response_metadata(Token 使用量、停止原因等)和 .tool_calls(工具调用指令)的多维数据包

至此,我们已经完成了一个完美的闭环:Prompt 把变量变成了模型能理解的 PromptValue,而 Model 则吐出了标准化的 BaseMessage

但这里隐藏着一个工程上的尴尬:大模型本质上是一个“概率预测机器”,它吐出来的永远是概率最高的文本

如果你直接把 BaseMessage.content 扔给下游的业务逻辑,你的程序大概率会因为 AI 多说了一句“好的,这是你要的结果”而直接崩溃。

这就是 langchain-core 必须存在的第四大支柱——OutputParser(输出解析器)

(4)OutputParser体系

由于 BaseOutputParser 也是 Runnable 的子类,它天然具备了与 PromptModel 同等的地位。这种接口一致性允许你使用管道符 | 实现极致简洁的逻辑串联:

chain = prompt | model | parser

在这种链式写法下,当你调用 chain.invoke() 时,模型输出的结果会自动“流”进解析器。你不再需要手动提取 response.content,解析器会自动接管后续的所有清洗与转换工作。

作为 Runnable 序列中的下游节点,解析器的输入规格(Input Schema)是高度明确的:它接收的是 BaseMessage(或其子类如 AIMessage)。

无论模型返回的是一段 JSON 字符串、一个 Markdown 表格,还是简单的纯文本,这些内容都被封装在 BaseMessage 对象中。解析器通过多态机制,从这个标准对象中提取出 content 字段,作为其处理的原始素材。

虽然输入是固定的 BaseMessage,但解析器的输出规格(Output Schema)则是完全开放的 Any。这意味着解析器承担了“格式坍缩”或“数据升维”的任务:

字符串化StrOutputParser 将消息还原为纯粹的 str

结构化JsonOutputParser 将文本解析成 Python 的 dictlist

对象化PydanticOutputParser 将零散的信息映射为强类型的 Pydantic 模型。

二.Langchain-community

langchain-community 是 LangChain 生态中规模最庞大、更新最频繁的包。它的核心任务是:利用 core 定义的抽象基类,对数以百计的外部 SDK 进行“标准化包装”

在源码层面,它几乎涵盖了 AI 开发所需的所有外部基础设施:

  1. LLMs/ChatModels:集成了非主流或国产大模型(如 Baidu Qianfan, ZhipuAI, Ollama)。
  2. VectorStores:对 50+ 种向量数据库(FAISS, Chroma, Milvus)的统一 API 实现。
  3. Retrievers:各种检索逻辑,如 ElasticsearchRetrieverWikipediaRetriever
  4. Tools:让 Agent 能够执行的操作,如 GoogleSearchAPIWrapperSQLDatabase 交互工具。

在实际开发中,你几乎不可能只用一个模型。你可能在测试阶段用 Ollama 跑本地模型省钱,在生产环境换成 Claude文心一言,你写的 chain = prompt | model | parser,其中的 model 无论是来自 langchain_community.chat_models.ChatOllama 还是 ChatZhipuAI,其调用方法、流式输出、甚至异常处理都被标准化了,它提供了对 OllamaLlama.cpp 等本地推理引擎的极佳封装,让你可以零成本在本地私有化部署

LLM 的大脑再强,如果没有外部数据(RAG),它就会“一本正经地胡说八道”。community 在这里提供了极其庞大的数据搬运工具

document_loaders 目录下有上百个类。你可以一键加载 PDFNotion 笔记B站评论GitHub 仓库、甚至是 飞书/钉钉文档,如果你想把数据存起来,communityFAISS(轻量本地搜索)、Chroma(嵌入式数据库)提供了原生支持。你不需要去学这些数据库复杂的原生 API,只需调用 vectorstore.add_documents() 即可

在使用 community 库时,一定要注意延迟安装。因为这个库太大了,LangChain 采用了“按需安装”策略。当你 from langchain_community.xxx import yyy 时,如果本地没装对应的第三方 SDK,它会直接报错并给出明确的安装命令(如 pip install ollama)。

三.langchain-partners

如果说 community 是社区大杂烩,那么 partners 就是官方精选店。它是 LangChain 官方为了解决“代码质量”与“维护效率”矛盾而专门划分出来的独立集成包体系

在早期,所有的第三方插件都塞在 community 里,有些插件是开发者业余时间写的,API 变了没人管,用户用起来到处是 Bug,即使你只用 OpenAI,装包时也可能因为其他几百个插件的依赖关系导致版本冲突,不同插件的参数命名、报错信息不统一,完全不像一个整体

为了解决这些问题,LangChain 联合主流厂商(OpenAI, Anthropic, Google, Mistral 等)推出了 Partner Packages

langchain-partners 并不是一个单独的包,而是一系列以 langchain-{provider} 命名的独立 Python 包(例如 langchain-openai

如果你的模型/工具在 Partner 名单里(如 OpenAI, Anthropic, Google, Groq, Mistral, Pinecone),必须安装对应的独立包。

示例:from langchain_openai import ChatOpenAI

次选 Community 包:如果你用的是国产模型(如文心、通义)或者一些较冷门的工具,再去 community 里找。

示例:from langchain_community.chat_models import ChatZhipuAI

四.langchain

最后我们聊聊这个名字最正统,但在现代架构中地位最“微妙”的包:langchain

在 LangChain 早期,这个包就是全部。但随着 V0.2/V0.3 版本的架构大手术,它的定位从“核心”变成了一层“胶水(Glue Layer)”

它的存在是为了提供高层级的抽象函数。如果你需要构建一个标准的 RAG(检索增强生成)应用,你可以不从 Runnable 开始手搓逻辑,而是直接调用 langchain.chains 模块

create_retrieval_chain:这是一个内置函数,它封装了“用户输入 -> 检索文档 -> 格式化 Prompt -> 模型调用 -> 输出解析”的全过程。

create_history_aware_retriever:专门处理带对话历史的检索逻辑,自动将用户的追问改写为独立的搜索查询。

虽然目前复杂的 Agent 逻辑正在向 LangGraph 迁移,但基础的智能体运行逻辑仍然保留在 langchain.agents 模块中

大厂为了工程化和可维护性依旧是这个包的主力受众,很多初创公司如果需要在一周内上线一个 RAG 机器人。也会直接调用 create_retrieval_chain,不过根据2026最新数据,langchain-core 的月下载量已经突破千万级别,远超主包,这说明大家正在从“全家桶”转向“按需取用”

须明确,我们整篇文章讨论的所有包名、依赖关系以及源码实现,全部是基于 Python 版本的 LangChain。虽然 JavaScript 版本在设计哲学上(如 Runnable 协议)保持了一致,但 Python 版在多包管理、异步协程处理(asyncio)以及对国产大模型插件的适配上,有着自己非常独立且复杂的生态实现。

虽然其设计哲学与 JS 版本互通,但在具体的异步生态和包分发机制上有着本质区别。理清这四层包的协作关系,是 Python 开发者从手错脚本进化为构建工业级 LLM 应用架构师的必经之路。

文末附加内容

评论

  1. 小白
    Windows Edge
    3 周前
    2026-4-03 20:28:20

    嘿嘿

    • 博主
      小白
      Windows Chrome
      3 周前
      2026-4-03 20:28:38

      谁准你嘿嘿

  2. 小白
    Windows Edge
    3 周前
    2026-4-03 20:28:53

    哈哈

    • 博主
      小白
      Windows Chrome
      3 周前
      2026-4-03 20:29:13

  3. 小白
    Windows Edge
    3 周前
    2026-4-03 20:30:12

    撒西不理

    • 博主
      小白
      Windows Chrome
      3 周前
      2026-4-03 20:30:58

      你继续伪装小白!

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇