引言:为什么要用 LangChain?
随着大语言模型(LLM)的蓬勃发展,开发者面临着 API 标准各异、模型缺乏外部知识、提示词管理困难等现实痛点。LangChain 由此诞生。它本身并非是一个新的大模型,而是一个标准化的 LLM 应用开发框架。它通过为各种 LLM 提供统一的接口,将模型、提示词、数据源、记忆体等组件标准化地"串联"起来,快速构建复杂的生成式 AI 应用。
本文将抛开概念堆砌,从核心组件解析、重构后的实战代码、工程化架构逻辑三个维度,系统地带您掌握 LangChain 的开发内功。
一、解密 LangChain 的六维组件架构
一个成熟的 LangChain 应用,依赖六大核心组件各司其职:
-
Models(模型):屏蔽底层 API 差异,提供统一的模型调用入口。
-
Prompts(提示词):管理输入大模型的提示内容,支持结构化和复用。
-
Chains(链):将模型、提示词、甚至其他链连接成一条执行流水线。
-
Agents(代理):让大模型化身为"智能大脑",自主决定是否调用外部工具。
-
Memory(记忆):突破大模型无状态限制,存储和读取短期或长期的对话记录。
-
Indexes(索引):赋予大模型处理非结构化外部私有数据的能力,这是 RAG 技术的基石。
二、模型层(Models):统一接口与消息结构
在工程实践中,我们常常需要切换不同的模型(如从通义千问切换到百川大模型)。LangChain 的模型层提供了统一的调用接口。
1. LLMs 与 Chat Models
目前大模型主要以两种形式提供服务:
-
LLMs:输入一串纯文本,输出也是一串纯文本。
-
Chat Models :输入是有结构的聊天消息列表 ,输出也是特定的消息对象。这一点非常重要,它直接对应了多轮对话上下文记忆的数据结构。
实战代码 :
我们使用 ChatOpenAI 适配国内主流通义千问模型进行演示。通过 stream 方法实现类打字机的流式输出。
python
import os
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, AIMessage, SystemMessage
llm = ChatOpenAI(
model="qwen-max",
api_key=os.getenv("ALIBABA_API_KEY"),
base_url=os.getenv("ALIBABA_BASE_URL")
)
# 1. 流式聊天模式
for chunk in llm.stream("请用一首五言绝句描述春日的生机。"):
print(chunk.content, end="")
# 2. 多轮对话的消息结构
messages = [
SystemMessage("你是一个精通金融知识的资深顾问。"),
HumanMessage("我对今年黄金的走势很关心,有什么建议吗?"),
AIMessage("黄金通常被视为避险资产,受美元走势和地缘政治影响较大。建议关注美联储的利率决议。"),
HumanMessage("那在黄金投资中,我需要注意哪些风险?"),
]
response = llm.invoke(messages)
print(f"\n[上下文推理结果]:\n{response.content}")
(注:上述代码通过向模型传入 HumanMessage 和 AIMessage 的交替列表,完美复现了多轮对话的历史上下文环境。)
2. 文本嵌入模型(Embeddings)
嵌入模型用于将人类语言转换为高维浮点数向量。这些向量之间的距离代表语义的相似度,是实现检索增强生成(RAG)和数据去重的基础。
python
from langchain_community.embeddings import DashScopeEmbeddings
embeddings = DashScopeEmbeddings(
dashscope_api_key=os.getenv('ALIBABA_API_KEY'),
model="text-embedding-v1"
)
vector = embeddings.embed_query("大语言模型正在改变各行各业的开发范式。")
print(f"生成的向量维度为:{len(vector)}")
三、提示词层(Prompts):让大模型"听懂"任务
想要模型输出准确,提示词结构必须严谨。LangChain 提供了两种核心的提示词构建范式:零样本(Zero-shot)和少样本(Few-shot)。
1. 少样本提示(Few-shot)的原理剖析
在构建少样本提示词时,标准的结构化公式是: 前缀(角色与任务) + 示例对(输入与输出) + 后缀(当前用户输入)。
这种设计可以极大地激发大模型的推理和模仿能力。我们重构一个寻找同义词的业务场景:
python
from langchain_core.prompts import PromptTemplate, FewShotPromptTemplate
# 定义基础示例
examples = [
{"word": "开心", "synonym": "愉悦"},
{"word": "震撼", "synonym": "震惊"},
{"word": "珍贵", "synonym": "宝贵"},
]
example_prompt = PromptTemplate(
input_variables=["word", "synonym"],
template="原词:{word}\n近义词:{synonym}\n"
)
# 组装少样本模板
few_shot_prompt = FewShotPromptTemplate(
examples=examples,
example_prompt=example_prompt,
prefix="你是一个中文词汇专家,请参考以下示例,准确找出给定词汇的近义词。",
suffix="原词:{input}\n近义词:",
input_variables=["input"]
)
final_prompt = few_shot_prompt.format(input="辉煌")
print(final_prompt)
# 执行后,控制台将清晰展示【前缀】+【示例列表】+【当前输入】的完整拼接结构。
四、链式调用层(Chains):构建复杂业务流
在复杂的业务逻辑中,往往需要大模型执行**"先写一篇文章,再从中提炼摘要,最后翻译成英文"**的流程。LangChain 的 Chain 组件就是处理这类任务的"流水线"。
工程化语法演变 :经过框架版本迭代,现在推荐使用 |(管道符) 来组合链。这种 Linux 管道风格的写法,极大地提升了代码的可读性和解耦性。
python
from langchain_core.prompts import PromptTemplate
from langchain_core.output_parsers import StrOutputParser
# 第一步链:生成内容
prompt1 = PromptTemplate.from_template("请根据主题 {topic},写一篇 80 字左右的科普短文。")
# 第二步链:提炼标题
prompt2 = PromptTemplate.from_template("根据上面的短文内容:\n{content}\n请提炼一个不超过 8 个字的标题。")
# 组合链条
chain = prompt1 | llm | prompt2 | llm | StrOutputParser()
# 只需要传入初始的 {topic} 变量,后续的 {content} 会自动由上一步的输出传递
result = chain.invoke({"topic": "5G 通信技术的优势"})
print(result)
五、代理层(Agents):大模型的"手脚"进化
大模型本身存在知识截止日期、无法进行复杂数学计算、无法查询实时天气等严重局限。Agents(代理) 赋予了大模型调用外部工具的能力。
1. 代理的决策流程
实践中,代理的执行通常包含四个步骤:
-
用户请求(如:"帮我计算 24 × 12 的结果,顺便查一下上海今天的天气。")
-
大模型"大脑"解析:判断需要调用两个工具,并解析出工具所需的参数。
-
执行工具调用:LangChain 执行计算函数和天气 API 请求。
-
集成回复:大模型拿到工具返回的数据,将其合成为流畅的人类语言。
2. 自定义工具(@tool 装饰器)
我们可以用 @tool 装饰器将普通的 Python 函数直接封装成 LangChain 可识别的工具,并自动提取函数的参数说明。
python
from langchain.tools import tool
from langchain.agents import create_agent
# 定义自定义工具1:数学计算
@tool
def calculator(a: int, b: int) -> int:
"""用于计算两个整数的乘积。"""
print(f"正在调用数学工具:{a} × {b}")
return a * b
# 定义自定义工具2:模拟天气预报
@tool
def weather_lookup(city: str) -> str:
"""获取指定城市的实时温度(模拟数据)。"""
temp_map = {"北京": 25, "上海": 29, "深圳": 33}
temp = temp_map.get(city, 23)
return f"{city} 当前气温为 {temp} 摄氏度。"
# 构建代理
agent = create_agent(
model=llm,
tools=[calculator, weather_lookup],
system_prompt="你是一个多面手助手,可根据用户指令自主决定调用数学或天气工具。"
)
# 用户一次性提出复合指令
user_msgs = {"messages": [{"role": "user", "content": "帮我算 15 * 8 的结果,然后查一下北京今天的气温。"}]}
for step in agent.stream(user_msgs):
print(step)
# 流式输出将清晰展示:解析工具 -> 执行工具 -> 最后回答的完整过程。
六、记忆层(Memory):突破"瞬忘"的魔咒
大模型的底层接口本身是无状态的,如果不做特殊处理,每次提问都是独立的。因此,记忆层是构建对话机器人的核心支柱。
1. 短期记忆
ChatMessageHistory 提供了一个简单的列表容器,存储当前运行环境内的消息记录。
2. 长期记忆(数据库持久化)
在实际商业化应用中,我们需要将不同用户的对话保存在数据库中。LangChain 结合 langgraph 框架提供了检查点(Checkpoint)机制 。我们通过设置 thread_id(用户会话ID),可以将对话状态持久化到 MySQL 等数据库中。
(注:下面的代码使用内存代替数据库进行模拟,其原理与挂载 MySQL 检查点完全一致)
python
from langgraph.checkpoint.memory import InMemorySaver # 此处用内存模拟数据库持久化
# 初始化持久化检查点
checkpointer = InMemorySaver()
agent_memory = create_agent(model=llm, tools=[], checkpointer=checkpointer)
# 模拟用户1的独立会话 ID
user1_config = {"configurable": {"thread_id": "user_001"}}
# 第一轮对话:存储用户信息
agent_memory.invoke(
{"messages": [{"role": "user", "content": "我叫李雷,今天第一次接触LangChain。"}]},
config=user1_config
)
# 第二轮对话:读取历史上下文
response = agent_memory.invoke(
{"messages": [{"role": "user", "content": "还记得我叫什么名字吗?我在学什么?"}]},
config=user1_config
)
print(response['messages'][-1].content) # 输出结果将完美回忆起上一条信息
七、索引层(Indexes):搭建本地 RAG 知识库
RAG(检索增强生成)是当前大模型落地最火热的方向,它允许大模型回答它在训练数据中没见过的"企业私有知识"。
1. 核心步骤:文档加载与语义分割
在工程实践中,当面对数十页的 PDF 或 TXT 文档时,直接喂给大模型会导致 Token 溢出 。因此必须使用文本分割器。
需要重点强调的是 :普通的 CharacterTextSplitter 会按指定的字符长度生硬截断,极易将一段完整的话切为两半。业界标准做法是使用 RecursiveCharacterTextSplitter(递归字符分割器)。
-
它会优先尝试使用段落符(
\n\n)切割。 -
如果切出来的片段过长,再尝试用句号(
。)切割。 -
如果还过长,再降低到逗号(
,),直到切分出的文本块足够短且语义完整。
python
from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
# 1. 加载本地文本文件
loader = TextLoader("./data/rag_sources.txt")
documents = loader.load()
# 2. 递归分割器(保证语义最大化不被破坏)
text_splitter = RecursiveCharacterTextSplitter(chunk_size=100, chunk_overlap=20)
chunks = text_splitter.split_documents(documents)
print(f"成功切分成 {len(chunks)} 个语义块。")
2. 向量化存储与检索
切分后,我们需要使用嵌入模型 将这些块转化为高维向量,并存入专用的向量数据库 (如 Chroma、FAISS)。
当用户提问时,系统会将用户的问句向量化,并在数据库中找到语义最相似的 Top N 个文档片段作为"参考资料",最后将"参考资料 + 用户提问"打包发送给大模型生成最终回答。
python
from langchain_community.vectorstores import Chroma
from langchain_community.embeddings import DashScopeEmbeddings
embeddings = DashScopeEmbeddings(dashscope_api_key=os.getenv('ALIBABA_API_KEY'), model="text-embedding-v1")
vectorstore = Chroma.from_documents(documents=chunks, embedding=embeddings)
# 检索测试
query = "LangChain 的索引层主要解决什么业务问题?"
retriever = vectorstore.as_retriever(search_kwargs={'k': 2})
top_results = retriever.invoke(query)
print(f"匹配到的Top 2片段:{top_results}")
八、结构化输出(Structured Output):向业务 API 对接
在大多数生产环境中,大模型直接返回"自然语言文本"往往会增加后续解析的复杂度。LangChain 提供了一整套结构化输出机制,通过 Pydantic 数据模型作为"约束框架",强制大模型以 JSON 格式返回所需的数据。
- 底层原理:LangChain 将 Pydantic 模型转化为大模型内部的"工具调用(Tool Call)"结构。大模型不会生成文本,而是生成一个结构化的参数对象,从而实现数据精准抽取。
python
from typing import Optional
from pydantic import BaseModel, Field
# 1. 定义提取目标的数据结构
class UserProfile(BaseModel):
full_name: str = Field(description="用户的真实姓名")
age: int = Field(description="用户的年龄,单位岁")
city: Optional[str] = Field(description="用户目前的居住城市")
# 2. 将数据结构绑定到代理上
agent = create_agent(
model=llm,
response_format=UserProfile,
)
# 3. 输入一段非结构化的自然语言描述
raw_text = "你好,我叫张三,今年31岁,之前在北京互联网大厂工作,今年刚刚搬到杭州发展。"
response = agent.invoke({"messages": [{"role": "user", "content": raw_text}]})
# 4. 直接获取结构化的 Python 对象
profile = response['structured_response']
print(f"姓名:{profile.full_name}, 年龄:{profile.age}, 城市:{profile.city}")
# 输出:姓名:张三, 年龄:31, 城市:杭州
结语:LangChain 的"积木哲学"与工程化落地
纵观 LangChain 的 6 大核心组件(模型、提示词、链、代理、记忆、索引)以及结构化输出设计,其本质都在传达一种**"积木式"的开发思维**。
-
如果您需要构建一个智能客服,您可以 Memory + Chat Models。
-
如果您需要连接企业内部数据库,您可以 Agents + 自定义工具。
-
如果您需要解答私有文档问题,您可以 Indexes + Embeddings + Vector DB + Chain。
掌握了这些核心组件及其背后的架构逻辑,就等于拿到了大模型应用开发的"标准工程图纸"。在这个大模型技术飞速迭代的时代,LangChain 的这六大核心模块依然是绝大多数 AI 应用不可或缺的地基,值得所有 AI 开发者深入学习与实践。