LangChain大模型应用开发:消息管理与聊天历史存储
介绍
大家好,博主又来给大家分享知识了。今天要给大家分享的是LangChain中的消息管理与聊天历史存储。
在LangChain里,消息管理可精细区分用户、助手、系统等不同角色消息,有序调度处理,让交互更顺畅。而聊天历史存储则赋予模型 “记忆”,多轮对话时能参考过往记录,理解意图更精准,回复更连贯。二者相辅相成,为构建智能对话应用筑牢基础。希望大家能通过我本次的分享,对它们有更清晰的认知。
消息存储在内存
在LangChain的应用场景中,消息存储是保障多轮对话连贯性的关键环节。它主要负责留存聊天过程中的历史信息,使得模型在后续交互时能够参考过往对话,更精准地理解用户意图并生成回复。
内存存储是消息存储的方式之一。将消息存储在内存里,数据访问速度快,能即时响应对话需求,适用于一些对实时性要求高、会话规模相对较小的场景。不过,它也存在局限性,如内存容量有限,可能因数据量过大导致性能问题。
下面我们展示一个简单的示例,其中聊天历史保存在内存中,此处通过全局Python字典实现。
我们构建一个名为get_session_history的可调用对象,引用此字典以返回ChatMessageHistory实例。通过在运行时向RunnableWithMessageHistory传递配置,可以指定可调用对象的参数。默认情况下,期望配置参数是一个字符串session_id 。可以通过history_factory_config关键字参数进行调整。
开启新会话
此次调用开启了一个新的会话。用户询问了 "行列式是什么意思?",由于是新会话,消息历史为空,模型会基于系统提示和当前问题进行回复。
完整代码
from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_core.chat_history import BaseChatMessageHistory
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_openai import ChatOpenAI# 用于存储不同会话的聊天消息历史
store = {}# 初始化OpenAI的聊天模型,使用gpt-3.5-turbo
chat_model = ChatOpenAI(model_name="gpt-3.5-turbo")# 创建聊天提示模板,包含系统指令、消息历史占位和用户输入
prompt = ChatPromptTemplate.from_messages([("system", "你是一名得力助手并且非常擅长{ability}"),MessagesPlaceholder(variable_name="history"),("human", "{input}"),
])# 将提示模板和聊天模型组合成一个可执行链
chain = prompt | chat_model# 定义函数,根据会话ID获取对应的聊天消息历史,若不存在则创建
def get_session_history(session_id: str) -> BaseChatMessageHistory:if session_id not in store:store[session_id] = ChatMessageHistory()return store[session_id]# 创建带消息历史管理功能的可运行对象
with_message_history = RunnableWithMessageHistory(chain,get_session_history,input_messages_key="input",history_messages_key="history",
)# 调用带消息历史管理的对象,传入用户问题和会话配置并获取结果
result = with_message_history.invoke({"ability": "数学", "input": "行列式是什么意思?"},config={"configurable": {"session_id": "qaz123"}},
)# 打印聊天模型针对用户问题给出的回复结果
print(result)
运行结果
content='在线性代数中,行列式是一个针对一个方阵的函数,它将一个方阵映射到一个标量。行列式可以通过方阵的元素来定义,用来描述方阵的性质和特征。行列式的值可以提供有关于方阵是否可逆、方阵的秩以及方阵的特征值等信息。' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 105, 'prompt_tokens': 40, 'total_tokens': 145, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': 'fp_0165350fbb', 'finish_reason': 'stop', 'logprobs': None} id='run-e5230a21-a183-4df5-9a1f-2aedeef4fba0-0' usage_metadata={'input_tokens': 40, 'output_tokens': 105, 'total_tokens': 145, 'input_token_details': {}, 'output_token_details': {}}进程已结束,退出代码为 0
在同一会话中进行追问
再次使用相同的会话ID,用户询问 "什么?"。此时模型会结合之前的消息历史,也就是第一次询问的 "行列式是什么意思?" 以及对应的回复,来理解用户的追问意图并给出基于上下文的回答。
完整代码
from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_core.chat_history import BaseChatMessageHistory
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_openai import ChatOpenAI# 用于存储不同会话的聊天消息历史
store = {}# 初始化OpenAI的聊天模型,使用gpt-3.5-turbo
chat_model = ChatOpenAI(model_name="gpt-3.5-turbo")# 创建聊天提示模板,包含系统指令、消息历史占位和用户输入
prompt = ChatPromptTemplate.from_messages([("system", "你是一名得力助手并且非常擅长{ability}"),MessagesPlaceholder(variable_name="history"),("human", "{input}"),
])# 将提示模板和聊天模型组合成一个可执行链
chain = prompt | chat_model# 定义函数,根据会话ID获取对应的聊天消息历史,若不存在则创建
def get_session_history(session_id: str) -> BaseChatMessageHistory:if session_id not in store:store[session_id] = ChatMessageHistory()return store[session_id]# 创建带消息历史管理功能的可运行对象
with_message_history = RunnableWithMessageHistory(chain,get_session_history,input_messages_key="input",history_messages_key="history",
)# 调用带消息历史管理的对象,传入用户问题和会话配置并获取结果
result = with_message_history.invoke({"ability": "math", "input": "行列式是什么意思?"},config={"configurable": {"session_id": "qaz123"}},
)# 打印聊天模型针对用户问题给出的回复结果
print(result)# 记住
result = with_message_history.invoke({"ability": "数学", "input": "什么?"},config={"configurable": {"session_id": "qaz123"}},
)print(result)
运行结果
content='在线性代数中,行列式是一个针对一个方阵的函数,它将一个方阵映射到一个标量。行列式可以通过方阵的元素来定义,用来描述方阵的性质和特征。行列式的值可以提供有关于方阵是否可逆、方阵的秩以及方阵的特征值等信息。' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 105, 'prompt_tokens': 40, 'total_tokens': 145, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': 'fp_0165350fbb', 'finish_reason': 'stop', 'logprobs': None} id='run-e5230a21-a183-4df5-9a1f-2aedeef4fba0-0' usage_metadata={'input_tokens': 40, 'output_tokens': 105, 'total_tokens': 145, 'input_token_details': {}, 'output_token_details': {}}
content='抱歉,我的回答可能有点复杂了。简单来说,行列式就是一个针对方阵的数学工具,用来描述和分析方阵的性质和特征。行列式的值可以提供有关方阵性质的重要信息。' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 79, 'prompt_tokens': 156, 'total_tokens': 235, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': 'fp_0165350fbb', 'finish_reason': 'stop', 'logprobs': None} id='run-2dbeed9e-9ab6-4910-a220-c9aadf83bfba-0' usage_metadata={'input_tokens': 156, 'output_tokens': 79, 'total_tokens': 235, 'input_token_details': {}, 'output_token_details': {}}进程已结束,退出代码为 0
再次开启新会话
使用新的会话ID,用户再次询问 "什么?"。由于是新会话,消息历史为空,模型无法获取之前关于行列式的对话信息,会将此次询问当作全新的问题进行处理,回复可能与之前不同。
完整代码
from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_core.chat_history import BaseChatMessageHistory
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_openai import ChatOpenAI# 用于存储不同会话的聊天消息历史
store = {}# 初始化OpenAI的聊天模型,使用gpt-3.5-turbo
chat_model = ChatOpenAI(model_name="gpt-3.5-turbo")# 创建聊天提示模板,包含系统指令、消息历史占位和用户输入
prompt = ChatPromptTemplate.from_messages([("system", "你是一名得力助手并且非常擅长{ability}"),MessagesPlaceholder(variable_name="history"),("human", "{input}"),
])# 将提示模板和聊天模型组合成一个可执行链
chain = prompt | chat_model# 定义函数,根据会话ID获取对应的聊天消息历史,若不存在则创建
def get_session_history(session_id: str) -> BaseChatMessageHistory:if session_id not in store:store[session_id] = ChatMessageHistory()return store[session_id]# 创建带消息历史管理功能的可运行对象
with_message_history = RunnableWithMessageHistory(chain,get_session_history,input_messages_key="input",history_messages_key="history",
)# 调用带消息历史管理的对象,传入用户问题和会话配置并获取结果
result = with_message_history.invoke({"ability": "math", "input": "行列式是什么意思?"},config={"configurable": {"session_id": "qaz123"}},
)# 打印聊天模型针对用户问题给出的回复结果
print(result)# 记住
result = with_message_history.invoke({"ability": "数学", "input": "什么?"},config={"configurable": {"session_id": "qaz123"}},
)print(result)# 新的session_id-->不记得了。
result = with_message_history.invoke({"ability": "math", "input": "什么?"},config={"configurable": {"session_id": "wsx234"}},
)print(result)
运行结果
content='在线性代数中,行列式是一个针对一个方阵的函数,它将一个方阵映射到一个标量。行列式可以通过方阵的元素来定义,用来描述方阵的性质和特征。行列式的值可以提供有关于方阵是否可逆、方阵的秩以及方阵的特征值等信息。' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 105, 'prompt_tokens': 40, 'total_tokens': 145, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': 'fp_0165350fbb', 'finish_reason': 'stop', 'logprobs': None} id='run-e5230a21-a183-4df5-9a1f-2aedeef4fba0-0' usage_metadata={'input_tokens': 40, 'output_tokens': 105, 'total_tokens': 145, 'input_token_details': {}, 'output_token_details': {}}
content='抱歉,我的回答可能有点复杂了。简单来说,行列式就是一个针对方阵的数学工具,用来描述和分析方阵的性质和特征。行列式的值可以提供有关方阵性质的重要信息。' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 79, 'prompt_tokens': 156, 'total_tokens': 235, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': 'fp_0165350fbb', 'finish_reason': 'stop', 'logprobs': None} id='run-2dbeed9e-9ab6-4910-a220-c9aadf83bfba-0' usage_metadata={'input_tokens': 156, 'output_tokens': 79, 'total_tokens': 235, 'input_token_details': {}, 'output_token_details': {}}
content='抱歉,我是不是给你带来了困惑?需要我为你解答什么数学问题吗?' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 36, 'prompt_tokens': 34, 'total_tokens': 70, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': 'fp_0165350fbb', 'finish_reason': 'stop', 'logprobs': None} id='run-ac1e77d1-8956-447d-8282-db2a2821734e-0' usage_metadata={'input_tokens': 34, 'output_tokens': 36, 'total_tokens': 70, 'input_token_details': {}, 'output_token_details': {}}进程已结束,退出代码为 0
综上,这段代码主要实现了一个基于LangChain的聊天系统,该系统可以管理不同会话的聊天消息历史,并利用这些历史信息为用户提供上下文感知的回复。
具体来说,它创建了一个带有消息历史管理功能的可运行对象with_message_history,通过不同的session_id来区分不同的会话,在每次调用invoke方法时,会根据session_id维护对应的消息历史,并将其作为上下文传递给语言模型。
配置会话唯一键
我们可以通过向history_factory_config参数传递一个ConfigurableFieldSpec对象列表来自定义跟踪消息历史的配置参数。下面我们使用了两个参数:user_id和conversation_id。
我们配置user_id和 conversation_id作为会话唯一键。
完整代码
from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_core.chat_history import BaseChatMessageHistory
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_openai import ChatOpenAI
from langchain_core.runnables import ConfigurableFieldSpec# 初始化一个字典用于存储不同用户和对话的消息历史
store = {}# 初始化OpenAI的聊天模型,使用gpt-3.5-turbo
chat_model = ChatOpenAI(model_name="gpt-3.5-turbo")# 创建聊天提示模板,包含系统指令、消息历史占位和用户输入
prompt = ChatPromptTemplate.from_messages([("system", "你是一名得力助手并且非常擅长{ability}"),MessagesPlaceholder(variable_name="history"),("human", "{input}"),
])# 将提示模板和聊天模型组合成一个可执行链
chain = prompt | chat_model# 定义函数,根据用户ID和对话ID获取或创建对应的消息历史
def get_session_history(user_id: str, conversation_id: str) -> BaseChatMessageHistory:if (user_id, conversation_id) not in store:store[(user_id, conversation_id)] = ChatMessageHistory()return store[(user_id, conversation_id)]# 创建带消息历史管理功能的可运行对象,并配置用户和对话ID相关参数
with_message_history = RunnableWithMessageHistory(chain,get_session_history,input_messages_key="input",history_messages_key="history",history_factory_config=[ConfigurableFieldSpec(id="user_id",annotation=str,name="User ID",description="用户的唯一标识符。",default="",is_shared=True,),ConfigurableFieldSpec(id="conversation_id",annotation=str,name="Conversation ID",description="对话的唯一标识符。",default="",is_shared=True,),],
)# 第一次调用,传入用户123在对话1中询问行列式的含义并打印结果
result = with_message_history.invoke({"ability": "数学", "input": "行列式是什么意思"},config={"configurable": {"user_id": "123", "conversation_id": "1"}},
)
print(result)# 第二次调用,同一用户在同一会话中追问并打印结果
result = with_message_history.invoke({"ability": "数学", "input": "什么?"},config={"configurable": {"user_id": "123", "conversation_id": "1"}},
)
print(result)# 第三次调用,不同用户在同一会话中追问并打印结果
result = with_message_history.invoke({"ability": "数学", "input": "什么?"},config={"configurable": {"user_id": "456", "conversation_id": "1"}},
)
print(result)
运行结果
content='行列式是矩阵特有的一种性质,是一个方阵(行数与列数相等的矩阵)所具有的一个标量值。行列式的计算方法比较复杂,但简单来说,行列式的值表示了矩阵线性变换所导致的尺度变化的比例系数。行列式的值为零表示矩阵不可逆,非零表示矩阵可逆。行列式在代数、几何学、物理学等领域有着广泛应用。' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 160, 'prompt_tokens': 40, 'total_tokens': 200, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': 'fp_0165350fbb', 'finish_reason': 'stop', 'logprobs': None} id='run-9974ad1b-452f-4878-ab3d-916dc729a9de-0' usage_metadata={'input_tokens': 40, 'output_tokens': 160, 'total_tokens': 200, 'input_token_details': {}, 'output_token_details': {}}
content='抱歉,我的解释可能有点晦涩难懂。简单来说,行列式可以看作是一个方阵的重要属性,它的值可以帮助我们了解这个矩阵的特性,比如是否可逆、线性变换的比例系数等。行列式的计算方法可能有点复杂,但在数学和其他领域中扮演着非常重要的角色。希望这次解释更容易理解一些!如果你有任何具体的问题或者需要进一步的解释,请随时告诉我。' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 165, 'prompt_tokens': 212, 'total_tokens': 377, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': 'fp_0165350fbb', 'finish_reason': 'stop', 'logprobs': None} id='run-cf46774c-e380-4695-9989-9661a2d02748-0' usage_metadata={'input_tokens': 212, 'output_tokens': 165, 'total_tokens': 377, 'input_token_details': {}, 'output_token_details': {}}
content='对不起,我想说的是我是一名得力助手,而且非常擅长数学。有什么数学问题我可以帮你解答吗?' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 50, 'prompt_tokens': 35, 'total_tokens': 85, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': 'fp_0165350fbb', 'finish_reason': 'stop', 'logprobs': None} id='run-e8ac3267-d81f-41ad-99fd-9b81bb8d9b0e-0' usage_metadata={'input_tokens': 35, 'output_tokens': 50, 'total_tokens': 85, 'input_token_details': {}, 'output_token_details': {}}进程已结束,退出代码为 0
我们看到上述代码基于LangChain库构建了一个具备消息历史管理功能的智能聊天系统,能与 OpenAI的gpt-3.5-turbo模型进行交互。我们看到第三次调用,不同的用户问同一个会话问题会变成一个新的会话。
系统会根据不同的用户ID(user_id)和对话ID(conversation_id)来管理和维护每个用户在不同对话场景下的消息历史,在每次与模型交互时,会将对应的消息历史作为上下文信息传递给模型,使得模型可以根据历史对话内容生成更符合当前语境的回复。
修改聊天历史
修改存储的聊天消息可以帮助我们的聊天机器人处理各种情况。
裁剪消息
LLM和聊天模型都有有限的上下文窗口,即使我们没有直接达到限制,我们可能也希望限制模型处理的干扰量。一种解决方案是只加载和存储最近的N条消息。让我们使用一个带有一些预加载消息的示例历史记录:
预加载完整代码
# 导入LangChain中用于管理聊天消息历史的模块
from langchain_community.chat_message_histories import ChatMessageHistory
# 导入用于创建人类消息和AI消息的类
from langchain_core.messages import HumanMessage, AIMessage
# 导入用于创建聊天提示模板和消息占位符的类
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
# 导入用于处理带有消息历史的可运行对象的类
from langchain_core.runnables.history import RunnableWithMessageHistory
# 导入用于与OpenAI聊天模型交互的类
from langchain_openai import ChatOpenAI# 初始化OpenAI的聊天模型,使用gpt-3.5-turbo作为模型
chat_model = ChatOpenAI(model_name="gpt-3.5-turbo")# 创建一个聊天消息历史对象,用于存储聊天记录
temp_chat_history = ChatMessageHistory()
temp_chat_history.add_user_message("我叫Ethan,你好")
temp_chat_history.add_ai_message("你好")
temp_chat_history.add_user_message("我今天心情特别好")
temp_chat_history.add_ai_message("你今天心情怎么样")
temp_chat_history.add_user_message("我上午在踢足球")
temp_chat_history.add_ai_message("你上午在做什么")# 直接修改聊天历史记录,以列表形式重新设置聊天消息
temp_chat_history.messages = [HumanMessage(content='我叫Ethan,你好'), AIMessage(content='你好'), HumanMessage(content='我今天心情特别好'), AIMessage(content='你今天心情怎么样'), HumanMessage(content='我上午在踢足球'), AIMessage(content='你上午在做什么'), HumanMessage(content='我今天心情怎么样?'), AIMessage(content='你今天心情特别好。')]# 根据消息列表创建一个聊天提示模板
prompt = ChatPromptTemplate.from_messages([("system","你是一个乐于助人的助手。尽力回答所有问题。提供的聊天历史包括与您交谈的用户的事实。",),MessagesPlaceholder(variable_name="chat_history"),("human", "{input}"),]
)# 将提示模板和聊天模型组合成一个可运行的链
chain = prompt | chat_model# 创建一个带有消息历史功能的可运行对象
chain_with_message_history = RunnableWithMessageHistory(# 传入前面组合好的链chain,# 一个函数,用于根据会话ID获取聊天历史记录,这里直接返回之前创建的聊天历史对象lambda session_id: temp_chat_history,# 指定输入消息的键名input_messages_key="input",# 指定历史消息的键名history_messages_key="chat_history",
)# 调用带有消息历史功能的可运行对象,传入用户新输入和配置信息
result = chain_with_message_history.invoke(# 用户新输入的问题{"input": "我今天心情如何?"},# 配置信息,指定会话ID{"configurable": {"session_id": "unused"}},
)# 打印AI针对用户新输入问题的回复结果
print(result)
预加载运行结果
content='你今天心情特别好。' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 9, 'prompt_tokens': 159, 'total_tokens': 168, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': 'fp_0165350fbb', 'finish_reason': 'stop', 'logprobs': None} id='run-5ccbbbfc-abf0-4344-90cb-9a33bc476951-0' usage_metadata={'input_tokens': 159, 'output_tokens': 9, 'total_tokens': 168, 'input_token_details': {}, 'output_token_details': {}}进程已结束,退出代码为 0
从上面可以看到,Chain记住了预加载的地点。
但是假设我们有一个非常小的上下文窗口,并且我们想要将传递给链的消息数量减少到最近的2 条。我们可以使用clear方法来删除消息并重新将它们添加到历史记录中。我们不一定要这样做,但让我们将这个方法放在链的最前面,以确保它总是被调用:
近两条完整代码
# 导入LangChain中用于管理聊天消息历史的模块
from langchain_community.chat_message_histories import ChatMessageHistory
# 导入用于创建人类消息和AI消息的类
from langchain_core.messages import HumanMessage, AIMessage
# 导入用于创建聊天提示模板和消息占位符的类
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
# 导入用于处理带有消息历史的可运行对象的类
from langchain_core.runnables.history import RunnableWithMessageHistory
# 导入用于与OpenAI聊天模型交互的类
from langchain_openai import ChatOpenAI
# 导入RunnablePassthrough类,该类用于在链式调用中直接传递输入,可结合其他操作对输入进行处理,常用于构建复杂的可运行对象链
from langchain_core.runnables import RunnablePassthrough# 初始化OpenAI的聊天模型,使用gpt-3.5-turbo作为模型
chat_model = ChatOpenAI(model_name="gpt-3.5-turbo")# 创建一个聊天消息历史对象,用于存储聊天记录
temp_chat_history = ChatMessageHistory()
temp_chat_history.add_user_message("我叫Ethan,你好")
temp_chat_history.add_ai_message("你好")
temp_chat_history.add_user_message("我今天心情特别好")
temp_chat_history.add_ai_message("你今天心情怎么样")
temp_chat_history.add_user_message("我上午在踢足球")
temp_chat_history.add_ai_message("你上午在做什么")# 根据消息列表创建一个聊天提示模板
prompt = ChatPromptTemplate.from_messages([("system","你是一个乐于助人的助手。尽力回答所有问题。提供的聊天历史包括与您交谈的用户的事实。",),MessagesPlaceholder(variable_name="chat_history"),("human", "{input}"),]
)# 将提示模板和聊天模型组合成一个可运行的链
chain = prompt | chat_model# 创建一个带有消息历史功能的可运行对象
chain_with_message_history = RunnableWithMessageHistory(# 传入前面组合好的链chain,# 一个函数,用于根据会话ID获取聊天历史记录,这里直接返回之前创建的聊天历史对象lambda session_id: temp_chat_history,# 指定输入消息的键名input_messages_key="input",# 指定历史消息的键名history_messages_key="chat_history",
)# 定义一个函数,用于裁剪聊天历史记录,该函数接收链的输入作为参数
def trim_messages(chain_input):stored_messages = temp_chat_history.messagesif len(stored_messages) <= 2:return Falsetemp_chat_history.clear()for message in stored_messages[-2:]:temp_chat_history.add_message(message)return True# 创建一个新的可运行对象链,该链集成了消息裁剪和消息历史功能
chain_with_trimming = (RunnablePassthrough.assign(messages_trimmed=trim_messages)| chain_with_message_history
)# 调用带有消息裁剪功能和消息历史的链,传入用户输入和配置信息
result = chain_with_trimming.invoke(# 用户输入的新问题{"input": "我上午在做什么"},# 配置信息,指定会话ID{"configurable": {"session_id": "unused"}},
)# 打印AI针对用户新输入问题的回复结果
print(result)
print(temp_chat_history.messages)
近两条运行结果
content='你上午在踢足球。踢得开心吗?' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 20, 'prompt_tokens': 93, 'total_tokens': 113, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': 'fp_0165350fbb', 'finish_reason': 'stop', 'logprobs': None} id='run-032774e1-4e86-4309-a639-15a924f510e3-0' usage_metadata={'input_tokens': 93, 'output_tokens': 20, 'total_tokens': 113, 'input_token_details': {}, 'output_token_details': {}}
[HumanMessage(content='我上午在踢足球', additional_kwargs={}, response_metadata={}), AIMessage(content='你上午在做什么', additional_kwargs={}, response_metadata={}), HumanMessage(content='我上午在做什么', additional_kwargs={}, response_metadata={}), AIMessage(content='你上午在踢足球。踢得开心吗?', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 20, 'prompt_tokens': 93, 'total_tokens': 113, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': 'fp_0165350fbb', 'finish_reason': 'stop', 'logprobs': None}, id='run-032774e1-4e86-4309-a639-15a924f510e3-0', usage_metadata={'input_tokens': 93, 'output_tokens': 20, 'total_tokens': 113, 'input_token_details': {}, 'output_token_details': {}})]进程已结束,退出代码为 0
我们重新调用这个新链并检查消息发现大模型已经知道我们在做什么了。
我们可以看到我们的历史记录已经删除了两条最旧的消息,同时在末尾添加了最近的对话。下次调用链时,trim_messages将再次被调用,只有最近的两条消息将被传递给模型。在这种情况下,这意味着下次调用时模型将忘记我们给它的名字。
删除询问完整代码
# 导入LangChain中用于管理聊天消息历史的模块
from langchain_community.chat_message_histories import ChatMessageHistory
# 导入用于创建聊天提示模板和消息占位符的类
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
# 导入用于处理带有消息历史的可运行对象的类
from langchain_core.runnables.history import RunnableWithMessageHistory
# 导入用于与OpenAI聊天模型交互的类
from langchain_openai import ChatOpenAI
# 导入RunnablePassthrough类,该类用于在链式调用中直接传递输入,可结合其他操作对输入进行处理,常用于构建复杂的可运行对象链
from langchain_core.runnables import RunnablePassthrough# 初始化OpenAI的聊天模型,使用gpt-3.5-turbo作为模型
chat_model = ChatOpenAI(model_name="gpt-3.5-turbo")# 创建一个聊天消息历史对象,用于存储聊天记录
temp_chat_history = ChatMessageHistory()
temp_chat_history.add_user_message("我叫Ethan,你好")
temp_chat_history.add_ai_message("你好")
temp_chat_history.add_user_message("我今天心情特别好")
temp_chat_history.add_ai_message("你今天心情怎么样")
temp_chat_history.add_user_message("我上午在踢足球")
temp_chat_history.add_ai_message("你上午在做什么")# 根据消息列表创建一个聊天提示模板
prompt = ChatPromptTemplate.from_messages([("system","你是一个乐于助人的助手。尽力回答所有问题。提供的聊天历史包括与您交谈的用户的事实。",),MessagesPlaceholder(variable_name="chat_history"),("human", "{input}"),]
)# 将提示模板和聊天模型组合成一个可运行的链
chain = prompt | chat_model# 创建一个带有消息历史功能的可运行对象
chain_with_message_history = RunnableWithMessageHistory(# 传入前面组合好的链chain,# 一个函数,用于根据会话ID获取聊天历史记录,这里直接返回之前创建的聊天历史对象lambda session_id: temp_chat_history,# 指定输入消息的键名input_messages_key="input",# 指定历史消息的键名history_messages_key="chat_history",
)# 定义一个函数,用于裁剪聊天历史记录,该函数接收链的输入作为参数
def trim_messages(chain_input):stored_messages = temp_chat_history.messagesif len(stored_messages) <= 2:return Falsetemp_chat_history.clear()for message in stored_messages[-2:]:temp_chat_history.add_message(message)return True# 创建一个新的可运行对象链,该链集成了消息裁剪和消息历史功能
chain_with_trimming = (RunnablePassthrough.assign(messages_trimmed=trim_messages)| chain_with_message_history
)# 调用带有消息裁剪功能和消息历史的链,传入用户输入和配置信息
result = chain_with_trimming.invoke({"input": "我叫什么名字?"},{"configurable": {"session_id": "unused"}},
)# 打印AI针对用户新输入问题的回复结果
print(result)
print(temp_chat_history.messages)
删除询问运行结果
content='很抱歉,我无法知道你的名字。如果你想告诉我,我会很高兴知道。有什么我可以帮你的吗?' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 49, 'prompt_tokens': 93, 'total_tokens': 142, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': 'fp_0165350fbb', 'finish_reason': 'stop', 'logprobs': None} id='run-5f2619e5-ea12-4fd7-8857-213b878d6dac-0' usage_metadata={'input_tokens': 93, 'output_tokens': 49, 'total_tokens': 142, 'input_token_details': {}, 'output_token_details': {}}
[HumanMessage(content='我上午在踢足球', additional_kwargs={}, response_metadata={}), AIMessage(content='你上午在做什么', additional_kwargs={}, response_metadata={}), HumanMessage(content='我叫什么名字?', additional_kwargs={}, response_metadata={}), AIMessage(content='很抱歉,我无法知道你的名字。如果你想告诉我,我会很高兴知道。有什么我可以帮你的吗?', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 49, 'prompt_tokens': 93, 'total_tokens': 142, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': 'fp_0165350fbb', 'finish_reason': 'stop', 'logprobs': None}, id='run-5f2619e5-ea12-4fd7-8857-213b878d6dac-0', usage_metadata={'input_tokens': 93, 'output_tokens': 49, 'total_tokens': 142, 'input_token_details': {}, 'output_token_details': {}})]进程已结束,退出代码为 0
我们看到,此时大模型已经忘记我们叫什么名字了。
总结记忆
我们也可以以其他方式使用相同的模式。例如,我们可以使用额外的LLM调用来在调用链之前生成对话摘要。让我们重新创建我们的聊天历史和聊天机器人链:
重创聊天代码演示
# 创建一个聊天消息历史对象,用于存储聊天记录
temp_chat_history = ChatMessageHistory()
temp_chat_history.add_user_message("我叫Ethan,你好")
temp_chat_history.add_ai_message("你好")
temp_chat_history.add_user_message("我今天心情特别好")
temp_chat_history.add_ai_message("你今天心情怎么样")
temp_chat_history.add_user_message("我上午在踢足球")
temp_chat_history.add_ai_message("你上午在做什么")# 直接修改聊天历史记录,以列表形式重新设置聊天消息
temp_chat_history.messages = [HumanMessage(content='我叫Ethan,你好'), AIMessage(content='你好'),HumanMessage(content='我今天心情特别好'), AIMessage(content='你今天心情怎么样'),HumanMessage(content='我上午在踢足球'), AIMessage(content='你上午在做什么'),HumanMessage(content='我今天心情怎么样?'), AIMessage(content='最为一个人工智能,我无法知道您的心情。您可以告诉我您今天感觉如何,我会尽我所能提供帮助')]
我们稍微修改提示,让LLM意识到它将收到一个简短摘要而不是聊天历史。
简短摘要代码演示
# 根据消息列表创建一个聊天提示模板
prompt = ChatPromptTemplate.from_messages([("system","你是一个乐于助人的助手。尽力回答所有问题。提供的聊天历史包括与您交谈的用户的事实。",),MessagesPlaceholder(variable_name="chat_history"),("user", "{input}"),]
)# 将提示模板和聊天模型组合成一个可运行的链
chain = prompt | chat_model# 创建一个带有消息历史功能的可运行对象
chain_with_message_history = RunnableWithMessageHistory(# 传入前面组合好的链chain,# 一个函数,用于根据会话ID获取聊天历史记录,这里直接返回之前创建的聊天历史对象lambda session_id: temp_chat_history,# 指定输入消息的键名input_messages_key="input",# 指定历史消息的键名history_messages_key="chat_history",
)
现在,让我们创建一个函数,将之前的交互总结为摘要。我们也可以将这个函数添加到链的最前面。
创建摘要代码演示
# 定义一个函数,用于对聊天历史消息进行总结
def summarize_messages(chain_input):# 从聊天历史对象中获取存储的消息stored_messages = temp_chat_history.messages# 如果存储的消息数量为 0,即没有聊天记录,则直接返回 Falseif len(stored_messages) == 0:return False# 创建一个用于总结消息的提示模板,该模板会将历史聊天消息和总结要求传递给模型summarization_prompt = ChatPromptTemplate.from_messages([# 占位符,用于插入聊天历史消息MessagesPlaceholder(variable_name="chat_history"),("user",# 要求模型将上述聊天消息浓缩成一条摘要消息,并尽可能包含多个具体细节"将上述聊天消息浓缩成一条摘要消息。尽可能包含多个具体细节。",),])# 将总结提示模板和聊天模型组合成一个可运行的链,用于执行消息总结操作summarization_chain = summarization_prompt | chat_model# 调用总结链,传入聊天历史消息,得到总结后的消息summary_message = summarization_chain.invoke({"chat_history": stored_messages})# 清空原有的聊天历史记录temp_chat_history.clear()# 将总结后的消息添加到聊天历史记录中temp_chat_history.add_message(summary_message)# 表示消息总结操作成功完成,返回 Truereturn True# 构建带有消息总结功能的可运行对象链
chain_with_summarization = (RunnablePassthrough.assign(messages_summarized=summarize_messages)| chain_with_message_history
)
我们在对大模型进行提问,看看它是否还记得我们的名字。
完整代码
# 导入LangChain中用于管理聊天消息历史的模块
from langchain_community.chat_message_histories import ChatMessageHistory
# 导入用于创建人类消息和AI消息的类
from langchain_core.messages import HumanMessage, AIMessage
# 导入用于创建聊天提示模板和消息占位符的类
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
# 导入用于处理带有消息历史的可运行对象的类
from langchain_core.runnables.history import RunnableWithMessageHistory
# 导入用于与OpenAI聊天模型交互的类
from langchain_openai import ChatOpenAI
# 导入RunnablePassthrough类,该类用于在链式调用中直接传递输入,可结合其他操作对输入进行处理,常用于构建复杂的可运行对象链
from langchain_core.runnables import RunnablePassthrough# 初始化OpenAI的聊天模型,使用gpt-3.5-turbo作为模型
chat_model = ChatOpenAI(model_name="gpt-3.5-turbo")# 创建一个聊天消息历史对象,用于存储聊天记录
temp_chat_history = ChatMessageHistory()
temp_chat_history.add_user_message("我叫Ethan,你好")
temp_chat_history.add_ai_message("你好")
temp_chat_history.add_user_message("我今天心情特别好")
temp_chat_history.add_ai_message("你今天心情怎么样")
temp_chat_history.add_user_message("我上午在踢足球")
temp_chat_history.add_ai_message("你上午在做什么")# 直接修改聊天历史记录,以列表形式重新设置聊天消息
temp_chat_history.messages = [HumanMessage(content='我叫Ethan,你好'), AIMessage(content='你好'),HumanMessage(content='我今天心情特别好'), AIMessage(content='你今天心情怎么样'),HumanMessage(content='我上午在踢足球'), AIMessage(content='你上午在做什么'),HumanMessage(content='我今天心情怎么样?'), AIMessage(content='最为一个人工智能,我无法知道您的心情。您可以告诉我您今天感觉如何,我会尽我所能提供帮助')]# 根据消息列表创建一个聊天提示模板
prompt = ChatPromptTemplate.from_messages([("system","你是一个乐于助人的助手。尽力回答所有问题。提供的聊天历史包括与您交谈的用户的事实。",),MessagesPlaceholder(variable_name="chat_history"),("user", "{input}"),]
)# 将提示模板和聊天模型组合成一个可运行的链
chain = prompt | chat_model# 创建一个带有消息历史功能的可运行对象
chain_with_message_history = RunnableWithMessageHistory(# 传入前面组合好的链chain,# 一个函数,用于根据会话ID获取聊天历史记录,这里直接返回之前创建的聊天历史对象lambda session_id: temp_chat_history,# 指定输入消息的键名input_messages_key="input",# 指定历史消息的键名history_messages_key="chat_history",
)# 定义一个函数,用于对聊天历史消息进行总结
def summarize_messages(chain_input):# 从聊天历史对象中获取存储的消息stored_messages = temp_chat_history.messages# 如果存储的消息数量为 0,即没有聊天记录,则直接返回 Falseif len(stored_messages) == 0:return False# 创建一个用于总结消息的提示模板,该模板会将历史聊天消息和总结要求传递给模型summarization_prompt = ChatPromptTemplate.from_messages([# 占位符,用于插入聊天历史消息MessagesPlaceholder(variable_name="chat_history"),("user",# 要求模型将上述聊天消息浓缩成一条摘要消息,并尽可能包含多个具体细节"将上述聊天消息浓缩成一条摘要消息。尽可能包含多个具体细节。",),])# 将总结提示模板和聊天模型组合成一个可运行的链,用于执行消息总结操作summarization_chain = summarization_prompt | chat_model# 调用总结链,传入聊天历史消息,得到总结后的消息summary_message = summarization_chain.invoke({"chat_history": stored_messages})# 清空原有的聊天历史记录temp_chat_history.clear()# 将总结后的消息添加到聊天历史记录中temp_chat_history.add_message(summary_message)# 表示消息总结操作成功完成,返回 Truereturn True# 构建带有消息总结功能的可运行对象链
chain_with_summarization = (RunnablePassthrough.assign(messages_summarized=summarize_messages)| chain_with_message_history
)# 调用带有消息总结功能和消息历史的链,传入用户输入和配置信息
result = chain_with_summarization.invoke({"input": "我叫什么名字?"},{"configurable": {"session_id": "unused"}},
)# 打印AI针对用户新输入问题的回复结果
print(result)# 打印当前的聊天历史记录
print(temp_chat_history.messages)
运行结果
content='您的名字是Ethan。' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 8, 'prompt_tokens': 90, 'total_tokens': 98, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': 'fp_0165350fbb', 'finish_reason': 'stop', 'logprobs': None} id='run-85e2181b-e204-472c-b95c-dedcebec56ce-0' usage_metadata={'input_tokens': 90, 'output_tokens': 8, 'total_tokens': 98, 'input_token_details': {}, 'output_token_details': {}}
[AIMessage(content='Ethan今天上午在踢足球,心情特别好。', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 20, 'prompt_tokens': 174, 'total_tokens': 194, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': 'fp_0165350fbb', 'finish_reason': 'stop', 'logprobs': None}, id='run-0abfa757-9811-43a7-990d-08a1e56d557f-0', usage_metadata={'input_tokens': 174, 'output_tokens': 20, 'total_tokens': 194, 'input_token_details': {}, 'output_token_details': {}}), HumanMessage(content='我叫什么名字?', additional_kwargs={}, response_metadata={}), AIMessage(content='您的名字是Ethan。', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 8, 'prompt_tokens': 90, 'total_tokens': 98, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_name': 'gpt-3.5-turbo-0125', 'system_fingerprint': 'fp_0165350fbb', 'finish_reason': 'stop', 'logprobs': None}, id='run-85e2181b-e204-472c-b95c-dedcebec56ce-0', usage_metadata={'input_tokens': 90, 'output_tokens': 8, 'total_tokens': 98, 'input_token_details': {}, 'output_token_details': {}})]进程已结束,退出代码为 0
我们再次调用链式模型会生成一个新的摘要,该摘要包括初始摘要以及新的消息等。我们还可以设计一种混合方法,其中一定数量的消息保留在聊天历史记录中,而其他消息则被总结成摘要。
结束
好了,以上就是本次分享的全部内容了。不知道大家是否掌握并了解了LangChain中的消息管理与聊天历史存储。
LangChain的消息管理与聊天历史存储在对话应用开发中很关键。消息管理含多种消息类型,且可灵活处理;聊天历史存储方式有本地文件、数据库、内存等;在与语言模型交互时,能传递上下文,开发者还可制定管理策略。
那么本次分享就到这了。最后,博主还是那句话:请大家多去大胆的尝试和使用,成功总是在不断的失败中试验出来的,敢于尝试就已经成功了一半。如果大家对博主分享的内容感兴趣或有帮助,请点赞和关注。大家的点赞和关注是博主持续分享的动力🤭,博主也希望让更多的人学习到新的知识。
相关文章:
LangChain大模型应用开发:消息管理与聊天历史存储
介绍 大家好,博主又来给大家分享知识了。今天要给大家分享的是LangChain中的消息管理与聊天历史存储。 在LangChain里,消息管理可精细区分用户、助手、系统等不同角色消息,有序调度处理,让交互更顺畅。而聊天历史存储则赋予模型 …...
flink-cdc同步数据到doris中
1 创建数据库和表 1.1 数据库脚本 -- 创建数据库eayc create database if not exists ods_eayc; -- 创建数据表2 数据同步 2.1 flnk-cdc 参考Flink CDC实时同步MySQL到Doris Flink CDC 概述 2.1.1 最简单的单表同步 从下面的yml脚本可以看到,并没有doris中创建…...
轻量级在线ETL数据集成工具架构设计与技术实现深度剖析
在当今数字化时代,企业面临着海量异构数据的整合挑战。ETL(Extract, Transform, Load)工具作为数据集成的核心,负责将分散在不同数据源中的数据进行抽取、转换和加载,以构建统一的数据视图。本文将深入剖析一款基于诺依框架开发的在线ETL数据集成工具,重点阐述其架构设计…...
【Linux专栏】find命令+同步 实验
Linux & Oracle相关文档,希望互相学习,共同进步 风123456789~-CSDN博客 1.实验背景 需要把一个目录中所有文件,按照目录把某个时间点之前的同步到一个盘中,之后的同步备份到另一个盘中,实现不同时间段的备份。 本次实现目标:把common文件夹中 2025年之后的含文件夹…...
15-最后一个单词的长度
给你一个字符串 s,由若干单词组成,单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度。 单词 是指仅由字母组成、不包含任何空格字符的最大子字符串。 方法一:使用字符串分割 可以使用字符串的 split 方法将字符串按空格分割…...
DeepSeek和ChatGPT的全面对比
一、模型基础架构对比(2023技术版本) 维度DeepSeekChatGPT模型家族LLAMA架构改进GPT-4优化版本参数量级开放7B/35B/120B闭源175B位置编码RoPE NTK扩展ALiBiAttention机制FlashAttention-3FlashAttention-2激活函数SwiGLU ProGeGLU训练框架DeepSpeedMeg…...
LlamaFactory可视化模型微调-Deepseek模型微调+CUDA Toolkit+cuDNN安装
LlamaFactory https://llamafactory.readthedocs.io/zh-cn/latest/ 安装 必须保证版本匹配,否则到训练时,找不到gpu cuda。 否则需要重装。下面图片仅供参考。因为cuda12.8装了没法用,重新搞12.6 cudacudnnpytorch12.69.612.6最新…...
什么是网关,网关的作用是什么?网络安全零基础入门到精通实战教程!
1. 什么是网关 网关又称网间连接器、协议转换器,也就是网段(局域网、广域网)关卡,不同网段中的主机不能直接通信,需要通过关卡才能进行互访,比如IP地址为192.168.31.9(子网掩码:255.255.255.0)和192.168.7.13(子网掩码…...
Linux基础25-C语言之分支结构Ⅱ【入门级】
用if语句实现分支结构 单分支:if… 语法: //语法1:舍弃{} if(条件表达式);单语句; //语法2:必须保留{} if(条件表达式) {单语句或者复合语句; }流程图 功能 若表达式值为真(非0),则执行表达式…...
实战开发coze应用-姓氏头像生成器(上)
欢迎关注【AI技术开发者】 上次,我们开发了一个对话形式的头像生成器智能体(Agents),广受大家欢迎。 同时也接收到一些用户的反馈,生成前无法看到头像样式、初次使用不会用等等。 对此,我准备使用Coze开…...
企业内部知识库:安全协作打造企业智慧运营基石
内容概要 作为企业智慧运营的核心载体,企业内部知识库通过结构化的信息聚合与动态化的知识流动,为组织提供了从数据沉淀到价值转化的系统性框架。其底层架构以权限管理为核心,依托数据加密技术构建多层级访问控制机制,确保敏感信…...
uniapp 滚动尺
scale组件代码(部分class样式使用到了uview1.0的样式) <template><view><view class"scale"><view class"pointer u-flex-col u-col-center"><u-icon name"arrow-down-fill" size"26&qu…...
vue3之echarts3D圆柱
vue3之echarts3D圆柱 效果: 版本 "echarts": "^5.1.2" 核心代码: <template><div ref"charts" class"charts"></div><svg><linearGradient id"labColor" x1"0&q…...
SQL 优化工具使用之 explain 详解
一、导读 对于大部分开发人员来说,平常接触的无非就是增删改查这些基本操作,创建存储过程,视图等等都是 DBA 该干的活,但是想要把这些基本操作写的近乎完美也是一件难事。 而 explain 显示了 MySQL 如何使用索引来处理 select 语…...
SpringBoot启动失败之application.yml缩进没写好
修改前: spring前面空格了 报错输出:Failed to configure a DataSource: ‘url’ attribute is not specified and no embedded datasource could be configured. Reason: Failed to determine a suitable driver class Action: Consider the follow…...
基于SpringBoot+vue粮油商城小程序系统
粮油商城小程序为用户提供方便快捷的在线购物体验,包括大米、面粉、食用油、调味品等各种粮油产品的选购,用户可以浏览商品详情、对比价格、下单支付等操作。同时,商城还提供优惠活动、积分兑换等福利,让用户享受到更多实惠和便利…...
【JavaEE进阶】Spring MVC(3)
欢迎关注个人主页:逸狼 创造不易,可以点点赞吗 如有错误,欢迎指出~ 返回响应 返回静态页面 //RestController Controller RequestMapping("/response") public class ResponseController {RequestMapping("/returnHtmlPage&…...
案例-17.文件上传-阿里云OSS-入门
一.文件上传 我们在OSS页面中找到SDK下载,点击SDK示例,找到帮助文档。 点击JAVA并安装 在安装SDK中找到在Maven项目中加入依赖项(推荐方式) 将其中的依赖复制到pom文件中去。 <dependency><groupId>com.aliyun.oss</groupId><ar…...
java数据结构_优先级队列(堆)_6.2
3. 常用接口 3.1 PriorityQueue的特性 Java集合框架中提供了PriorityQueue和PriorityBlockingQueue两种类型的优先级队列,PriorityQueue的线性不安全的,PriorityBlockingQueue是线程安全的,这里主要介绍PriorityQueueu。 关于PriorityQueue…...
如何维护和保养直线模组?
直线模组是一种常见的传动机构,被广泛应用到各种各样的设备中,如激光焊接、激光切割、涂胶机、喷涂机、小型数控机床等设备。其保养与维护对于其使用寿命和性能至关重要,为了维护和保养直线模组并确保其使用寿命,可以采取以下措施…...
DeepSeek 助力 Vue 开发:打造丝滑的表单验证(Form Validation)
前言:哈喽,大家好,今天给大家分享一篇文章!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏关注哦 💕 目录 Deep…...
java连接redis
1.使用 1.创建java工程 2.引入依赖 <dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>5.2.0</version> </dependency> 3. //1.获取jedis对象,把所有对redis的操作都封装到…...
DeepSeek掀起推理服务器新风暴,AI应用迎来变革转折点?
AI 浪潮下,推理服务器崭露头角 在科技飞速发展的当下,AI 是耀眼明星,席卷各行业,深刻改变生活与工作模式,从语音助手到医疗诊断、金融风险预测,AI 无处不在。其发展分数据收集整理、模型训练、推理应用三个…...
宏块划分的原理
宏块划分并不是物理上的划分,而是逻辑上的划分。 宏块的划分是编码器在处理视频帧时的一种逻辑操作,用于将视频帧分解为更小的编码单元,以便后续的预测、变换、量化和编码等操作。视频帧的物理存储方式(如 YUV 数据的存储顺序)并不会因为宏块的划分而发生改变。 接下来,…...
分享8款AI生成PPT的工具!含测评
随着人工智能技术的飞速进步,制作PPT变得愈发便捷,仅需输入主题指令,便能在瞬间获得一份完整的演示文稿。尤其在制作篇幅较长的PPT时,手动编写每一页内容并设计格式和排版,不仅效率低下,而且耗时耗力。 本…...
【NLP算法面经】字节跳动算法岗四面详细面经(★附面题总结★)
【NLP算法面经】字节跳动算法岗四面详细面经(★附面题总结★) 🌟 嗨,你好,我是 青松 ! 🌈 自小刺头深草里,而今渐觉出蓬蒿。 NLP Github 项目推荐: 【AI 藏经阁】&#…...
[AI相关]Unity的C#代码如何简写
是一个某培训机构的飞行棋教学源码 不知道,是否有人想知道怎么可以简写 (这个问AI,DeepSeek也应该找不到答案的) 静态变量 属性引用 单例 注入 一些UnityEvent特性就不说了。。。 IL 注入 运算符号改写...
DeepSeek模型快速部署教程-搭建自己的DeepSeek
前言:在人工智能技术飞速发展的今天,深度学习模型已成为推动各行各业智能化转型的核心驱动力。DeepSeek 作为一款领先的 AI 模型,凭借其高效的性能和灵活的部署方式,受到了广泛关注。无论是自然语言处理、图像识别,还是…...
TaskBuilder创建客户信息文件夹
数据模型创建好之后,我们就可以进行前后端功能的开发了。首先,我们需要创建好客户信息文件夹,以便专门存放与客户信息管理有关的前端文件,操作步骤如下: 点击销售管理示例项目“前端文件”右侧的加号按钮: …...
javaSE学习笔记22-线程(thread)-线程通信、线程池
线程通信 应用场景:生产者和消费者问题 假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中产品取走消费 如果仓库中没有产品,则生产者将产品放入仓库,否则停止生产并等待,…...
解决 WSL Ubuntu 中 /etc/resolv.conf 自动重置问题
解决 WSL Ubuntu 中 /etc/resolv.conf 自动重置问题 前言问题描述问题原因尝试过的命令及分析解决方案:修改 wsl.conf 禁用自动生成总结 前言 在使用 Windows Subsystem for Linux (WSL) 的 Ubuntu 子系统时,你可能会遇到 /etc/resolv.conf 文件被自动重…...
使用mybatis -基本的增删改查
目录 项目准备 项目步骤 具体细节 1 主配置文件的处理 2 Test 测试类 3 在 loginMapper 接口中书写 对 数据库操作的方法 4 实体类 pojo 、entity 要和 数据库对应的表的字段 一一对应 5 在 loginMapper.xml 映射文件 书写 具体实现 loginMapper 接口中方法的sql 语句…...
通过API 调用本地部署 deepseek-r1 模型
如何本地部署 deepseek 请参考(windows 部署安装 大模型 DeepSeek-R1) 那么实际使用中需要开启API模式,这样可以无拘无束地通过API集成的方式,集成到各种第三方系统和应用当中。 上遍文章是基于Ollama框架运行了deepSeek R1模型…...
模型量化初始知识
背景 PyTorch对量化的支持目前有如下三种方式: Post Training Dynamic Quantization,模型训练完毕后的动态量化; Post Training Static Quantization,模型训练完毕后的静态量化; QAT(Quantization Aware T…...
成熟开发者需具备的能力
精业务 • 指深入理解和熟悉所开发软件的业务逻辑和需求。 • 开发者需要明确软件要解决的问题、面向的用户群体以及核心功能等。 • 精业务有助于开发者更好地设计系统架构、编写符合业务需求的代码,并能根据业务变化灵活调整开发计划。 懂原理 • 指掌握编程的基…...
java练习(32)
ps:题目来自力扣 环形链表 给你一个链表的头节点 head ,判断链表中是否有环。 如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表…...
linux配置网络安全服务图
系统安全防范: 1:用户与口令安全。避免使用脆弱口令,连续多次登录失败将禁止再次登录。 2:对象访问的安全性。对文件,目录和进程等对象的访问采用强制访问控制(MAC)来实现,不同的用…...
PTA:使用指针方式求一个给定的m×n矩阵各行元素之和
本题要求编写程序,使用指针方式求一个给定的mn矩阵各行元素之和。(例如:scanf("%d", *(matrix i) j); // 使用指针方式访问二维数组元素) 输入格式: 输入第一行给出两个正整数m和n(1<m<6, 1<n&…...
一.AI大模型开发-初识机器学习
机器学习基本概念 前言 本文主要介绍了深度学习基础,包括机器学习、深度学习的概念,机器学习的两种典型任务分类任务和回归任务,机器学习中的基础名词解释以及模型训练的基本流程等。 一.认识机器学习 1.人工智能和机器学习 人工智能&am…...
【DeepSeek服务器部署全攻略】Linux服务器部署DeepSeek R1模型、实现API调用、搭建Web页面以及专属知识库
DeepSeek R1模型的Linux服务器搭建、API访问及Web页面搭建 1,引言2,安装Ollama工具3,下载DeepSeek R1 模型4,DeepSeek命令行对话5,DeepSeek API接口远程调用6,DeepSeek结合Web-ui实现图形化界面远程访问6.1…...
利用多线程加速ESMC-6B模型API调用以及403Forbidden问题的解决
前言 只对之前这篇文章进行了补充 403 Forbidden问题的解决 这几天用了一下ESMC-6B的API,发现被403 forbidden了 排查问题查来查去,发现需要翻墙才可以访问(怎么又被针对了) 于是就需要在服务器上面接入VPN,想了想…...
zyNo.25
SSRF漏洞 在了解ssrf漏洞前先了解curl命令的使用 1.curl命令的使用 基本格式:curl<参数值>请求地址 get请求:curl http://127.0.0.1 post请求:curl -X POST -d "a1&b2" http://127.0.0.1/(其中,使用-X参…...
golang中数组和slice的区别及使用
来自于《go语言中文文档》的学习及自我分析 数组和切片的区别 golang中有两个很相似的数据结构:数组(Array)和slice。数组和slice实际有各自的优缺点和区别,这里列出最主要的区别 功能点数组slice概念是同一种数据类型的固定长…...
撕碎QT面具(7):container控件被spacer挤扁,无法进行控件添加的处理方案。
调节容器控件最小大小,然后把内部设计好后,对容器使用水平布局或垂直布局。这样容器的控件就不会被挤扁。...
2月19号
寒假每天敲代码的过程中,从先前的什么都不懂,在一步步看题解,学习新知识,运用学到的知识,解决问题,很多时候对数据结构和算法的选择有问题,不能准确选择,这个时候还是得多敲代码,就我自己而言,代码敲多了会让自己更熟练掌握这个知识点,也能更好的去运用,遇到相似的问题还可以举…...
EX_25/2/19
1. 封装一个 File 类,用有私有成员 File* fp 实现以下功能 File f "文件名" 要求打开该文件 f.write(string str) 要求将str数据写入文件中 string str f.read(int size) 从文件中读取最多size个字节,并将读取到的数据返回 析构函数 …...
纯新手教程:用llama.cpp本地部署DeepSeek蒸馏模型
0. 前言 llama.cpp是一个基于纯C/C实现的高性能大语言模型推理引擎,专为优化本地及云端部署而设计。其核心目标在于通过底层硬件加速和量化技术,实现在多样化硬件平台上的高效推理,同时保持低资源占用与易用性。 最近DeepSeek太火了&#x…...
ubuntu源码方式安装TensorRT-LLM推理框架
简要记录安装过程和遇到的问题 写在前面: 一切的二手安装教程都不如官方手册,建议先根据手册进行安装,遇到问题再自行谷歌: TensorRT官方文档 先安装docker TensorRT-LLM 官方推荐使用 Docker 进行构建和运行 ubuntu安装docker…...
集合 数据结构 泛型
文章目录 1.Collection集合1.1数组和集合的区别【理解】1.2集合类体系结构【理解】1.3Collection 集合概述和使用【应用】内部类匿名内部类Lambda表达式 1.4Collection集合的遍历【应用】1.5增强for循环【应用】 2.List集合2.1List集合的概述和特点【记忆】2.2List集合的特有方…...
python脚本文件设置进程优先级(在.py文件中实现)
在 Python 代码中可以直接通过 psutil 模块或 系统调用 来设置进程优先级,无需依赖终端命令。以下是具体方法和示例: 1. 使用 psutil 模块(跨平台推荐) psutil 是一个跨平台库,支持 Windows、Linux 和 macOS。通过其 …...