🎯 适合人群:有 Python 基础、了解大语言模型基本概念、想系统学习 LangChain 的工程师
⏱️ 阅读时间:约 20 分钟
💬 本文从"直接调 API 有什么问题"切入,介绍 LangChain 的核心定位与基础抽象,并手把手构建第一个可运行的 Chain


一、直接调 API 有什么问题?

在接触 LangChain 之前,大多数工程师构建 LLM 应用的方式是这样的:

# ❌ 直接调用 OpenAI API 的典型写法
import openai

client = openai.OpenAI()

response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[
        {"role": "system", "content": "你是一个专业的翻译助手。"},
        {"role": "user", "content": f"请将以下文本翻译成英文:{text}"}
    ]
)

result = response.choices[0].message.content

这段代码能跑,但随着需求增长,问题逐渐暴露:

问题一:提示词散落在代码各处

提示词以字符串形式硬编码,当同一个模板在多处复用时,修改一处就需要全局搜索替换。

问题二:模型切换成本高

如果需要从 OpenAI 切换到 Anthropic 或本地模型,意味着要修改所有调用点的 API 结构、参数名称和响应解析逻辑。

问题三:组合逻辑难以维护

实际应用往往需要多步调用:先检索文档,再构建 prompt,再调用模型,再解析输出。用原生 API 实现时,这些步骤散落在函数调用链中,可读性和可测试性都很差。

问题四:常用能力需要重复实现

流式输出、重试策略、并行调用、输出解析——这些能力每个项目都需要,但都得自己实现一遍。

LangChain 正是为了解决这些问题而设计的。


二、LangChain 是什么

LangChain 是一个用于构建 LLM 驱动应用的开源框架,核心思想是将 LLM 应用的各个组成部分抽象为可组合的模块,通过统一接口进行拼装。

⚡ LCEL Pipeline

|

|

📤 Output Layer

StrOutputParser

JsonOutputParser

PydanticParser

📝 Prompt Layer

PromptTemplate

ChatPromptTemplate

FewShotPrompt

🤖 Model Layer

ChatOpenAI

ChatAnthropic

ChatOllama

chain.invoke({"key": "value"})

与直接调用 API 相比,LangChain 提供了以下核心价值:

维度 直接调 API 使用 LangChain
模型切换 修改所有调用点 替换一行模型初始化
提示词管理 字符串硬编码 类型化 Template 对象
组合多步逻辑 手写函数调用链 LCEL 管道操作符 |
流式输出 手动处理 SSE .stream() 内置支持
可观测性 自行实现日志 LangSmith 集成

📝 LangChain 的版本演进较快。本系列基于 LangChain 0.3.x,使用 LCEL(LangChain Expression Language)作为主要编程范式,避免使用已废弃的 LLMChainConversationChain 等旧式 API。


三、环境搭建

3.1 安装依赖

pip install langchain langchain-openai python-dotenv
包名 用途
langchain 核心框架,提供抽象接口和工具
langchain-openai OpenAI 模型集成(ChatOpenAI、OpenAIEmbeddings)
python-dotenv .env 文件加载环境变量

💡 LangChain 采用拆包设计:langchain 只包含核心抽象,具体模型集成在独立的 langchain-openailangchain-anthropiclangchain-ollama 等包中。这样做的好处是按需安装,不引入不必要的依赖。

3.2 配置 API Key

在项目根目录创建 .env 文件:

OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxx

在代码入口处加载:

from dotenv import load_dotenv
load_dotenv()  # 加载 .env 文件中的环境变量

⚠️ 切勿将 API Key 硬编码在代码中或提交到版本控制系统。.env 文件应加入 .gitignore


四、核心抽象:LangChain 的"积木体系"

LangChain 将 LLM 应用拆解为几类标准化组件,就像乐高积木——每块形状统一,可以自由拼装。

4.1 Messages:消息类型体系

langchain_core.messages 模块提供了以下消息类型:

用途
HumanMessage 表示用户发送的消息
AIMessage 表示模型返回的消息,包含 content 和可选的 tool_calls
SystemMessage 表示系统级指令,设定模型的角色和行为边界
ToolMessage 表示工具调用的返回结果,需关联 tool_call_id
AIMessageChunk / HumanMessageChunk 流式输出时的消息片段,可拼接为完整的消息对象
FunctionMessage 兼容旧版 Function Calling 的消息类型,已废弃,推荐用 ToolMessage

常用的三种如下:

from langchain_core.messages import HumanMessage, AIMessage, SystemMessage

# 用户发出的消息
human_msg = HumanMessage(content="LangChain 是什么框架?")

# 模型返回的消息
ai_msg = AIMessage(content="LangChain 是一个用于构建 LLM 应用的框架...")

# 系统级别的指令
system_msg = SystemMessage(content="你是一个专业的技术文档助手。")

