Day15:LangChain 基础入门:开启与大模型高效交互之旅
Day15:LangChain 基础入门:开启与大模型高效交互之旅
摘要:本文旨在帮助初学者轻松入门 LangChain,以通义千问大模型为例,详细介绍 LangChain 库的安装、核心概念及其使用场景,并通过示例代码展示如何使用 PromptTemplate 创建可复用的 Prompt 模板,结合大模型 API 实现简单的问答 Chain。
引言
很多初学者在学习大模型应用开发时,都会遇到一个问题:“我已经会调用OpenAI或通义千问的API了,但如何构建一个更复杂的应用,比如带记忆的聊天机器人、能检索文档的问答系统?”
LangChain 就是为了解决这个问题而生的。它就像一个 “AI应用的乐高积木” ,把各种复杂的功能模块化,让我们可以轻松地组合、串联,快速构建强大的LLM应用。
本文将带你从零开始,以阿里通义千问为例,手把手掌握LangChain最核心的几个概念:PromptTemplate、Chain、Model 和 Memory。文章最后会提供一个完整的可运行代码,让你直观感受LangChain的魅力。
🧐 一、LangChain是什么?
简单来说,LangChain 是一个用于开发由大语言模型(LLM)驱动的应用程序的框架 。
它本身不提供模型,而是提供了一套标准接口和丰富组件,让我们可以:
-
统一接口调用各种模型(OpenAI、通义千问、文心一言等)。
-
通过提示词模板复用和管理Prompt 。
-
将多个步骤组合成一个工作流(Chain)。
-
给应用增加短期或长期记忆(Memory)。
-
让AI调用外部工具(Tool),如搜索引擎、计算器、API等 。
🏗️ 二、LangChain四大核心概念
为了完成今天的任务,我们需要先了解这四个基础概念,它们就像乐高积木中的基础块。
PromptTemplate(提示词模板)
-
作用:告别硬编码字符串!它允许你定义一个带变量的模板,在运行时再填充具体内容,实现Prompt的复用和管理 。
-
场景:比如你需要让AI翻译,可以定义一个模板 “请将下面的中文翻译成英文:{text}”,每次只需要传入不同的 {text} 即可。
Model(模型)
-
作用:LangChain对各类大模型进行了统一封装,无论是文本补全模型(LLM)还是对话模型(Chat Model),都可以用同样的方式调用 。
-
场景:今天我们使用的 ChatTongyi 就是LangChain社区版中专为通义千问提供的Chat Model封装 。
Chain(链)
-
作用:将多个组件组合成一个整体。最简单的 LLMChain 就是将 PromptTemplate 和 Model 连接起来:输入 -> PromptTemplate -> Model -> 输出 。
-
场景:实现一个简单的问答、数据清洗后调用模型、或者一个多步骤的复杂任务流。
Memory(记忆)
-
作用:默认情况下,大模型是无状态的,它记不住你上一轮说了什么。Memory 组件就是为了解决这个问题,它可以在链的内部维护一个缓冲区,自动存储和传递历史对话信息 。
-
场景:多轮对话机器人、需要上下文的交互场景。
Tool(工具)
- 作用:让LLM具备使用外部工具的能力,比如联网搜索、执行代码、查天气等 。本文将不展开,但它是Agent(智能体)的核心。
🛠️ 三、环境准备
在开始写代码前,我们需要先安装库和配置好API密钥。
安装必要的库
打开你的终端,使用pip安装以下库。建议在虚拟环境中进行。
# 安装核心langchain库和社区集成库(包含通义千问)
pip install langchain langchain-community
# 安装通义千问的SDK(阿里云百炼平台)
pip install dashscope
# 安装用于加载环境变量的库(可选,但推荐)
pip install python-dotenv
四、实战:从Prompt到Chain
我们将编写两个简单的示例,由浅入深。
场景一:无状态问答(基础Chain)
这个场景演示了最核心的流程:定义模板 -> 实例化模型 -> 构建Chain -> 运行。
完整代码:**langchain_basic_demo.py**
import os
from dotenv import load_dotenv
from langchain_community.chat_models import ChatTongyi
from langchain_core.prompts import PromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
# 加载 .env 文件中的环境变量
load_dotenv()
# 1. 初始化通义千问模型
# 使用 langchain-community 中的 ChatTongyi
# 模型可以选择 qwen-turbo, qwen-plus, qwen-max 等 [citation:6]
llm = ChatTongyi(
model="qwen-plus",
temperature=0.7, # 控制创造性,0.7 是个平衡值
# dashscope_api_key 会自动从环境变量 DASHSCOPE_API_KEY 读取,所以这里可以不传
)
# 2. 创建 PromptTemplate
# 定义两个变量:topic 和 style
template = "你是一位{style}专家。请用一句话向小学生解释什么是{topic}。"
prompt = PromptTemplate.from_template(template)
# 3. 构建 Chain (使用现代 LCEL 语法,更加简洁直观) [citation:5]
# | 管道符将各个组件连接起来:输入 -> prompt -> llm -> 字符串解析器 -> 输出
chain = (
{"topic": RunnablePassthrough(), "style": RunnablePassthrough()} # 接收两个输入
| prompt
| llm
| StrOutputParser() # 将模型返回的 AIMessage 对象转换成字符串
)
# 4. 运行 Chain
print("="*50)
print("🚀 无状态问答演示")
print("="*50)
response = chain.invoke({"topic": "量子计算", "style": "科普"})
print(f"用户提问: 用科普风格解释量子计算")
print(f"AI回答: {response}\n")
response2 = chain.invoke({"topic": "神经网络", "style": "教育"})
print(f"用户提问: 用教育风格解释神经网络")
print(f"AI回答: {response2}\n")
# 测试模型是否有记忆(它应该不记得之前的事)
response3 = chain.invoke({"topic": "我刚刚问了你什么?", "style": "通用"})
print(f"用户提问: 我刚刚问了你什么?(测试记忆)")
print(f"AI回答: {response3}")
print("="*50)
输出示例:
==================================================
🚀 无状态问答演示
==================================================
用户提问: 用科普风格解释量子计算
AI回答: 量子计算就像是一台超级厉害的“魔法算盘”,它不用普通的0和1来思考,而是让信息像小精灵一样同时扮演0和1(叫“量子叠加”),还能让多个小精灵手拉手“心灵感应”(叫“量子纠缠”),所以能飞快解决普通电脑要算几百年的大难题!✨
用户提问: 用教育风格解释神经网络
AI回答: 神经网络就像一个超级聪明的“数字大脑”,它由很多像小灯泡一样会亮会灭的小单元(叫“神经元”)连在一起,通过不断看图片、听声音或读文字来学习规律——比如认出猫和狗的区别,就像你多看几次小猫小狗,慢慢就能分清一样!🐱→💡→🐶
用户提问: 我刚刚问了你什么?(测试记忆)
AI回答: 你刚刚问我的问题就是“我刚刚问了你什么?”,这其实是一个有趣的“自己问自己”的问题,就像照镜子时镜子也在照你一样——它在请我回头看看你上一句话说了什么!😊
==================================================
解析:
-
我们使用了
PromptTemplate动态生成了两个不同风格的提问。 -
最后一问证实了模型确实没有记忆,因为它不知道历史。
场景二:带记忆的对话(Memory初探)
为了解决无状态的问题,我们需要引入Memory。LangChain新版本推荐使用 RunnableWithMessageHistory 配合 ChatMessageHistory 来实现 。
完整代码:在同一个文件中追加以下内容
from langchain_core.messages import HumanMessage, AIMessage
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_core.chat_history import BaseChatMessageHistory
from langchain_community.chat_message_histories import ChatMessageHistory
# 创建一个字典来存储不同会话的历史记录(简单内存存储)
store = {}
def get_session_history(session_id: str) -> BaseChatMessageHistory:
"""根据 session_id 获取或创建历史记录"""
if session_id not in store:
store[session_id] = ChatMessageHistory()
return store[session_id]
# 1. 使用适合对话的 ChatPromptTemplate
# 关键点:使用 MessagesPlaceholder 作为历史消息的占位符 [citation:6]
prompt_with_memory = ChatPromptTemplate.from_messages([
("system", "你是一个知识渊博、乐于助人的助手。请用简洁的中文回答。"),
MessagesPlaceholder(variable_name="history"), # 历史消息会填充到这里
("human", "{input}") # 用户当前的输入
])
# 2. 构建基础链(无记忆)
chain_without_memory = prompt_with_memory | llm | StrOutputParser()
# 3. 用 RunnableWithMessageHistory 给链装上“记忆” [citation:6]
chain_with_memory = RunnableWithMessageHistory(
chain_without_memory,
get_session_history, # 历史获取函数
input_messages_key="input", # 指定哪个是用户的输入
history_messages_key="history" # 指定哪个是历史占位符
)
# 4. 模拟多轮对话
print("\n" + "="*50)
print("🧠 带记忆的对话演示 (Session ID: user_001)")
print("="*50)
config = {"configurable": {"session_id": "user_001"}}
resp1 = chain_with_memory.invoke({"input": "你好,我是小明,我最喜欢猫!"}, config=config)
print(f"用户: 你好,我是小明,我最喜欢猫!")
print(f"AI: {resp1}\n")
resp2 = chain_with_memory.invoke({"input": "你能记住我的名字和爱好吗?我叫什么?喜欢什么?"}, config=config)
print(f"用户: 你能记住我的名字和爱好吗?我叫什么?喜欢什么?")
print(f"AI: {resp2}\n")
resp3 = chain_with_memory.invoke({"input": "那你能给我推荐一部关于动物的电影吗?"}, config=config)
print(f"用户: 那你能给我推荐一部关于动物的电影吗?")
print(f"AI: {resp3}\n")
# 测试会话隔离 (新建一个会话)
print("\n" + "="*50)
print("🧠 带记忆的对话演示 (Session ID: user_002 - 新用户)")
print("="*50)
config2 = {"configurable": {"session_id": "user_002"}}
resp4 = chain_with_memory.invoke({"input": "你好,我叫小红,我最喜欢宇宙探索。还记得我吗?"}, config=config2)
print(f"用户: 你好,我叫小红,我最喜欢宇宙探索。还记得我吗?")
print(f"AI: {resp4}\n")
resp5 = chain_with_memory.invoke({"input": "我刚才告诉你我叫什么?"}, config=config2)
print(f"用户: 我刚才告诉你我叫什么?")
print(f"AI: {resp5}")
输出示例(预期):
==================================================
🚀 无状态问答演示
==================================================
用户提问: 用科普风格解释量子计算
AI回答: 量子计算就像是一台超级厉害的“魔法算盘”,它不用普通的0和1来思考,而是用能同时是0又是1的“量子小精灵”(叫量子比特),所以它能在一瞬间试好多好多条路,帮我们解决普通电脑要算几百年才能搞定的难题!
用户提问: 用教育风格解释神经网络
AI回答: 神经网络就像一个超级聪明的“数字大脑”,它由很多像小灯泡一样会亮会灭的小单元(叫“神经元”)连在一起,通过不断看图片、听声音或读文字来学习规律——比如认出猫和狗的区别,就像你多看几次小猫小狗,慢慢就能分清一样!🐱→💡→🐶
用户提问: 我刚刚问了你什么?(测试记忆)
AI回答: 你刚刚问我的问题就是“我刚刚问了你什么?”,这其实是一个有趣的“自己问自己”的问题,就像照镜子时镜子也在照你一样——它在请我回头看看你上一句话说了什么!😊
==================================================
==================================================
🧠 带记忆的对话演示 (Session ID: user_001)
==================================================
用户: 你好,我是小明,我最喜欢猫!
AI: 你好,小明!很高兴认识一位爱猫的朋友~🐱
猫确实超可爱:高冷又粘人,傲娇还治愈,连打个哈欠都像在演小短剧 😄
你养猫了吗?还是有特别喜欢的猫品种/网红猫?欢迎分享!
用户: 你能记住我的名字和爱好吗?我叫什么?喜欢什么?
AI: 当然可以!你叫**小明**,最喜欢**猫**~ 🐾
我已经记在小本本上了(🧠✨),以后聊猫、养猫、云吸猫,或者想听猫的冷知识、趣事、品种推荐……随时找我! 😺
用户: 那你能给我推荐一部关于动物的电影吗?
AI: 当然可以!既然你超爱猫,我特别推荐这部温暖又治愈的电影:
🎬 **《流浪猫鲍勃》(A Street Cat Named Bob,2016)**
✅ 真人真事改编|✅ 主角是一只超有灵性的橘猫鲍勃|✅ 充满善意与希望
故事讲的是伦敦街头一位陷入低谷的街头艺人詹姆斯,在最艰难时收留了一只受伤的流浪橘猫——鲍勃。没想到,这只戴围巾、爱坐肩头的猫不仅改变了他的人生,还帮他在街头重拾尊严与勇气……连猫的“演技”都自然到让人怀疑它是不是拿了奥斯卡提名!😂
✨ 亮点:
- 鲍勃本人(真猫!)全程本色出演,慵懒、傲娇、关键时刻超暖心;
- 没有刻意煽情,却让人笑着流泪;
- 看完会忍不住摸摸身边猫主子的头说:“谢谢你也在”。
需要我再推荐一部风格不同的(比如动画、搞笑、或科普向)?或者想了解鲍勃后来的真实故事?随时告诉我~ 😺
==================================================
🧠 带记忆的对话演示 (Session ID: user_002 - 新用户)
==================================================
用户: 你好,我叫小红,我最喜欢宇宙探索。还记得我吗?
AI: 你好,小红!很高兴再次见到你~虽然作为AI我没有记忆功能,不记得之前的对话,但听到你最喜欢宇宙探索,真让人兴奋!✨
你想聊黑洞、火星探测、中国空间站,还是最近的詹姆斯·韦布望远镜新发现?我随时陪你仰望星空 🌌🚀
用户: 我刚才告诉你我叫什么?
AI: 是的,你刚才告诉我——你叫**小红**!😊
而且你还特别提到:**最喜欢宇宙探索** 🌟
(这可是个超酷的爱好!)
需要我帮你查某个航天任务、解释一个天文概念,或者一起畅想星际旅行?随时开始~ 🚀
解析:
-
历史填充:
MessagesPlaceholder是关键,它会自动将history变量展开为多条历史消息。 -
自动记忆:
RunnableWithMessageHistory会自动将每次对话的(Human, AI)对存入store中对应的session_id。 -
会话隔离:
user_002的对话完全不受user_001的影响,这是多用户系统的基石。
📝 五、总结与展望
今天我们完成了LangChain的入门学习:
-
我们理解了LangChain的核心设计理念:模块化和链式组合。
-
我们亲手实践了
**PromptTemplate**、**Model**、**Chain**和**Memory**这四个最基础的组件。 -
我们学会了如何通过
ChatTongyi将通义千问集成到LangChain中。 -
我们成功搭建了一个带记忆的问答Chain,实现了真正的多轮对话。
这只是一个开始。LangChain的世界还有更多精彩等待你去探索:
-
RAG(检索增强生成):连接你私有的知识库或文档,让AI基于你的资料回答问题。
-
Agent(智能体):赋予AI调用工具的能力,让它能帮你查天气、订机票、执行代码。
-
LCEL(LangChain表达式语言):用更简洁、强大的方式构建复杂的链。
希望这篇教程能为你打开一扇门。如果你有任何问题,欢迎在评论区留言交流!
附录:完整代码
# langchain_qwen_tutorial.py
import os
from dotenv import load_dotenv
from langchain_community.chat_models import ChatTongyi
from langchain_core.prompts import PromptTemplate, ChatPromptTemplate, MessagesPlaceholder
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_core.chat_history import BaseChatMessageHistory
from langchain_community.chat_message_histories import ChatMessageHistory
# --- 0. 加载环境变量 ---
load_dotenv()
# --- 1. 初始化模型 (全局共用) ---
llm = ChatTongyi(
model="qwen-turbo",
temperature=0.7,
)
# ================== 场景一:无状态问答 ==================
def demo_stateless():
print("="*50)
print("🚀 无状态问答演示")
print("="*50)
template = "你是一位{style}专家。请用一句话向小学生解释什么是{topic}。"
prompt = PromptTemplate.from_template(template)
chain = (
{"topic": RunnablePassthrough(), "style": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
response = chain.invoke({"topic": "量子计算", "style": "科普"})
print(f"用户提问: 用科普风格解释量子计算")
print(f"AI回答: {response}\n")
response3 = chain.invoke({"topic": "我刚刚问了你什么?", "style": "通用"})
print(f"用户提问: 我刚刚问了你什么?(测试记忆)")
print(f"AI回答: {response3}")
print("="*50)
# ================== 场景二:带记忆的对话 ==================
# 存储历史记录的字典
store = {}
def get_session_history(session_id: str) -> BaseChatMessageHistory:
if session_id not in store:
store[session_id] = ChatMessageHistory()
return store[session_id]
def demo_stateful():
print("\n" + "="*50)
print("🧠 带记忆的对话演示")
print("="*50)
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个知识渊博、乐于助人的助手。请用简洁的中文回答。"),
MessagesPlaceholder(variable_name="history"),
("human", "{input}")
])
chain = prompt | llm | StrOutputParser()
chain_with_memory = RunnableWithMessageHistory(
chain,
get_session_history,
input_messages_key="input",
history_messages_key="history"
)
# 会话1
config1 = {"configurable": {"session_id": "user_001"}}
resp1 = chain_with_memory.invoke({"input": "你好,我是小明,我最喜欢猫!"}, config=config1)
print(f"用户1: 你好,我是小明,我最喜欢猫!")
print(f"AI: {resp1}\n")
resp2 = chain_with_memory.invoke({"input": "我叫什么名字?"}, config=config1)
print(f"用户1: 我叫什么名字?")
print(f"AI: {resp2}\n")
# 会话2
config2 = {"configurable": {"session_id": "user_002"}}
resp3 = chain_with_memory.invoke({"input": "你好,我是小红。"}, config=config2)
print(f"用户2: 你好,我是小红。")
print(f"AI: {resp3}\n")
resp4 = chain_with_memory.invoke({"input": "我叫什么名字?"}, config=config2)
print(f"用户2: 我叫什么名字?")
print(f"AI: {resp4}")
print("="*50)
if __name__ == "__main__":
demo_stateless()
demo_stateful()
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)