LangChain实战:基于Streamlit+ LangChain + Qwen 快速构建一个多会话AI聊天页面

0 阅读16分钟

在大模型应用开发中,对话系统是最常见的场景之一。LangChain 作为大模型应用开发框架,在 v1.0 版本中进行了架构大重构,摒弃了旧版冗余组件,引入了更简洁、可扩展的 LCEL(LangChain 表达式语言),让开发者能更高效地构建对话应用。

本文将手把手教你,使用 Streamlit(前端交互)+ LangChain v1.0+(大模型调用与流程管理)+ 通义千问(大模型),实现一个功能完整、架构规范的多会话AI聊天页面,支持会话新建、切换、删除、清空,以及本地会话持久化、流式输出等核心功能,全程采用 LangChain v1.0+ 新版写法,彻底抛弃旧版 Chain 和 Memory 组件。

一、技术选型与核心优势

在开始编码前,先明确各技术栈的选型原因和核心优势,确保整个技术架构简洁、高效、可维护。

1. 前端:Streamlit

Streamlit 是一款专为数据科学和机器学习开发者设计的 Web 应用框架,无需前端开发经验,用 Python 代码即可快速构建交互式页面。选择它的核心原因:

  • 开发效率极高:几行代码就能实现聊天输入框、会话列表、消息展示等核心组件,无需编写 HTML/CSS/JS。
  • 原生支持聊天组件:st.chat_messagest.chat_input 可直接实现聊天界面,无需额外封装。
  • 会话状态管理:st.session_state 可轻松保存会话数据、当前选中会话等状态,刷新页面不丢失(配合本地文件持久化,实现长期保存)。
  • 流式输出支持:通过 st.empty() 占位符,可轻松实现 AI 回复的“打字机效果”,提升用户体验。

2. 大模型框架:LangChain v1.0+

LangChain v1.0+ 相比旧版(v0.x)有了根本性的架构优化,核心优势的是引入了 LCEL,让大模型调用流程更简洁、更灵活。本文全程采用新版写法,核心特点:

  • 摒弃旧版冗余组件:不再使用 ConversationChainConversationBufferMemory 等旧版组件,改用更轻量的 ChatPromptTemplate + MessagesPlaceholder 实现对话记忆。
  • LCEL 管道式调用:通过 | 符号将提示词模板、大模型串联成管道,调用逻辑一目了然,可灵活扩展(如添加输出解析、过滤等中间环节)。
  • 新版模型封装:使用 ChatTongyi(而非旧版 Tongyi),更贴合大模型的聊天交互场景,支持流式输出、温度调节等核心参数。
  • 可扩展性强:支持异步调用、多模型切换、RAG(检索增强生成)等高级功能,为后续功能升级预留空间。

3. 大模型:通义千问(qwen-turbo)

选择通义千问(阿里云出品)的核心原因:

  • 免费额度高:qwen-turbo 模型免费额度充足,适合开发者调试和小型应用部署,无需担心成本。
  • 中文支持优秀:针对中文场景优化,对话流畅、理解准确,适合中文用户交互。
  • LangChain 原生支持:通过 langchain-community 包中的 ChatTongyi 可直接调用,无需额外封装 API 请求。

4. 会话持久化:本地 JSON 文件

采用本地 JSON 文件保存会话数据,优势是无需依赖数据库,部署简单、轻量,适合小型应用;后续可轻松扩展为 MySQL、Redis 等数据库,兼容性强。

二、环境准备与依赖安装

在开始编码前,先完成环境配置和依赖安装,确保所有组件能正常运行。

1. 环境要求

  • Python 版本:3.8+(推荐 3.10+,兼容性更好)
  • 依赖包:streamlit、langchain、langchain-community、dashscope

2. 依赖安装命令

打开终端,执行以下命令安装所有依赖:

pip install streamlit langchain langchain-community dashscope

