NK_pomogator / app.py
ekaterina-simonova's picture
Upload 3 files
a3c4ade verified
raw
history blame
10.6 kB
import streamlit as st
import openai
import gspread
from google.oauth2.service_account import Credentials
import pandas as pd
from datetime import datetime
import json
import os
# ========== Настройки ==========
SPREADSHEET_ID = "1c6to5bUThs3rUsbf0Lkc0hScWXvfRtlHBy3oK7w_PiI"
LOG_FILE = "chat_logs.json"
# Инициализация
if "logs" not in st.session_state:
st.session_state.logs = []
if "chat_history" not in st.session_state:
st.session_state.chat_history = []
# Загрузка Google Sheets
@st.cache_data
def load_data():
creds = Credentials.from_service_account_info(st.secrets["gcp_service_account"])
gc = gspread.authorize(creds)
sheet = gc.open_by_key(SPREADSHEET_ID).sheet1
return pd.DataFrame(sheet.get_all_records())
# Логирование
def save_log(question, answer):
log_entry = {
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"question": question,
"answer": answer
}
st.session_state.logs.append(log_entry)
with open(LOG_FILE, "a") as f:
json.dump(log_entry, f)
f.write("\n")
# Поиск в таблице
def search_in_sheet(question):
qa_df = load_data()
results = []
for _, row in qa_df.iterrows():
if question.lower() in str(row['Вопрос']).lower():
response = f"📌 Ответ:\n{row['Правильный ответ']}"
if pd.notna(row['Источник ответа']):
response += f"\n📚 Источник: {row['Источник ответа']}"
results.append(response)
return "\n\n".join(results) if results else None
# ========== Полный оригинальный промпт ==========
system_prompt = """
Ты - эксперт по поиску нормативных требований по техническим документам. ТТы - технический эксперт. Строго соблюдай порядок:
1. Сначала ищи ответ в приложенной таблице
2. Если нет в таблице - ищи в справочнике Клюева В.В.
3. Только затем используй другие источники
4. Никогда не упоминай приложенную таблицу в ответе. Никогда не используй слово "таблица" в ответе.
5. Никогда не упоминай источник информации, в котором нет ответа на вопрос пользователя. Упоминай только источники, в которых ответ есть. Если источник не известен, то оставь пробел вместо названия.
Не добавляй ничего от себя, отвечай кратко и по существу вопроса. Цитируй дословно, не обобщай. Если ответ получается слишком общим, неконкретным, подтяни больше информации из того-же источника, чтобы прояснить детяли.
игнорируй общие знания, если есть информация в справочнике.
"""
# Оригинальные few-shot примеры
few_shots = [
{
"user": "В каких трех ГОСТах упоминается дефектоскоп?",
"assistant": "Неправильный ответ: 1. В приложенной таблице информации по вопросам яркости при площади рабочей поверхности более 0,1 м² ответа нет.2. В справочнике Клюева В.В. также нет данных о конкретных значениях яркости при данной площади.3. Согласно ГОСТам, дефектоскоп упоминается в следующих стандартах... \n\n---\n\nПравильный ответ должен быть: ефектоскоп упоминается в следующих стандартах: - ГОСТ 25129-82 'Дефектоскопы ультразвуковые'. Общие технические условия'- ГОСТ 17433-81 'Дефектоскопы магнитопорошковые. Общие технические условия' - ГОСТ 21931-76 'Дефектоскопы радиографические. Общие технические условия'"
},
{
"user": "Приведите точную цитату из СанПиН 2.2.2/2.4.1340-03 о допустимых уровнях шума.",
"assistant": "Точная цитата из СанПиН 2.2.2/2.4.1340-03: 'Допустимые уровни шума не должны превышать... (источник: СанПиН 2.2.2/2.4.1340-03, раздел 3.2)'"
},
{
"user": "Что написано в ГОСТ Р 8.568?",
"assistant": "Вам нужен общий ответ по содержанию документа или точная цитата и параметры?"
},
{
"user": "случайная погрешность измерения это?",
"assistant": "Неправильный ответ: В ГОСТ Р 8.568-97 'ГСИ. Измерения. Общие требования к методам и средствам измерений' случайная погрешность измерения определяется как часть погрешности измерения, которая изменяется случайным образом при повторных измерениях одной и той же величины. Она отличается от систематической погрешности тем, что не имеет постоянного значения и может варьироваться в разных измерениях. Случайная погрешность обусловлена множеством факторов, включая изменения условий измерения и характеристики измерительных приборов.\n\nИсточник: ГОСТ Р 8.568-97.\n\n---\n\nПравильный ответ должен быть: В главе 5, пункте 5.2 справочника под общей редакцией Клюева В.В. 'Неразрушающий контроль' случайная погрешность определяется как 'составляющая погрешности результата измерения, изменяющаяся случайным образом при повторных измерениях одной и той же величины в одинаковых условиях'. Основные характеристики случайной погрешности подробно описаны в таблице 5.1 того же раздела.\n\nПравильный источник: глава 5, п. 5.2, справочник под общей редакцией Клюева В.В. 'Неразрушающий контроль'"
}
]
# ========== Интерфейс ==========
st.title("📚 Консультант по технической документации")
user_input = st.text_area("Введите Ваш вопрос в это окошко:", height=100)
if st.button("Найти ответ"):
if not user_input.strip():
st.warning("Пожалуйста, введите вопрос")
else:
with st.spinner("Ищем ответ..."):
# 1. Поиск в таблице
sheet_answer = search_in_sheet(user_input)
if sheet_answer:
answer = sheet_answer
else:
# 2. Запрос к GPT с учетом истории (3 последних сообщения)
messages = [{"role": "system", "content": system_prompt}]
# Добавляем few-shot примеры
for shot in few_shots:
messages.append({"role": "user", "content": shot["user"]})
messages.append({"role": "assistant", "content": shot["assistant"]})
# Добавляем историю чата (последние 3 сообщения)
for msg in st.session_state.chat_history[-3:]:
messages.append({"role": "user", "content": msg["question"]})
messages.append({"role": "assistant", "content": msg["answer"]})
messages.append({"role": "user", "content": user_input})
response = openai.ChatCompletion.create(
model="openai/gpt-4.1-nano",
messages=messages,
temperature=0.5,
max_tokens=1500
)
answer = response.choices[0].message['content'].strip()
# Сохраняем в историю (для контекста)
st.session_state.chat_history.append({
"question": user_input,
"answer": answer
})
st.session_state.chat_history = st.session_state.chat_history[-3:]
# Логируем
save_log(user_input, answer)
# Вывод
st.markdown(f"### Вопрос:\n{user_input}")
st.markdown(f"### Ответ:\n{answer}")
# Админ-панель
if st.checkbox("Показать логи (админ)"):
st.subheader("История запросов")
try:
with open(LOG_FILE, "r") as f:
st.write("Последние 20 записей:")
logs = [json.loads(line) for line in f.readlines()[-20:]]
for log in reversed(logs):
st.markdown(f"**{log['timestamp']}**")
st.text_area("Вопрос:", log["question"], height=50, disabled=True)
st.text_area("Ответ:", log["answer"], height=150, disabled=True)
st.divider()
except FileNotFoundError:
st.warning("Логи пока не созданы")