File size: 4,441 Bytes
05e7db4
18e63c4
 
2751e09
afc0184
 
2751e09
18e63c4
 
f1e9803
2751e09
f1e9803
18e63c4
137975e
18e63c4
afc0184
18e63c4
 
 
 
 
 
137975e
 
 
 
 
 
f1e9803
137975e
 
 
2751e09
137975e
 
 
 
 
 
 
 
2751e09
f1e9803
137975e
 
2751e09
137975e
05e7db4
afc0184
137975e
 
 
 
18e63c4
f1e9803
137975e
 
18e63c4
 
 
 
137975e
18e63c4
137975e
18e63c4
 
137975e
18e63c4
 
 
137975e
 
18e63c4
 
137975e
 
afc0184
f1e9803
 
 
 
 
 
137975e
f1e9803
 
137975e
 
 
 
 
 
 
f1e9803
 
 
137975e
f1e9803
137975e
18e63c4
137975e
 
 
 
 
 
f1e9803
05e7db4
f1e9803
137975e
 
2751e09
137975e
 
 
 
2751e09
 
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
import gradio as gr
import glob
import requests
import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
from docx import Document
from ddgs import DDGS
import trafilatura
from transformers import AutoTokenizer, AutoModelForSeq2SeqLM

# ---------- 1. Загрузка документа ----------
def find_docx():
    for pattern in ["*.docx", "*.DOCX", "knowledge.docx"]:
        files = glob.glob(pattern)
        if files:
            return files[0]
        files = glob.glob(f"**/{pattern}", recursive=True)
        if files:
            return files[0]
    return None

def load_local_doc():
    path = find_docx()
    if not path:
        raise FileNotFoundError("Документ не найден.")
    doc = Document(path)
    full = "\n".join([p.text for p in doc.paragraphs if p.text.strip()])
    chunks, cur = [], ""
    for para in full.split("\n"):
        if len(cur) + len(para) < 1500:
            cur += para + "\n"
        else:
            if cur:
                chunks.append(cur.strip())
            cur = para + "\n"
    if cur:
        chunks.append(cur.strip())
    vec = TfidfVectorizer(stop_words=None, ngram_range=(1,2), max_features=5000)
    tfidf = vec.fit_transform(chunks)
    return chunks, vec, tfidf

print("Загрузка документа...")
local_chunks, vectorizer, tfidf_matrix = load_local_doc()
print(f"Документ загружен, {len(local_chunks)} фрагментов.")

def local_search(query, k=4):
    if not query.strip():
        return []
    qv = vectorizer.transform([query])
    sim = cosine_similarity(qv, tfidf_matrix).flatten()
    top = sim.argsort()[-k:][::-1]
    return [local_chunks[i] for i in top if sim[i] > 0.05]

# ---------- 2. Веб‑поиск (DuckDuckGo) ----------
def web_search(query, max_results=3):
    snippets = []
    try:
        with DDGS() as ddgs:
            results = ddgs.text(query, max_results=max_results)
            for r in results:
                url = r.get('href')
                title = r.get('title')
                if not url:
                    continue
                try:
                    resp = requests.get(url, timeout=10, headers={'User-Agent': 'Mozilla/5.0'})
                    if resp.status_code == 200:
                        text = trafilatura.extract(resp.text, include_comments=False, include_tables=False)
                        if text and len(text) > 200:
                            snippets.append(f"{title}\n{url}\n{text[:1500]}")
                except Exception:
                    continue
    except Exception as e:
        print(f"Веб-поиск: {e}")
    return snippets

# ---------- 3. Локальная генерация (flan-t5-small) ----------
print("Загрузка модели генерации (первый раз может занять минуту)...")
tokenizer = AutoTokenizer.from_pretrained("google/flan-t5-small")
model = AutoModelForSeq2SeqLM.from_pretrained("google/flan-t5-small")
print("Модель готова.")

def generate_answer(question, context):
    context = context[:3000]
    prompt = f"""Ответь на вопрос, используя только информацию из Контекста. Если ответа нет, напиши "Не найдено".

Контекст:
{context}

Вопрос: {question}

Ответ:"""
    inputs = tokenizer(prompt, return_tensors="pt", truncation=True, max_length=1024)
    outputs = model.generate(**inputs, max_new_tokens=200, temperature=0.3, do_sample=False)
    return tokenizer.decode(outputs[0], skip_special_tokens=True)

# ---------- 4. Основная функция ----------
def answer(query):
    if not query.strip():
        return "Введите вопрос."
    local = local_search(query)
    web = web_search(query)
    all_context = local + web
    if not all_context:
        return "Ничего не найдено."
    return generate_answer(query, "\n\n---\n\n".join(all_context))

# ---------- 5. Интерфейс ----------
with gr.Blocks() as demo:
    gr.Markdown("# Поиск")
    with gr.Row():
        q = gr.Textbox(label="Вопрос", lines=2, placeholder="Введите запрос...")
        btn = gr.Button("Найти", variant="primary")
    out = gr.Markdown(label="Результат")
    btn.click(fn=answer, inputs=q, outputs=out)

demo.launch()