MrSimple07 commited on
Commit
c0bcb11
·
1 Parent(s): 32b4b31

a new chunking with 3 000 size for all types of data

Browse files
Files changed (5) hide show
  1. config.py +20 -16
  2. documents_prep.py +45 -66
  3. index_retriever.py +3 -3
  4. table_info.py +70 -0
  5. table_prep.py +6 -2
config.py CHANGED
@@ -50,7 +50,7 @@ AVAILABLE_MODELS = {
50
 
51
  DEFAULT_MODEL = "Gemini 2.5 Flash"
52
 
53
- CHUNK_SIZE = 25000
54
  CHUNK_OVERLAP = 256
55
 
56
  CUSTOM_PROMPT = """
@@ -88,42 +88,46 @@ CUSTOM_PROMPT = """
88
 
89
  ПРАВИЛА ФОРМИРОВАНИЯ ОТВЕТОВ:
90
 
91
- 1. ОБЯЗАТЕЛЬНОЕ УКАЗАНИЕ ИСТОЧНИКОВ:
92
- - Всегда указывайте конкретный документ (ГОСТ, раздел, пункт)
93
- - Формат: "Согласно [Документ], раздел [X], пункт [X.X]: [информация]"
94
- - При цитировании: используйте кавычки и точные ссылки
 
95
 
96
- 2. СТРУКТУРА ОТВЕТА:
97
  - Начинайте с прямого ответа на вопрос
98
  - Затем указывайте нормативные основания
99
  - Завершайте ссылками на конкретные документы и разделы
100
 
101
- 3. РАБОТА С КОНТЕКСТОМ:
102
  - Если информация найдена в контексте - предоставьте полный ответ
103
  - Если информация не найдена: "Информация по вашему запросу не найдена в доступной нормативной документации"
104
  - Не делайте предположений за пределами контекста
105
  - Не используйте общие знания
106
 
107
- 4. ТЕРМИНОЛОГИЯ И ЦИТИРОВАНИЕ:
108
  - Сохраняйте официальную терминологию НД
109
  - Цитируйте точные формулировки ключевых требований
110
  - При множественных источниках - укажите все релевантные
111
 
112
- 5. ФОРМАТИРОВАНИЕ:
113
  - Для перечислений: используйте нумерованные списки
114
  - Выделяйте критически важные требования
115
  - Структурируйте ответ логически
116
 
117
- ПРИМЕРЫ ПРАВИЛЬНОГО ФОРМАТИРОВАНИЯ:
118
 
119
- Вопрос: каких случаях могут быть признаны протоколы испытаний?"
120
- Ответ: "Протоколы испытаний могут быть признаны в следующих случаях:
121
 
122
- 1. Если они проведены испытательными лабораториями (центрами), аккредитованными в области использования атомной энергии (ГОСТ Р 50.08.04-2022, раздел 6 )
123
- 2. Если они проведены лабораториями, аккредитованными национальным органом Российской Федерации по аккредитации (ГОСТ Р 50.08.04-2022, пункт 4.1)
124
- 3. Если лаборатории прошли оценку состояния измерений
125
 
126
- Также допускается признание результатов испытаний, выполненных испытательными центрами (лабораториями), аккредитованными в национальных системах аккредитации страны изготовителя (ГОСТ Р 50.04.08-2019)."
 
 
 
 
 
 
127
 
128
  Контекст: {context_str}
129
 
 
50
 
51
  DEFAULT_MODEL = "Gemini 2.5 Flash"
52
 
53
+ CHUNK_SIZE = 3000
54
  CHUNK_OVERLAP = 256
55
 
56
  CUSTOM_PROMPT = """
 
88
 
89
  ПРАВИЛА ФОРМИРОВАНИЯ ОТВЕТОВ:
90
 
91
+ Работай исключительно с информацией из предоставленного контекста. Запрещено использовать:
92
+ - Общие знания
93
+ - Информацию из интернета
94
+ - Данные из предыдущих диалогов
95
+ - Собственные предположения
96
 
97
+ 1. СТРУКТУРА ОТВЕТА:
98
  - Начинайте с прямого ответа на вопрос
99
  - Затем указывайте нормативные основания
100
  - Завершайте ссылками на конкретные документы и разделы
101
 
102
+ 2. РАБОТА С КОНТЕКСТОМ:
103
  - Если информация найдена в контексте - предоставьте полный ответ
104
  - Если информация не найдена: "Информация по вашему запросу не найдена в доступной нормативной документации"
105
  - Не делайте предположений за пределами контекста
106
  - Не используйте общие знания
107
 
108
+ 3. ТЕРМИНОЛОГИЯ И ЦИТИРОВАНИЕ:
109
  - Сохраняйте официальную терминологию НД
110
  - Цитируйте точные формулировки ключевых требований
111
  - При множественных источниках - укажите все релевантные
112
 
113
+ 4. ФОРМАТИРОВАНИЕ:
114
  - Для перечислений: используйте нумерованные списки
115
  - Выделяйте критически важные требования
116
  - Структурируйте ответ логически
117
 
118
+ # КАК РАБОТАТЬ С ЗАПРОСОМ
119
 
120
+ **Шаг 1:** Определи, что именно ищет пользователь (термин, требование, процедура, условие)
 
121
 
122
+ **Шаг 2:** Найди релевантную информацию в контексте
 
 
123
 
124
+ **Шаг 3:** Сформируй ответ:
125
+ - Если нашел: укажи документ и пункт, процитируй нужную часть
126
+ - Если не нашел: четко сообщи об отсутствии информации
127
+
128
+ **Шаг 4:** При наличии нескольких источников:
129
+ - Представь их последовательно с указанием источника каждого
130
+ - Если источников много (>4) — сначала дай их список, потом цитаты
131
 
132
  Контекст: {context_str}
133
 
documents_prep.py CHANGED
@@ -49,84 +49,63 @@ def process_documents_with_chunking(documents):
49
 
50
  for doc in documents:
51
  doc_type = doc.metadata.get('type', 'text')
 
52
 
53
- if doc_type == 'table':
54
- # Add tables as-is, no chunking
55
- table_count += 1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  all_chunked_docs.append(doc)
57
 
 
 
 
 
 
 
 
58
  chunk_info.append({
59
  'document_id': doc.metadata.get('document_id', 'unknown'),
60
  'section_id': doc.metadata.get('section_id', 'unknown'),
61
  'chunk_id': 0,
62
- 'chunk_size': len(doc.text),
63
  'chunk_preview': doc.text[:200] + "..." if len(doc.text) > 200 else doc.text,
64
- 'type': 'table',
65
- 'table_number': doc.metadata.get('table_number', 'unknown')
 
 
 
66
  })
67
-
68
- elif doc_type == 'image':
69
- image_count += 1
70
- doc_size = len(doc.text)
71
- if doc_size > CHUNK_SIZE:
72
- chunked_docs = chunk_document(doc)
73
- all_chunked_docs.extend(chunked_docs)
74
-
75
- for i, chunk_doc in enumerate(chunked_docs):
76
- chunk_info.append({
77
- 'document_id': chunk_doc.metadata.get('document_id', 'unknown'),
78
- 'section_id': chunk_doc.metadata.get('section_id', 'unknown'),
79
- 'chunk_id': i,
80
- 'chunk_size': len(chunk_doc.text),
81
- 'chunk_preview': chunk_doc.text[:200] + "..." if len(chunk_doc.text) > 200 else chunk_doc.text,
82
- 'type': 'image',
83
- 'image_number': chunk_doc.metadata.get('image_number', 'unknown')
84
- })
85
- else:
86
- all_chunked_docs.append(doc)
87
- chunk_info.append({
88
- 'document_id': doc.metadata.get('document_id', 'unknown'),
89
- 'section_id': doc.metadata.get('section_id', 'unknown'),
90
- 'chunk_id': 0,
91
- 'chunk_size': doc_size,
92
- 'chunk_preview': doc.text[:200] + "..." if len(doc.text) > 200 else doc.text,
93
- 'type': 'image',
94
- 'image_number': doc.metadata.get('image_number', 'unknown')
95
- })
96
-
97
- else:
98
- doc_size = len(doc.text)
99
- if doc_size > CHUNK_SIZE:
100
- chunked_docs = chunk_document(doc)
101
- all_chunked_docs.extend(chunked_docs)
102
- text_chunks_count += len(chunked_docs)
103
-
104
- for i, chunk_doc in enumerate(chunked_docs):
105
- chunk_info.append({
106
- 'document_id': chunk_doc.metadata.get('document_id', 'unknown'),
107
- 'section_id': chunk_doc.metadata.get('section_id', 'unknown'),
108
- 'chunk_id': i,
109
- 'chunk_size': len(chunk_doc.text),
110
- 'chunk_preview': chunk_doc.text[:200] + "..." if len(chunk_doc.text) > 200 else chunk_doc.text,
111
- 'type': 'text'
112
- })
113
- else:
114
- all_chunked_docs.append(doc)
115
- chunk_info.append({
116
- 'document_id': doc.metadata.get('document_id', 'unknown'),
117
- 'section_id': doc.metadata.get('section_id', 'unknown'),
118
- 'chunk_id': 0,
119
- 'chunk_size': doc_size,
120
- 'chunk_preview': doc.text[:200] + "..." if len(doc.text) > 200 else doc.text,
121
- 'type': 'text'
122
- })
123
-
124
  log_message(f"\n{'='*60}")
125
  log_message(f"ИТОГО ОБРАБОТАНО ДОКУМЕНТОВ:")
126
- log_message(f" • Таблицы: {table_count} (добавлены целиком)")
127
- log_message(f" • Изображения: {image_count}")
128
  log_message(f" • Текстовые чанки: {text_chunks_count}")
129
- log_message(f" • Всего документов: {len(all_chunked_docs)}")
130
  log_message(f"{'='*60}\n")
131
 
132
  return all_chunked_docs, chunk_info
 
49
 
50
  for doc in documents:
51
  doc_type = doc.metadata.get('type', 'text')
52
+ doc_size = len(doc.text)
53
 
54
+ # Apply chunking to ALL documents if they exceed CHUNK_SIZE
55
+ if doc_size > CHUNK_SIZE:
56
+ chunked_docs = chunk_document(doc)
57
+ all_chunked_docs.extend(chunked_docs)
58
+
59
+ if doc_type == 'table':
60
+ table_count += len(chunked_docs)
61
+ elif doc_type == 'image':
62
+ image_count += len(chunked_docs)
63
+ else:
64
+ text_chunks_count += len(chunked_docs)
65
+
66
+ for i, chunk_doc in enumerate(chunked_docs):
67
+ chunk_info.append({
68
+ 'document_id': chunk_doc.metadata.get('document_id', 'unknown'),
69
+ 'section_id': chunk_doc.metadata.get('section_id', 'unknown'),
70
+ 'chunk_id': i,
71
+ 'chunk_size': len(chunk_doc.text),
72
+ 'chunk_preview': chunk_doc.text[:200] + "..." if len(chunk_doc.text) > 200 else chunk_doc.text,
73
+ 'type': doc_type,
74
+ 'table_number': chunk_doc.metadata.get('table_number', 'unknown') if doc_type == 'table' else None,
75
+ 'table_title': chunk_doc.metadata.get('table_title', '') if doc_type == 'table' else None,
76
+ 'image_number': chunk_doc.metadata.get('image_number', 'unknown') if doc_type == 'image' else None,
77
+ 'image_title': chunk_doc.metadata.get('image_title', '') if doc_type == 'image' else None
78
+ })
79
+ else:
80
+ # Document is small enough, add as-is
81
  all_chunked_docs.append(doc)
82
 
83
+ if doc_type == 'table':
84
+ table_count += 1
85
+ elif doc_type == 'image':
86
+ image_count += 1
87
+ else:
88
+ text_chunks_count += 1
89
+
90
  chunk_info.append({
91
  'document_id': doc.metadata.get('document_id', 'unknown'),
92
  'section_id': doc.metadata.get('section_id', 'unknown'),
93
  'chunk_id': 0,
94
+ 'chunk_size': doc_size,
95
  'chunk_preview': doc.text[:200] + "..." if len(doc.text) > 200 else doc.text,
96
+ 'type': doc_type,
97
+ 'table_number': doc.metadata.get('table_number', 'unknown') if doc_type == 'table' else None,
98
+ 'table_title': doc.metadata.get('table_title', '') if doc_type == 'table' else None,
99
+ 'image_number': doc.metadata.get('image_number', 'unknown') if doc_type == 'image' else None,
100
+ 'image_title': doc.metadata.get('image_title', '') if doc_type == 'image' else None
101
  })
102
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
  log_message(f"\n{'='*60}")
104
  log_message(f"ИТОГО ОБРАБОТАНО ДОКУМЕНТОВ:")
105
+ log_message(f" • Таблицы (чанки): {table_count}")
106
+ log_message(f" • Изображения (чанки): {image_count}")
107
  log_message(f" • Текстовые чанки: {text_chunks_count}")
108
+ log_message(f" • Всего чанков: {len(all_chunked_docs)}")
109
  log_message(f"{'='*60}\n")
110
 
111
  return all_chunked_docs, chunk_info
index_retriever.py CHANGED
@@ -16,18 +16,18 @@ def create_query_engine(vector_index):
16
  try:
17
  bm25_retriever = BM25Retriever.from_defaults(
18
  docstore=vector_index.docstore,
19
- similarity_top_k=40
20
  )
21
 
22
  vector_retriever = VectorIndexRetriever(
23
  index=vector_index,
24
- similarity_top_k=40,
25
  similarity_cutoff=0.65
26
  )
27
 
28
  hybrid_retriever = QueryFusionRetriever(
29
  [vector_retriever, bm25_retriever],
30
- similarity_top_k=60,
31
  num_queries=1
32
  )
33
 
 
16
  try:
17
  bm25_retriever = BM25Retriever.from_defaults(
18
  docstore=vector_index.docstore,
19
+ similarity_top_k=20
20
  )
21
 
22
  vector_retriever = VectorIndexRetriever(
23
  index=vector_index,
24
+ similarity_top_k=30,
25
  similarity_cutoff=0.65
26
  )
27
 
28
  hybrid_retriever = QueryFusionRetriever(
29
  [vector_retriever, bm25_retriever],
30
+ similarity_top_k=40,
31
  num_queries=1
32
  )
33
 
table_info.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ from pathlib import Path
4
+ from collections import defaultdict
5
+
6
+ def analyze_json_tables(folder_path):
7
+ """
8
+ Анализирует JSON файлы и подсчитывает количество таблиц в каждом документе
9
+ """
10
+ # Словари для хранения статистики
11
+ doc_stats = defaultdict(lambda: {'tables': 0, 'chars': 0})
12
+ total_tables = 0
13
+ total_chars = 0
14
+
15
+ # Получаем все JSON файлы в папке
16
+ json_files = list(Path(folder_path).glob('*.json'))
17
+
18
+ if not json_files:
19
+ print(f"Не найдено JSON файлов в папке: {folder_path}")
20
+ return
21
+
22
+ # Обрабатываем каждый файл
23
+ for json_file in json_files:
24
+ try:
25
+ with open(json_file, 'r', encoding='utf-8') as f:
26
+ data = json.load(f)
27
+
28
+ # Получаем имя документа
29
+ doc_name = data.get('document', json_file.stem)
30
+
31
+ # Подсчитываем таблицы
32
+ if 'sheets' in data and isinstance(data['sheets'], list):
33
+ num_tables = len(data['sheets'])
34
+
35
+ # Подсчитываем символы (примерный размер JSON)
36
+ json_str = json.dumps(data, ensure_ascii=False)
37
+ num_chars = len(json_str)
38
+
39
+ # Обновляем статистику
40
+ doc_stats[doc_name]['tables'] += num_tables
41
+ doc_stats[doc_name]['chars'] += num_chars
42
+ total_tables += num_tables
43
+ total_chars += num_chars
44
+
45
+ except Exception as e:
46
+ print(f"Ошибка при обработке файла {json_file.name}: {e}")
47
+
48
+ # Выводим результаты
49
+ print(f"\nВсего таблиц добавлено: {total_tables}")
50
+ print(f"Общий размер: {total_chars:,} символов".replace(',', ' '))
51
+
52
+ if total_tables > 0:
53
+ avg_size = total_chars // total_tables
54
+ print(f"Средний размер таблицы: {avg_size:,} символов".replace(',', ' '))
55
+
56
+ print("\nПо документам:")
57
+ # Сортируем по имени документа
58
+ for doc_name in sorted(doc_stats.keys()):
59
+ stats = doc_stats[doc_name]
60
+ print(f"• {doc_name}: {stats['tables']} таблиц, {stats['chars']:,} символов".replace(',', ' '))
61
+
62
+ # Использование
63
+ if __name__ == "__main__":
64
+ # Укажите путь к папке с JSON файлами
65
+ folder_path = "Табличные данные_JSON" # Текущая папка, или укажите свой путь
66
+
67
+ # Можно также запросить путь у пользователя
68
+ # folder_path = input("Введите путь к папке с JSON файлами: ")
69
+
70
+ analyze_json_tables(folder_path)
table_prep.py CHANGED
@@ -30,7 +30,7 @@ def create_table_content(table_data):
30
  return content
31
 
32
  def table_to_document(table_data, document_id=None):
33
- """Convert table data to a single Document"""
34
  if not isinstance(table_data, dict):
35
  return []
36
 
@@ -47,6 +47,7 @@ def table_to_document(table_data, document_id=None):
47
  log_message(f"✓ ДОБАВЛЕНА: Таблица {table_num} из документа '{doc_id}' | "
48
  f"Размер: {content_size} символов | Строк: {row_count}")
49
 
 
50
  return [Document(
51
  text=content,
52
  metadata={
@@ -56,8 +57,11 @@ def table_to_document(table_data, document_id=None):
56
  "document_id": doc_id,
57
  "section": section,
58
  "section_id": section,
 
59
  "total_rows": row_count,
60
- "content_size": content_size
 
 
61
  }
62
  )]
63
 
 
30
  return content
31
 
32
  def table_to_document(table_data, document_id=None):
33
+ """Convert table data to a single Document with rich metadata"""
34
  if not isinstance(table_data, dict):
35
  return []
36
 
 
47
  log_message(f"✓ ДОБАВЛЕНА: Таблица {table_num} из документа '{doc_id}' | "
48
  f"Размер: {content_size} символов | Строк: {row_count}")
49
 
50
+ # Store all table metadata including headers for preservation during chunking
51
  return [Document(
52
  text=content,
53
  metadata={
 
57
  "document_id": doc_id,
58
  "section": section,
59
  "section_id": section,
60
+ "section_path": section, # Add for consistency with text chunks
61
  "total_rows": row_count,
62
+ "content_size": content_size,
63
+ "headers": table_data.get('headers', []), # Preserve headers
64
+ "original_table_data": True # Mark as original table
65
  }
66
  )]
67