国家铁路局
44.74M · 2026-04-11
本文将手把手教你,使用 Streamlit(前端交互)+ LangChain v1.0+(大模型调用与流程管理)+ 通义千问(大模型),实现一个功能完整、架构规范的多会话AI聊天页面,支持会话新建、切换、删除、清空,以及本地会话持久化、流式输出等核心功能,全程采用 LangChain v1.0+ 新版写法,彻底抛弃旧版 Chain 和 Memory 组件。
在开始编码前,先明确各技术栈的选型原因和核心优势,确保整个技术架构简洁、高效、可维护。
Streamlit 是一款专为数据科学和机器学习开发者设计的 Web 应用框架,无需前端开发经验,用 Python 代码即可快速构建交互式页面。选择它的核心原因:
st.chat_message、st.chat_input 可直接实现聊天界面,无需额外封装。st.session_state 可轻松保存会话数据、当前选中会话等状态,刷新页面不丢失(配合本地文件持久化,实现长期保存)。st.empty() 占位符,可轻松实现 AI 回复的“打字机效果”,提升用户体验。LangChain v1.0+ 相比旧版(v0.x)有了根本性的架构优化,核心优势的是引入了 LCEL,让大模型调用流程更简洁、更灵活。本文全程采用新版写法,核心特点:
ConversationChain、ConversationBufferMemory 等旧版组件,改用更轻量的 ChatPromptTemplate + MessagesPlaceholder 实现对话记忆。| 符号将提示词模板、大模型串联成管道,调用逻辑一目了然,可灵活扩展(如添加输出解析、过滤等中间环节)。ChatTongyi(而非旧版 Tongyi),更贴合大模型的聊天交互场景,支持流式输出、温度调节等核心参数。选择通义千问(阿里云出品)的核心原因:
qwen-turbo 模型免费额度充足,适合开发者调试和小型应用部署,无需担心成本。langchain-community 包中的 ChatTongyi 可直接调用,无需额外封装 API 请求。采用本地 JSON 文件保存会话数据,优势是无需依赖数据库,部署简单、轻量,适合小型应用;后续可轻松扩展为 MySQL、Redis 等数据库,兼容性强。
在开始编码前,先完成环境配置和依赖安装,确保所有组件能正常运行。
打开终端,执行以下命令安装所有依赖:
pip install streamlit langchain langchain-community dashscope
说明:
ChatTongyi)调用通义千问需要 API Key,获取步骤如下:
注意:API Key 属于敏感信息,请勿泄露,后续可通过环境变量配置,避免硬编码。
本文实现的 AI 聊天页面,核心功能如下:
暂时无法在豆包文档外展示此内容
下面是完整的代码实现,每一部分都添加了详细注释,同时结合前文的架构解析,帮你理解每一行代码的作用。代码可直接复制运行,只需替换自己的通义千问 API Key 即可。
import streamlit as st
import json
import os
from datetime import datetime
# -------------------- LangChain v1.0+ 新版导入(核心) --------------------
# 通义千问新版聊天模型(替代旧版 Tongyi)
from langchain_community.chat_models import ChatTongyi
# 聊天提示词模板(支持插入历史消息)
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
# 聊天消息相关(用于构造历史消息格式)
from langchain_core.chat_history import BaseChatMessageHistory
from langchain_core.messages import BaseMessage
# -------------------------- 页面基础配置 --------------------------
# 设置页面标题、图标、布局,提升用户体验
st.set_page_config(
page_title="通义千问 AI 助手(LangChain v1.0+)",
page_icon="",
layout="wide" # 宽布局,适配会话列表+聊天区域的双栏结构
)
# -------------------------- 敏感信息配置 --------------------------
# 替换为你自己的通义千问 API Key(建议后续用环境变量配置,避免硬编码)
DASHSCOPE_API_KEY = "你的通义千问 API KEY"
# -------------------------- 会话管理模块(本地持久化) --------------------------
# 会话数据保存的本地文件路径
CHAT_FILE = "sessions.json"
# 初始化 Streamlit 会话状态(保存所有会话、当前选中会话)
# sessions:字典,key=会话ID,value=聊天记录列表(每个元素是{"role": "user/assistant", "content": "消息内容"})
if "sessions" not in st.session_state:
st.session_state.sessions = {}
# current_session:当前选中的会话ID,初始为None(无选中会话)
if "current_session" not in st.session_state:
st.session_state.current_session = None
# 1. 加载本地会话数据(应用启动时执行,恢复之前的会话)
def load_sessions():
# 检查会话文件是否存在,存在则加载,不存在则初始化空字典
if os.path.exists(CHAT_FILE):
with open(CHAT_FILE, "r", encoding="utf-8") as f:
# 从JSON文件中读取会话数据,赋值给session_state.sessions
st.session_state.sessions = json.load(f)
# 2. 保存会话数据(每次会话变化时执行:新建、发送消息、删除、清空)
def save_sessions():
# 将session_state.sessions中的数据写入JSON文件,确保中文正常显示(ensure_ascii=False)
with open(CHAT_FILE, "w", encoding="utf-8") as f:
json.dump(st.session_state.sessions, f, ensure_ascii=False, indent=2)
# 3. 新建会话(点击"新建会话"按钮触发)
def new_session():
# 生成唯一会话ID(基于当前时间戳,避免重复)
session_id = f"会话_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
# 为新会话初始化空的聊天记录
st.session_state.sessions[session_id] = []
# 将当前会话切换到新建的会话
st.session_state.current_session = session_id
# 保存会话到本地文件
save_sessions()
# 4. 切换会话(点击历史会话列表中的会话触发)
def switch_session(session_id):
# 将当前会话ID切换为选中的会话ID
st.session_state.current_session = session_id
# 5. 清空当前会话(点击"清空当前会话"按钮触发)
def clear_session():
# 仅当有选中会话时,清空该会话的聊天记录
if st.session_state.current_session:
st.session_state.sessions[st.session_state.current_session] = []
# 保存清空后的会话数据
save_sessions()
# 6. 删除会话(点击会话右侧的删除按钮触发)
def del_session(session_id):
# 从会话字典中删除指定会话
del st.session_state.sessions[session_id]
# 如果删除的是当前选中的会话,将当前会话置为None
if st.session_state.current_session == session_id:
st.session_state.current_session = None
# 保存删除后的会话数据
save_sessions()
# -------------------- LangChain v1.0+ 对话模块(LCEL 核心) --------------------
def get_chain():
"""
构建 LangChain v1.0+ 对话链(LCEL 管道式写法)
返回:prompt | llm 的 LCEL 管道,可直接调用 stream 方法实现流式输出
"""
# 1. 初始化通义千问聊天模型
llm = ChatTongyi(
model_name="qwen-turbo", # 通义千问标准版,免费额度高,适合调试和小型应用
dashscope_api_key=DASHSCOPE_API_KEY, # 传入通义千问API Key
temperature=0.7, # 温度值(0=严谨,1=创意),根据需求调整
streaming=True # 开启流式输出,实现打字机效果
)
# 2. 构建聊天提示词模板(新版核心,替代旧版 ConversationChain)
# MessagesPlaceholder:用于插入历史对话消息,variable_name="history" 对应后续传入的历史参数
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个有用、友好的AI助手,能够清晰、准确地回答用户的各种问题,语气自然亲切。"), # 系统提示词,定义AI角色
MessagesPlaceholder(variable_name="history"), # 历史对话占位符,自动插入之前的聊天记录
("human", "{input}") # 用户输入占位符,对应用户的新问题
])
# 3. LCEL 管道式组合(新版标准写法,替代旧版 Chain)
# prompt | llm:将提示词模板和大模型串联,调用时传入 history 和 input 即可
return prompt | llm
# -------------------------- 前端界面渲染(Streamlit) --------------------------
# 加载本地会话数据(应用启动时执行)
load_sessions()
# 侧边栏:会话管理(新建、切换、删除会话)
with st.sidebar:
st.title(" 会话管理") # 侧边栏标题
st.divider() # 分隔线,提升UI美观度
# 新建会话按钮(占满侧边栏宽度,使用use_container_width=True)
if st.button(" 新建会话", use_container_width=True):
new_session()
st.subheader("历史会话") # 历史会话标题
# 获取所有会话ID,遍历展示
session_list = list(st.session_state.sessions.keys())
if session_list:
# 为每个会话创建"会话名称+删除按钮"的双栏布局
for sid in session_list:
col1, col2 = st.columns([7, 3]) # 两列,比例7:3,左侧会话名称,右侧删除按钮
with col1:
# 点击会话名称,切换到该会话
if st.button(sid, key=f"btn_{sid}", use_container_width=True):
switch_session(sid)
with col2:
# 点击删除按钮,删除该会话(key需唯一,避免冲突)
if st.button("", key=f"del_{sid}", use_container_width=True):
del_session(sid)
else:
# 无历史会话时,显示提示信息
st.info("暂无会话,点击「新建会话」开始聊天吧~")
# 主界面:聊天区域
st.title(" 通义千问 AI 助手")
st.markdown("### LangChain v1.0+ 实战 | LCEL 标准写法 | 多会话支持")
st.divider()
# 无选中会话时,显示提示,阻止继续操作
if not st.session_state.current_session:
st.warning(" 请先在左侧「会话管理」中新建或选择一个会话,再开始聊天~")
st.stop() # 停止后续代码执行
# 获取当前选中会话的ID和聊天记录
current_sid = st.session_state.current_session
current_messages = st.session_state.sessions[current_sid]
# 清空当前会话按钮(主界面顶部)
if st.button(" 清空当前会话", type="primary"): # primary类型按钮,突出显示
clear_session()
st.rerun() # 刷新页面,立即显示清空后的效果
# 渲染当前会话的聊天记录
st.markdown(f"#### 当前会话:{current_sid}")
for msg in current_messages:
# 根据消息角色(user/assistant),渲染对应的聊天气泡
with st.chat_message(msg["role"]):
st.markdown(msg["content"]) # 渲染消息内容
# 聊天输入框(页面底部,支持回车发送)
user_input = st.chat_input("请输入你的问题...")
# 处理用户输入,调用AI回复
if user_input:
# 1. 显示用户输入的消息
with st.chat_message("user"):
st.markdown(user_input)
# 2. 将用户消息添加到当前会话的聊天记录中
current_messages.append({"role": "user", "content": user_input})
# 3. 保存会话数据(更新聊天记录)
save_sessions()
# 4. 获取 LangChain 对话链(LCEL 管道)
chain = get_chain()
# 5. 构造历史消息格式(适配 LangChain 的 MessagesPlaceholder)
# 排除刚发送的用户消息(因为用户消息已作为 input 传入,历史消息只需之前的对话)
history_messages = [
{"role": msg["role"], "content": msg["content"]}
for msg in current_messages[:-1]
]
# 6. 调用AI模型,实现流式输出(打字机效果)
with st.chat_message("assistant"):
# 创建占位符,用于动态更新AI回复内容
placeholder = st.empty()
full_answer = "" # 存储完整的AI回复
# LCEL 流式调用(新版核心,stream方法返回生成器,逐块获取AI回复)
for chunk in chain.stream({
"history": history_messages, # 历史对话消息
"input": user_input # 用户新输入的问题
}):
# 逐字符拼接AI回复(实现打字机效果)
full_answer += chunk.content
# 实时更新占位符内容,添加"▌"模拟打字光标
placeholder.markdown(full_answer + "▌")
# 打字机效果结束,显示完整回复(移除光标)
placeholder.markdown(full_answer)
# 7. 将AI回复添加到当前会话的聊天记录中
current_messages.append({"role": "assistant", "content": full_answer})
# 8. 保存会话数据(更新AI回复)
save_sessions()
下面针对代码中最关键的 3 个模块,进行更细致的解析,帮助你理解 LangChain v1.0+ 的新版写法核心。
这是本文的重点,也是 LangChain v1.0+ 与旧版的核心区别。代码中 get_chain() 函数实现了 LCEL 管道的构建,核心逻辑如下:
Tongyi(旧版属于 LLM,新版 ChatTongyi 属于 ChatModel,更贴合聊天场景,支持流式输出)。MessagesPlaceholder 是关键,用于动态插入历史对话,实现上下文连续聊天(替代旧版 ConversationBufferMemory 的记忆功能)。| 符号将提示词模板和大模型串联,形成一个可调用的管道。调用时,只需传入 history(历史消息)和 input(用户新输入),管道会自动完成“拼接提示词 → 调用大模型 → 返回结果”的流程,无需像旧版那样手动管理 Chain 和 Memory。会话管理模块的核心是 st.session_state + 本地 JSON 文件,实现会话的“临时存储+长期保存”:
save_sessions(),确保数据同步保存到本地。流式输出是提升用户体验的关键,实现逻辑如下:
ChatTongyi 时,设置 streaming=True,开启流式输出。stream() 方法,返回一个生成器,逐块获取 AI 回复的内容。st.empty() 创建占位符,逐字符拼接 AI 回复,并实时更新占位符内容,添加“▌”模拟打字光标,实现打字机效果。streamlit_qwen_chat_v1.py。DASHSCOPE_API_KEY 为你自己的通义千问 API Key。streamlit run streamlit_qwen_chat_v1.py。Invalid API Key 错误,请检查 API Key 是否正确,是否有拼写错误,或是否在阿里云平台激活了通义千问服务。ChatTongyi 初始化时设置了 streaming=True,且 LangChain 版本为 v1.0+。save_sessions() 函数是否在每次会话操作后都被调用,确保 JSON 文件路径正确(默认保存在当前运行目录)。pip install langchain==1.0.0 streamlit==1.32.0)。本文实现的是基础版本,可根据需求扩展以下功能,提升应用的实用性和体验:
os.getenv("DASHSCOPE_API_KEY") 获取)。ainvoke、astream),提升应用响应速度。本文基于 LangChain v1.0+ 新版架构,结合 Streamlit 和通义千问,实现了一个功能完整、架构规范的多会话 AI 聊天页面。核心亮点在于:
通过本文的实战,你不仅能掌握 Streamlit 构建前端交互页面的方法,还能深入理解 LangChain v1.0+ 的 LCEL 架构,为后续构建更复杂的大模型应用(如智能体、RAG 系统)打下基础。