这套消息体系屏蔽了不同模型提供商的格式差异——OpenAI 的 {"role": "user", "content": "..."} 和 Anthropic 的消息格式都被统一抽象为这三种类型。

4.2 Chat Models:统一的模型接口

所有 Chat Model 都继承自同一个基类,暴露相同的方法:

from langchain_openai import ChatOpenAI

# 初始化模型
model = ChatOpenAI(
    model="gpt-4o-mini",
    temperature=0.7,      # 生成随机性,0 为确定性输出
    max_tokens=1024,      # 最大输出 token 数
)

# invoke:同步调用,返回 AIMessage
response = model.invoke([HumanMessage(content="用一句话解释什么是向量数据库")])
print(response.content)  # 直接访问文本内容

💡 切换到其他模型只需替换初始化行:

# 切换到 Anthropic Claude
from langchain_anthropic import ChatAnthropic
model = ChatAnthropic(model="claude-3-5-sonnet-20241022")

# 切换到本地 Ollama 模型
from langchain_ollama import ChatOllama
model = ChatOllama(model="llama3.2")

之后的所有代码无需修改。

4.3 PromptTemplate:将提示词结构化

langchain_core.prompts 模块提供了以下模板类:

用途
PromptTemplate 单轮文本模板,适用于纯文本输入的 LLM(非 Chat Model)
ChatPromptTemplate 多轮对话模板,由多条消息组成,适用于 Chat Model,最常用
HumanMessagePromptTemplate ChatPromptTemplate 中用于定义用户消息的子模板
AIMessagePromptTemplate ChatPromptTemplate 中用于定义 AI 消息的子模板,常用于 Few-shot 示例
SystemMessagePromptTemplate ChatPromptTemplate 中用于定义系统消息的子模板
MessagesPlaceholder ChatPromptTemplate 中插入动态消息列表,常用于注入对话历史
FewShotPromptTemplate 包含 examples 的单轮提示模板,适用于 LLM
FewShotChatMessagePromptTemplate 包含 examples 的多轮对话模板,适用于 Chat Model
PipelinePromptTemplate 将多个 PromptTemplate 组合为一个,支持模块化提示词管理

PromptTemplate 的本质是带有占位符的字符串模板,类似 Python 的 str.format(),但增加了类型校验和可复用性:

from langchain_core.prompts import ChatPromptTemplate

# 定义模板:{target_language} 和 {text} 是占位符
prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个专业翻译,擅长将文本翻译成{target_language}。"),
    ("human", "请翻译以下内容:\n\n{text}")
])

# 渲染模板(生成具体的消息列表)
messages = prompt.invoke({
    "target_language": "英文",
    "text": "大语言模型正在改变软件开发的方式。"
})
print(messages)
# ChatPromptValue containing:
# [SystemMessage("你是一个专业翻译,擅长将文本翻译成英文。"),
#  HumanMessage("请翻译以下内容:\n\n大语言模型正在改变软件开发的方式。")]

4.4 Output Parsers:解析模型输出

langchain_core.output_parsers 及相关模块提供了以下解析器:

用途
StrOutputParser 提取 AIMessage.content,返回纯字符串,最常用
JsonOutputParser 将模型输出解析为 Python dict,可结合 Pydantic 指定 schema
PydanticOutputParser 将模型输出解析为 Pydantic BaseModel 对象,带类型校验
CommaSeparatedListOutputParser 将逗号分隔的输出解析为 Python list
NumberedListOutputParser 将编号列表输出解析为 Python list
XMLOutputParser 将 XML 格式的输出解析为嵌套字典
DatetimeOutputParser 将输出解析为 Python datetime 对象
EnumOutputParser 将输出解析为指定的枚举类型
OutputFixingParser 包装其他 parser,当解析失败时自动调用模型修正格式

模型默认返回 AIMessage 对象,Output Parser 负责将其转换为所需的数据类型:

from langchain_core.output_parsers import StrOutputParser, JsonOutputParser

# StrOutputParser:提取 AIMessage.content,返回纯字符串
parser = StrOutputParser()
text = parser.invoke(AIMessage(content="这是模型输出的文本"))
print(type(text))  # <class 'str'>

# JsonOutputParser:将模型输出解析为 Python 字典
json_parser = JsonOutputParser()

4.5 Chain:用 | 连接所有组件

LCEL(LangChain Expression Language) 使用管道操作符 | 将各组件串联,构成一个 Chain:

# Chain = Prompt | Model | Parser
chain = prompt | model | parser

这行代码的含义是:

  1. prompt 接收输入字典,渲染出消息列表
  2. model 接收消息列表,调用 LLM 返回 AIMessage
  3. parser 接收 AIMessage,提取 .content 返回字符串

