前端转agent-第一周【python】-05 Ollama+Qwen3实战:会话记忆实战

16 阅读6分钟

AI Agent 会话记忆:用 Ollama + Qwen3:0.6b 从零搭建带记忆的聊天机器人

习惯了 JavaScript/TypeScript 里的状态管理,那么 Python 里的 “Agent 记忆” 就是那个一直在用的 messages 数组。
今天我们跳过 Python 基础,直接用 Ollama 本地模型跑一个带会话记忆的聊天 Demo。

为什么 Agent 需要记忆?

大语言模型本身是无状态的——每次 API 调用都是独立的,模型不记得你上一句说过什么。这就像你在前端写一个聊天应用,如果不把聊天记录存下来(比如放在 useState 里),每次发送消息时只发当前文本,对话就永远连不起来。

记忆 = 上下文窗口中的历史消息列表。在 Python 里,它就是一个 list[dict],每个 dict 都有 rolecontent

messages = [
    {"role": "user", "content": "我叫小明"},
    {"role": "assistant", "content": "你好小明!"},
]

是不是很像你在 TypeScript 里定义的 type Message = { role: 'user' | 'assistant'; content: string }
没错,思维模型完全一样,只是换成了 Python 的字典和列表。

准备环境:Ollama + Qwen3:0.6b

我们选用阿里通义千问的 0.6B 超小模型,完全可以在普通笔记本上跑。
确保你已经安装了 Ollama,然后拉取模型:

# 安装 Ollama (如果还没有)
# macOS / Linux: curl -fsSL https://ollama.com/install.sh | sh
# 或直接下载桌面版: https://ollama.com

ollama pull qwen3:0.6b

Python 需要安装 ollama 官方库:

pip install ollama

最简版:自带记忆的命令行聊天

先写一个不到 40 行的脚本,直观感受“把历史消息一股脑塞进去”的效果。

# simple_memory_chat.py
import ollama

MODEL = "qwen3:0.6b"

# 系统提示词:定义角色和行为规范,qwen3:0.6b 只有 6亿参数 ,能力非常有限。即使加了 System Prompt,小模型的指令遵循能力仍然较弱。
SYSTEM_PROMPT = """你是一个友好、聪明的 AI 聊天助手。
重要规则:
1. 你是"助手",用户是"用户",不要混淆角色
2. 认真记住用户告诉你的信息(名字、住址、宠物等)
3. 回答要简洁、准确,基于用户提供的上下文
4. 如果用户问之前提到过的信息,从记忆中准确回答
5. 不要编造或猜测用户没说过的信息"""

messages = [{"role": "system", "content": SYSTEM_PROMPT}]  # 这就是记忆

print("🤖 带记忆的聊天机器人 (输入 /clear 清空记忆, /bye 退出)")
while True:
    user_input = input("\n🧑 你: ")
    if user_input == "/bye":
        break
    if user_input == "/clear":
        messages = [{"role": "system", "content": SYSTEM_PROMPT}]  # 保留系统提示词
        print("✅ 记忆已清空")
        continue

    # 将用户消息追加到记忆
    messages.append({"role": "user", "content": user_input})

    # 调用模型,传入完整历史
    response = ollama.chat(model=MODEL, messages=messages)
    assistant_msg = response["message"]["content"]

    # 把助手回复也加入记忆
    messages.append({"role": "assistant", "content": assistant_msg})
    print(f"🤖 助手: {assistant_msg}")

测试一下:

🧑 你: 我叫小明,我有一只猫叫花花
🤖 助手: 你好小明,花花这个名字真好听!
🧑 你: 我的猫叫什么名字?
🤖 助手: 你的猫叫花花。

(模型能力有限,回答可能不太正常。条件允许可以换大一点的模型。)

是不是很简单?messages 数组一直在变长,每次请求都把全部历史发给模型,模型自然就“记住”了前面的对话。

JS/TS 对比:这就好比你用 Express 写一个 /chat 接口,在服务器内存里维护了一个 const history = [],每次请求都 history.push(userMsg),然后调用 OpenAI 时把整个 history 传进去。
缺点也是一样的:服务重启后记忆丢失,而且数组太长会撑爆上下文窗口。

进阶:一个可管理的 Memory 类

