Image

Создаем мощного ИИ-агента с долговременной памятью, используя LangGraph, RAG и веб-скрапер

5c30160adb354178e52d53b9be5d7c39

В этой статье я поделюсь с вами очень быстрым руководством, которое покажет, как создать мультиагентного чат-бота с использованием LangGraph, RAG и долговременной памяти для создания мощного чат-бота-агента для вашего бизнеса или личного использования.

При создании ИИ-агента одним из важнейших элементов является память. Правильная система памяти необходима для запоминания прошлых разговоров с пользователем и дальнейшего их использования для понимания контекста.

В последнее время я все больше работаю над созданием внутренних ИИ-агентов, но всегда сталкиваюсь с проблемами в системах памяти. Когда я долго взаимодействую с ИИ-агентом, он часто теряет контекст или забывает предыдущие разговоры, что создает ощущение, будто я разговариваю с кем-то, страдающим амнезией.

Агент начинает каждый разговор с чистого листа и не помнит предыдущий контекст или извлеченные уроки. Это означает, что:

  • Он не помнит ваши личные настройки или предпочтения.

  • Ему не хватает последовательности в долгосрочных проектах.

  • Одно и то же объяснение приходится повторять много раз.

  • Он не может выстраивать глубокие доверительные отношения.

В человеческих отношениях лучшее общение возникает из общего опыта и узнанных друг о друге черт, и ИИ нуждается в такой же способности.

Однако технология, позволяющая наделить ИИ памятью, уже существует, и разрабатывается множество проектов с открытым исходным кодом.

Именно здесь на сцену выходит Mem0, чтобы решить эту проблему, предоставляя постоянную память для ИИ-моделей. Это помогает им сохранять информацию между сессиями, делая взаимодействие с пользователем более естественным и обеспечивая персонализацию для больших языковых моделей, чтобы предоставлять уникальный опыт каждому пользователю.

Делегируйте часть рутинных задач вместе с BotHub! Для доступа к сервису не требуется VPN и можно использовать российскую карту. По ссылке вы можете получить 100 000 бесплатных токенов для первых задач и приступить к работе с нейросетями прямо сейчас!

Итак, позвольте мне быстро продемонстрировать живого чат-бота, чтобы показать, что я имею в виду.

2fc2ae32953ff686531962104d73586f

1. Вход в рабочее пространство ИИ

Когда вы вводите запрос, он сначала попадает в функцию chat_with_memories.

  • Это настраивает рабочее пространство ИИ.

  • Состояние инициализируется с пустыми полями для воспоминаний, контекста документов и ответа.

2. Извлечение воспоминаний

ИИ-агент ищет в своем хранилище памяти прошлые разговоры или заметки, связанные с вашим запросом.

  • Он собирает до 50 релевантных записей.

  • Эти записи форматируются в строку для контекста.

3. Извлечение документов

Далее ИИ-агент извлекает релевантные новостные статьи:

  • Собирает данные из RSS-лент Seeking Alpha, Yahoo Finance и Investing.com.

  • Загружает полные статьи с помощью requests и парсит их с помощью BeautifulSoup.

  • Извлекает наиболее релевантное содержимое и добавляет его в контекст.

4. Генерация ответа

Имея под рукой воспоминания и документы, ИИ конструирует системную подсказку, объединяя:

  • Ваш запрос

  • Релевантные воспоминания

  • Извлеченный новостной контент Затем он вызывает языковую модель для получения краткого ответа.

  • Выполняются инструкции, такие как «ответь примерно в 50 словах» и «каждое предложение пиши с новой строки».

5. Сохранение взаимодействия

Наконец:

  • ИИ сохраняет ответ обратно в память.

  • Это позволяет ему помнить это взаимодействие для будущих запросов.

  • Окончательный ответ аккуратно отображается в терминале, готовый к следующему вопросу.

Mem0 и другие слои памяти

