Эмбеддинг длинного текста усредняет смыслы — конкретный вопрос не найдёт нужный абзац. Решение: нарезать документ на чанки и индексировать каждый отдельно.
Базовая нарезка с перекрытием
def chunk_text(text: str, chunk_size: int = 500, overlap: int = 50) -> list[str]:
"""Нарезает текст на чанки с перекрытием."""
chunks = []
step = chunk_size - overlap # шаг меньше размера → перекрытие
start = 0
while start < len(text):
end = start + chunk_size
chunks.append(text[start:end])
start += step
return chunks
Пример:
- chunk_size=500, overlap=50 → step=450
- Чанк 1: символы 0–500
- Чанк 2: символы 450–950
- Чанк 3: символы 900–1400
Перекрытие гарантирует что смысл на границе чанка не теряется.
Почему нельзя один большой чанк
# ❌ Плохо: весь документ как один чанк
collection.add(documents=[full_text], ids=["doc"])
# Эмбеддинг = среднее всех смыслов → поиск неточный
# ✅ Хорошо: нарезать на куски
chunks = chunk_text(full_text)
collection.add(
documents=chunks,
ids=[f"doc_chunk_{i}" for i in range(len(chunks))]
)
Метаданные для трассировки
def chunk_document(text: str, source: str, chunk_size=500, overlap=50) -> list[dict]:
chunks = chunk_text(text, chunk_size, overlap)
return [
{
"text": chunk,
"id": f"{source}_chunk_{i}",
"metadata": {
"source": source,
"chunk_index": i,
"total_chunks": len(chunks)
}
}
for i, chunk in enumerate(chunks)
]
Загрузка папки с документами
from pathlib import Path
def load_documents(folder: str) -> list[dict]:
docs = []
for path in Path(folder).iterdir():
if path.suffix in (".txt", ".md"):
text = path.read_text(encoding="utf-8")
docs.extend(chunk_document(text, source=path.name))
return docs
Параметры нарезки
| chunk_size | overlap | Когда использовать |
|---|---|---|
| 200–300 | 20–30 | Точный поиск по коротким фактам |
| 500–800 | 50–100 | Баланс (большинство случаев) |
| 1000–2000 | 100–200 | Длинные рассуждения, контекст |
Слишком маленький чанк теряет контекст. Слишком большой — размывает смысл.
💬 Комментарии (0)
Комментариев пока нет
Станьте первым, кто поделится мнением об этой статье!