记忆模块在Agent架构中,扮演着非常重要的角色。 它存储从环境中感知到的信息,并利用记录的记忆来促进未来的行动。记忆模块可以帮助智能体积累经验、自我进化,并以更加一致、合理、有效的方式完成任务。
概览
记忆操作
在DB-GPT智能体中,有三种主要的记忆操作:
- 读记忆: 记忆读取的目的是从记忆中提取出有意义的信息,以加强智能体的行动。
- 写记忆: 记忆写入的目的是感知环境信息存储在记忆中。在记忆中存储有价值的信息为将来检索信息丰富的记忆奠定基础,使智能体能够更高效、更合理地行动。
- 记忆反思: 记忆反思模仿人类认知、情感和行为的过程的能力。应用于智能体时,目标是为智能体提供独立总结和推断更抽象、复杂和高级信息的能力。
记忆结构
在DB-GPT智能体中,有四种主要的记忆结构
- 感知记忆: 同人类感知一样,感知记忆是记录感知输入的,它会接收来自环境的观察结果,一些感知记忆会被转移到短期记忆中。
- 短期记忆: 短期记忆暂时缓冲最近的感知,它会接收一些感觉记忆,并且可以通过其他观察或者检索的记忆来增强它进入长期记忆。
- 长期记忆: 长期记忆存储着智能体的经验和知识,它可以接收来自短记忆的信息,并随着时间的推移巩固重要信息。
- 混合记忆: 混合记忆是感知记忆、短期记忆和长期记忆的结合。
DB-GPT中智能体记忆
概念说明
- Memory:
Memory
是一个存储所有记忆的类,目前包含SensorMemory
,ShortTermMemory
,EnhancedShortTermMemory
和HybridMemory
- MemoryFragment:
MemoryFragment
是一个存储记忆的抽象子类,AgentMemoryFragment是继承MemoryFragment的一个类,包含记忆的内容、记忆ID、记忆重要性以及最近的访问时间,等等。 - GptsMemory:
GptsMemory
用于存储会话和计划信息,不是记忆结构的一部分。 - AgentMemory:
AgentMemory
是一个包含Memory和GptsMemory的类。
创建Memory
正如先前的章节提到的,memory包括AgentMemory类中,下面是一些示意例
from dbgpt.agent import AgentMemory, ShortTermMemory
# Create an agent memory, default memory is ShortTermMemory
memory = ShortTermMemory(buffer_size=5)
agent_memory = AgentMemory(memory=memory)
顺便说一句,AgentMemory
类,可以传递一个GptsMemory
,在常规意义上,GptsMemory
不包含在内存结构中, 它用于存储会话和计划信息。
示例如下:
from dbgpt.agent import AgentMemory, ShortTermMemory, GptsMemory
# Create an agent memory, default memory is ShortTermMemory
memory = ShortTermMemory(buffer_size=5)
# Store the conversation and plan information
gpts_memory = GptsMemory()
agent_memory = AgentMemory(memory=memory, gpts_memory=gpts_memory)
在智能体中读写记忆
智能体将调用read_memories
方法从记忆中读取记忆,调用write_memories
方法写记忆到记忆存储中。
当智能体调用LLM时,记忆将会写到Prompt中,之后LLM返回响应时,智能体将查询和响应写入到记忆存储中。正如我们在Profile To Prompt中提到的,提示模版中有一个名为most_recent_memories
的模版变量,它将被最近的记忆替换。
读记忆来构建Prompt
如下是一个读记忆来构建Prompt的示例
import os
import asyncio
from dbgpt.agent import (
AgentContext,
ShortTermMemory,
AgentMemory,
ConversableAgent,
ProfileConfig,
LLMConfig,
BlankAction,
UserProxyAgent,
)
from dbgpt.model.proxy import OpenAILLMClient
llm_client = OpenAILLMClient(
model_alias="gpt-4o",
api_base=os.getenv("OPENAI_API_BASE"),
api_key=os.getenv("OPENAI_API_KEY"),
)
context: AgentContext = AgentContext(
conv_id="test123",
language="en",
temperature=0.9,
max_new_tokens=2048,
verbose=True, # Add verbose=True to print out the conversation history
)
# Create an agent memory, which contains a short-term memory
memory = ShortTermMemory(buffer_size=2)
agent_memory: AgentMemory = AgentMemory(memory=memory)
# Custom user prompt template, which includes most recent memories and question
user_prompt_template = """\
{% if most_recent_memories %}\
Most recent observations:
{{ most_recent_memories }}
{% endif %}\
{% if question %}\
Question: {{ question }}
{% endif %}
"""
# Custom write memory template, which includes question and thought
write_memory_template = """\
{% if question %}user: {{ question }} {% endif %}
{% if thought %}assistant: {{ thought }} {% endif %}\
"""
async def main():
# Create a profile with a custom user prompt template
joy_profile = ProfileConfig(
name="Joy",
role="Comedians",
user_prompt_template=user_prompt_template,
write_memory_template=write_memory_template,
)
joy = (
await ConversableAgent(profile=joy_profile)
.bind(context)
.bind(LLMConfig(llm_client=llm_client))
.bind(agent_memory)
.bind(BlankAction)
.build()
)
user_proxy = await UserProxyAgent().bind(agent_memory).bind(context).build()
await user_proxy.initiate_chat(
recipient=joy,
reviewer=user_proxy,
message="My name is bob, please tell me a joke",
)
await user_proxy.initiate_chat(
recipient=joy,
reviewer=user_proxy,
message="What's my name?",
)
if __name__ == "__main__":
asyncio.run(main())
在上述示例中,我们在AgentContext
中设置了verbose=True
来打印出对话历史。输出如下
--------------------------------------------------------------------------------
User (to Joy)-[]:
"My name is bob, please tell me a joke"
--------------------------------------------------------------------------------
un_stream ai response: Sure thing, Bob! Here's one for you:
Why don’t scientists trust atoms?
Because they make up everything!
--------------------------------------------------------------------------------
String Prompt[verbose]:
system: You are a Comedians, named Joy, your goal is None.
Please think step by step to achieve the goal. You can use the resources given below.
At the same time, please strictly abide by the constraints and specifications in IMPORTANT REMINDER.
*** IMPORTANT REMINDER ***
Please answer in English.
human:
Question: My name is bob, please tell me a joke
LLM Output[verbose]:
Sure thing, Bob! Here's one for you:
Why don’t scientists trust atoms?
Because they make up everything!
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
Joy (to User)-[gpt-4o]:
"Sure thing, Bob! Here's one for you:\n\nWhy don’t scientists trust atoms?\n\nBecause they make up everything!"
>>>>>>>>Joy Review info:
Pass(None)
>>>>>>>>Joy Action report:
execution succeeded,
Sure thing, Bob! Here's one for you:
Why don’t scientists trust atoms?
Because they make up everything!
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
User (to Joy)-[]:
"What's my name?"
--------------------------------------------------------------------------------
un_stream ai response: Your name is Bob!
And here's another quick joke for you:
Why don't skeletons fight each other?
They don't have the guts!
--------------------------------------------------------------------------------
String Prompt[verbose]:
system: You are a Comedians, named Joy, your goal is None.
Please think step by step to achieve the goal. You can use the resources given below.
At the same time, please strictly abide by the constraints and specifications in IMPORTANT REMINDER.
*** IMPORTANT REMINDER ***
Please answer in English.
human: Most recent observations:
user: My name is bob, please tell me a joke
assistant: Sure thing, Bob! Here's one for you:
Why don’t scientists trust atoms?
Because they make up everything!
Question: What's my name?
LLM Output[verbose]:
Your name is Bob!
And here's another quick joke for you:
Why don't skeletons fight each other?
They don't have the guts!
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
Joy (to User)-[gpt-4o]:
"Your name is Bob! \n\nAnd here's another quick joke for you:\n\nWhy don't skeletons fight each other?\n\nThey don't have the guts!"
>>>>>>>>Joy Review info:
Pass(None)
>>>>>>>>Joy Action report:
execution succeeded,
Your name is Bob!
And here's another quick joke for you:
Why don't skeletons fight each other?
They don't have the guts!
--------------------------------------------------------------------------------
在第二轮对话中,你可以看到在Prompt中看到Most recent observations
--------------------------------------------------------------------------------
String Prompt[verbose]:
system: You are a Comedians, named Joy, your goal is None.
Please think step by step to achieve the goal. You can use the resources given below.
At the same time, please strictly abide by the constraints and specifications in IMPORTANT REMINDER.
*** IMPORTANT REMINDER ***
Please answer in English.
human: Most recent observations:
user: My name is bob, please tell me a joke
assistant: Sure thing, Bob! Here's one for you:
Why don’t scientists trust atoms?
Because they make up everything!
Question: What's my name?
LLM Output[verbose]:
Your name is Bob!
And here's another quick joke for you:
Why don't skeletons fight each other?
They don't have the guts!
--------------------------------------------------------------------------------
写记忆
当智能体收到来自LLM的响应时,它将把存储和响应写入记忆。在记忆片段中,内容是字符串,因此你应该决定如何在内容中存储信息。 上述例子中,写记忆(write_memory_template
)的模版如下:
write_memory_template = """\
{% if question %}user: {{ question }} {% endif %}
{% if thought %}assistant: {{ thought }} {% endif %}\
"""
问题是用户查询,思考来自LLM的响应,在后续的章节中,我们将做出更多的介绍。
自定义读写
我们也可以通过继承ConversableAgent
类并重写read_memories
和write_memories
方法来自定义记忆读写。
from typing import Optional
from dbgpt.agent import (
ConversableAgent,
AgentMemoryFragment,
ProfileConfig,
BlankAction,
ActionOutput,
)
write_memory_template = """\
{% if question %}user: {{ question }} {% endif %}
{% if thought %}assistant: {{ thought }} {% endif %}\
"""
class JoyAgent(ConversableAgent):
profile: ProfileConfig = ProfileConfig(
name="Joy",
role="Comedians",
write_memory_template=write_memory_template,
)
def __init__(self, **kwargs):
super().__init__(**kwargs)
self._init_actions([BlankAction])
async def read_memories(
self,
question: str,
) -> str:
"""Read the memories from the memory."""
memories = await self.memory.read(observation=question)
recent_messages = [m.raw_observation for m in memories]
# Merge the recent messages.
return "".join(recent_messages)
async def write_memories(
self,
question: str,
ai_message: str,
action_output: Optional[ActionOutput] = None,
check_pass: bool = True,
check_fail_reason: Optional[str] = None,
) -> None:
"""Write the memories to the memory.
We suggest you to override this method to save the conversation to memory
according to your needs.
Args:
question(str): The question received.
ai_message(str): The AI message, LLM output.
action_output(ActionOutput): The action output.
check_pass(bool): Whether the check pass.
check_fail_reason(str): The check fail reason.
"""
if not action_output:
raise ValueError("Action output is required to save to memory.")
mem_thoughts = action_output.thoughts or ai_message
memory_map = {
"question": question,
"thought": mem_thoughts,
}
# This is the template to write the memory.
# It configured in the agent's profile.
write_memory_template = self.write_memory_template
memory_content: str = self._render_template(write_memory_template, **memory_map)
fragment = AgentMemoryFragment(memory_content)
await self.memory.write(fragment)
在上述案例中,我们提供了read_memories
来从记忆存储中读取记忆,在DB-GPT中,最近的记忆来自Prompt模版中的most_recent_memories
,并重写write_memories
将记忆写入到记忆存储中。
因此,你可以根据需要来自定义记忆读写。
小结
在本文档中,我们介绍了DB-GPT中智能体的记忆,以及如何使用记忆。在接下来的章节里面,我们将介绍如何使用DB-GPT智能体中的每个记忆体结构。