From 0af4730b3cbc904dbbca644901ace9d4cec2df72 Mon Sep 17 00:00:00 2001 From: "ki.sagidullin" Date: Tue, 31 Mar 2026 23:31:09 +0500 Subject: [PATCH] Second --- agent_core.py | 132 ++++++++++++++++++++++++++++++++++++ agentic_rag_interface.py | 9 --- local_knowledge_base.py | 13 +++- main.py | 140 +++------------------------------------ 4 files changed, 152 insertions(+), 142 deletions(-) create mode 100644 agent_core.py diff --git a/agent_core.py b/agent_core.py new file mode 100644 index 0000000..79642a7 --- /dev/null +++ b/agent_core.py @@ -0,0 +1,132 @@ +from typing import TypedDict, List, Optional, Annotated +import operator +from langchain_community.llms import Ollama +from langchain_community.embeddings import OllamaEmbeddings +from langchain_community.vectorstores import Qdrant +from langchain.text_splitter import RecursiveCharacterTextSplitter +from langchain_community.document_loaders import DirectoryLoader, PyPDFLoader +from langgraph.graph import StateGraph, END +from langgraph.graph.message import add_messages +from langchain_core.messages import HumanMessage, AIMessage, SystemMessage +from langchain.tools import tool +from langchain.agents import AgentExecutor, create_react_agent +import numpy as np + +# Определяем состояние агента +class AgentState(TypedDict): + messages: Annotated[List, add_messages] # История сообщений + knowledge_base: Optional[str] # Релевантные документы + current_step: str # Текущий шаг выполнения + needs_search: bool # Нужен ли поиск в RAG + final_answer: Optional[str] # Финальный ответ + +# Инициализация LLM через Ollama +llm = Ollama( + model="llama3.2:3b", + temperature=0.1, # Низкая температура для консистентности + num_predict=1024, # Максимальная длина ответа +) + +# Инициализация эмбеддингов +embeddings = OllamaEmbeddings( + model="nomic-embed-text", # Хорошие локальные эмбеддинги +) + +# Создаем инструменты для агента +@tool +def search_knowledge_base(query: str) -> str: + """Поиск информации в локальной базе знаний""" + # Здесь будет подключение к Qdrant + return "Найденная информация из базы знаний" + +@tool +def calculate(expression: str) -> str: + """Выполнение математических вычислений""" + try: + result = eval(expression) + return f"Результат: {result}" + except: + return "Ошибка в выражении" + +@tool +def web_search(query: str) -> str: + """Поиск в интернете (если нужно)""" + # Можно подключить локальный поиск через DuckDuckGo + return "Результаты поиска из интернета" + +# Создаем граф агента +def create_agent_graph(): + workflow = StateGraph(AgentState) + + # Узел: анализ запроса + def analyze_query(state: AgentState): + messages = state["messages"] + last_message = messages[-1].content if messages else "" + + # Простой анализ: проверяем, нужен ли поиск + search_keywords = ["информация", "документ", "найди", "ищи", "база знаний"] + needs_search = any(keyword in last_message.lower() for keyword in search_keywords) + + return { + "needs_search": needs_search, + "current_step": "analyzing_query" + } + + # Узел: поиск в RAG + def rag_search(state: AgentState): + if not state["needs_search"]: + return {"knowledge_base": None, "current_step": "generating_answer"} + + # Получаем последний запрос + query = state["messages"][-1].content + + # Здесь должен быть реальный поиск в Qdrant + # Пока заглушка + results = ["Документ 1: Информация о...", "Документ 2: Данные по..."] + + return { + "knowledge_base": "\n".join(results), + "current_step": "generating_answer" + } + + # Узел: генерация ответа + def generate_answer(state: AgentState): + messages = state["messages"] + knowledge = state.get("knowledge_base", "") + + # Формируем промпт с контекстом + prompt = f"""Ты — интеллектуальный ассистент. Используй следующую информацию если она релевантна: + + Контекст из базы знаний: + {knowledge} + + История разговора: + {messages[-5:] if len(messages) > 5 else messages} + + Текущий запрос: {messages[-1].content if messages else ''} + + Ответь максимально полезно и точно:""" + + response = llm.invoke(prompt) + + return { + "final_answer": response, + "current_step": "completed", + "messages": messages + [AIMessage(content=response)] + } + + # Добавляем узлы в граф + workflow.add_node("analyze", analyze_query) + workflow.add_node("search", rag_search) + workflow.add_node("generate", generate_answer) + + # Определяем edges (переходы) + workflow.set_entry_point("analyze") + workflow.add_edge("analyze", "search") + workflow.add_edge("search", "generate") + workflow.add_edge("generate", END) + + return workflow.compile() + +# Создаем и компилируем граф +agent_graph = create_agent_graph() \ No newline at end of file diff --git a/agentic_rag_interface.py b/agentic_rag_interface.py index d77fdbe..c5698ab 100644 --- a/agentic_rag_interface.py +++ b/agentic_rag_interface.py @@ -133,12 +133,3 @@ def create_gradio_interface(): ) return demo - -# Запускаем интерфейс -if __name__ == "__main__": - demo = create_gradio_interface() - demo.launch( - server_name="0.0.0.0", - server_port=7860, - share=False # Не создавать публичную ссылку - ) \ No newline at end of file diff --git a/local_knowledge_base.py b/local_knowledge_base.py index 9b3f3d9..71e8714 100644 --- a/local_knowledge_base.py +++ b/local_knowledge_base.py @@ -1,6 +1,8 @@ import os from qdrant_client import QdrantClient from qdrant_client.models import Distance, VectorParams, PointStruct +from langchain_community.document_loaders import TextLoader, PyPDFLoader, DirectoryLoader +from langchain.text_splitter import Language class LocalKnowledgeBase: def __init__(self, collection_name="documents", persist_dir="./qdrant_data"): @@ -37,6 +39,11 @@ class LocalKnowledgeBase: '.pdf': PyPDFLoader, '.txt': lambda path: DirectoryLoader(path, glob="**/*.txt"), '.docx': lambda path: DirectoryLoader(path, glob="**/*.docx"), + '.hpp': lambda path: TextLoader(path, encoding='utf-8'), + '.cpp': lambda path: TextLoader(path, encoding='utf-8'), # добавить + '.h': lambda path: TextLoader(path, encoding='utf-8'), # добавить + '.cc': lambda path: TextLoader(path, encoding='utf-8'), # добавить + } all_documents = [] @@ -54,7 +61,8 @@ class LocalKnowledgeBase: print(f"Ошибка загрузки {file_path}: {e}") # Разбиваем на чанки - text_splitter = RecursiveCharacterTextSplitter( + text_splitter = RecursiveCharacterTextSplitter.from_language( + language=Language.CPP, chunk_size=1000, chunk_overlap=200, length_function=len @@ -82,7 +90,8 @@ class LocalKnowledgeBase: payload={ "text": doc.page_content, "source": doc.metadata.get("source", "unknown"), - "page": doc.metadata.get("page", 0) + "page": doc.metadata.get("page", 0), + "file_type": "cpp" } ) points.append(point) diff --git a/main.py b/main.py index 79642a7..b5bf8b5 100644 --- a/main.py +++ b/main.py @@ -1,132 +1,10 @@ -from typing import TypedDict, List, Optional, Annotated -import operator -from langchain_community.llms import Ollama -from langchain_community.embeddings import OllamaEmbeddings -from langchain_community.vectorstores import Qdrant -from langchain.text_splitter import RecursiveCharacterTextSplitter -from langchain_community.document_loaders import DirectoryLoader, PyPDFLoader -from langgraph.graph import StateGraph, END -from langgraph.graph.message import add_messages -from langchain_core.messages import HumanMessage, AIMessage, SystemMessage -from langchain.tools import tool -from langchain.agents import AgentExecutor, create_react_agent -import numpy as np +from agentic_rag_interface import create_gradio_interface -# Определяем состояние агента -class AgentState(TypedDict): - messages: Annotated[List, add_messages] # История сообщений - knowledge_base: Optional[str] # Релевантные документы - current_step: str # Текущий шаг выполнения - needs_search: bool # Нужен ли поиск в RAG - final_answer: Optional[str] # Финальный ответ - -# Инициализация LLM через Ollama -llm = Ollama( - model="llama3.2:3b", - temperature=0.1, # Низкая температура для консистентности - num_predict=1024, # Максимальная длина ответа -) - -# Инициализация эмбеддингов -embeddings = OllamaEmbeddings( - model="nomic-embed-text", # Хорошие локальные эмбеддинги -) - -# Создаем инструменты для агента -@tool -def search_knowledge_base(query: str) -> str: - """Поиск информации в локальной базе знаний""" - # Здесь будет подключение к Qdrant - return "Найденная информация из базы знаний" - -@tool -def calculate(expression: str) -> str: - """Выполнение математических вычислений""" - try: - result = eval(expression) - return f"Результат: {result}" - except: - return "Ошибка в выражении" - -@tool -def web_search(query: str) -> str: - """Поиск в интернете (если нужно)""" - # Можно подключить локальный поиск через DuckDuckGo - return "Результаты поиска из интернета" - -# Создаем граф агента -def create_agent_graph(): - workflow = StateGraph(AgentState) - - # Узел: анализ запроса - def analyze_query(state: AgentState): - messages = state["messages"] - last_message = messages[-1].content if messages else "" - - # Простой анализ: проверяем, нужен ли поиск - search_keywords = ["информация", "документ", "найди", "ищи", "база знаний"] - needs_search = any(keyword in last_message.lower() for keyword in search_keywords) - - return { - "needs_search": needs_search, - "current_step": "analyzing_query" - } - - # Узел: поиск в RAG - def rag_search(state: AgentState): - if not state["needs_search"]: - return {"knowledge_base": None, "current_step": "generating_answer"} - - # Получаем последний запрос - query = state["messages"][-1].content - - # Здесь должен быть реальный поиск в Qdrant - # Пока заглушка - results = ["Документ 1: Информация о...", "Документ 2: Данные по..."] - - return { - "knowledge_base": "\n".join(results), - "current_step": "generating_answer" - } - - # Узел: генерация ответа - def generate_answer(state: AgentState): - messages = state["messages"] - knowledge = state.get("knowledge_base", "") - - # Формируем промпт с контекстом - prompt = f"""Ты — интеллектуальный ассистент. Используй следующую информацию если она релевантна: - - Контекст из базы знаний: - {knowledge} - - История разговора: - {messages[-5:] if len(messages) > 5 else messages} - - Текущий запрос: {messages[-1].content if messages else ''} - - Ответь максимально полезно и точно:""" - - response = llm.invoke(prompt) - - return { - "final_answer": response, - "current_step": "completed", - "messages": messages + [AIMessage(content=response)] - } - - # Добавляем узлы в граф - workflow.add_node("analyze", analyze_query) - workflow.add_node("search", rag_search) - workflow.add_node("generate", generate_answer) - - # Определяем edges (переходы) - workflow.set_entry_point("analyze") - workflow.add_edge("analyze", "search") - workflow.add_edge("search", "generate") - workflow.add_edge("generate", END) - - return workflow.compile() - -# Создаем и компилируем граф -agent_graph = create_agent_graph() \ No newline at end of file +# Запускаем интерфейс +if __name__ == "__main__": + demo = create_gradio_interface() + demo.launch( + server_name="0.0.0.0", + server_port=7860, + share=False # Не создавать публичную ссылку + ) \ No newline at end of file