Модуль md.2 · Урок 2
Индексация медицинских PDF: FAISS и embeddings
Чему вы научитесь
- Разбивать медицинские PDF на осмысленные фрагменты (чанки)
- Превращать фрагменты в эмбеддинги и складывать их в FAISS
- Делать семантический поиск похожих случаев
- Обезличивать данные до индексации — это обязательный шаг
Зачем индексировать, а не «скармливать» PDF целиком
Модель не может держать в контексте всю медицинскую библиотеку, и заземление работает лучше на коротких релевантных фрагментах. Поэтому документы заранее разбивают на чанки, превращают в числовые векторы (эмбеддинги) и складывают в индекс для быстрого семантического поиска. На запрос агент достаёт несколько ближайших по смыслу фрагментов.
flowchart LR
PDF["PDF-кейсы"] --> CL["Обезличивание"]
CL --> CH["Чанкинг\n(фрагменты)"]
CH --> EM["Эмбеддинги"]
EM --> FA["FAISS-индекс"]
Q["Вопрос"] --> EM2["Эмбеддинг вопроса"]
EM2 --> FA
FA --> R["Top-k фрагментов"]
style CL fill:#fee2e2,stroke:#DC2626
style FA fill:#eef2ff,stroke:#4400FF
style R fill:#ecfdf5,stroke:#059669
Обезличивание идёт первым
Это не последний шаг и не «когда-нибудь потом». До любой индексации из текста убираются идентифицирующие данные: имена, даты рождения, номера полисов, адреса, телефоны.
Чанкинг: размер имеет значение
Слишком крупные чанки размывают смысл, слишком мелкие теряют контекст. Для медицинских текстов обычно берут фрагменты по несколько предложений с небольшим перекрытием, чтобы не разрезать мысль.
| Параметр | Типичное значение | Зачем |
|---|---|---|
| Размер чанка | 500–1000 символов | Баланс смысла и точности поиска |
| Перекрытие | 10–20% | Чтобы не терять контекст на границе |
| Метаданные | источник, страница | Для цитат в отчёте |
Метаданные важны не меньше текста: без указания источника и страницы цитата в отчёте будет непроверяемой.
Сборка индекса на Python
Учебный пример: разбиваем, считаем эмбеддинги, кладём в FAISS. Конкретная модель эмбеддингов и LLM выбирается под доступность; в открытом MedicalAgentX в демо использовались эмбеддинги OpenAI.
import faiss
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("intfloat/multilingual-e5-base") # локально, без отправки данных
chunks = chunk_documents(deidentified_pdfs, size=800, overlap=120)
texts = [c.text for c in chunks]
vectors = model.encode(texts, normalize_embeddings=True)
index = faiss.IndexFlatIP(vectors.shape[1]) # косинусная близость
index.add(vectors)
def search(question: str, k: int = 5):
qv = model.encode([question], normalize_embeddings=True)
scores, ids = index.search(qv, k)
return [chunks[i] for i in ids[0]] # фрагменты с метаданными для цитат
Проверка качества поиска
Соберите тестовые вопросы. Несколько вопросов, на которые вы точно знаете ответ из проиндексированных документов.
Прогоните поиск. Убедитесь, что в top-k попадают именно релевантные фрагменты.
Проверьте метаданные. У каждого фрагмента должны быть источник и страница для цитирования.
Что дальше
База готова. Следующий урок — как подключить реальный медицинский тул-сервер через MCP: md.2/03.