在本地LLM聊天中实现消息历史与上下文:Python实战指南
上一部分基于LiteLLM和Ollama的控制台聊天独立处理每个请求。模型无法查看之前的消息,这会导致对话中断:对过往回复的引用会被忽略。解决方案是在Python列表中存储消息历史,并在每次新请求时传递。
这将一系列调用转变为连贯的对话。模型接收到完整上下文:系统指令、过往的用户/助手对话对,以及当前输入。
上下文的消息结构
消息格式遵循OpenAI兼容API的标准:
system:持久性指令,单独添加user:用户的查询assistant:模型的回复
历史仅包含用户/助手对话对。发送时结构如下:
[
{"role": "system", "content": SYSTEM_PROMPT},
*conversation_history,
{"role": "user", "content": user_message}
]
顺序至关重要:系统指令设定行为,历史提供上下文,新用户消息是当前查询。
更新后的main.py代码
包含历史记录和上下文修剪的完整脚本:
# -*- coding: utf-8 -*-
import time
from typing import Optional
from litellm import completion
MODEL = "ollama_chat/qwen2.5:3b"
API_BASE = "http://localhost:11434"
SYSTEM_PROMPT = "你是一个乐于助人的助手。请简洁明了地回答问题。"
MAX_HISTORY_MESSAGES = 6
def trim_history(history: list, limit: int) -> list:
if len(history) <= limit:
return history
return history[-limit:]
def send_request_to_llm(user_message: str, history: list) -> Optional[str]:
messages = [
{"role": "system", "content": SYSTEM_PROMPT},
*history,
{"role": "user", "content": user_message},
]
try:
start_time = time.time()
response = completion(
model=MODEL,
messages=messages,
api_base=API_BASE,
request_timeout=120,
)
duration = time.time() - start_time
print(f"\n生成时间: {duration:.2f} 秒")
return response.choices[0].message.content
except Exception as e:
print(f"\n请求错误: {e}")
return None
def main() -> None:
print("带记忆功能的本地AI助手已启动。")
print("输入问题或'exit'退出。\n")
conversation_history = []
while True:
user_input = input("你: ").strip()
if user_input.lower() in ("exit", "quit"):
print("再见!")
break
if not user_input:
print("请输入问题。")
continue
print("\n模型思考中...")
answer = send_request_to_llm(user_input, conversation_history)
if answer is not None:
print(f"\nAI: {answer}\n")
conversation_history.append({"role": "user", "content": user_input})
conversation_history.append({"role": "assistant", "content": answer})
conversation_history = trim_history(conversation_history, MAX_HISTORY_MESSAGES)
else:
print("\n获取回复失败。请检查Ollama是否正在运行。\n")
if __name__ == "__main__":
main()
关键逻辑变更
- conversation_history:main()中的列表存储消息对
- send_request_to_llm:接收历史记录,通过
*展开 - 保存对话对:回复后,添加用户和助手消息
- trim_history:修剪至MAX_HISTORY_MESSAGES(6条消息——3轮对话)
| 组件 | 更新前 | 更新后 |
|-----------|---------------|--------------|
| 消息 | 系统 + 用户 | 系统 + 历史 + 用户 |
| 记忆 | 无 | RAM中的列表 |
| 大小 | 无限制 | 最多6条消息 |
修剪防止上下文溢出:旧消息会增加令牌数并拖慢推理速度。
测试上下文
依赖查询的对话示例:
- 你:列举三个Python Web开发框架
- AI:Django、Flask、FastAPI
- 你:详细介绍一下第二个
- AI:Flask是一个轻量级微框架...
模型通过查看之前的回复,正确引用了“第二个”。
当前实现的局限性
- 历史记录在RAM中:重启后丢失
- 固定限制:6条消息(可配置)
- 无持久化:生产环境需Redis/数据库
对于Telegram机器人或Web应用,逻辑保持不变:历史记录存储在用户会话中。
重点注意事项
- 消息格式:系统指令单独处理,历史仅含用户/助手对话对
- 历史修剪:[-limit:]保留最近的对话对
- 职责分离:LLM请求在send_request_to_llm中处理,状态在main()中管理
- 超时设置:request_timeout=120适应长上下文
- 日志记录:生成时间用于性能调试
— Editorial Team
暂无评论。