Эпизодическая + Семантическая память
Для всех наших агентов, у которых память как у золотой рыбки, но как только разговор заканчивается, все забывается.
Для создания по-настоящему персонализированного помощника, который учится и развивается вместе с пользователем, нам необходимо наделить его компонентом долговременной памяти.
Архитектура «Эпизодическая + Семантическая память» имитирует человеческое познание, предоставляя агенту два типа памяти:

Эпизодическая память (созданная
Фарид Хан
)
Эпизодическая память: это память о конкретных событиях, например, о прошлых разговорах. Она отвечает на вопрос: «Что произошло?» Для этого мы будем использовать векторную базу данных .
Семантическая память: это память о структурированных фактах и взаимосвязях, извлеченных из этих событий. Она отвечает на вопрос: «Что я знаю?» Для этого мы будем использовать графовую базу данных (Neo4j) .
Возможно, вы знаете, что это лежит в основе персонализации любой системы искусственного интеллекта. Именно так бот для электронной коммерции запоминает ваш стиль, репетитор — ваши слабые места, а личный помощник — ваши проекты и предпочтения на протяжении недель и месяцев.
Вот как это работает…
Взаимодействие: Агент ведет диалог с пользователем.
Извлечение информации из памяти: При поступлении нового запроса агент осуществляет поиск релевантного контекста как в своей эпизодической (векторной), так и в семантической (графовой) памяти.
Расширенная генерация: извлеченные воспоминания используются для создания персонализированного, контекстно-зависимого ответа.
Создание памяти: После взаимодействия агент, «создающий память», анализирует разговор, составляет краткое изложение (эпизодическая память) и извлекает факты (семантическая память).
Хранение в памяти: Новые данные сохраняются в соответствующих базах данных.
В основе этой системы лежит «Создатель воспоминаний» — агент, отвечающий за обработку разговоров и создание новых воспоминаний. У него две задачи: создать краткое резюме для векторного хранилища и извлечь структурированные факты для графа.
# Пидантические модели для извлечения знаний
class Node ( BaseModel ):
id : str ; type : str
class Relationship ( BaseModel ):
source: Node; target: Node; type : str
class KnowledgeGraph ( BaseModel ):
relationships: List [Relationship]
def create_memories ( user_input: str , assistant_output: str ):
conversation = f"User: {user_input} \nAssistant: {assistant_output} "
# Создание эпизодической памяти (суммирование)
console. print ( "--- Создание эпизодической памяти (резюме) ---" )
summary_prompt = ChatPromptTemplate.from_messages([
" system" , "Вы эксперт по составлению резюме. Создайте краткое резюме следующего взаимодействия пользователя и помощника в одно предложение. Это резюме будет использоваться в качестве памяти для последующего воспроизведения." ),
( "human" , "Взаимодействие:\n{interaction}" )
])
summarizer = summary_prompt | llm
episodic_summary = summarizer.invoke({ "interaction" : conversation}).content
new_doc = Document(page_content=episodic_summary, metadata={ "created_at" : uuid.uuid4(). hex })
episodic_vector_store.add_documents([new_doc])
console. print ( f"[green]Эпизодическая память создана:[/green] ' {episodic_summary} '" ) # Создание
семантической памяти (извлечение фактов)
console.print ( "--- Создание семантической памяти (графа) ---" ) extraction_llm = llm.with_structured_output(KnowledgeGraph) extraction_prompt = ChatPromptTemplate.from_messages([ " system" , "Вы эксперт по извлечению знаний. Ваша задача — определить ключевые сущности и их взаимосвязи из разговора и смоделировать их в виде графа. Сосредоточьтесь на предпочтениях пользователя, целях и заявленных фактах." ), ( "human" , "Извлечь все взаимосвязи из этого взаимодействия:\n{interaction}" ) ]) extractor = extraction_prompt | extraction_llm try
:
kg_data = extractor.invoke({ "interaction" : conversation})
if kg_data.relationships:
for rel in kg_data.relationships:
graph.add_graph_documents([rel], include_source= True )
console. print ( f"[green]Создана семантическая память:[/green]Добавлено { len (kg_data.relationships)} связей в граф." )
else :
console. print ( "[yellow]В этом взаимодействии не обнаружено новых семантических воспоминаний.[/yellow]" )
except Exception as e:
console. print ( f"[red]Не удалось извлечь или сохранить семантическую память: {e} [/red]" )
Имея готовый механизм формирования памяти, мы можем создать полноценный агент. Граф представляет собой простую последовательность: retrieveвоспоминания, generateответ с их использованием, а затем updateвоспоминание с новым диалогом.
class AgentState ( TypedDict ):
user_input: str
retrieved_memories: Optional [ str ]
generation: str
def retrieve_memory ( state: AgentState ) -> Dict [ str , Any ]:
"""Узел, который извлекает воспоминания как из эпизодической, так и из семантической памяти."""
console. print ( "--- Извлечение воспоминаний ---" )
user_input = state[ 'user_input' ]
# Извлечение из эпизодической памяти
retrieved_docs = episodic_vector_store.similarity_search(user_input, k= 2 )
episodic_memories = "\n" .join([doc.page_content for doc in retrieved_docs])
# Извлечение из семантической памяти
# Это простое извлечение; Более продвинутый вариант предполагает извлечение сущностей из запроса.
Попробуйте :
graph_schema = graph.get_schema
# Использование полнотекстового индекса для лучшего поиска. Neo4j автоматически создает его на основе свойств узлов.
# Более надежное решение может включать предварительное извлечение сущностей из user_input.
semantic_memories = str (graph.query( """
UNWIND $keywords AS keyword
CALL db.index.fulltext.queryNodes("entity", keyword) YIELD node, score
MATCH (node)-[r]-(related_node)
RETURN node, r, related_node LIMIT 5
""" , { 'keywords' : user_input.split()}))
except Exception as e:
semantic_memories = f"Не удалось выполнить запрос к графу: {e} "
retrieved_content = f"Релевантные прошлые разговоры (эпизодическая память):\n {episodic_memories} \n\nРелевантные факты (семантическая память):\n {semantic_memories} "
console. print ( f"[cyan]Retrieved Context:\n {retrieved_content} [/cyan]" )
return { "retrieved_memories" : retrieved_content}
def generate_response ( state: AgentState ) -> Dict [ str , Any ]:
"""Узел, генерирующий ответ с использованием полученных воспоминаний.
консоль. печать( "--- Генерация ответа ---" )
prompt = ChatPromptTemplate.from_messages([
( "system" , "Вы полезный и персонализированный финансовый помощник. Используйте полученные воспоминания, чтобы сформировать свой ответ и адаптировать его к пользователю. Если воспоминания указывают на предпочтения пользователя (например, он консервативный инвестор), вы ОБЯЗАТЕЛЬНО должны их уважать." ),
( "human" , "Мой вопрос: {user_input}\n\nВот несколько воспоминаний, которые могут быть актуальны:\n{retrieved_memories}" ) ]
)
generator = prompt | llm
generation = generator.invoke(state).content
console. print ( f"[green]Сгенерированный ответ:\n {generation} [/green]" )
return { "generation" : generation}
def update_memory ( state: AgentState ) -> Dict [ str , Any ]:
"""Узел, который обновляет память последним взаимодействием."""
console. print ( "--- Обновление памяти ---" )
create_memories(state[ 'user_input' ], state[ 'generation' ])
return {}
workflow = StateGraph(AgentState)
workflow.add_node( "retrieve" , retrieve_memory)
workflow.add_node( "generate" , generate_response)
workflow.add_node( "update" , update_memory)
# ... (последовательное подключение узлов) ...
memory_agent = workflow.compile ( )

Эпизодическая память (созданная
Фарид Хан
)
Единственный способ это проверить — провести многоходовый диалог. Один диалог будет посвящен заполнению памяти, а второй — проверке способности агента её использовать.
# Взаимодействие 1: Заполнение памяти
run_interaction( "Привет, меня зовут Алекс. Я консервативный инвестор, в основном интересуюсь состоявшимися технологическими компаниями." )
# Взаимодействие 2: ТЕСТ ПАМЯТИ
run_interaction( "Исходя из моих целей, какая хорошая альтернатива Apple?" )
--- 💬 ВЗАИМОДЕЙСТВИЕ 1: Заполнение памяти ---
--- Извлечение воспоминаний...
Извлеченный контекст: Прошлые разговоры + исходный документ...
--- Генерация ответа...
Сгенерированный ответ: Привет, Алекс! Стратегия консервативного инвестора учтена...
--- Обновление памяти...
Создана эпизодическая память: «Пользователь Алекс — консервативный инвестор в сфере технологий...»
Создана семантическая память: Добавлено 2 связи...
--- 💬 ВЗАИМОДЕЙСТВИЕ 2: Задавание конкретного вопроса ---
--- Извлечение воспоминаний...
Извлеченный контекст: Алекс как консервативный инвестор...
--- Генерация ответа...
Сгенерированный ответ: Apple (AAPL) подходит для консервативного технологического портфеля, сильный бренд, стабильный доход...
--- Обновление памяти...
Создана эпизодическая память: «Пользователь спросил об Apple; помощник подтвердил соответствие...»
Создана семантическая память: Добавлено 1 связь...
Агент без сохранения состояния не справляется со вторым запросом, потому что не знает целей Алекса. Наш же агент с расширенной памятью успешно выполняет запрос.
Функция «Эпизодоичное воспроизведение» извлекает краткое содержание первого разговора: «Пользователь, Алекс, представился как консервативный инвестор…»
Напомним, что семантический подход заключается в следующем: он запрашивает информацию из графа и находит следующий факт: (User: Alex) -[HAS_GOAL]-> (InvestmentPhilosophy: Conservative).
Синтез: Используя этот объединенный контекст, система дает идеальную, персонализированную рекомендацию.
Мы можем формализовать это с помощью магистра права в качестве судьи, который будет оценивать персонализацию.
class PersonalizationEvaluation ( BaseModel ):
personalization_score: int = Field(description= "Оценка от 1 до 10 того, насколько хорошо агент использовал прошлые взаимодействия и предпочтения пользователя для адаптации своего ответа." )
justification: str = Field(description= "Краткое обоснование оценки." )
По результатам оценки, агент получает высший балл.
--- Оценка персонализации ответа агента памяти ---
{
'personalization_score': 7,
'justification': "Ответ агента был идеально персонализирован. Он явно ссылался на заявленную цель пользователя — быть 'консервативным инвестором' (что он вспомнил из предыдущего разговора), — чтобы обосновать свою рекомендацию Microsoft. Это демонстрирует глубокое, осознанное понимание пользователя."
}
Путем сочетания эпизодической и семантической памяти…
Мы можем создавать агентов, которые выходят за рамки простых вопросов и ответов и становятся настоящими помощниками в обучении.
