aleksandrrnt commited on
Commit
9de1f87
·
verified ·
1 Parent(s): b6e50cd

Upload 5 files

Browse files
Files changed (5) hide show
  1. app.py +159 -56
  2. db.py +3 -0
  3. llm.py +32 -0
  4. prompts.py +55 -0
  5. rag.py +2 -18
app.py CHANGED
@@ -1,57 +1,160 @@
1
- import gradio as gr
2
- from db import db
3
- from rag import process_query
4
-
5
- def init_db():
6
- with open("sources.txt", encoding="utf-8") as f:
7
- urls = f.read().splitlines()
8
- db.add(urls)
9
-
10
-
11
-
12
- # Высота столбцов (в пикселях)
13
- COLUMN_HEIGHT = 280
14
-
15
- NUM_LINES = 12
16
-
17
- # Интерфейс Gradio
18
- with gr.Blocks() as demo:
19
- gr.Markdown("# Интерфейс для анализа пресейла")
20
-
21
- # Создаем два столбца: левый для загрузки файлов, правый для системного промпта
22
- with gr.Row():
23
- with gr.Column(scale=1): # Левый столбец
24
- file_input = gr.File(
25
- label="Прикрепите рассматриваемый запрос",
26
- )
27
-
28
- with gr.Column(scale=2): # Правый столбец
29
- system_prompt_input = gr.Textbox(
30
- label="Системный промпт",
31
- placeholder="Введите системный промпт...",
32
- lines=NUM_LINES, # Количество строк зависит от доступной высоты
33
- max_lines=NUM_LINES,
34
- interactive=True,
35
- )
36
-
37
- # Кнопка "Отправить"
38
- send_button = gr.Button("Отправить")
39
-
40
- # Текстовое поле для отображения результата
41
- result_output = gr.Textbox(label="Результат", lines=5, interactive=False)
42
-
43
- def respond(file2, system_prompt):
44
- # Обработка запроса (заглушка)
45
- llm_response = process_query(file2, system_prompt)
46
- return llm_response
47
-
48
- # Привязываем кнопку к функции обработки
49
- send_button.click(
50
- respond,
51
- inputs=[file_input, system_prompt_input],
52
- outputs=result_output
53
- )
54
-
55
- init_db()
56
- # Запуск приложения
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  demo.launch()
 
