LangChain学习与项目实战4 -- 多轮聊天机器人
·
一、历史对话管理
1)规则:大模型是无状态的,每次调用都需要传递完整对话历史。
# 第一轮
[system , user] -> AI回复 -> 保存回复
# 第二轮
[system , user, assistant, user] -> AI回复 -> 保存回复
# 第三轮
[system , user, assistant, user, assistant, user] -> AI回复 -> 保存回复
2)调用举例
import os
from rich import print as rprint
from langchain_openai import ChatOpenAI
from dotenv import load_dotenv
load_dotenv(override=True)
DEEPSEEK_API_KEY = os.getenv("NEB_API")
DEEPSEEK_BASE_URL = os.getenv("NEB_URL")
model = ChatOpenAI(
model="nebulacoder-cot-v8.0",
api_key=DEEPSEEK_API_KEY,
base_url=DEEPSEEK_BASE_URL,
)
conversation = []
# 第一次
conversation.append({"role": "user", "content": "我叫张三"})
response1 = model.invoke(conversation)
# 保存AI回复
conversation.append({"role": "assistant", "content": response1.content})
# 第二次 传递完整历史
conversation.append({"role": "user", "content": "我叫什么?"})
response2 = model.invoke(conversation)
rprint(response2.content)
# AI回复:你叫张三。这是你在我们对话开始时告诉我的名字。
3)对话历史优化
问题:对话历史会越来越长,消耗大量token成本。
方案在之前大模型基础中提到过,可以采用滑动窗口,摘要压缩等方式。
这里以滑动窗口举例。
* 总是保留system消息(定义角色)。
* 只保留最近N轮,丢弃更早的对话。
import os
from rich import print as rprint
from langchain_openai import ChatOpenAI
from dotenv import load_dotenv
load_dotenv(override=True)
DEEPSEEK_API_KEY = os.getenv("NEB_API")
DEEPSEEK_BASE_URL = os.getenv("NEB_URL")
model = ChatOpenAI(
model="nebulacoder-cot-v8.0",
api_key=DEEPSEEK_API_KEY,
base_url=DEEPSEEK_BASE_URL,
)
def keep_recent_messages(messages, max_pairs = 3):
# 分离system消息和
system_messages = [m for m in messages if m.get("role") == "system"]
conversation_messages = [m for m in messages if m.get("role") != "system"]
# 只保留最近的消息对
recent_messages = conversation_messages[-(max_pairs * 2):]
return system_messages + recent_messages
conversation = [
{"role": "system", "content": "你是一个python导师。"}
]
# 第一次
conversation.append({"role": "user", "content": "什么是列表?用一句话解释。"})
response1 = model.invoke(conversation)
conversation.append({"role": "assistant", "content": response1.content})
# 第二次
conversation.append({"role": "user", "content": "列表和元组有什么区别?用一句话解释。"})
response2 = model.invoke(conversation)
conversation.append({"role": "assistant", "content": response2.content})
# 第三次
conversation.append({"role": "user", "content": "什么是字典?用一句话解释。"})
response3 = model.invoke(conversation)
conversation.append({"role": "assistant", "content": response3.content})
rprint(f"原始消息数:{len(conversation)}")
# 优化:只保留最近N轮
optimized = keep_recent_messages(conversation, max_pairs=2)
# 添加新的用户问题
optimized.append({"role": "user", "content": "我问的第一个问题是什么?"})
response4 = model.invoke(optimized)
rprint(f"AI回复:{response4.content}")
二、多轮对话聊天机器人案例
import os
from langchain_openai import ChatOpenAI
from dotenv import load_dotenv
load_dotenv(override=True)
DEEPSEEK_API_KEY = os.getenv("NEB_API")
DEEPSEEK_BASE_URL = os.getenv("NEB_URL")
model = ChatOpenAI(
model="nebulacoder-cot-v8.0",
api_key=DEEPSEEK_API_KEY,
base_url=DEEPSEEK_BASE_URL,
)
def keep_recent_messages(messages, max_pairs=3):
# 分离system消息和
system_messages = [m for m in messages if m.get("role") == "system"]
conversation_messages = [m for m in messages if m.get("role") != "system"]
# 只保留最近的消息对
recent_messages = conversation_messages[-(max_pairs * 2):]
return system_messages + recent_messages
# 维护一个消息列表
conversation = [
{"role": "system", "content": "你是一个友好的AI聊天助手,你将扮演刘亦菲的角色,回答用户的问题。"}
]
EXIT_WORD = "exit"
PAIRS = 10
print(f"请输入问题,当输入{EXIT_WORD}时结束对话")
i = 1
while True:
print("\n", "=" * 10, f"第{i}轮对话", "\n")
user_input = input("请输入:")
if user_input == EXIT_WORD:
print("会话结束。")
break
conversation.append({"role": "user", "content": user_input})
# 拼接AI回复信息
reply_content = ""
# 优化历史记忆
memory_message = keep_recent_messages(conversation, max_pairs=PAIRS)
for chunk in model.stream(memory_message):
print(chunk.content, end="", flush=True)
reply_content += chunk.content
print("\n", "=" * 10, f"第{i}轮对话结束", "\n")
i += 1
conversation.append({"role": "assistant", "content": reply_content})
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐



所有评论(0)