🤔 这里的 | 不是位运算符,而是 LangChain 通过 __or__ 方法重载的管道操作符,语义上等同于 Unix shell 的 |——将左侧的输出作为右侧的输入。LCEL 的详细机制将在第二篇深入讲解。


五、第一个完整示例

将上述概念组合,构建一个多语言翻译助手:

from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

load_dotenv()

# 1. 定义 Prompt 模板
prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个专业翻译助手,擅长将文本准确翻译成{target_language},保持原文语气和风格。"),
    ("human", "请翻译以下内容:\n\n{text}")
])

# 2. 初始化模型
model = ChatOpenAI(model="gpt-4o-mini", temperature=0)

# 3. 定义输出解析器
parser = StrOutputParser()

# 4. 组合成 Chain
translation_chain = prompt | model | parser

# 5. 调用
result = translation_chain.invoke({
    "target_language": "英文",
    "text": "大语言模型正在深刻改变软件工程的边界,从代码补全到自主 Agent,每一层抽象都在重新定义。"
})

print(result)
# Large language models are profoundly reshaping the boundaries of software engineering,
# from code completion to autonomous agents, redefining every layer of abstraction.

5.1 批量处理

Chain 内置 batch 方法,支持并发处理多个输入:

inputs = [
    {"target_language": "英文", "text": "人工智能的发展日新月异。"},
    {"target_language": "日文", "text": "向量数据库是 RAG 系统的核心组件。"},
    {"target_language": "法文", "text": "大模型推理成本正在快速下降。"},
]

# 并发执行,max_concurrency 控制并发数
results = translation_chain.batch(inputs, config={"max_concurrency": 3})
for r in results:
    print(r)

5.2 流式输出

对于需要实时展示生成过程的场景,使用 stream 方法:

# stream 返回一个生成器,逐 token 输出
for chunk in translation_chain.stream({
    "target_language": "英文",
    "text": "这是一段需要实时翻译并流式输出的文本。"
}):
    print(chunk, end="", flush=True)
print()

六、常见坑与最佳实践

坑一:直接打印 Chain 输出得到 AIMessage 对象

# ❌ 忘记加 Output Parser
chain = prompt | model
result = chain.invoke({...})
print(result)
# content='翻译结果' additional_kwargs={} response_metadata={...}

# ✅ 加上 StrOutputParser
chain = prompt | model | StrOutputParser()
result = chain.invoke({...})
print(result)
# 翻译结果

坑二:使用已废弃的旧式 API

# ❌ 旧式写法(LangChain 0.1.x 时代,现已废弃)
from langchain.chains import LLMChain
chain = LLMChain(llm=model, prompt=prompt)
result = chain.run(text="...")

# ✅ 新式 LCEL 写法
chain = prompt | model | StrOutputParser()
result = chain.invoke({"text": "..."})

⚠️ LLMChainConversationChain 等旧式 Chain 类在 LangChain 0.2.x 已标记为废弃,0.3.x 起推荐完全迁移到 LCEL。

坑三:temperature=0 与 temperature=1 混用

# 翻译、信息提取、代码生成等需要确定性输出的任务
model = ChatOpenAI(model="gpt-4o-mini", temperature=0)   # ✅ 确定性

# 创意写作、头脑风暴等需要多样性的任务
model = ChatOpenAI(model="gpt-4o-mini", temperature=0.9)  # ✅ 多样性

# ❌ 全部用默认值(0.7),不根据任务类型调整

坑四:API Key 未正确加载

# ❌ 忘记调用 load_dotenv(),导致 OPENAI_API_KEY 为 None
from langchain_openai import ChatOpenAI
model = ChatOpenAI()  # AuthenticationError

# ✅ 在程序入口处加载
from dotenv import load_dotenv
load_dotenv()  # 必须在初始化模型之前调用
from langchain_openai import ChatOpenAI
model = ChatOpenAI()

七、总结

概念 类比 核心作用
HumanMessage / SystemMessage 对话角色 统一消息格式,屏蔽不同模型的接口差异
ChatOpenAI 模型插槽 统一调用接口,一行代码切换模型提供商
ChatPromptTemplate 可复用模板 结构化提示词,支持动态变量注入
StrOutputParser 输出适配器 将 AIMessage 转为所需数据类型
LCEL 管道 | 流水线 将各组件串联为完整的处理链

🎯 LangChain 的核心价值不在于"能做什么",而在于"如何让 LLM 应用的各个部分可组合、可替换、可测试"。掌握这套抽象体系,是构建生产级 LLM 应用的基础。


参考资料


下期预告

本篇通过 prompt | model | parser 初次展示了 LCEL 的管道写法,但这背后的机制远不止如此。

第二篇《LCEL:用管道的方式写 AI 逻辑》 将深入 Runnable 接口,讲解 invokestreambatchainvoke 四种调用模式,以及并行执行、条件分支、错误重试等高级用法——这些是构建生产级 Chain 的必备技能。

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