Spaces:
Sleeping
Sleeping
Upload 11 files
Browse files- .gitattributes +1 -0
- app.py +54 -42
- architecture.png +3 -0
- info.md +15 -0
- llm.py +35 -4
- prompts.py +104 -28
- rag.py +2 -6
- requirements.txt +3 -1
- summary.py +65 -0
- team.md +4 -0
.gitattributes
CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
36 |
+
architecture.png filter=lfs diff=lfs merge=lfs -text
|
app.py
CHANGED
@@ -4,7 +4,7 @@ from db import db
|
|
4 |
from rag import process_query
|
5 |
from llm import LLM
|
6 |
from prompts import default_system_promot
|
7 |
-
|
8 |
|
9 |
def init_db():
|
10 |
with open("sources.txt", encoding="utf-8") as f:
|
@@ -12,43 +12,30 @@ def init_db():
|
|
12 |
db.add(urls)
|
13 |
|
14 |
|
15 |
-
MISTRAL_KEY=os.getenv('MISTRAL_API_KEY')
|
16 |
-
MISTRAL_URL="https://api.mistral.ai/v1"
|
17 |
-
|
18 |
-
OPENROUTER_KEY = os.getenv('OPENROUTER_API_KEY')
|
19 |
-
OPENROUTER_URL = "https://openrouter.ai/api/v1"
|
20 |
-
|
21 |
-
|
22 |
-
model_creds = {
|
23 |
-
'qwen2.5-vl-72b-instruct': {
|
24 |
-
"url": OPENROUTER_URL,
|
25 |
-
"key": OPENROUTER_KEY,
|
26 |
-
"model": "qwen/qwen2.5-vl-72b-instruct:free"
|
27 |
-
},
|
28 |
-
'deepseek_v3': {
|
29 |
-
"url": OPENROUTER_URL,
|
30 |
-
"key": OPENROUTER_KEY,
|
31 |
-
"model": "deepseek/deepseek-chat:free"
|
32 |
-
},
|
33 |
-
'llama-3.3-70b': {
|
34 |
-
"url": OPENROUTER_URL,
|
35 |
-
"key": OPENROUTER_KEY,
|
36 |
-
"model": "meta-llama/llama-3.3-70b-instruct:free"
|
37 |
-
},
|
38 |
-
'mistral': {
|
39 |
-
"url": MISTRAL_URL,
|
40 |
-
"key": MISTRAL_KEY,
|
41 |
-
"model": "mistral-small-latest"
|
42 |
-
}
|
43 |
-
}
|
44 |
-
|
45 |
-
|
46 |
|
47 |
-
def get_llm(model):
|
48 |
-
|
49 |
|
50 |
-
return LLM(model_info["url"], model_info["key"], model_info["model"])
|
51 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
52 |
|
53 |
# Высота столбцов (в пикселях)
|
54 |
COLUMN_HEIGHT = 280
|
@@ -106,22 +93,29 @@ def tab1():
|
|
106 |
# with gr.Accordion('Результат:', open=True):
|
107 |
# result_output = gr.Markdown(value="Здесь будет представлен результат")
|
108 |
# Группа с лейблом и рамкой
|
109 |
-
with gr.
|
110 |
-
gr.
|
111 |
-
|
112 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
113 |
|
114 |
|
115 |
def respond(file, system_prompt, model, temperature, alpha):
|
116 |
llm = get_llm(model)
|
117 |
llm_response = process_query(file, system_prompt, llm, temperature, alpha)
|
118 |
-
|
|
|
119 |
|
120 |
# Привязываем кнопку к функции обработки
|
121 |
send_button.click(
|
122 |
respond,
|
123 |
inputs=[file_input, system_prompt_input, model, temperature, alpha],
|
124 |
-
outputs=result_output
|
125 |
)
|
126 |
|
127 |
|
@@ -171,6 +165,23 @@ def tab2():
|
|
171 |
markdown_output = gr.Markdown("Изначальное содержимое Markdown")
|
172 |
|
173 |
dynamic_tab.select(get_sources, outputs=markdown_output)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
174 |
|
175 |
|
176 |
css = """
|
@@ -198,10 +209,11 @@ css = """
|
|
198 |
|
199 |
# Интерфейс Gradio
|
200 |
with gr.Blocks(css=css) as demo:
|
201 |
-
gr.Markdown("#
|
202 |
with gr.Tabs():
|
203 |
tab1()
|
204 |
tab2()
|
|
|
205 |
|
206 |
|
207 |
init_db()
|
|
|
4 |
from rag import process_query
|
5 |
from llm import LLM
|
6 |
from prompts import default_system_promot
|
7 |
+
import re
|
8 |
|
9 |
def init_db():
|
10 |
with open("sources.txt", encoding="utf-8") as f:
|
|
|
12 |
db.add(urls)
|
13 |
|
14 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
|
16 |
+
def get_llm(model):
|
17 |
+
return LLM(model)
|
18 |
|
|
|
19 |
|
20 |
+
def extract_json_and_clean_text(text):
|
21 |
+
# print(text)
|
22 |
+
# Регулярное выражение для поиска содержимого между <json> и </json>
|
23 |
+
pattern = r"(```json.*?```)"
|
24 |
+
|
25 |
+
# Ищем совпадения (re.DOTALL позволяет захватывать многострочные данные)
|
26 |
+
match = re.search(pattern, text, re.DOTALL)
|
27 |
+
|
28 |
+
if match:
|
29 |
+
# Извлекаем найденный JSON как строку
|
30 |
+
json_string = match.group(1).strip()
|
31 |
+
|
32 |
+
# Удаляем JSON блок из исходного текста
|
33 |
+
cleaned_text = re.sub(pattern, "", text, flags=re.DOTALL).strip()
|
34 |
+
|
35 |
+
return cleaned_text, json_string
|
36 |
+
else:
|
37 |
+
print("JSON блок не найден в тексте.")
|
38 |
+
return text.strip(), ""
|
39 |
|
40 |
# Высота столбцов (в пикселях)
|
41 |
COLUMN_HEIGHT = 280
|
|
|
93 |
# with gr.Accordion('Результат:', open=True):
|
94 |
# result_output = gr.Markdown(value="Здесь будет представлен результат")
|
95 |
# Группа с лейблом и рамкой
|
96 |
+
with gr.Row():
|
97 |
+
with gr.Column(elem_classes="markdown-group", scale=2):
|
98 |
+
gr.HTML("<div class='markdown-label'>Результат:</div>") # Лейбл внутри рамки
|
99 |
+
with gr.Column(elem_classes="markdown-content"):
|
100 |
+
result_output = gr.Markdown(value="Здесь будет представлен результат", min_height=200, show_copy_button=True)
|
101 |
+
|
102 |
+
with gr.Column(elem_classes="markdown-group", scale=1):
|
103 |
+
gr.HTML("<div class='markdown-label'>Json результат:</div>") # Лейбл внутри рамки
|
104 |
+
with gr.Column(elem_classes="markdown-content"):
|
105 |
+
json_output = gr.Markdown(value="Здесь будет представлен json", min_height=200, show_copy_button=True)
|
106 |
|
107 |
|
108 |
def respond(file, system_prompt, model, temperature, alpha):
|
109 |
llm = get_llm(model)
|
110 |
llm_response = process_query(file, system_prompt, llm, temperature, alpha)
|
111 |
+
|
112 |
+
return extract_json_and_clean_text(llm_response)
|
113 |
|
114 |
# Привязываем кнопку к функции обработки
|
115 |
send_button.click(
|
116 |
respond,
|
117 |
inputs=[file_input, system_prompt_input, model, temperature, alpha],
|
118 |
+
outputs=[result_output, json_output]
|
119 |
)
|
120 |
|
121 |
|
|
|
165 |
markdown_output = gr.Markdown("Изначальное содержимое Markdown")
|
166 |
|
167 |
dynamic_tab.select(get_sources, outputs=markdown_output)
|
168 |
+
|
169 |
+
# Функция для чтения содержимого файла и возврата его как строки
|
170 |
+
def read_file_content(file_path):
|
171 |
+
try:
|
172 |
+
with open(file_path, 'r', encoding='utf-8') as file:
|
173 |
+
content = file.read()
|
174 |
+
return content
|
175 |
+
except FileNotFoundError:
|
176 |
+
return "Файл не найден."
|
177 |
+
except Exception as e:
|
178 |
+
return f"Произошла ошибка: {str(e)}"
|
179 |
+
|
180 |
+
def tab3():
|
181 |
+
with gr.Tab("Инфо"):
|
182 |
+
gr.Markdown(read_file_content('info.md'))
|
183 |
+
gr.Image("architecture.png")
|
184 |
+
gr.Markdown(read_file_content('team.md'))
|
185 |
|
186 |
|
187 |
css = """
|
|
|
209 |
|
210 |
# Интерфейс Gradio
|
211 |
with gr.Blocks(css=css) as demo:
|
212 |
+
gr.Markdown("# Анализ тендерных заявок")
|
213 |
with gr.Tabs():
|
214 |
tab1()
|
215 |
tab2()
|
216 |
+
tab3()
|
217 |
|
218 |
|
219 |
init_db()
|
architecture.png
ADDED
![]() |
Git LFS Details
|
info.md
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Описание
|
2 |
+
Цель – оценка релевантности тендерной заявки на соответствие нашему опыту
|
3 |
+
|
4 |
+
Целевая аудитория – сотрудники отдела продаж
|
5 |
+
|
6 |
+
Практическая ценность:
|
7 |
+
* Ускорение обработки тендерной заявки
|
8 |
+
* Скоринговая оценка заявки и указание потенциальных рисков
|
9 |
+
* Подчеркивание сильных сторон для формирования предложения по продажам
|
10 |
+
|
11 |
+
# Технологии
|
12 |
+
* Large Language Models
|
13 |
+
* ChromaDB
|
14 |
+
* LLM провайдеры (OpenRouter, Mistral)
|
15 |
+
* RAG (semantic+bm25)
|
llm.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1 |
import openai
|
2 |
import logging
|
|
|
3 |
|
4 |
logger = logging.getLogger("llm")
|
5 |
logging.basicConfig(
|
@@ -8,14 +9,44 @@ logging.basicConfig(
|
|
8 |
datefmt="%Y-%m-%d %H:%M:%S",
|
9 |
)
|
10 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
11 |
|
12 |
class LLM:
|
13 |
-
def __init__(self,
|
14 |
-
self.url = url
|
15 |
-
self.key = key
|
16 |
self.client = openai.OpenAI(api_key=self.key, base_url=self.url)
|
17 |
|
18 |
-
self.model = model
|
19 |
|
20 |
def chat(self, messages, temperature=0):
|
21 |
logger.info("LLM call")
|
|
|
1 |
import openai
|
2 |
import logging
|
3 |
+
import os
|
4 |
|
5 |
logger = logging.getLogger("llm")
|
6 |
logging.basicConfig(
|
|
|
9 |
datefmt="%Y-%m-%d %H:%M:%S",
|
10 |
)
|
11 |
|
12 |
+
MISTRAL_KEY=os.getenv('MISTRAL_API_KEY')
|
13 |
+
MISTRAL_URL="https://api.mistral.ai/v1"
|
14 |
+
|
15 |
+
OPENROUTER_KEY = os.getenv('OPENROUTER_API_KEY')
|
16 |
+
OPENROUTER_URL = "https://openrouter.ai/api/v1"
|
17 |
+
|
18 |
+
model_creds = {
|
19 |
+
'qwen2.5-vl-72b-instruct': {
|
20 |
+
"url": OPENROUTER_URL,
|
21 |
+
"key": OPENROUTER_KEY,
|
22 |
+
"model": "qwen/qwen2.5-vl-72b-instruct:free"
|
23 |
+
},
|
24 |
+
'deepseek_v3': {
|
25 |
+
"url": OPENROUTER_URL,
|
26 |
+
"key": OPENROUTER_KEY,
|
27 |
+
"model": "deepseek/deepseek-chat:free"
|
28 |
+
},
|
29 |
+
'llama-3.3-70b': {
|
30 |
+
"url": OPENROUTER_URL,
|
31 |
+
"key": OPENROUTER_KEY,
|
32 |
+
"model": "meta-llama/llama-3.3-70b-instruct:free"
|
33 |
+
},
|
34 |
+
'mistral': {
|
35 |
+
"url": MISTRAL_URL,
|
36 |
+
"key": MISTRAL_KEY,
|
37 |
+
"model": "mistral-small-latest"
|
38 |
+
}
|
39 |
+
}
|
40 |
+
|
41 |
+
|
42 |
|
43 |
class LLM:
|
44 |
+
def __init__(self, model):
|
45 |
+
self.url = model_creds[model]["url"]
|
46 |
+
self.key = model_creds[model]["key"]
|
47 |
self.client = openai.OpenAI(api_key=self.key, base_url=self.url)
|
48 |
|
49 |
+
self.model = model_creds[model]["model"]
|
50 |
|
51 |
def chat(self, messages, temperature=0):
|
52 |
logger.info("LLM call")
|
prompts.py
CHANGED
@@ -1,48 +1,63 @@
|
|
1 |
default_system_promot = """\
|
2 |
-
Вы — AI
|
3 |
|
4 |
---
|
5 |
|
6 |
### **Инструкции**
|
7 |
1. **Ключевые критерии оценки**:
|
8 |
-
|
9 |
-
-
|
10 |
-
- `Технологии` (минимум 50% совпадения)
|
11 |
-
- **Гибкие (вторичные)**:
|
12 |
- `Цели проекта` (соответствие бизнес-целям клиента)
|
13 |
- `Решаемые задачи` (наличие аналогичных кейсов)
|
14 |
- `Компетенции` (экспертиза в требуемой области)
|
15 |
- `Сложность` (опыт работы с проектами аналогичного уровня)
|
16 |
|
17 |
2. **Методология**:
|
18 |
-
- Для каждого критерия:
|
19 |
-
1.
|
20 |
-
2.
|
|
|
|
|
21 |
- **Формула скоринга**:
|
22 |
```
|
23 |
-
(Отрасль × 0.
|
24 |
```
|
25 |
|
26 |
-
3.
|
|
|
|
|
|
|
|
|
|
|
27 |
```markdown
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
|
|
45 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
46 |
```
|
47 |
|
48 |
---
|
@@ -52,4 +67,65 @@ default_system_promot = """\
|
|
52 |
- При **совпадении по целям <30%** → вердикт ⚠️, даже если скоринг высокий.
|
53 |
- **Запрещено:**
|
54 |
- Использовать данные вне контекста.
|
55 |
-
- Обобщения без ссылок на проекты (например, "у нас богатый опыт").
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
default_system_promot = """\
|
2 |
+
Вы — AI-аналитик в IT компании, оценивающий соответствие опыта компании входящим проектам. **Строго соблюдайте структуру и правила ниже.**
|
3 |
|
4 |
---
|
5 |
|
6 |
### **Инструкции**
|
7 |
1. **Ключевые критерии оценки**:
|
8 |
+
- `Отрасль` (отрасль применения)
|
9 |
+
- `Технологии` (используемые программные продукты)
|
|
|
|
|
10 |
- `Цели проекта` (соответствие бизнес-целям клиента)
|
11 |
- `Решаемые задачи` (наличие аналогичных кейсов)
|
12 |
- `Компетенции` (экспертиза в требуемой области)
|
13 |
- `Сложность` (опыт работы с проектами аналогичного уровня)
|
14 |
|
15 |
2. **Методология**:
|
16 |
+
- Для каждого критерия:
|
17 |
+
1. **Извлеките релевантные данные** из предоставленного описания опыта компании и входящих проектов.
|
18 |
+
2. Рассчитайте % совпадения на основе данных.
|
19 |
+
3. Укажите **конкретные примеры** (название проекта, url, описание связи).
|
20 |
+
4. Если критерий не отображен в документах, то не включай его для анализа. Отсутствие упоминания считай за полное соответствие 100%.
|
21 |
- **Формула скоринга**:
|
22 |
```
|
23 |
+
(Отрасль × 0.2) + (Технологии × 0.2) + (Цели × 0.2) + (Задачи × 0.2) + (Компетенции × 0.2)
|
24 |
```
|
25 |
|
26 |
+
3. **Правила использования данных**:
|
27 |
+
- Используйте **только предоставленные входные данные** для анализа.
|
28 |
+
- **Не используйте примеры из формата ответа**, если они не соответствуют входным данным.
|
29 |
+
- Если данных недостаточно для анализа, укажите это явно.
|
30 |
+
|
31 |
+
4. **Формат ответа**:
|
32 |
```markdown
|
33 |
+
### Решение
|
34 |
+
**Вердикт:** [✅ Подходит (80-100%) / ⚠️ Условно подходит (50-80%) / ❌ Не подходит (<50%)]
|
35 |
+
|
36 |
+
**Скоринг:** X%
|
37 |
+
|
38 |
+
#### Анализ критериев
|
39 |
+
| Критерий | Совпадение | Примеры из опыта | Риски | Сильные стороны |
|
40 |
+
|------------------|------------|-------------------------------------------|------------------|------------------------------|
|
41 |
+
| Отрасль | X% | Проект "Названи��" (описание связи, url) | риск (если есть) | сильные стороны (если есть) |
|
42 |
+
| Технологии | X% | Проект "Название" (описание связи, url) | риск (если есть) | сильные стороны (если есть) |
|
43 |
+
| Цели проекта | X% | Проект "Название" (описание связи, url) | риск (если есть) | сильные стороны (если есть) |
|
44 |
+
| Решаемые задачи | X% | Проект "Название" (описание связи, url) | риск (если есть) | сильные стороны (если есть) |
|
45 |
+
| Компетенции | X% | Проект "Название" (описание связи, url) | риск (если есть) | сильные стороны (если есть) |
|
46 |
+
|
47 |
+
#### Рекомендации
|
48 |
+
- **Сильные стороны:**
|
49 |
+
- **Риски:**
|
50 |
+
- **Оптимизация:**
|
51 |
|
52 |
+
|
53 |
+
```json
|
54 |
+
{
|
55 |
+
"score": скоринг,
|
56 |
+
"solution": вердикт,
|
57 |
+
"recommendations": рекомендации
|
58 |
+
}
|
59 |
+
```
|
60 |
+
|
61 |
```
|
62 |
|
63 |
---
|
|
|
67 |
- При **совпадении по целям <30%** → вердикт ⚠️, даже если скоринг высокий.
|
68 |
- **Запрещено:**
|
69 |
- Использовать данные вне контекста.
|
70 |
+
- Обобщения без ссылок на проекты (например, "у нас богатый опыт").
|
71 |
+
- Использовать примеры из формата ответа, если они не соответствуют входным данным."""
|
72 |
+
|
73 |
+
|
74 |
+
summary_system_prompt = """\
|
75 |
+
#### Инструкции
|
76 |
+
1. **Цель**:
|
77 |
+
Проанализируйте входной файл с запросом на проект и извлеките информацию, которая может быть полезна для оценки соответствия опыта компании проекту. Результат будет использован как входные данные для анализа по следующим критериям:
|
78 |
+
- **Отрасль**
|
79 |
+
- **Технологии**
|
80 |
+
- **Цели проекта**
|
81 |
+
- **Решаемые задачи**
|
82 |
+
- **Компетенции**
|
83 |
+
|
84 |
+
2. **Методология**:
|
85 |
+
- Сфокусируйтесь на ключевых разделах документа, таких как:
|
86 |
+
- Описание проекта
|
87 |
+
- Требования к технологиям
|
88 |
+
- Бизнес-цели
|
89 |
+
- Ожидаемые результаты
|
90 |
+
- Условия реализации
|
91 |
+
- Игнорируйте юридические формальности, такие как:
|
92 |
+
- Условия контракта
|
93 |
+
- Правовые обязательства
|
94 |
+
- Финансовые детали (если они не связаны с целями проекта)
|
95 |
+
|
96 |
+
3. **Формат ответа**:
|
97 |
+
```markdown
|
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 |
+
4. **Правила извлечения данных**:
|
124 |
+
- Используйте только информацию из входного файла.
|
125 |
+
- Если какой-либо раздел отсутствует в документе, укажите это явно.
|
126 |
+
- Не добавляйте предположения или обобщения.
|
127 |
+
- Извлекайте только фактические данные, которые могут быть использованы для анализа.
|
128 |
+
|
129 |
+
5. **Ограничения**:
|
130 |
+
- Не включайте юридические или финансовые детали, если они не связаны с ключевыми критериями.
|
131 |
+
- Не используйте примеры из шаблона о��вета, если они не соответствуют входным данным."""
|
rag.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1 |
from db import db
|
2 |
import logging
|
|
|
3 |
|
4 |
logger = logging.getLogger("rag")
|
5 |
logging.basicConfig(
|
@@ -24,12 +25,7 @@ message_template = """\
|
|
24 |
def process_query(req_file, system_prompt, llm, temperature, alpha):
|
25 |
logger.info("Process query")
|
26 |
|
27 |
-
|
28 |
-
with open(req_file.name, 'r', encoding='utf-8') as f:
|
29 |
-
req_file_content = f.read()
|
30 |
-
else:
|
31 |
-
logger.warning("File is not loaded!")
|
32 |
-
req_file_content = ""
|
33 |
|
34 |
logger.info("Retrive docs")
|
35 |
|
|
|
1 |
from db import db
|
2 |
import logging
|
3 |
+
from summary import file_summary
|
4 |
|
5 |
logger = logging.getLogger("rag")
|
6 |
logging.basicConfig(
|
|
|
25 |
def process_query(req_file, system_prompt, llm, temperature, alpha):
|
26 |
logger.info("Process query")
|
27 |
|
28 |
+
req_file_content = file_summary(req_file)
|
|
|
|
|
|
|
|
|
|
|
29 |
|
30 |
logger.info("Retrive docs")
|
31 |
|
requirements.txt
CHANGED
@@ -2,4 +2,6 @@ gradio==5.17.0
|
|
2 |
openai==1.63.2
|
3 |
sentence-transformers==3.4.1
|
4 |
chromadb==0.6.3
|
5 |
-
rank-bm25==0.2.2
|
|
|
|
|
|
2 |
openai==1.63.2
|
3 |
sentence-transformers==3.4.1
|
4 |
chromadb==0.6.3
|
5 |
+
rank-bm25==0.2.2
|
6 |
+
python-docx==1.1.2
|
7 |
+
pypdf==5.3.0
|
summary.py
ADDED
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import docx
|
2 |
+
from pypdf import PdfReader
|
3 |
+
from prompts import summary_system_prompt
|
4 |
+
from llm import LLM
|
5 |
+
import logging
|
6 |
+
|
7 |
+
|
8 |
+
logger = logging.getLogger("summary")
|
9 |
+
logging.basicConfig(
|
10 |
+
format="%(asctime)s %(levelname)-8s %(message)s",
|
11 |
+
level=logging.INFO,
|
12 |
+
datefmt="%Y-%m-%d %H:%M:%S",
|
13 |
+
)
|
14 |
+
|
15 |
+
def getTextFromDocx(filename):
|
16 |
+
doc = docx.Document(filename)
|
17 |
+
fullText = []
|
18 |
+
for para in doc.paragraphs:
|
19 |
+
fullText.append(para.text)
|
20 |
+
return '\n'.join(fullText)
|
21 |
+
|
22 |
+
def getTextFromPDF(filename):
|
23 |
+
reader = PdfReader(filename)
|
24 |
+
text = ""
|
25 |
+
for page in reader.pages:
|
26 |
+
text += page.extract_text() + "\n"
|
27 |
+
return text
|
28 |
+
|
29 |
+
|
30 |
+
def getTextFromFile(filename):
|
31 |
+
content = ""
|
32 |
+
if (filename.lower().endswith(".pdf")):
|
33 |
+
logger.info(f"pdf detected")
|
34 |
+
content = getTextFromPDF(filename)
|
35 |
+
elif (filename.lower().endswith(".docx")):
|
36 |
+
logger.info(f"docx detected")
|
37 |
+
content = getTextFromDocx(filename)
|
38 |
+
elif (filename.lower().endswith(".txt")):
|
39 |
+
logger.info(f"pdf detected")
|
40 |
+
with open(filename, 'r', encoding='utf-8') as f:
|
41 |
+
content = f.read()
|
42 |
+
return content
|
43 |
+
|
44 |
+
# Функция для обработки запроса к LLM
|
45 |
+
def file_summary(file):
|
46 |
+
logger.info(f"Start summarization")
|
47 |
+
# Чтение содержимого файлов
|
48 |
+
if file is not None:
|
49 |
+
file_content = getTextFromFile(file.name)
|
50 |
+
else:
|
51 |
+
logger.warning(f"Invalid input file")
|
52 |
+
file_content = ""
|
53 |
+
|
54 |
+
# Формирование сообщения для LLM
|
55 |
+
messages = [
|
56 |
+
{"role": "system", "content": summary_system_prompt},
|
57 |
+
{"role": "user", "content": f"Задание: {file_content}"}
|
58 |
+
]
|
59 |
+
|
60 |
+
llm = LLM('mistral')
|
61 |
+
|
62 |
+
# Получение ответа от LLM
|
63 |
+
llm_response = llm.chat(messages).choices[0].message.content
|
64 |
+
logger.info(f"Finish summarization")
|
65 |
+
return llm_response
|
team.md
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Команда
|
2 |
+
* Дитятина Татьяна
|
3 |
+
* Зубчевский Виталий
|
4 |
+
* Митенев Александр
|