使用 Python 实现支持多轮对话的 CLI 聊天机器人(基于 Ollama)

使用 Python 实现支持多轮对话的 CLI 聊天机器人(基于 Ollama)

摘要: 本文在上一篇单轮问答的基础上,使用 Python 实现一个支持多轮对话的 CLI 聊天机器人。核心思路是改用 Ollama 的 /api/chat 接口,并在本地维护一个 messages 消息历史列表,每次请求时把完整对话历史一起发送给模型,从而让模型"记住"之前的对话内容。这个 messages 列表,正是 Agent 架构中"短期记忆"的最朴素实现。


前言

上一篇《Python 实现与 Ollama 运行的模型对话》介绍了如何使用 Python 调用 Ollama 的 /api/generate 接口与本地大模型进行单轮问答。

但是用过之后会发现一个问题:每次提问都是独立的,模型完全不记得之前聊过什么。 比如先问"我叫小明",再问"我叫什么名字",模型会一脸茫然。

原因很简单------上一篇的实现中,每次请求只发送了当前这一个问题(prompt),模型自然不知道上下文。

本文就来解决这个问题,实现一个支持多轮对话的命令行聊天机器人。

多轮对话的核心原理

让模型"记得"之前的对话,办法出乎意料地简单:

每次请求时,把之前所有的对话记录一起发给模型。

大语言模型本身是没有记忆的,它根据输入的文本预测下一个词。我们把完整的对话历史拼进输入里,模型读到历史,就能"推断"出上下文,表现得像记住了对话一样。

这个用来存放对话历史的列表,在 Agent 架构中有个专门的名字------短期记忆(Short-term Memory)。它有几个特点:

  • 随会话存亡:程序运行期间一直在,进程结束就没了;
  • 有容量上限:对话历史会越来越长,最终受模型上下文窗口限制;
  • 逐条累积:每轮对话的用户输入和模型回复,都要追加进去。

本文的实现,就是短期记忆的最朴素版本。

/api/generate 与 /api/chat 的区别

上一篇用的是 /api/generate 接口,它只接受一个 prompt 字段,适合单轮生成任务。

Ollama 为对话场景提供了专门的 /api/chat 接口:

对比项 /api/generate /api/chat
输入字段 prompt(单条字符串) messages(消息列表)
多轮对话 需手动拼接历史文本 原生支持,结构化传历史
消息角色 无角色概念 支持 system / user / assistant
适用场景 文本生成、补全 多轮对话

messages 是一个列表,每条消息包含 role(角色)和 content(内容)两个字段:

  • system:系统提示词,定义模型的行为和人设,对话开始时设置一次;
  • user:用户发送的消息;
  • assistant:模型的回复。

完整代码

创建文件 continueChat.py,内容如下:

python 复制代码
import requests
import json

class ContinueChatBot:
    def __init__(self, model="qwen2:7b", system_prompt="你是一个有帮助的AI助手。"):
        self.model = model
        self.url = "http://localhost:11434/api/chat"
        # 消息历史:system + 对话记录
        self.messages = [
            {"role": "system", "content": system_prompt}
        ]

    def ask(self, user_input):
        # 把用户的问题加入历史
        self.messages.append({"role": "user", "content": user_input})

        payload = {
            "model": self.model,
            "messages": self.messages,
            "stream": False
        }

        response = requests.post(self.url, json=payload)
        data = response.json()

        # 提取模型的回答
        assistant_reply = data["message"]["content"]

        # 把模型的回答也加入历史,下次对话就能记得
        self.messages.append({"role": "assistant", "content": assistant_reply})

        return assistant_reply

    def chat_loop(self):
        print("=== 多轮对话模式 ===")
        print("输入你的问题,回车发送。输入 'exit' 或 'quit' 结束。\n")

        while True:
            user_input = input("你: ").strip()
            if user_input.lower() in ("exit", "quit", "退出"):
                print("\n对话结束。")
                break
            if not user_input:
                continue

            print("模型思考中...")
            reply = self.ask(user_input)
            print(f"模型: {reply}\n")

if __name__ == "__main__":
    bot = ContinueChatBot(
        model="qwen2:7b",
        system_prompt="你是一个耐心、幽默的AI助手,喜欢用简洁的方式回答问题。"
    )
    bot.chat_loop()

代码解析

整个实现只有一个类 ContinueChatBot,重点在 messages 列表的维护上。

1. 初始化:放入系统提示词

python 复制代码
def __init__(self, model="qwen2:7b", system_prompt="你是一个有帮助的AI助手。"):
    self.messages = [
        {"role": "system", "content": system_prompt}
    ]

构造函数创建了一个空的对话历史,并放入一条 system 消息。系统提示词(System Prompt)用于设定模型的角色和行为风格,它始终位于历史的最前面。

2. 提问:双向追加消息

python 复制代码
def ask(self, user_input):
    self.messages.append({"role": "user", "content": user_input})   # 先记下问题
    # ... 发送请求 ...
    self.messages.append({"role": "assistant", "content": assistant_reply})  # 再记下回答
    return assistant_reply

