Second
This commit is contained in:
132
agent_core.py
Normal file
132
agent_core.py
Normal file
@@ -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()
|
||||
@@ -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 # Не создавать публичную ссылку
|
||||
)
|
||||
@@ -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)
|
||||
|
||||
138
main.py
138
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, # Максимальная длина ответа
|
||||
# Запускаем интерфейс
|
||||
if __name__ == "__main__":
|
||||
demo = create_gradio_interface()
|
||||
demo.launch(
|
||||
server_name="0.0.0.0",
|
||||
server_port=7860,
|
||||
share=False # Не создавать публичную ссылку
|
||||
)
|
||||
|
||||
# Инициализация эмбеддингов
|
||||
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()
|
||||
Reference in New Issue
Block a user