说明:

  • streamlit:构建前端交互页面
  • langchain:LangChain 核心框架(v1.0+)
  • langchain-community:社区提供的第三方组件(包含 ChatTongyi
  • dashscope:阿里云通义千问的 Python SDK,用于调用大模型 API

3. 通义千问 API Key 获取

调用通义千问需要 API Key,获取步骤如下:

  1. 访问 阿里云百炼平台,使用阿里云账号登录。
  2. 登录后,点击右上角头像 → 「API-KEY管理」。
  3. 点击「创建API-KEY」,生成后复制保存(后续代码中需要使用)。

注意:API Key 属于敏感信息,请勿泄露,后续可通过环境变量配置,避免硬编码。

三、核心功能设计与架构解析

本文实现的 AI 聊天页面,核心功能如下:

  • 会话管理:新建会话、切换会话、删除会话、清空当前会话
  • 对话功能:用户输入、AI 流式回复(打字机效果)、上下文连续对话
  • 持久化:会话数据保存到本地 JSON 文件,关闭页面、重启应用不丢失
  • 交互体验:简洁美观的 UI、会话状态提示、操作反馈

整体架构流程图

暂时无法在豆包文档外展示此内容

核心模块拆解

  1. 前端交互模块:负责页面渲染、用户操作接收(会话管理、消息输入)、消息展示。
  2. 会话管理模块:负责会话的新建、切换、删除、清空,以及会话数据的加载和保存。
  3. LangChain 对话模块:基于 LCEL 构建对话流程,负责提示词模板构造、历史消息拼接、大模型调用、流式输出处理。
  4. 持久化模块:负责将会话数据(聊天记录)保存到本地 JSON 文件,确保数据不丢失。

四、完整代码实现与详细解析

下面是完整的代码实现,每一部分都添加了详细注释,同时结合前文的架构解析,帮你理解每一行代码的作用。代码可直接复制运行,只需替换自己的通义千问 API Key 即可。

完整代码(streamlit_qwen_chat_v1.py)

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+ 的新版写法核心。

1. LangChain v1.0+ 对话链(LCEL 核心)

这是本文的重点,也是 LangChain v1.0+ 与旧版的核心区别。代码中 get_chain() 函数实现了 LCEL 管道的构建,核心逻辑如下:

  • ChatTongyi:新版通义千问聊天模型封装,替代了旧版的 Tongyi(旧版属于 LLM,新版 ChatTongyi 属于 ChatModel,更贴合聊天场景,支持流式输出)。
  • ChatPromptTemplate.from_messages:新版提示词模板,支持传入多轮消息(系统提示词、历史消息、用户输入),MessagesPlaceholder 是关键,用于动态插入历史对话,实现上下文连续聊天(替代旧版 ConversationBufferMemory 的记忆功能)。
  • LCEL 管道(prompt | llm):这是新版的核心写法,通过 | 符号将提示词模板和大模型串联,形成一个可调用的管道。调用时,只需传入 history(历史消息)和 input(用户新输入),管道会自动完成“拼接提示词 → 调用大模型 → 返回结果”的流程,无需像旧版那样手动管理 Chain 和 Memory。

2. 会话管理与持久化

会话管理模块的核心是 st.session_state + 本地 JSON 文件,实现会话的“临时存储+长期保存”:

  • st.session_state:Streamlit 提供的会话状态管理工具,用于保存当前应用的所有会话数据和当前选中会话,刷新页面时不会丢失(但重启应用会丢失,因此需要配合本地文件持久化)。
  • load_sessions() / save_sessions():这两个函数负责将会话数据读写到本地 JSON 文件,确保重启应用后,之前的会话记录依然存在。
  • 会话操作函数(new_session/switch_session/clear_session/del_session):封装了会话的所有操作,每个操作后都会调用 save_sessions(),确保数据同步保存到本地。

3. 流式输出(打字机效果)

流式输出是提升用户体验的关键,实现逻辑如下:

  1. 初始化 ChatTongyi 时,设置 streaming=True,开启流式输出。
  2. 调用 LCEL 管道的 stream() 方法,返回一个生成器,逐块获取 AI 回复的内容。
  3. 使用 st.empty() 创建占位符,逐字符拼接 AI 回复,并实时更新占位符内容,添加“▌”模拟打字光标,实现打字机效果。
  4. 当 AI 回复全部生成后,移除光标,显示完整回复。

五、应用运行与效果演示

image.png

1. 运行步骤

  1. 将上述代码保存为 streamlit_qwen_chat_v1.py
  2. 替换代码中的 DASHSCOPE_API_KEY 为你自己的通义千问 API Key。
  3. 打开终端,执行以下命令运行应用:streamlit run streamlit_qwen_chat_v1.py
  4. 应用启动后,会自动打开浏览器,访问地址:http://localhost:8501

2. 效果演示

  • 新建会话:点击左侧“新建会话”,生成带有时间戳的会话,切换到该会话即可开始聊天。
  • 发送消息:在底部输入框输入问题,回车发送,AI 会以打字机效果返回回复,同时保存对话记录。
  • 会话管理:可切换历史会话(自动恢复该会话的聊天记录)、删除不需要的会话、清空当前会话的聊天记录。
  • 持久化效果:关闭浏览器、重启应用后,之前的会话记录依然存在,无需重新开始。

六、常见问题与优化方向

1. 常见问题解决

  • API Key 错误:如果出现 Invalid API Key 错误,请检查 API Key 是否正确,是否有拼写错误,或是否在阿里云平台激活了通义千问服务。
  • 流式输出不生效:确保 ChatTongyi 初始化时设置了 streaming=True,且 LangChain 版本为 v1.0+。
  • 会话数据丢失:检查 save_sessions() 函数是否在每次会话操作后都被调用,确保 JSON 文件路径正确(默认保存在当前运行目录)。
  • 依赖版本冲突:如果出现导入错误,可尝试指定依赖版本(如 pip install langchain==1.0.0 streamlit==1.32.0)。

2. 优化方向(可扩展功能)

本文实现的是基础版本,可根据需求扩展以下功能,提升应用的实用性和体验:

  • 敏感信息优化:将 API Key 配置到环境变量,避免硬编码(使用 os.getenv("DASHSCOPE_API_KEY") 获取)。
  • UI 美化:添加暗黑模式、微信风格聊天气泡、会话重命名功能。
  • 多模型切换:支持切换通义千问不同模型(qwen-turbo、qwen-plus),或添加其他大模型(如文心一言、DeepSeek)。
  • 聊天记录导出:支持将当前会话的聊天记录导出为 TXT、Markdown 文件。
  • RAG 检索增强:添加文档上传功能,实现基于文档的问答(LangChain v1.0+ 原生支持 RAG 流程)。
  • 异步调用:使用 LangChain 的异步 API(ainvokeastream),提升应用响应速度。
  • 错误处理:添加异常捕获(如 API 调用失败、网络错误),显示友好的错误提示,提升应用稳定性。

七、最后

本文基于 LangChain v1.0+ 新版架构,结合 Streamlit 和通义千问,实现了一个功能完整、架构规范的多会话 AI 聊天页面。核心亮点在于:

  • 完全采用 LangChain v1.0+ 新版写法,摒弃旧版冗余组件,使用 LCEL 管道式调用,代码更简洁、可扩展。
  • 功能完整,涵盖会话管理、流式输出、本地持久化等核心需求,可直接用于生产环境或二次开发。
  • 入门门槛低,无需前端开发经验,Python 开发者可快速上手,同时详细的代码解析帮助理解 LangChain v1.0+ 的核心变化。

通过本文的实战,你不仅能掌握 Streamlit 构建前端交互页面的方法,还能深入理解 LangChain v1.0+ 的 LCEL 架构,为后续构建更复杂的大模型应用(如智能体、RAG 系统)打下基础。