实战-AI智能伴侣-会话记忆

实战-AI智能伴侣-会话记忆

2026年9月18日·#编程学习/python学习笔记Python/AI·1753 字 9 分钟
浏览量加载中...
AI 摘要

大模型本身没有记忆,靠"会话历史滚雪球"实现多轮对话:messages 累积、*解包展开与上下文顺序

问题:AI 记不住上一句#

与 AI 大模型的交互本质是无状态的,每一次请求响应都是相互独立的,AI 大模型本身没有真正的会话记忆能力。

课程里的例子很直观:先问”12 个苹果,3 个人怎么均分”,得到”每人 4 个”;接着问”那 2 个人呢?“,模型却反问”你是指哪两个人呢?”

原因:第二次请求里只有”那 2 个人呢?”这一句话,服务端根本不知道前文。

"messages": [
{ "role": "system", "content": "你是一名可爱的AI助手……" },
{ "role": "user", "content": "那2个人呢?" }
]

方案:会话历史滚雪球#

之前所有对话都塞进 messages 一起发过去,模型就”看起来记得”了。

"messages": [
{ "role": "system", "content": "你是一名可爱的AI助手……" },
{ "role": "user", "content": "12个苹果,3个人怎么均分?" },
{ "role": "assistant", "content": "嘻嘻,12个苹果分给3个人,每个人可以分到 **4个苹果** 哦~" },
{ "role": "user", "content": "那2个人呢?" }
]

这样模型就能正确回答”每人 6 个苹果”。每多聊一轮,这个列表就长一截,所以叫”滚雪球”。

代码实现#

1. 每轮把一问一答都存进 session_state#

# 保存用户输入的提示词
st.session_state.messages.append({"role": "user", "content": prompt})
...
# 保存大模型返回的结果
st.session_state.messages.append({"role": "assistant", "content": full_response})
Important

必须把 assistant 的回复也存下来。只存用户的问题是不行的:模型看到的是”我问了 3 个问题”,却看不到自己答过什么,前后逻辑会断裂。

2. 发请求时带上全部历史#

response = client.chat.completions.create(
model="deepseek-chat",
messages=[
{"role": "system", "content": system_prompt},
*st.session_state.messages # 把历史消息一条条"摊开"接到后面
],
stream=True
)
写法含义
[A, *B]列表解包:把 B 里的元素逐个展开,和 A 拼成一个新列表
结果[system消息, 历史user, 历史assistant, ..., 本轮user]
Warning

system 消息必须放在最前面,且只放一条。它负责设定身份和行为准则,位置错了模型的行为约束会失效。

3. 顺序为什么是这样的#

messages[0] system 设定身份
messages[1] user 第1轮提问
messages[2] assistant 第1轮回答
messages[3] user 第2轮提问
...
最后一条 user 本轮提问(在调用前已经 append 进去)

调用前先 append 用户的问题,所以列表最后一条正好是本轮提问,不用再单独拼一次。

代价与优化方向#

问题说明
Token 越来越贵每轮都要重发全部历史,Token 消耗随对话轮数增长
超出模型上下文上限历史太长会超过模型的最大上下文长度,需要截断

常见的优化方向(超出本章范围,知道有这回事即可):只保留最近 N 轮对话、或者让模型把早期对话压缩成一段摘要。

Tip

这也解释了为什么”会话记忆”这件事必须由客户端负责:模型是无状态的,记忆只能由调用方保存并在每次请求时重新提交。本章后面的 会话管理 就是把这份历史存到文件里,实现”关掉网页也能接着聊”。

相关#

练习题#

一、回忆填空(写完再展开对答案)#

  1. 与 AI 大模型的交互本质是 ____ 的:每一次请求响应相互独立,模型本身____ 真正的会话记忆能力
  2. 实现会话记忆的方案叫 ____(把之前对话都带上)
  3. 发请求时把历史”摊开”接在 system 后面的写法:messages=[{"role": "system", ...}, ____st.session_state.messages]
  4. 列表解包符号是 ____,作用是把列表里的元素逐个展开
  5. system 消息必须放在最 ____ 面,而且只放 ____ 条
  6. 调用前先 append 用户的问题,所以列表最后一条正好是 ____
  7. 只存用户问题、不存 assistant 回复会怎样:____
  8. 滚雪球的两个代价:每轮 Token 越来越 ____、可能超出模型的 ____ 上限
填空答案(做完再点开)
  1. 无状态 / 没有 2. 会话历史滚雪球 3. * 4. * 5. 前 / 一 6. 本轮提问 7. 模型看不到自己答过什么,前后逻辑会断 8. 贵 / 上下文长度

