ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Python全栈入门到实战【AI实战篇 07】AI应用会话记忆功能全实现,第一个AI项目连续对话核心

Python全栈入门到实战【AI实战篇 07】AI应用会话记忆功能全实现,第一个AI项目连续对话核心 前言上一篇《AI实战篇 06》中我们已经完成了基于Streamlit的AI聊天消息展示模块开发解决了Streamlit无状态特性导致的历史消息被覆盖问题实现了聊天记录的持久化展示。但此时的应用仅能被动展示历史消息不具备真正的会话记忆能力——AI无法关联连续提问的上下文导致追问类场景的回复完全偏离预期。本篇作为第一个AI项目的第七篇我们将从问题发现、本质分析、思路梳理到代码落地一步步带你彻底实现AI应用的会话记忆功能这是所有对话类AI应用的核心体验基础。本文为Python全栈开发者与零基础AI入门者量身打造全程遵循“发现问题→分析问题→解决问题→验证效果”的完整开发逻辑仅需修改一行核心代码即可实现功能同时深入讲解底层原理和关键注意事项即使是完全没有AI开发经验的同学也能理解会话记忆的本质并成功实现连续对话功能。本节核心学习内容问题复现直观展示AI无法理解连续追问的异常现象本质解析深入理解大模型API无状态设计的核心特性思路梳理从问题定位到技术选型的完整解决思路推导代码落地核心原理讲解、一行代码修改实现会话记忆、完整最终代码功能测试多场景验证会话记忆效果、用户隔离验证关键注意事项上下文长度限制、消息格式、系统提示词位置等核心避坑点核心总结会话记忆实现核心速查表方便开发时快速查阅文章目录前言一、问题发现与本质分析1.1 问题场景复现1.2 问题本质深度解析大模型API的无状态特性此前代码的缺陷二、核心解决思路梳理2.1 问题定位思路2.2 原理推导思路2.3 技术选型思路2.4 代码实现思路2.5 验证思路三、解决方案与代码落地3.1 核心原理存储-传递-更新三步法3.2 核心代码修改仅需修改一行修改前的代码修改后的代码3.3 核心代码逐行解析3.4 完整最终代码四、功能测试与效果验证4.1 基础测试步骤4.2 测试结果4.3 进一步验证五、关键注意事项六、核心总结会话记忆实现核心速查表七、专栏订阅一、问题发现与本质分析在完成历史消息展示功能后我们进行连续对话测试发现了一个严重影响用户体验的核心问题应用仅能被动展示历史聊天消息但AI无法关联连续提问的上下文导致追问类场景的回复完全偏离预期。1.1 问题场景复现我们通过最典型的“分苹果”连续追问场景稳定复现这个问题第一轮提问在聊天输入框中输入“现在有12个苹果4个人怎么均分苹果”点击发送AI正常回复“12个苹果分给4个人每人可以分到3个苹果”第二轮追问紧接着输入“那两个人呢”点击发送AI异常回复无法理解“那两个人呢”的指代含义会回答“两个人可以一起学习、一起工作”等完全无关的内容1.2 问题本质深度解析很多人会误以为这是AI模型的能力缺陷但实际上这是所有大模型API的通用设计特性并非bug。我们通过排查代码和API调用日志最终定位到问题的根源大模型API的无状态特性所有大模型API都采用“单次请求-单次响应”的无状态设计服务器不会为任何用户存储历史会话信息模型的所有输出都100%依赖本次请求中传入的messages参数此前代码的缺陷此前的代码中调用DeepSeek API时的messages参数是这样的messages[{role:system,content:system_prompt},{role:user,content:prompt},]可以清晰地看到这里只传入了当前用户的单次提问没有携带任何历史会话信息。因此模型根本不知道用户之前问过“分苹果”的问题自然无法理解“那两个人呢”的指代含义。二、核心解决思路梳理基于上述问题分析我们梳理出了一套完整的解决思路从问题定位到最终验证环环相扣2.1 问题定位思路先通过最小可复现场景分苹果连续追问稳定复现问题排除基础错误检查代码语法、API密钥、网络连接、模型名称是否正确抓包查看API请求参数对比正常请求和异常请求的差异发现核心差异异常请求的messages参数中缺少历史会话信息得出结论问题根源是API调用时未传递上下文而非模型能力不足2.2 原理推导思路既然大模型API本身不存储任何会话那么要实现会话记忆只能由客户端主动存储并传递上下文模型只能识别[{role: xxx, content: xxx}]格式的消息列表因此历史消息也必须遵循这个格式每次调用API时需要将“系统提示词 所有历史用户提问 所有历史AI回复”一起传递给模型这样模型就能看到完整的对话过程从而理解连续提问的上下文关联2.3 技术选型思路我们需要一个轻量、会话级、支持用户隔离的存储容器来保存历史消息逐一排除不合适的方案❌ 普通变量Streamlit每次交互都会重新运行整个脚本普通变量会被重新初始化无法保留数据❌ 本地文件多用户场景下会出现文件读写冲突且无法实现用户隔离❌ 数据库对于轻量应用来说过于复杂增加开发和部署成本✅Streamlit原生st.session_state会话级别的字典存储生命周期与用户浏览器会话一致天然支持用户隔离无需额外配置是最优方案2.4 代码实现思路初始化阶段在st.session_state中创建一个空列表messages用于存储所有历史聊天消息页面渲染阶段遍历messages列表按角色用户/助手渲染所有历史消息用户输入阶段捕获用户的新提问先渲染到页面再追加到messages列表API调用阶段将系统提示词与messages列表中的所有历史消息拼接作为messages参数传入API结果处理阶段提取AI的回复渲染到页面再追加到messages列表为下一次提问准备上下文2.5 验证思路再次执行“分苹果”的连续追问场景验证AI能否正确关联上下文多轮连续提问验证会话记忆是否能持续生效刷新浏览器页面验证历史消息是否仍然保留打开多个浏览器标签页验证不同用户的会话是否相互隔离三、解决方案与代码落地3.1 核心原理存储-传递-更新三步法实现会话记忆的核心逻辑可以总结为“存储-传递-更新”三步法存储用st.session_state.messages存储所有历史聊天消息格式为[{role: xxx, content: xxx}]传递调用API时将系统提示词与所有历史消息一起传递给模型更新每次用户提问、AI回复后都将新内容追加到st.session_state.messages中形成上下文闭环3.2 核心代码修改仅需修改一行对比历史消息展示版本的代码我们只需要修改API调用时的messages参数这一行就能实现完整的会话记忆功能。修改前的代码# 调用OpenAI APIresponseclient.chat.completions.create(modeldeepseek-chat,messages[{role:system,content:system_prompt},{role:user,content:prompt},],streamFalse)修改后的代码# 调用OpenAI API核心修改传入全量历史消息responseclient.chat.completions.create(modeldeepseek-chat,messages[{role:system,content:system_prompt},*st.session_state.messages,# 解包session_state中的所有历史消息],streamFalse)3.3 核心代码逐行解析*st.session_state.messages这是Python中的解包语法作用是将st.session_state.messages列表中的每一个元素都作为独立的元素传入messages数组中。举个具体的例子如果st.session_state.messages中有两条历史消息st.session_state.messages[{role:user,content:现在有12个苹果4个人怎么均分苹果},{role:assistant,content:12个苹果分给4个人每人可以分到3个苹果}]那么*st.session_state.messages解包后最终传递给API的messages参数就变成了messages[{role:system,content:我是一名Python编程老师我的名字叫做小辉辉有10年的教学经验},{role:user,content:现在有12个苹果4个人怎么均分苹果},{role:assistant,content:12个苹果分给4个人每人可以分到3个苹果}]这样模型就能完整看到之前的对话过程当用户再问“那两个人呢”时模型就知道是在问“12个苹果分给2个人怎么分”。3.4 完整最终代码将ai_chat.py中的代码全部替换为以下内容importstreamlitasstimportosfromopenaiimportOpenAI# 设置页面的配置项st.set_page_config(page_title小辉AI,# 标题page_iconimgs/logo.png,# 图标layoutwide,# 布局占页面全部initial_sidebar_stateexpanded,# 侧边栏状态menu_items{})# 页面的大标题st.title(小辉AI)# 设置logo图片st.logo(imgs/logo.png,sizelarge)# 创建OpenAI客户端clientOpenAI(api_keyos.environ.get(DEEPSEEK_API_KEY),base_urlhttps://api.deepseek.com)# 初始化聊天信息ifmessagesnotinst.session_state:st.session_state.messages[]# 创建一个列表用于存储聊天信息, 默认值为空# 定义系统提示词system_prompt我是一名Python编程老师我的名字叫做小辉辉有10年的教学经验# 将聊天信息显示在页面上formessageinst.session_state.messages:ifmessage[role]user:st.chat_message(user).write(message[content])else:st.chat_message(assistant).write(message[content])# 消息输入框promptst.chat_input(请输入您的问题)# prompt获取到用户输入的提示词ifprompt:st.chat_message(user).write(prompt)# 将用户输入的提示词添加到session_state.messages中st.session_state.messages.append({role:user,content:prompt})# 调用OpenAI API核心修改传入全量历史消息responseclient.chat.completions.create(modeldeepseek-chat,messages[{role:system,content:system_prompt},*st.session_state.messages,# 获取session_state.messages中的所有消息],streamFalse)st.chat_message(assistant).write(response.choices[0].message.content)print(response.choices[0].message.content)# 将OpenAI API返回的答案添加到session_state.messages中st.session_state.messages.append({role:assistant,content:response.choices[0].message.content})四、功能测试与效果验证4.1 基础测试步骤按CtrlC终止之前的Streamlit服务重新执行启动命令streamlit run .\ai_chat.py第一轮提问输入“现在有12个苹果4个人怎么均分苹果”点击发送等待AI回复完成后第二轮追问输入“那两个人呢”点击发送4.2 测试结果AI成功关联上一轮的上下文返回了完全符合预期的结果第一轮回复“12个苹果分给4个人每人可以分到3个苹果”第二轮回复“12个苹果分给2个人的话每人可以分到6个苹果”4.3 进一步验证继续第三轮追问“那三个人呢”AI会正确回答“每人分4个苹果”刷新浏览器页面所有历史消息仍然保留在页面上打开一个新的浏览器标签页访问相同地址会得到一个全新的空白会话验证了不同用户的会话相互隔离五、关键注意事项上下文长度限制DeepSeek-chat模型的上下文窗口为128k token当会话消息过多时会超出token限制导致API调用失败。此时可以通过截断历史消息解决例如只保留最近的20条消息# 在调用API前添加这行代码st.session_state.messagesst.session_state.messages[-20:]消息格式正确性st.session_state.messages中的每一条消息都必须严格遵循{role: xxx, content: xxx}的格式不能有其他格式的元素否则会导致API调用报错。系统提示词位置系统提示词必须放在messages数组的第一个位置不能放在中间或末尾否则模型可能无法正确识别系统提示词。会话生命周期st.session_state的生命周期与浏览器标签页一致关闭标签页后会话会自动清除。如果需要跨会话记忆需要结合数据库实现消息持久化。六、核心总结会话记忆实现核心速查表为了方便后续开发时快速查阅整理了会话记忆实现的核心速查表核心环节关键操作核心要点问题本质大模型API无状态不存储历史会话所有输出仅依赖本次传入的messages参数核心原理存储-传递-更新三步法客户端主动存储并传递完整历史上下文存储方案st.session_state.messages会话级存储天然支持用户隔离核心代码修改解包全量历史消息传入API*st.session_state.messages消息格式{“role”: “user/assistant”, “content”: “xxx”}必须严格遵循否则API调用失败系统提示词必须放在messages数组的第一个位置不能放在中间或末尾长度控制截断早期非关键对话避免超出模型的token上限用户隔离不同用户的st.session_state相互独立无需额外配置自动实现七、专栏订阅专栏优点《Python从入门到实战》专栏内容涵盖Python基础到高级编程、并发编程进程/线程/协程、网络编程TCP/UDP/Socket、核心内置/第三方模块、数据库核心实战、Web开发Django/Flask/FastAPI框架、数据库MySQL/ORM/异步数据库、网络爬虫同步/异步/分布式、AI实战、Linux部署运维等全栈核心知识以项目驱动教学构建清晰学习路径适合零基础入门和进阶提升的同学跟着一步步从入门到精通专栏地址https://blog.csdn.net/zsh_1314520/category_13108073.html文章是永久吗一次订阅后可永久免费查看专栏内所有文章后续会持续更新全栈相关内容第一时间获取最新教程有答疑交流群吗订阅专栏后有专属的全栈学习答疑群群内提供专业问题答疑、和众多学习者抱团取暖一起沉淀技术、赋能成长进群方式订阅专栏后可直接在专栏内申请加入答疑群或私信博主沟通进群事宜https://bbs.csdn.net/topics/620104702更多干货点赞收藏关注博主不迷路博主博客链接https://blog.csdn.net/zsh_1314520?spm1000.2115.3001.5343专注Python全栈技术分享评论区留言问题会一一回复助力大家轻松搞定Python全栈【原创声明】除本文原文地址以外如发现同款内容皆为盗版本文已收录于《Python全栈从入门到实战》请勿购买盗版文章和专栏如购买盗版内容不提供任何服务。原文地址https://blog.csdn.net/zsh_1314520/article/details/163779577
返回列表