本文将演示使用大语言模型自制聊天机器人。主要的内容有:
- 如何让机器人具有记忆
- 如何使用
langgraph
自动记录聊天历史
我们将同时使用
llama3.1
和deepseek
做演示。由于langchain
可能对不同大模型支持程度不同,不同大模型的特点也不同,所以这个对比并不能说明哪个模型更好。
准备
在正式开始撸代码之前,需要准备一下编程环境。
-
计算机
本文涉及的所有代码可以在没有显存的环境中执行。 我使用的机器配置为:- CPU: Intel i5-8400 2.80GHz
- 内存: 16GB
-
Visual Studio Code 和 venv 这是很受欢迎的开发工具,相关文章的代码可以在
Visual Studio Code
中开发和调试。 我们用python
的venv
创建虚拟环境, 详见:
在Visual Studio Code中配置venv。 -
Ollama 在
Ollama
平台上部署本地大模型非常方便,基于此平台,我们可以让langchain
使用llama3.1
、qwen2.5
等各种本地大模型。详见:
在langchian中使用本地部署的llama3.1大模型 。
通过历史记录了解上下文
大模型本身不具备记忆功能,通常我们在调用大模型时,将聊天历史也喂给大模型,这样大模型就可以理解上下文,而产生记忆能力。
ChatModel
(例如:ChatOllama
) 是 LangChain Runnable
的实例,这意味着它们公开了一个用于与其交互的标准接口。比如 .invoke
方法。
下面我们做一下测试:
def chat(model_name):
model = ChatOllama(model=model_name,temperature=0.3,verbose=True)
response = model.invoke([HumanMessage(content="Hi! I'm Bob")])
print(f'chat_with_no_memory:\n{response.content}')
# We can see that it doesn't take the previous conversation turn into context, and cannot answer the question. This makes for a terrible chatbot experience!
response = model.invoke([HumanMessage(content="What's my name?")])
print(f'chat_with_no_memory 2:\n{response.content}')
def chat_with_memory(model_name):
'''具有记忆功能'''
model = ChatOllama(model=model_name,temperature=0.3,verbose=True)
response = model.invoke(
[
HumanMessage(content="Hi! I'm Bob"),
AIMessage(content="Hello Bob! How can I assist you today?"),
HumanMessage(content="What's my name?"),
]
)
print(f'chat_with_memory:\n{response.content}')
我们调用一下上述方法,看看返回结果:
- 调用 chat 方法:
llama3.1 | deepseek-r1 | |
---|---|---|
Hi! I’m Bob | Nice to meet you, Bob! How’s your day going so far? | Hello, Bob! How can I assist you today? 😊 |
What’s my name? | I’m a large language model, … , we can just chat as “you” and “I” for now. | Hi! I’m DeepSeek-R1, … , we invite you to consult our official documentation. |
- 调用 chat_with_memory 方法:
llama3.1 | deepseek-r1 |
---|---|
Your name is Bob. We just established that! | Hi! I’m DeepSeek-R1, … Your name is Bob. |
看起来 llama3.1
和 deepseek-r1
给出的回答很相似,并且,在给出历史聊天信息的情况下,它们都可以自动推断出当前用户的名字。
用 LangGraph
自动记录聊天信息
LangGraph
实现了内置持久层,使其成为支持多轮对话的聊天应用程序的理想选择。
将我们的聊天模型包装在最小的 LangGraph
应用程序中,使我们能够自动保存消息历史记录,从而简化多轮对话应用程序的开发。
def build_app(model_name):
model = ChatOllama(model=model_name,temperature=0.3,verbose=True)
# Define the function that calls the model
def call_model(state: MessagesState):
response = model.invoke(state["messages"])
return {"messages": response}
# Define a new graph
workflow = StateGraph(state_schema=MessagesState)
# Define the (single) node in the graph
workflow.add_edge(START, "model")
workflow.add_node("model", call_model)
# Add memory
memory = MemorySaver()
app = workflow.compile(checkpointer=memory)
return app
感觉
LangGraph
实现的代码比较直观优雅,更加适合实现复杂一些的Agent
,后面我们将有更多关于LangGraph
实现Agent
的演示。
下面用 config
设定不同的会话,查看大模型的表现。
def message_persistence():
app = build_app()
config = {"configurable": {"thread_id": "abc123"}}
query = "Hi! I'm Bob."
input_messages = [HumanMessage(query)]
output = app.invoke({"messages": input_messages}, config)
print(output["messages"][-1].pretty_print()) # output contains all messages in state
query = "What's my name?"
input_messages = [HumanMessage(query)]
output = app.invoke({"messages": input_messages}, config)
print(output["messages"][-1].pretty_print())
# different thread_id
config = {"configurable": {"thread_id": "abc234"}}
input_messages = [HumanMessage(query)]
output = app.invoke({"messages": input_messages}, config)
print(output["messages"][-1].pretty_print())
config = {"configurable": {"thread_id": "abc123"}}
input_messages = [HumanMessage(query)]
output = app.invoke({"messages": input_messages}, config)
print(output["messages"][-1].pretty_print())
我们来看看大模型本轮表现:
- llama3.1
================================== Ai Message ==================================
Nice to meet you, Bob! How's your day going so far? Is there something on your mind that you'd like to chat about or is this just a friendly hello?
None
================================== Ai Message ==================================
Your name is Bob! We established that right at the beginning of our conversation.
None
================================== Ai Message ==================================
I'm a large language model, I don't have any information about your personal identity or name. ... , I'd be happy to chat with you!
None
================================== Ai Message ==================================
Your name is Bob, as we've already discussed!
None
- deepseek-r1
================================== Ai Message ==================================
<think>
</think>
Hello, Bob! How can I assist you today? 😊
None
================================== Ai Message ==================================
<think>
Alright, the user greeted me as Bob and mentioned they're Bob. Now, they asked, "What's my name?"
...
I should make sure the response is warm and approachable, letting them know I'm here to help.
</think>
Hello! My name is Bob. How can I assist you today? 😊
None
================================== Ai Message ==================================
<think>
</think>
Hi! I don’t have access to personal information like names or identities. Could you ask something else I can help you with?
None
================================== Ai Message ==================================
<think>
Okay, so the user has been interacting with me as Bob multiple times now. They asked "What's my name?" again after I told them it was Bob.
...
I should make sure the response is clear and friendly, letting them know I'm here to help without overcomplicating things.
</think>
It seems like you might be asking about my name, but as an AI, I don't have personal experiences or feelings. My "name" is a label you've given me in our conversation. How can I assist you further? 😊
None
我们看到 deepseek-r1
返回了思考过程,想法比较复杂,最后并未直接回答名字。
总结
通过实现这个简单的聊天机器人,我们可以发现 llama3.1
和 deepseek-r1
都可以轻松处理聊天任务,不过似乎 deepseek-r1
更加 “狡猾” 一点。
代码
本文涉及的所有代码以及相关资源都已经共享,参见:
参考:
🪐祝好运🪐