这是多轮对话的关键步骤,顺序不能错:

  1. 发送前,先把用户的问题追加到历史里------这样本次请求就能带上这条新消息;
  2. 发送请求,拿到模型回复;
  3. 返回前,把模型的回复也追加到历史里------这样下一轮请求就能带上模型的回答。

每一轮都重复这个过程,messages 就完整记录了"系统设定 → 用户问 → 模型答 → 用户再问 → 模型再答......"的完整脉络。

3. 对话循环:CLI 交互

python 复制代码
def chat_loop(self):
    while True:
        user_input = input("你: ").strip()
        if user_input.lower() in ("exit", "quit", "退出"):
            break
        # ...

一个标准的 while 循环:input() 读取输入,空输入直接跳过,输入 exit / quit / 退出 结束对话。

4. 启动:自定义系统提示词

python 复制代码
bot = ContinueChatBot(
    model="qwen2:7b",
    system_prompt="你是一个耐心、幽默的AI助手,喜欢用简洁的方式回答问题。"
)

通过参数可以方便地更换模型和设定人设。改成 deepseek-r1:7b 等本地已拉取的模型名即可切换模型。

运行效果

确保 Ollama 服务正在运行(浏览器访问 http://localhost:11434 能返回 Ollama is running),然后执行:

bash 复制代码
python continueChat.py

运行效果:

text 复制代码
=== 多轮对话模式 ===
输入你的问题,回车发送。输入 'exit' 或 'quit' 结束。

你: 你好,我叫小明,请记住这个名字
模型思考中...
模型: 你好,小明!很高兴遇到你。你的名字会牢牢地刻在我的记忆中。有什么我能帮你解答的问题吗?

你: 我叫什么名字?
模型思考中...
模型: 你是小明。这个名字挺有亲切感的!有什么其他问题我可以帮助你解答的吗?

你: exit

对话结束。

可以看到,模型成功记住了第一轮对话中的信息------这正是 messages 历史列表起的作用。

完整流程

下面是多轮对话的完整交互流程,注意每次请求都会携带全部历史消息 :

目前的局限与改进方向

这个实现已经能用,但作为 Agent 的"短期记忆",它还有两个明显的问题:

1. 历史无限增长,最终会撑爆上下文窗口

对话轮数多了以后,messages 会越来越长。模型的上下文窗口是有限的(qwen2:7b 为 32K tokens),超出后 API 会直接报错。

改进思路:设置一个最大轮数,超过后丢弃最早的对话;或者当历史过长时,让模型先把早期对话总结成一段摘要,用摘要替换原文(这就是上下文压缩技术)。

2. 记忆只存在于内存中,程序退出就丢失

每次重新运行脚本,messages 都是全新开始的,模型又"失忆"了。

改进思路:把 messages 定期保存到文件或数据库,启动时加载------这就演进成了 Agent 的"长期记忆"。

这两个改进方向,分别对应记忆管理中的压缩 和持久化,后续文章会陆续展开。

总结

本文从单轮问答出发,通过改用 /api/chat 接口并维护一个 messages 消息历史列表,实现了一个支持多轮对话的 CLI 聊天机器人。要点回顾:

  • 多轮对话的本质:每次请求携带完整对话历史;
  • system / user / assistant 三种角色构成对话的基本结构;
  • messages 列表就是 Agent 短期记忆的最简实现;
  • 下一步的演进方向:上下文压缩与记忆持久化。

本文原创作者:冯一川(CSDN:ifeng12358),未经作者授权同意,请勿转载。

相关推荐
Febrie1 小时前
Codex插件推荐:Local Figma Agent MCP,不购买付费套餐,也能让 Agent 读懂并修改Figma设计稿
人工智能
edtoplort1 小时前
OpenAI紧急叫停GPT-6.1 Astra训练,奥尔特曼为何踩刹车
人工智能·gpt·算法
打工仔折腾 AI1 小时前
Docker镜像分层与卷挂载到底怎么工作:一次文件系统层面的实测分析
运维·人工智能·后端·python·docker·容器·性能优化
墨染天姬2 小时前
【人工智能训练师】python语法二
开发语言·人工智能·python
深蓝AI2 小时前
748GB 统一内存装进桌面:英伟达 DGX Station 本地跑万亿参数模型,瓶颈不在算力在插座
人工智能·agent
成旭先生2 小时前
AI 文本审核 API:一段中文文本判风险等级、命中标签与处置建议
java·前端·人工智能·api接口·内容风控·文本审核·ugc审核
草上飞95273 小时前
把前沿能力装进便宜产物,本身是多数模型还不会的能力
人工智能·深度学习·llm
林澈在路上3 小时前
2026生成后可发行的AI音乐工具怎么选
人工智能·版权·ai音乐·音乐发行·melo音乐
LoneEon3 小时前
CentOS7 部署 Nacos3.x 集群实战:从注册中心到 AI 管理中心
linux·人工智能·nacos