До появления Mem0 основным подходом к памяти ИИ была технология Retrieval-Augmented Generation (RAG). RAG, извлекая релевантные данные и генерируя ответы, хорошо себя показывал во многих сценариях применения.

Однако возможности RAG в области памяти ограничены: у него нет встроенного механизма памяти, что делает его неэффективным для обработки долгосрочной информации и требует постоянного обновления базы данных для поддержания точности. В отличие от этого, Mem0 значительно расширяет возможности памяти ИИ.

Mem0 постоянно запоминает предоставленные пользователем данные, включая текст, изображения, голосовые заметки и PDF-файлы, формируя всеобъемлющую базу знаний.

Это позволяет ИИ эффективно работать в течение длительных периодов и, при необходимости, получать доступ к релевантным воспоминаниям, предоставляя более точные ответы.

Давайте начнем кодить!

Теперь давайте шаг за шагом разберемся, как создать чат-бота с LangGraph, Mem0, долговременной памятью, RAG и веб-скрапером. Мы установим библиотеки, которые поддерживают модель. Для этого выполним pip install requirements.

pip install -r requirements.txt

Следующий шаг — обычный: мы импортируем соответствующие библиотеки, значение которых станет очевидным по ходу дела.

  • mem0: добавляет слой памяти к ИИ-приложениям, делая их stateful (с сохранением состояния), что позволяет им хранить и вызывать взаимодействия с пользователем, предпочтения и релевантный контекст.

  • FlashrankRerank: это ультралегкая и сверхбыстрая библиотека Python для добавления переранжирования в ваши существующие конвейеры поиска и извлечения.

  • Contextual Compression Retriever: это система, предназначенная для эффективного извлечения и сжатия релевантной информации из большого набора документов.

import os import logging import warnings from dotenv import load_dotenv from mem0 import Memory from langchain_openai import ChatOpenAI, OpenAIEmbeddings from langchain_community.document_loaders import DirectoryLoader, UnstructuredFileLoader from langchain_community.vectorstores import FAISS from langchain_community.document_compressors import FlashrankRerank from langchain.retrievers import ContextualCompressionRetriever from langchain_core.messages import HumanMessage, SystemMessage from langgraph.graph import StateGraph, END from typing import TypedDict from scraper import fetch_seekingalpha_trending_news

1. Веб-скрапер

Давайте импортируем os для файловых операций, feedparser для чтения RSS-лент, requests для загрузки веб-страниц и BeautifulSoup для парсинга HTML. Далее, вспомогательная функция get_folder_path(pre_path, folder_name) создает путь вида pre_path/folder_name, создает эту папку, если она не существует, и возвращает путь. Затем fetch_seekingalpha_trending_news() определяет список URL-адресов RSS-лент, обеспечивает наличие папки ./data и пути news.txt, и собирает элементы лент, парся каждую ленту (она берет до 15 записей с каждой ленты и пропускает те, которые не удалось обработать).

После этого она устанавливает User-Agent, похожий на браузерный, открывает news.txt для записи в кодировке UTF-8 и для 30 собранных статей извлекает title и link. Она пытается выполнить GET-запрос по ссылке с 5-секундным тайм-аутом и использует BeautifulSoup с парсером lxml, чтобы взять первые десять параграфов <p> в качестве тела статьи. Если извлечение/парсинг не удается, она возвращается к summary или description из записи ленты. Заголовок и очищенное содержимое каждой статьи записываются в файл с разделителем из дефисов, и по завершении.

Функция выводит, сколько статей было собрано, и возвращает пустой список.

