使用Autogen和Zep构建具有长期记忆的代理
本笔记本将介绍如何构建一个具有长期记忆的Autogen代理。Zep从用户与代理的交互中构建知识图,使代理能够回忆起之前对话或用户交互中的相关事实。
在本笔记本中我们将:- 创建一个Autogen代理类,该类通过添加长期记忆扩展了ConversableAgent
- 创建一个心理健康助理代理,CareBot,作为顾问和教练。- 创建一个用户代理,Cathy,代表我们的预期用户。- 演示将聊天历史预加载到Zep中。- 演示代理在对话中的表现,CareBot回忆起之前与Cathy的对话中的事实。- 检查Zep中的事实,并演示如何使用Zep的事实评级来提高返回事实的质量。
要求
import os
import uuid
from typing import Dict, Union
from dotenv import load_dotenv
from autogen import Agent, ConversableAgent
load_dotenv()
config_list = [
{
"model": "gpt-4o-mini",
"api_key": os.environ.get("OPENAI_API_KEY"),
"max_tokens": 1024,
}
]
flaml.automl is not available. Please install flaml[automl] to enable AutoML functionalities.
初始化 Zep 客户端
你可以在这里注册一个Zep账户:https://www.getzep.com/
from zep_cloud import FactRatingExamples, FactRatingInstruction, Message
from zep_cloud.client import AsyncZep
MIN_FACT_RATING = 0.3
# Configure Zep
zep = AsyncZep(api_key=os.environ.get("ZEP_API_KEY"))
def convert_to_zep_messages(chat_history: list[dict[str, str | None]]) -> list[Message]:
"""
Convert chat history to Zep messages.
Args:
chat_history (list): List of dictionaries containing chat messages.
Returns:
list: List of Zep Message objects.
"""
return [
Message(
role_type=msg["role"],
role=msg.get("name", None),
content=msg["content"],
)
for msg in chat_history
]
ZepConversableAgent
ZepConversableAgent
是一个自定义的 ConversableAgent
实现,它与 Zep 集成,用于长期记忆管理。该类通过添加 Zep 特定功能来扩展基础 ConversableAgent
的功能,以便从长期记忆中存储和检索信息。
class ZepConversableAgent(ConversableAgent):
"""
A custom ConversableAgent that integrates with Zep for long-term memory.
"""
def __init__(
self,
name: str,
system_message: str,
llm_config: dict,
function_map: dict,
human_input_mode: str,
zep_session_id: str,
):
super().__init__(
name=name,
system_message=system_message,
llm_config=llm_config,
function_map=function_map,
human_input_mode=human_input_mode,
)
self.zep_session_id = zep_session_id
# store the original system message as we will update it with relevant facts from Zep
self.original_system_message = system_message
self.register_hook("a_process_last_received_message", self.persist_user_messages)
self.register_hook("a_process_message_before_send", self.persist_assistant_messages)
async def persist_assistant_messages(
self, sender: Agent, message: Union[Dict, str], recipient: Agent, silent: bool
):
"""Agent sends a message to the user. Add the message to Zep."""
# Assume message is a string
zep_messages = convert_to_zep_messages([{"role": "assistant", "name": self.name, "content": message}])
await zep.memory.add(session_id=self.zep_session_id, messages=zep_messages)
return message
async def persist_user_messages(self, messages: list[dict[str, str]] | str):
"""
User sends a message to the agent. Add the message to Zep and
update the system message with relevant facts from Zep.
"""
# Assume messages is a string
zep_messages = convert_to_zep_messages([{"role": "user", "content": messages}])
await zep.memory.add(session_id=self.zep_session_id, messages=zep_messages)
memory = await zep.memory.get(self.zep_session_id, min_rating=MIN_FACT_RATING)
# Update the system message with the relevant facts retrieved from Zep
self.update_system_message(
self.original_system_message
+ f"\n\nRelevant facts about the user and their prior conversation:\n{memory.relevant_facts}"
)
return messages
Zep 用户和会话管理
Zep用户
一个Zep用户代表与您的应用程序交互的个人。每个用户可以拥有多个会话,使您能够跟踪和管理随时间变化的交互。每个用户的唯一标识符是UserID
,它可以是任何字符串值(例如,用户名、电子邮件地址或UUID)。
Zep 会话
一个会话代表一次对话,并且可以与用户建立一对多的关系。聊天消息被添加到会话中,每个会话包含多条消息。
事实评级
事实评分是Zep中的一个功能,允许您对从对话中提取的事实的的重要性或相关性进行评分。这有助于在检索记忆工件时优先处理和过滤信息。在这里,我们根据感人的程度对事实进行评分。我们提供了感人程度的定义以及几个非常感人和不太感人的事实示例。在检索记忆时,您可以使用min_rating
参数根据事实的重要性进行过滤。
事实评价有助于确保使用最相关的信息,尤其是在长对话或复杂对话中,用以为基础代理提供支持。
bot_name = "CareBot"
user_name = "Cathy"
user_id = user_name + str(uuid.uuid4())[:4]
session_id = str(uuid.uuid4())
await zep.user.add(user_id=user_id)
fact_rating_instruction = """Rate the facts by poignancy. Highly poignant
facts have a significant emotional impact or relevance to the user.
Low poignant facts are minimally relevant or of little emotional significance.
"""
fact_rating_examples = FactRatingExamples(
high="The user received news of a family member's serious illness.",
medium="The user completed a challenging marathon.",
low="The user bought a new brand of toothpaste.",
)
await zep.memory.add_session(
user_id=user_id,
session_id=session_id,
fact_rating_instruction=FactRatingInstruction(
instruction=fact_rating_instruction,
examples=fact_rating_examples,
),
)
Session(classifications=None, created_at='2024-10-07T21:12:13.952672Z', deleted_at=None, ended_at=None, fact_rating_instruction=FactRatingInstruction(examples=FactRatingExamples(high="The user received news of a family member's serious illness.", low='The user bought a new brand of toothpaste.', medium='The user completed a challenging marathon.'), instruction='Rate the facts by poignancy. Highly poignant \nfacts have a significant emotional impact or relevance to the user. \nLow poignant facts are minimally relevant or of little emotional \nsignificance.'), fact_version_uuid=None, facts=None, id=774, metadata=None, project_uuid='00000000-0000-0000-0000-000000000000', session_id='f3854ad0-5bd4-4814-a814-ec0880817953', updated_at='2024-10-07T21:12:13.952672Z', user_id='Cathy1023', uuid_='31ab3314-5ac8-4361-ad11-848fb7befedf')
将先前的对话预加载到Zep
我们将把之前的对话加载到长期记忆中。当Cathy重新开始与CareBot的对话时,我们将使用从这次对话中提取的事实,以确保Carebot有上下文。
chat_history = [
{
"role": "assistant",
"name": "carebot",
"content": "Hi Cathy, how are you doing today?",
},
{
"role": "user",
"name": "Cathy",
"content": "To be honest, I've been feeling a bit down and demotivated lately. It's been tough.",
},
{
"role": "assistant",
"name": "CareBot",
"content": "I'm sorry to hear that you're feeling down and demotivated, Cathy. It's understandable given the challenges you're facing. Can you tell me more about what's been going on?",
},
{
"role": "user",
"name": "Cathy",
"content": "Well, I'm really struggling to process the passing of my mother.",
},
{
"role": "assistant",
"name": "CareBot",
"content": "I'm deeply sorry for your loss, Cathy. Losing a parent is incredibly difficult. It's normal to struggle with grief, and there's no 'right' way to process it. Would you like to talk about your mother or how you're coping?",
},
{
"role": "user",
"name": "Cathy",
"content": "Yes, I'd like to talk about my mother. She was a kind and loving person.",
},
]
# Convert chat history to Zep messages
zep_messages = convert_to_zep_messages(chat_history)
await zep.memory.add(session_id=session_id, messages=zep_messages)
SuccessResponse(message='OK')
在Zep中查看所有事实
我们查询此用户会话的所有会话事实。只返回满足MIN_FACT_RATING
阈值的事实。
response = await zep.memory.get_session_facts(session_id=session_id, min_rating=MIN_FACT_RATING)
for r in response.facts:
print(r)
created_at='2024-10-07T21:12:15.96584Z' fact='Cathy describes her mother as a kind and loving person.' rating=0.5 uuid_='6a086a73-d4b8-4c1b-9b2f-08d5d326d813'
created_at='2024-10-07T21:12:15.96584Z' fact='Cathy has been feeling down and demotivated lately.' rating=0.5 uuid_='e19d959c-2a01-4cc7-9d49-108719f1a749'
created_at='2024-10-07T21:12:15.96584Z' fact='Cathy is struggling to process the passing of her mother.' rating=0.75 uuid_='d6c12a5d-d2a0-486e-b25d-3d4bdc5ff466'
创建Autogen代理,CareBot,一个ZepConversableAgent
的实例
我们将当前的session_id
传递给CareBot代理,使其能够检索与Cathy对话相关的关键事实。
carebot_system_message = """
You are a compassionate mental health bot and caregiver. Review information about the user and their prior conversation below and respond accordingly.
Keep responses empathetic and supportive. And remember, always prioritize the user's well-being and mental health. Keep your responses very concise and to the point.
"""
agent = ZepConversableAgent(
bot_name,
system_message=carebot_system_message,
llm_config={"config_list": config_list},
function_map=None, # No registered functions, by default it is None.
human_input_mode="NEVER", # Never ask for human input.
zep_session_id=session_id,
)
创建Autogen代理,Cathy
Cathy 是一个人类的代表。在构建生产应用程序时,您可以将 Cathy 替换为人类参与的模式。
注意,我们指示Cathy通过询问她之前的会话来开始与CareBit的对话。这是我们测试是否可以从Zep的长期记忆中检索信息的机会。
cathy = ConversableAgent(
user_name,
system_message="You are returning to your conversation with CareBot, a mental health bot. Ask the bot about your previous session.",
llm_config={"config_list": config_list},
human_input_mode="NEVER", # Never ask for human input.
)
开始对话
我们使用Autogen的a_initiate_chat
方法让两个代理进行对话。CareBot是主要代理。
注意 Carebot如何能够详细回忆过去关于Cathy母亲的对话,因为其系统提示中添加了来自Zep的相关事实。
result = await agent.a_initiate_chat(
cathy,
message="Hi Cathy, nice to see you again. How are you doing today?",
max_turns=3,
)
在Zep中查看当前事实
让我们看看随着对话的进展,事实是如何演变的。
response = await zep.memory.get_session_facts(session_id, min_rating=MIN_FACT_RATING)
for r in response.facts:
print(r)
created_at='2024-10-07T20:04:28.397184Z' fact="Cathy wants to reflect on a previous conversation about her mother and explore the topic of her mother's passing further." rating=0.75 uuid_='56488eeb-d8ac-4b2f-8acc-75f71b56ad76'
created_at='2024-10-07T20:04:28.397184Z' fact='Cathy is struggling to process the passing of her mother and has been feeling down and demotivated lately.' rating=0.75 uuid_='0fea3f05-ed1a-4e39-a092-c91f8af9e501'
created_at='2024-10-07T20:04:28.397184Z' fact='Cathy describes her mother as a kind and loving person.' rating=0.5 uuid_='131de203-2984-4cba-9aef-e500611f06d9'
在Zep的长期记忆中搜索事实
除了使用当前对话来检索事实的memory.get
方法外,我们还可以使用自己的关键词在Zep中进行搜索。在这里,我们通过查询来检索事实。再次,我们使用事实评分来限制返回的事实,只保留那些具有高重要性评分的事实。
memory.search_sessions
API 可以作为代理工具使用,使代理能够跨用户记忆搜索相关事实。
response = await zep.memory.search_sessions(
text="What do you know about Cathy's family?",
user_id=user_id,
search_scope="facts",
min_fact_rating=MIN_FACT_RATING,
)
for r in response.results:
print(r.fact)
created_at='2024-10-07T20:04:28.397184Z' fact='Cathy describes her mother as a kind and loving person.' rating=0.5 uuid_='131de203-2984-4cba-9aef-e500611f06d9'
created_at='2024-10-07T20:04:28.397184Z' fact='Cathy is struggling to process the passing of her mother and has been feeling down and demotivated lately.' rating=0.75 uuid_='0fea3f05-ed1a-4e39-a092-c91f8af9e501'
created_at='2024-10-07T20:04:28.397184Z' fact="Cathy wants to reflect on a previous conversation about her mother and explore the topic of her mother's passing further." rating=0.75 uuid_='56488eeb-d8ac-4b2f-8acc-75f71b56ad76'