1
+ import os
2
+ import gradio as gr
3
+ 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:
11
+ urls = f.read().splitlines()
12
+ db.add(urls)
13
+
14
+
15
+ MISTRAL_KEY=os.getenv('MISTRAL_API_KEY')
16
+ MISTRAL_URL="https://api.mistral.ai/v1"
17
+ MISTRAL_MODEL="mistral-small-latest"
18
+
19
+ OPENROUTER_URL = "https://openrouter.ai/api/v1"
20
+ OPENROUTER_KEY = os.getenv('OPENROUTER_API_KEY')
21
+
22
+ def get_llm(model):
23
+ if model == 'qwen2.5-vl-72b-instruct':
24
+ url = OPENROUTER_URL
25
+ key = OPENROUTER_KEY
26
+ model = "qwen/qwen2.5-vl-72b-instruct:free"
27
+ elif model == 'deepseek_v3':
28
+ url = OPENROUTER_URL
29
+ key = OPENROUTER_KEY
30
+ model = "deepseek/deepseek-chat:free"
31
+ elif model == 'llama-3.3-70b':
32
+ url = OPENROUTER_URL
33
+ key = OPENROUTER_KEY
34
+ model = "meta-llama/llama-3.3-70b-instruct:free"
35
+ else:
36
+ url = MISTRAL_URL
37
+ key = MISTRAL_KEY
38
+ model = MISTRAL_MODEL
39
+
40
+ return LLM(url, key, model)
41
+
42
+
43
+ # Высота столбцов (в пикселях)
44
+ COLUMN_HEIGHT = 280
45
+ NUM_LINES = 12
46
+
47
+
48
+ def tab1():
49
+ with gr.Tab("Анализ пресейлов"):
50
+ # Создаем два столбца: левый для загрузки файлов, правый для системного промпта
51
+ with gr.Row():
52
+ with gr.Column(scale=1): # Левый столбец
53
+ file_input = gr.File(
54
+ label="Прикрепите рассматриваемый запрос",
55
+ )
56
+ with gr.Row():
57
+ model = gr.Dropdown(
58
+ choices=["mistral", "qwen2.5-vl-72b-instruct", "deepseek_v3", "llama-3.3-70b"], # Список строк для выбора
59
+ label="Выберите модель", # Подпись к выпадающему списку
60
+ multiselect=False # Если True, можно выбрать несколько значений
61
+ )
62
+
63
+ temperature = gr.Slider(
64
+ minimum=0,
65
+ maximum=1,
66
+ step=0.01,
67
+ label="Температура",
68
+ value=0
69
+ )
70
+
71
+ with gr.Column(scale=2): # Правый столбец
72
+ system_prompt_input = gr.Textbox(
73
+ label="Системный промпт",
74
+ placeholder="Введите системный промпт...",
75
+ lines=NUM_LINES, # Количество строк зависит от доступной высоты
76
+ max_lines=NUM_LINES,
77
+ interactive=True,
78
+ value=default_system_promot
79
+ )
80
+
81
+ # Кнопка "Отправить"
82
+ send_button = gr.Button("Отправить")
83
+
84
+ # Текстовое поле для отображения результата
85
+ # result_output = gr.Textbox(label="Результат", lines=5, interactive=False)
86
+ with gr.Accordion('Результат:', open=True):
87
+ result_output = gr.Markdown(value="Здесь будет представлен результат")
88
+
89
+ def respond(file, system_prompt, model, temperature):
90
+ llm = get_llm(model)
91
+ llm_response = process_query(file, system_prompt, llm, temperature)
92
+ return llm_response
93
+
94
+ # Привязываем кнопку к функции обработки
95
+ send_button.click(
96
+ respond,
97
+ inputs=[file_input, system_prompt_input, model, temperature],
98
+ outputs=result_output
99
+ )
100
+
101
+
102
+ def add_new_source():
103
+ new_url = gr.Textbox(
104
+ label="url нового источника",
105
+ placeholder="Введите url нового источника...",
106
+ lines=1,
107
+ max_lines=1,
108
+ interactive=True,
109
+ info="Поддерживается парсинг только с сайта https://www.reksoft.ru"
110
+ )
111
+
112
+ add_button = gr.Button("Добавить")
113
+
114
+ def add_source(url):
115
+ if url.startswith("https://www.reksoft.ru"):
116
+ db.add([url])
117
+ gr.Success(f"{url} успешно добавлен")
118
+ else:
119
+ gr.Error("Ошибка: представленная ссылка не принадлежит https://www.reksoft.ru")
120
+
121
+ return url
122
+
123
+ add_button.click(
124
+ add_source,
125
+ inputs=new_url,
126
+ outputs=new_url
127
+ )
128
+
129
+
130
+ def get_sources():
131
+ ids = db.get_ids()
132
+ str_ids = ""
133
+ for source in ids:
134
+ str_ids += f'* {source}\n'
135
+
136
+ return f"""\
137
+ # Источники
138
+ {str_ids}"""
139
+
140
+
141
+ def tab2():
142
+ with gr.Tab("Источники") as dynamic_tab:
143
+ add_new_source()
144
+
145
+ markdown_output = gr.Markdown("Изначальное содержимое Markdown")
146
+
147
+ dynamic_tab.select(get_sources, outputs=markdown_output)
148
+
149
+
150
+ # Интерфейс Gradio
151
+ with gr.Blocks() as demo:
152
+ gr.Markdown("# Интерфейс для анализа пресейла")
153
+ with gr.Tabs():
154
+ tab1()
155
+ tab2()
156
+
157
+
158
+ init_db()
159
+ # Запуск приложения
160
  demo.launch()
db.py CHANGED
@@ -56,6 +56,9 @@ class HacatonDB:
56
  def update(self, urls):
57
  pass
58
 
 
 
 
59
  def query(self, query, top_k):
60
  return self.collection.query(query_texts=query, n_results=top_k)
61
 
 
56
  def update(self, urls):
57
  pass
58
 
59
+ def get_ids(self):
60
+ return self.collection.get()["ids"]
61
+
62
  def query(self, query, top_k):
63
  return self.collection.query(query_texts=query, n_results=top_k)
64
 
