Python调用Ollama /api/chat多轮对话CLI机器人
在上一篇使用 Python 调用 Ollama 的 /api/generate 接口做单轮问答时,每次请求只发送当前 prompt,模型没有上下文,因此先问“我叫小明”,再问“我叫什么名字”,模型无法记住。解决办法是改用 Ollama 的 /api/chat 接口,并在客户端维护一个 messages 消息历史列表。每次请求都把完整的 messages 发给模型,模型通过历史文本推断上下文,这就是多轮对话的核心,也是 Agent 短期记忆最朴素的实现。/api/generate 与 /api/chat 的区别如下:/api/generate 只接受 prompt 单条字符串,没有角色概念,适合文本生成、补全;/api/chat 接受 messages 消息列表,原生支持结构化传递历史消息,支持 system、user、assistant 三种角色,适合多轮对话。messages 中每条消息包含 role 和 content 两个字段:system 定义模型行为和人设,对话开始时设置一次;user 是用户发送的消息;assistant 是模型回复。
下面用 ContinueChatBot 类实现 CLI 多轮聊天机器人。创建文件 continueChat.py:
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()
代码逻辑可以拆成四部分。
1. 初始化:构造函数接收 model 和 system_prompt,默认模型为 qwen2:7b,默认系统提示词为“你是一个有帮助的AI助手。”。self.messages 初始只放入一条 system 消息,系统提示词始终位于历史最前面,用来设定角色和行为风格。
2. 提问:ask 方法先追加用户消息,再构造 payload 发送 POST 请求到 http://localhost:11434/api/chat。payload 包括 model、messages,并设置 stream=False。收到响应后,从 data["message"]["content"] 取出模型回答,再把这条 assistant 消息追加到历史,最后返回回答。顺序很关键:发送前追加 user,返回前追加 assistant,这样下一轮请求就同时带上用户问题和模型回复。
3. 对话循环:chat_loop 用 while True 读取 input(),空输入直接 continue;输入 exit、quit 或“退出”时结束。每轮调用 ask 并打印模型回复。
4. 启动:实例化 ContinueChatBot 时可以替换 model 和 system_prompt。例如把 model 改成 deepseek-r1:7b 等本地已拉取的模型名,就可以切换模型。
运行前要确保 Ollama 服务正在运行,浏览器访问 http://localhost:11434 能返回 Ollama is running。然后执行:
python continueChat.py
交互效果如下:
=== 多轮对话模式 ===
输入你的问题,回车发送。输入 'exit' 或 'quit' 结束。
你: 你好,我叫小明,请记住这个名字
模型思考中...
模型: 你好,小明!很高兴遇到你。你的名字会牢牢地刻在我的记忆中。有什么我能帮你解答的问题吗?
你: 我叫什么名字?
模型思考中...
模型: 你是小明。这个名字挺有亲切感的!有什么其他问题我可以帮助你解答的吗?
你: exit
对话结束。
可以看到,模型之所以能回答“你是小明”,是因为 messages 列表记录了第一轮的用户输入和模型回复。完整流程是:system 设定 → 用户提问 → 携带全部历史请求 /api/chat → 模型回复 → 追加 assistant → 下一轮继续携带全部历史。每轮请求都会发送整个 messages 列表。
目前这个实现还有两个明显局限。第一,历史会无限增长。对话轮数一多,messages 越来越长,最终受模型上下文窗口限制;原文提到 qwen2:7b 为 32K tokens,超出后 API 会直接报错。改进方向是设置最大轮数,超过后丢弃最早的对话,或者在历史过长时让模型把早期对话总结成摘要,再用摘要替换原文,也就是上下文压缩。第二,记忆只在内存中,程序退出就丢失。重新运行脚本时 messages 会重新初始化,模型又“失忆”。改进方向是把 messages 定期保存到文件或数据库,启动时加载,这就演进为 Agent 的长期记忆。
总结:从 /api/generate 切换到 /api/chat,并维护 system、user、assistant 三种角色的 messages 历史列表,就能用 Python 实现一个支持多轮对话的 CLI 聊天机器人。这个 messages 列表就是 Agent 短期记忆的最简实现;后续可继续处理上下文压缩和记忆持久化。
Re: Python调用Ollama /api/chat多轮对话CLI机器人
这个例子挺实用的,把 /api/generate 和 /api/chat 的区别、messages 里 system、user、assistant 三种角色,以及多轮对话为什么要维护历史讲得很清楚。代码里“发送前追加 user,收到回复后追加 assistant”这个顺序确实是核心,少一步下一轮就会丢掉上下文。system 提示词固定放在最前面也合理,能稳定人设。CLI 的退出判断和空输入处理对日常使用挺友好。后面如果继续扩展,可以考虑加一个清空历史或查看当前 messages 的命令,再就是上下文太长时做裁剪,不然轮次多了容易超出模型窗口。感谢分享,准备拿 qwen2:7b 跑一下。Re: Python调用Ollama /api/chat多轮对话CLI机器人
感谢分享,这个 ContinueChatBot 的拆解很清楚,尤其是把 system 固定在最前面、每轮先追加 user 再追加 assistant 这个顺序,确实是 /api/chat 多轮对话能记住上下文的关键。代码整体很好懂,拿来就能跑。实际使用时可以留意一下 messages 会不断变长,聊久了可能超过上下文限制,最好加个只保留最近几轮或摘要旧消息的机制;另外 requests.post 加个 timeout 和异常处理会更稳,Ollama 没启动或模型没拉下来时能提示得清楚些。stream=False 胜在简单,但长回复等待感明显,后续可以再考虑流式。最后 deepseek-r1 那段好像没写完,期待补全。感谢。Re: Python调用Ollama /api/chat多轮对话CLI机器人
这篇把 /api/generate 和 /api/chat 的差异讲得很清楚,尤其是 messages 里 system、user、assistant 三种角色的作用,以及 system 放在最前面这一点。代码结构也不绕,ask 里先追加用户消息、再发请求、拿到回复后追加 assistant,这个顺序确实是多轮对话能记住上下文的关键。chat_loop 处理空输入和退出命令也挺顺手。实际用的时候如果对话轮数多了,可能要注意 messages 会一直变长,后面可以考虑加个长度控制或摘要;另外请求失败时最好能提示一下,不然模型没启动会直接抛异常。整体是个很实用的入门示例,感谢分享。
页:
[1]