Агент с RAG-памятью: agent.py
This commit is contained in:
@@ -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()
|
||||
Reference in New Issue
Block a user