llm.py ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import openai
2
+ import logging
3
+
4
+ logger = logging.getLogger("llm")
5
+ logging.basicConfig(
6
+ format="%(asctime)s %(levelname)-8s %(message)s",
7
+ level=logging.INFO,
8
+ datefmt="%Y-%m-%d %H:%M:%S",
9
+ )
10
+
11
+
12
+ class LLM:
13
+ def __init__(self, url, key, model):
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")
22
+
23
+ response = self.client.chat.completions.create(
24
+ messages=messages,
25
+ model=self.model,
26
+ temperature=temperature
27
+ )
28
+
29
+ logger.info("LLM call completed")
30
+
31
+ return response
32
+
prompts.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ default_system_promot = """\
2
+ Вы — AI-аналитик, оценивающий соответствие опыта компании входящим проектам. **Строго соблюдайте структуру и правила ниже.**
3
+
4
+ ---
5
+
6
+ ### **Инструкции**
7
+ 1. **Ключевые критерии оценки**:
8
+ - **Жесткие (обязательные)**:
9
+ - `Отрасль` (минимум 60% совпадения)
10
+ - `Технологии` (минимум 50% совпадения)
11
+ - **Гибкие (вторичные)**:
12
+ - `Цели проекта` (соответствие бизнес-целям клиента)
13
+ - `Решаемые задачи` (наличие аналогичных кейсов)
14
+ - `Компетенции` (экспертиза в требуемой области)
15
+ - `Сложность` (опыт работы с проектами аналогичного уровня)
16
+
17
+ 2. **Методология**:
18
+ - Для каждого критерия:
19
+ 1. Рассчитайте % совпадения на основе данных.
20
+ 2. Укажите **конкретные примеры** (название проекта, ID, описание связи).
21
+ - **Формула скоринга**:
22
+ ```
23
+ (Отрасль × 0.3) + (Технологии × 0.3) + (Цели × 0.2) + (Задачи × 0.1) + (Компетенции × 0.1)
24
+ ```
25
+
26
+ 3. **Формат ответа**:
27
+ ```markdown
28
+ ### Решение
29
+ **Вердикт:** [✅ Подходит / ⚠️ Условно подходит / ❌ Не подходит]
30
+ **Уверенность:** [High/Medium/Low]
31
+ **Скоринг:** X%
32
+
33
+ #### Анализ критериев
34
+ | Критерий | Совпадение | Примеры из опыта |
35
+ |-------------------|------------|-------------------------------------------|
36
+ | Отрасль | 75% | Проект "RetailX" (ID: 45, e-commerce) |
37
+ | Технологии | 60% | Проект "CloudFlow" (ID: 89, AWS, Python) |
38
+ | Цели проекта | 50% | Проект "DataSafe" (ID: 12, оптимизация Big Data) |
39
+ | Решаемые задачи | 80% | Проект "LogistAI" (ID: 33, автоматизация склада) |
40
+
41
+ #### Рекомендации
42
+ - **Сильные стороны:**
43
+ - **Риски:**
44
+ - **Оптимизация:**
45
+
46
+ ```
47
+
48
+ ---
49
+
50
+ ### **Ограничения**
51
+ - Если **жесткие критерии не пройдены** → автоматический ❌.
52
+ - При **совпадении по целям <30%** → вердикт ⚠️, даже если скоринг высокий.
53
+ - **Запрещено:**
54
+ - Использовать данные вне контекста.
55
+ - Обобщения без ссылок на проекты (например, "у нас богатый опыт")."""
rag.py CHANGED
@@ -1,5 +1,3 @@
1
- import os
2
- import openai
3
  from db import db
4
  import logging
5
 
@@ -11,13 +9,6 @@ logging.basicConfig(
11
  )
12
 
13
 
14
- MISTRAL_KEY=os.getenv('MISTRAL_API_KEY')
15
- MISTRAL_URL="https://api.mistral.ai/v1"
16
- MISTRAL_MODEL="mistral-small-latest"
17
-
18
-
19
- client = openai.OpenAI(api_key=MISTRAL_KEY, base_url=MISTRAL_URL)
20
-
21
  message_template = """\
22
  Далее представлена информацию по опыту нашей компании
23
  ---------------------
@@ -30,7 +21,7 @@ message_template = """\
30
  При проведении анализа опирайся только на представленную информацию"""
31
 
32
  # Функция для обработки запроса к LLM
33
- def process_query(req_file, system_prompt):
34
  logger.info("Process query")
35
 
36
  if req_file is not None:
@@ -58,14 +49,7 @@ def process_query(req_file, system_prompt):
58
  {"role": "user", "content": user_message}
59
  ]
60
 
61
- logger.info("LLM call")
62
-
63
- response = client.chat.completions.create(
64
- messages=messages,
65
- model=MISTRAL_MODEL,
66
- )
67
-
68
- logger.info("LLM call completed")
69
 
70
  # Получение ответа от LLM
71
  llm_response = response.choices[0].message.content
 
 
 
1
  from db import db
2
  import logging
3
 
 
9
  )
10
 
11
 
 
 
 
 
 
 
 
12
  message_template = """\
13
  Далее представлена информацию по опыту нашей компании
14
  ---------------------
 
21
  При проведении анализа опирайся только на представленную информацию"""
22
 
23
  # Функция для обработки запроса к LLM
24
+ def process_query(req_file, system_prompt, llm, temperature):
25
  logger.info("Process query")
26
 
27
  if req_file is not None:
 
49
  {"role": "user", "content": user_message}
50
  ]
51
 
52
+ response = llm.chat(messages, temperature)
 
 
 
 
 
 
 
53
 
54
  # Получение ответа от LLM
55
  llm_response = response.choices[0].message.content