import os import feedparser import requests from bs4 import BeautifulSoup def get_folder_path(pre_path, folder_name): folder_path = f'{pre_path}/{folder_name}’ os.makedirs(folder_path, exist_ok=True) return folder_path def fetch_seekingalpha_trending_news(): feeds = [ ‘https://seekingalpha.com/feed.xml’, ‘https://feeds.finance.yahoo.com/rss/2.0/headline’, ‘https://www.investing.com/rss/news.rss’, ] data_path = get_folder_path(‘.’, ‘data’) new_path = f'{data_path}/news.txt’ articles = [] for feed_url in feeds: try: feed = feedparser.parse(feed_url) articles.extend(feed.entries[:15]) except Exception as e: print(f»Error {feed_url}: {e}») headers = { ‘User-Agent’: ‘Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36’ } with open(new_path, «w», encoding=»utf-8″) as f: for entry in articles[:30]: title = entry.get(‘title’, ‘No title’) link = entry.get(‘link’, ») # Try to fetch full article try: resp = requests.get(link, headers=headers, timeout=5) soup = BeautifulSoup(resp.content, ‘lxml’) # Extract main content — works for most news sites paragraphs = soup.find_all(‘p’) content = ‘n’.join([p.get_text() for p in paragraphs[:10]]) except: # Fallback to summary content = entry.get(‘summary’, entry.get(‘description’, »)) if content: content = BeautifulSoup(content, ‘lxml’).get_text() f.write(f»{title}nn{content}nn{‘-‘*50}nn») print(f»✓ Fetched {len(articles)} articles») return []

2. RAG Rerank (Переранжирование)

Давайте настроим модели и API-ключ. Я выбираю gpt-4o для чат-модели, text-embedding-3-small для эмбеддингов и загружаю OPENAI_API_KEY из переменных окружения. Затем вызывается fetch_seekingalpha_trending_news(), которая сохраняет новостные статьи в файл ./data/news.txt. Далее создается DirectoryLoader, который загружает все .txt файлы из папки ./data/ с помощью UnstructuredFileLoader, и docs = loader.load() парсит эти файлы в объекты документов. После этого создается векторное хранилище FAISS с помощью vs = FAISS.from_documents(…), где каждый документ эмбеддируется с использованием OpenAIEmbeddings, настроенного с выбранной моделью эмбеддингов и API-ключом. Затем настраивается реранкер FlashrankRerank, который пересортировывает результаты с помощью легкой модели BERT (ms-marco-TinyBERT-L-2-v2) и возвращает топ-5 результатов.

Наконец, ретривер FAISS оборачивается в ContextualCompressionRetriever, который сначала извлекает до 5 кандидатов (k=5) из FAISS, а затем сжимает вывод с помощью реранкера, чтобы сохранить только самую релевантную информацию.

chat_model = «gpt-4o» emb_model = «text-embedding-3-small» openai_api_key = os.getenv(«OPENAI_API_KEY») fetch_seekingalpha_trending_news() loader = DirectoryLoader(«./data/», glob=»*.txt», loader_cls=UnstructuredFileLoader) docs = loader.load() vs = FAISS.from_documents(docs, OpenAIEmbeddings( model=emb_model, openai_api_key=openai_api_key )) reranker = FlashrankRerank(model=»ms-marco-TinyBERT-L-2-v2″, top_n=5) compressed_retriever = ContextualCompressionRetriever( base_retriever=vs.as_retriever(search_kwargs={«k»: 5}), base_compressor=reranker )

3. Память

Затем я инициализирую объект Memory() для долговременного хранения и определяю llm с помощью ChatOpenAI с моделью chat_model, нулевой температурой и API-ключом.

Далее определяется тип GraphState, который отслеживает сообщение пользователя, его ID, любые извлеченные воспоминания, извлеченные документы и окончательный ответ. Затем retrieve_memories(state) ищет в хранилище памяти до 50 релевантных прошлых записей на основе запроса пользователя, форматирует их в строку и сохраняет в state[«memories_str»]. retrieve_docs(state) запрашивает у compressed_retriever релевантные новости/документы и сохраняет их текстовое содержимое в state[«docs_ctx»]. generate_response(state) создает структурированную системную подсказку, которая включает инструкции, объединяет ее с сообщением пользователя и отправляет в LLM, сохраняя ответ ИИ в state[«response»]. Наконец, save_to_memory(state) упаковывает запрос пользователя и ответ ИИ в запись истории и добавляет ее обратно в долговременную память для будущего извлечения.

