实战-AI智能伴侣-会话记忆
实战-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})必须把 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] |
system 消息必须放在最前面,且只放一条。它负责设定身份和行为准则,位置错了模型的行为约束会失效。
3. 顺序为什么是这样的
messages[0] system 设定身份messages[1] user 第1轮提问messages[2] assistant 第1轮回答messages[3] user 第2轮提问...最后一条 user 本轮提问(在调用前已经 append 进去)调用前先 append 用户的问题,所以列表最后一条正好是本轮提问,不用再单独拼一次。
代价与优化方向
| 问题 | 说明 |
|---|---|
| Token 越来越贵 | 每轮都要重发全部历史,Token 消耗随对话轮数增长 |
| 超出模型上下文上限 | 历史太长会超过模型的最大上下文长度,需要截断 |
常见的优化方向(超出本章范围,知道有这回事即可):只保留最近 N 轮对话、或者让模型把早期对话压缩成一段摘要。
这也解释了为什么”会话记忆”这件事必须由客户端负责:模型是无状态的,记忆只能由调用方保存并在每次请求时重新提交。本章后面的 会话管理 就是把这份历史存到文件里,实现”关掉网页也能接着聊”。
相关
- Apifox 测试大模型接口(无状态与滚雪球的概念演示)
- 实战-AI智能伴侣-会话管理
练习题
一、回忆填空(写完再展开对答案)
- 与 AI 大模型的交互本质是 ____ 的:每一次请求响应相互独立,模型本身____ 真正的会话记忆能力
- 实现会话记忆的方案叫 ____(把之前对话都带上)
- 发请求时把历史”摊开”接在 system 后面的写法:
messages=[{"role": "system", ...}, ____st.session_state.messages] - 列表解包符号是 ____,作用是把列表里的元素逐个展开
system消息必须放在最 ____ 面,而且只放 ____ 条- 调用前先 append 用户的问题,所以列表最后一条正好是 ____
- 只存用户问题、不存 assistant 回复会怎样:____
- 滚雪球的两个代价:每轮 Token 越来越 ____、可能超出模型的 ____ 上限
填空答案(做完再点开)
- 无状态 / 没有 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 osimport streamlit as stfrom openai import OpenAIst.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.contentst.chat_message("assistant").write(reply)st.session_state.messages.append({"role": "assistant", "content": reply})# 验证:先问"12个苹果,3个人怎么均分",再问"那2个人呢" —— 这次 AI 能答对了
评论区
如果你喜欢,那么欢迎来到我的世界!
了解更多