实际做 Agent 时,不能无限制地堆积历史消息(Qwen3:0.6b 上下文窗口 32768 个 token,但历史太长也会让回复变慢,甚至丢失早期信息)。我们需要一个 MemoryManager,至少具备:

  • 最大消息条数限制(滑动窗口)
  • 可选:保留系统提示词(system prompt)
  • 可选:自动生成摘要(本次先做简单截断)
# memory_manager.py
from typing import Optional

class MemoryManager:
    """管理对话记忆,就像前端管理聊天记录的 store"""

    def __init__(self, system_prompt: Optional[str] = None, max_turns: int = 20):
        self.system_prompt = system_prompt
        self.max_turns = max_turns  # 保留最近的 N 轮对话(一轮 = user + assistant)
        self.messages = []
        if system_prompt:
            self.messages.append({"role": "system", "content": system_prompt})

    def add_user_message(self, content: str):
        self.messages.append({"role": "user", "content": content})

    def add_assistant_message(self, content: str):
        self.messages.append({"role": "assistant", "content": content})
        self._trim_history()

    def _trim_history(self):
        """只保留最近 max_turns 轮对话 + 系统提示词(如果有)"""
        # 计算需要保留的消息起始索引
        # 系统消息永远保留,所以从 index 1 开始计算
        system_offset = 1 if self.system_prompt else 0
        # 每轮对话包含 2 条消息(user + assistant),我们要保留 max_turns 轮
        max_messages = system_offset + self.max_turns * 2
        if len(self.messages) > max_messages:
            # 保留最后 max_messages 条
            self.messages = [self.messages[0]] + self.messages[-(max_messages - 1):] if system_offset else self.messages[-max_messages:]

    def get_messages(self) -> list[dict]:
        return self.messages

    def clear(self):
        self.messages = []
        if self.system_prompt:
            self.messages.append({"role": "system", "content": self.system_prompt})

使用示例:

# memory_chat_with_manager.py
import ollama
from memory_manager import MemoryManager

MODEL = "qwen3:0.6b"

memory = MemoryManager(
    system_prompt="你是一个友善的助手,喜欢用短句回答,并且记住用户提到的所有细节。",
    max_turns=5   # 只保留最近5轮对话
)

print("🧠 带记忆管理的聊天 (最多记住5轮)")
while True:
    user_input = input("\n🧑 你: ")
    if user_input == "/bye":
        break
    if user_input == "/clear":
        memory.clear()
        print("✅ 记忆已清空")
        continue

    memory.add_user_message(user_input)
    messages = memory.get_messages()

    response = ollama.chat(model=MODEL, messages=messages)
    assistant_msg = response["message"]["content"]
    memory.add_assistant_message(assistant_msg)

    print(f"🤖 助手: {assistant_msg}")
    print(f"(当前记忆条数: {len(messages)})")

JS/TS 开发者的视角

如果你用 TypeScript 实现类似的 MemoryManager,大概会是这样:

interface Message {
  role: "system" | "user" | "assistant";
  content: string;
}

class MemoryManager {
  private messages: Message[] = [];
  
  constructor(private systemPrompt?: string, private maxTurns = 20) {
    if (systemPrompt) this.messages.push({ role: "system", content: systemPrompt });
  }

  addUserMessage(content: string) { /* ... */ }
  addAssistantMessage(content: string) { /* ... + trim */ }
  getMessages(): Message[] { return this.messages; }
}

可以看到逻辑几乎一模一样,只是语法不同。记忆的本质就是一个状态容器,在前端你可能用 Redux、Zustand,在 Python 里我们用一个简单的类封装。

还能怎么增强?

  1. 摘要压缩:当对话过长时,调用模型把早期对话压缩成一段摘要,放进系统提示里,类似于“这是之前的对话要点:...”
  2. 持久化存储:把 messages 存到 SQLite 或 JSON 文件里,就像前端的 localStorage
  3. 多用户记忆隔离:用 session_id 作为 key,存储不同的消息列表,类似 Web 后端的 session。

用 Ollama 可以轻松实现这些,因为它的 Python 库封装了类似 OpenAI 的接口,只要专注管理好 messages 列表就行。

总结

  • Agent 记忆 = 消息历史列表,和你在前端管理聊天记录一样。
  • Python 中用 list[dict] 存储,每轮对话追加并传给 ollama.chat()
  • 实际项目需要控制历史长度(滑动窗口),甚至可以加入摘要。
  • Ollama + Qwen3:0.6b 让你在本地就能玩转带记忆的对话,0 成本,随便折腾。但是模型较小,不太智能。