File size: 10,573 Bytes
a3c4ade
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
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("Логи пока не созданы")