memory = Memory() llm = ChatOpenAI( model=chat_model, temperature=0, api_key=openai_api_key ) class GraphState(TypedDict): message: str user_id: str memories_str: str docs_ctx: str response: str def retrieve_memories(state: GraphState) -> GraphState: relevant_memories = memory.search(query=state[«message»], user_id=state[«user_id»], limit=50) state[«memories_str»] = «n».join(f»- {entry[‘memory’]}» for entry in relevant_memories[«results»]) return state def retrieve_docs(state: GraphState) -> GraphState: docs_res = compressed_retriever.invoke(state[«message»]) state[«docs_ctx»] = «n».join(f»- {d.page_content}» for d in docs_res) return state def generate_response(state: GraphState) -> GraphState: system_prompt = f»»» You are a helpful AI. Answer the question based on query and memories. based on retrieved info. Reply in approximately 50 English words. A deviation of a few words is acceptable. User Memories: {state[«memories_str»]} Docs: {state[«docs_ctx»]} Instructions: — When generating the answer, put a line break after each sentence ending (e.g., after «.», «!», or «?»). — Make sure each sentence is on its own line. «»» messages = [ SystemMessage(content=system_prompt), HumanMessage(content=state[«message»]) ] response = llm.invoke(messages) state[«response»] = response.content return state def save_to_memory(state: GraphState) -> GraphState: history = [ {«role»: «user», «content»: state[«message»]}, {«role»: «assistant», «content»: state[«response»]} ] memory.add(history, user_id=state[«user_id»]) return state

4. Граф

После этого я создал StateGraph с GraphState в качестве общего состояния: сначала он добавляет четыре узла, соответствующие функциям retrieve_memories, retrieve_docs, generate_response и save_to_memory.

Затем он устанавливает retrieve_memories в качестве точки входа, определяет ребра так, чтобы рабочий процесс шел от извлечения воспоминаний → извлечения документов → генерации ответа → сохранения разговора в память, и, наконец, помечает конец графа с помощью END.

Как только все узлы и ребра определены, workflow.compile() преобразует это определение графа в исполняемый конвейер, позволяя запросу пользователя автоматически проходить через каждый шаг по порядку, используя память, извлечение документов, генерацию ответа LLM и обновление памяти в едином, stateful процессе.

workflow = StateGraph(GraphState) workflow.add_node(«retrieve_memories», retrieve_memories) workflow.add_node(«retrieve_docs», retrieve_docs) workflow.add_node(«generate», generate_response) workflow.add_node(«save_memory», save_to_memory) workflow.set_entry_point(«retrieve_memories») workflow.add_edge(«retrieve_memories», «retrieve_docs») workflow.add_edge(«retrieve_docs», «generate») workflow.add_edge(«generate», «save_memory») workflow.add_edge(«save_memory», END) graph = workflow.compile()

5. Чат

Мы обернули весь рабочий процесс в интерактивный чат-интерфейс.

Функция chat_with_memories(message, user_id) действует как мозг ИИ: она вызывает скомпилированный граф, используя сообщение и ID пользователя, инициализирует поля GraphState (memories_str, docs_ctx, response) как пустые и возвращает ответ, сгенерированный ИИ.

Функция main() открывает разговор, печатая дружелюбный заголовок для ИИ-агента, и входит в цикл, где ожидает ввода пользователя. Если пользователь вводит «exit», чат вежливо завершается.

В противном случае, она вызывает chat_with_memories с фиксированным ID пользователя (news_importer) и генерирует ответ.