二、裸写题#

  • 2-1 模拟”滚雪球” 不接大模型:写一个函数 add(role, content) 往列表追加消息,然后模拟三轮对话(用户问 → 假设的 AI 答 → 用户再问),每一轮都把”这次要发给模型的完整 messages”打印出来,亲眼看到历史是怎么累积的。

    提示(先自己想,实在想不出再点开)

    一级 · 思路:发出去的是”system + 全部历史 + 本轮提问”,历史就是那个越来越长的列表 二级 · 方法:列表 append + 列表拼接([system消息] + 历史列表三级 · 骨架messages = [{"role": "system", "content": "……"}, ____history]

    参考答案(做完再点开)
    history = []
    def add(role, content):
    """往历史里追加一条消息"""
    history.append({"role": role, "content": content})
    def messages_for(question):
    """本轮要发给模型的完整 messages:system 在最前,后面接全部历史 + 本轮提问"""
    return [{"role": "system", "content": "你是小甜甜"}] + history + [{"role": "user", "content": question}]
    # 第一轮
    print(messages_for("12个苹果,3个人怎么均分?"))
    add("user", "12个苹果,3个人怎么均分?")
    add("assistant", "每个人可以分到 4 个苹果")
    # 第二轮:历史已经攒了 2 条
    msgs = messages_for("那2个人呢?")
    print(f"\n第二轮要发 {len(msgs)} 条:")
    for m in msgs:
    print(" ", m["role"], ":", m["content"])
    # 观察结论:同一个问题,轮次越靠后,发出去的消息条数越多 —— 这就是"滚雪球"
  • 2-2 给聊天页加上记忆 把基本交互那版改成带记忆的版本:每轮把 user 和 assistant 的消息都存进列表,发请求时带上全部历史;然后测两轮有前后关系的问题,验证 AI 能接上话。

    提示

    一级 · 思路:界面渲染用这份列表,发给模型也用这份列表——同一份数据 二级 · 方法st.session_state.messages.append(...) 两次(user 一次、assistant 一次) 三级 · 骨架messages=[{"role": "system", "content": system_prompt}, *st.session_state.messages]

    参考答案(做完再点开)
    import os
    import streamlit as st
    from openai import OpenAI
    st.set_page_config(page_title="AI智能伴侣", page_icon="🤖", layout="wide")
    st.title("AI智能伴侣")
    system_prompt = "你是一名非常可爱的AI助理, 你的名字叫小甜甜, 请你使用温柔可爱的语气回答用户的问题"
    if "messages" not in st.session_state:
    st.session_state.messages = []
    for message in st.session_state.messages:
    st.chat_message(message["role"]).write(message["content"])
    client = OpenAI(api_key=os.environ.get("DEEPSEEK_API_KEY"), base_url="https://api.deepseek.com")
    prompt = st.chat_input("请输入您要问的问题")
    if prompt:
    st.chat_message("user").write(prompt)
    st.session_state.messages.append({"role": "user", "content": prompt})
    response = client.chat.completions.create(
    model="deepseek-chat",
    messages=[
    {"role": "system", "content": system_prompt},
    *st.session_state.messages, # 关键:把历史一条条摊开接在后面
    ],
    stream=False,
    )
    reply = response.choices[0].message.content
    st.chat_message("assistant").write(reply)
    st.session_state.messages.append({"role": "assistant", "content": reply})
    # 验证:先问"12个苹果,3个人怎么均分",再问"那2个人呢" —— 这次 AI 能答对了

评论区

[ 标签 ]
# AI37# AI 编程2# AI工具1# Ajax2# Apifox1# AstrBot3# Astro2# CC Switch1# CDN2# Claude Code1# claudecode2# ClaudeCode1# Cloudflare2# CloudFlare2# CloudFlare-ImgBed3# coc3# CSS6# DeepSeek6# deepseek2# DELETE1# Docker1# EdgeOne3# Gist1# git1# GitHub1# hexo-circle-of-friends1# HTML6# HTTP5# ImageManager1# Java23# java13# JavaScript5# JDBC3# JSON2# JUnit1# LangChain25# Logback1# Maven6# Muse Spark1# Mybatis1# MyBatis4# MySQL28# MySql1# NapCat1# Node.js1# obsidian2# Obsidian5# OpenCode4# ORM1# PathVariable1# PicGo1# PyCharm1# Python65# RequestBody1# RequestMapping1# RESTful风格1# skills1# Slf4j1# SpringBoot11# SQL2# Streamlit5# Svelte2# TailwindCSS1# Telegram3# Tlias2# Vercel1# vscode2# Vue7# Waline3# WebDAV1# Web基础6# Web开发6# WinSCP1# YAML1# 三层架构1# 中二宣言1# 书籍1# 使用文档10# 写作1# 函数2# 刷步数1# 前端32# 动态1# 动漫1# 包1# 单词2# 博客7# 博客工作流1# 博客开发2# 参数接收1# 友链1# 反思2# 图床6# 地图1# 备份2# 大模型1# 奇思妙想1# 存储1# 学习方法6# 学校1# 宝塔面板3# 宝宝10# 对象1# 导航栏1# 工具2# 开发1# 开发工具1# 开发规范1# 开心1# 异常处理1# 影视2# 微信1# 性能优化2# 总结1# 想法15# 感受1# 感悟11# 指南1# 提示词工程1# 插件5# 故障排除1# 效率工具2# 教程10# 数据分析9# 数据库27# 数据结构1# 文件操作2# 斩神1# 日常92# 日志框架1# 朋友圈1# 朱元璋1# 模块1# 模板1# 正则表达式2# 测试1# 游戏2# 爬虫7# 生活迁移1# 电影2# 电脑1# 碎碎念1# 视觉识别1# 类1# 类型注解1# 网络基础2# 网络教室1# 羊毛2# 脚本2# 脚本工具1# 自动化2# 蓝奏云1# 订阅推荐2# 记录2# 评论系统1# 词根1# 词缀1# 说说1# 足迹1# 跑步2# 路径参数1# 转载2# 运动1# 部落冲突1# 配置1# 随机图1# 面向对象5# 音乐3# 音标1# 饮食1# 驼峰命名1# 高德地图1
[ 公告 ]

如果你喜欢,那么欢迎来到我的世界!

了解更多
[ 音乐 ]
封面

音乐

暂未播放

0:000:00
暂无歌词
找不到相关结果。
[ contents ]
[ 全部文章 ]