Агент с RAG-памятью: agent.py

This commit is contained in:
2026-05-27 10:24:08 +00:00
parent d10bd812c9
commit 8c60256b9d
@@ -0,0 +1,185 @@
import os
import json
import logging
from typing import List, Dict, Any
from langchain_ollama import OllamaEmbeddings, ChatOllama
from langchain_qdrant import QdrantStore
from langchain_core.tools import tool
from langchain.agents import AgentExecutor, ZeroShotAgent, Tool
from langchain.schema import HumanMessage, AIMessage
# ==========================
# Конфигурация и логирование
# ==========================
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s",
)
logger = logging.getLogger(__name__)
QDRANT_URL = os.getenv("QDRANT_URL", "http://localhost:6333")
QDRANT_COLLECTION = os.getenv("QDRANT_COLLECTION", "knowledge_base")
# ==========================
# Векторный хранилище
# ==========================
try:
embeddings = OllamaEmbeddings(model="nomic-embed-text")
except Exception as e:
logger.error(f"Не удалось инициализировать OllamaEmbeddings: {e}")
raise
try:
qdrant_store = QdrantStore(
url=QDRANT_URL,
collection_name=QDRANT_COLLECTION,
embeddings=embeddings,
)
except Exception as e:
logger.error(f"Не удалось подключиться к Qdrant: {e}")
raise
# ==========================
# Инструменты
# ==========================
@tool("search_knowledge_base")
def search_knowledge_base(query: str) -> List[Dict[str, Any]]:
"""
Поиск релевантных документов в базе знаний.
Args:
query (str): поисковый запрос.
Returns:
list of dict: список найденных документов с полями 'id', 'text' и 'score'.
"""
try:
results = qdrant_store.similarity_search_with_score(query, k=5)
except Exception as e:
logger.exception(f"Ошибка при поиске в Qdrant: {e}")
return []
docs = []
for doc, score in results:
docs.append(
{
"id": getattr(doc.metadata, "doc_id", None),
"text": doc.page_content,
"score": score,
}
)
logger.debug(f"search_knowledge_base: найдено {len(docs)} документов")
return docs
@tool("add_to_knowledge_base")
def add_to_knowledge_base(text: str) -> str:
"""
Добавление нового текста в базу знаний.
Args:
text (str): текст, который нужно добавить.
Returns:
str: сообщение об успехе или ошибке.
"""
try:
# Разбиваем на чанки с гибкой схемой
chunk_size = 500 # символов
overlap = 50 # символов перекрытия
chunks = []
start = 0
while start < len(text):
end = min(start + chunk_size, len(text))
chunk_text = text[start:end]
chunks.append(chunk_text)
start += chunk_size - overlap
for i, chunk in enumerate(chunks):
doc_id = f"{hash(chunk)}_{i}"
qdrant_store.add_documents(
documents=[chunk],
ids=[doc_id],
metadata={"doc_id": doc_id},
)
except Exception as e:
logger.exception(f"Ошибка при добавлении в Qdrant: {e}")
return "Не удалось добавить документ."
logger.info(f"add_to_knowledge_base: добавлено {len(chunks)} чанков")
return f"Документ успешно добавлен, {len(chunks)} чанков."
# ==========================
# Агент
# ==========================
tools = [
Tool.from_function(
func=search_knowledge_base,
name="search_knowledge_base",
description="Используйте для поиска информации в базе знаний.",
),
Tool.from_function(
func=add_to_knowledge_base,
name="add_to_knowledge_base",
description="Добавьте новый текст в базу знаний.",
),
]
prompt = ZeroShotAgent.create_prompt(
tools,
prefix="""
Вы — интеллектуальный агент, помогающий пользователю находить и добавлять информацию в базу знаний.
Используйте инструменты search_knowledge_base и add_to_knowledge_base по мере необходимости.
""",
)
llm = ChatOllama(model="llama3")
agent_chain = ZeroShotAgent(llm=llm, tools=tools, prompt=prompt)
agent_executor = AgentExecutor.from_agent_and_tools(
agent=agent_chain,
tools=tools,
verbose=True,
)
# ==========================
# Тесты (можно запускать как `python agent.py`)
# ==========================
def _test_add_and_search():
test_text = (
"Python — это высокоуровневый язык программирования, "
"который поддерживает несколько парадигм и широко используется в научных исследованиях."
)
add_result = add_to_knowledge_base(test_text)
assert "успешно" in add_result.lower(), f"Добавление не удалось: {add_result}"
search_query = "Python язык программирования"
results = search_knowledge_base(search_query)
assert isinstance(results, list), "Результат поиска должен быть списком."
assert len(results) > 0, "Ни один документ не найден."
logger.info("Тесты пройдены успешно.")
if __name__ == "__main__":
# Пример использования агента
user_query = input("Введите ваш запрос: ")
try:
response = agent_executor.invoke({"input": user_query})
print("\nОтвет агента:")
print(response["output"])
except Exception as e:
logger.exception(f"Ошибка при выполнении запроса: {e}")
# Запуск тестов
_test_add_and_search()