def chat_with_memories(message: str, user_id: str = «default_user») -> str: result = graph.invoke({ «message»: message, «user_id»: user_id, «memories_str»: «», «docs_ctx»: «», «response»: «» }) return result[«response»] def main(): print(«Deep Research AI Agent with Seeking Alpha Trending News») print(«Type ‘exit’ to quit.n») while True: print(‘n’ + ‘-‘*6 + » Start of Response » + ‘-‘*6) user_input = input(«You: «).strip() if user_input.lower() == «exit»: print(«Goodbye!») break print(«AI: «, end=»», flush=True) response = chat_with_memories(message=user_input, user_id=»news_importer») print(response) print(‘n’ + ‘-‘*6 + » End of Response » + ‘-‘*6 + ‘n’) if __name__ == «__main__»: main()

Заключение:

По мере того как технология ИИ продолжает развиваться, «память» станет ключевым фактором в переходе ИИ на следующий этап. mem0 — это интригующее решение, которое напрямую решает проблему «дефицита памяти» у LLM, наделяя ИИ-ассистентов постоянной и интеллектуальной памятью.

Благодаря своей масштабируемой архитектуре и улучшениям памяти на основе графов, mem0.ai внесет значительный вклад в реализацию более персонализированного опыта ИИ, снижая операционные расходы и упрощая разработку.

Исходный код:

ссылка

Источник: habr.com

✅ Найденные теги: новости, Создаем

ОСТАВЬТЕ СВОЙ КОММЕНТАРИЙ

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Каталог бесплатных опенсорс-решений, которые можно развернуть локально и забыть о подписках

галерея

Фото сгенерированных лиц: исследование показывает, что люди не могут отличить настоящие лица от сгенерированных
Нейросети построили капитализм за трое суток: 100 агентов Claude заперли…
Скетч: цифровой осьминог и виртуальный мир внутри компьютера с человечком.
Сцена с жестами пальцами, где один жест символизирует "VPN", а другой "KHP".
‼️Paramount купила Warner Bros. Discovery — сумма сделки составила безумные…
Скриншот репозитория GitHub "Claude Scientific Skills" AI для научных исследований.
Структура эффективного запроса Claude с элементами задачи, контекста и референса.
Эскиз и готовая веб-страница платформы для AI-дизайна в современном темном режиме.
ideipro logotyp
Image Not Found
Звёздное небо с галактиками и туманностями, космос, Вселенная, астрофотография.

Система оповещения обсерватории Рубина отправила 800 000 сигналов в первую ночь наблюдений.

Астрономы будут получать оповещения о небесных явлениях в течение нескольких минут после их обнаружения. Теренс О'Брайен, редактор раздела «Выходные». Публикации этого автора будут добавляться в вашу ежедневную рассылку по электронной почте и в ленту новостей на главной…

Мар 2, 2026
Женщина с длинными тёмными волосами в синем свете, нейтральный фон.

Расследование в отношении 61-фунтовой машины, которая «пожирает» пластик и выплевывает кирпичи.

Обзор компактного пресса для мягкого пластика Clear Drop — и что будет дальше. Шон Холлистер, старший редактор Публикации этого автора будут добавляться в вашу ежедневную рассылку по электронной почте и в ленту новостей на главной странице вашего…

Мар 2, 2026
Черный углеродное волокно с текстурой плетения, отражающий свет.

Материал будущего: как работает «бессмертный» композит

Учёные из Университета штата Северная Каролина представили композит нового поколения, способный самостоятельно восстанавливаться после серьёзных повреждений.  Речь идёт о модифицированном армированном волокном полимере (FRP), который не просто сохраняет прочность при малом весе, но и способен «залечивать» внутренние…

Мар 2, 2026
Круглый экран с изображением замка и горы, рядом электронная плата.

Круглый дисплей Waveshare для креативных проектов

Круглый 7-дюймовый сенсорный дисплей от Waveshare создан для разработчиков и дизайнеров, которым нужен нестандартный экран.  Это IPS-панель с разрешением 1 080×1 080 пикселей, поддержкой 10-точечного ёмкостного сенсора, оптической склейкой и защитным закалённым стеклом, выполненная в круглом форм-факторе.…

Мар 2, 2026

Впишите свой почтовый адрес и мы будем присылать вам на почту самые свежие новости в числе самых первых