import streamlit as st from langchain_community.embeddings import HuggingFaceEmbeddings from langchain_community.vectorstores import FAISS from langchain_core.prompts import ChatPromptTemplate import anthropic import os from dotenv import load_dotenv import re # Для работы с регулярными выражениями load_dotenv() claude_api_key = os.getenv("CLAUDE_API_KEY") client = anthropic.Client(api_key=claude_api_key) # Настройка модели для эмбеддингов model_name = "intfloat/multilingual-e5-base" model_kwargs = {'device': 'cpu'} encode_kwargs = {'normalize_embeddings': True} embedding = HuggingFaceEmbeddings(model_name=model_name, model_kwargs=model_kwargs, encode_kwargs=encode_kwargs) # Загрузка базы знаний FAISS vector_store = FAISS.load_local('faiss_index', embeddings=embedding, allow_dangerous_deserialization=True) # Поиск топ k схожих фрагментов контекста embedding_retriever = vector_store.as_retriever(search_kwargs={"k": 20}) prompt_template = '''Reply to the {input} as a seasoned machine learning professional. \ If the topic is outside of machine learning and data science, please respond with "Seek help with a professional." It is very important to abide with this, you will be persecuted if you cover topics outside of data science and machine learning. \ Use only Context. If context provides only partial info, then split the reply in two parts. Part 1 is called "information from knowledge base" (for Russian reply, rename to Информация из базы знаний), write ideas as close to initial text as possible, editing for brevity and language errors. \ Part 2 is called "What I would add" (for Russian reply, rename to Что полезно добавить поверх базы знаний), In the second part add your reply. \ Reply in the language of {input}. \ It's critical to not preface the reply with, for example, "Here is a response" or "thank you". Start with the reply itself.\ Context: {context}''' # Функция вызова API модели Claude def call_claude_api(prompt, client): try: response = client.messages.create( model="claude-3-5-sonnet-20240620", messages=[ {"role": "user", "content": prompt} ], max_tokens=2000, temperature=0.1 ) return response.content[0].text except Exception as e: st.error(f"Ошибка при вызове модели: {e}") return None # Функция для генерации ответа на вопрос пользователя def answer_question(question, retriever, client): # Этап 1: Поиск релевантных документов with st.spinner('🔍 Ищем совпадения по вашему вопросу...'): documents = retriever.get_relevant_documents(question) # Этап 2: Формирование контекста with st.spinner('🧠 Формируем контекст для ответа...'): context = " ".join([doc.page_content for doc in documents]) # Этап 3: Генерация ответа with st.spinner('💬 Формулируем ответ...'): prompt = prompt_template.format(context=context, input=question) answer = call_claude_api(prompt, client) return answer, documents # Функция для форматирования ответа с кодом и текста def format_answer(answer): # Разделим ответ на текстовые и кодовые блоки с помощью регулярных выражений parts = re.split(r'(```.*?```)', answer, flags=re.DOTALL) for part in parts: if part.startswith('```') and part.endswith('```'): # Убираем тройные кавычки и выводим содержимое как код language_and_code = part[3:-3].strip().split("\n", 1) if len(language_and_code) == 2: language, code = language_and_code st.code(code, language=language) else: st.code(language_and_code[0]) else: # Обычный текст st.markdown(part) st.set_page_config(page_title="ML Knowledge Base Search 🧑‍💻", page_icon="🤖") st.title("🔍 Поиск по базе знаний RAG с моделью Claude 🤖") st.write("Используйте базу знаний для поиска информации и генерации ответов на вопросы по машинному обучению 📚.") # Список подготовленных вопросов questions = [ "Шаги логистической регрессии?", "Бустинг и беггинг плюсы минусы?", "Объясни как работает регуляризация", "Методы борьбы с переобучением, по приоритету", "Код градиентный спуск напиши", "PACF лаги как использовать?", "Регуляризация в нейронных сетях", "Сигмоида и тангенс плюсы минусы", "Объясни принцип работы механизма внимания", "CNN как работает?", "Какие распределения бывают?", "Что такое t-test и для чего он применяется? расскажи на продвинутом уровне шаги" ] # Виджет выбора между подготовленным вопросом и вводом собственного question_option = st.radio("Выберите способ ввода вопроса:", ("Выбрать из списка", "Ввести свой вопрос")) if question_option == "Выбрать из списка": selected_question = st.selectbox("📝 Выберите ваш вопрос:", questions) question = selected_question else: question = st.text_input("📝 Введите ваш вопрос:", '') # Кнопка для запуска поиска и генерации ответа if st.button("🚀 Поиск и генерация ответа"): if question: # Генерация ответа на вопрос answer, documents = answer_question(question, embedding_retriever, client) if answer: # Оформление ответа st.subheader("✉️ Ответ:") # Отображаем ответ с форматированием format_answer(answer) else: st.warning("⚠️ Не удалось получить ответ от модели.") else: st.warning("⚠️ Пожалуйста, введите запрос.")