У каждого из нас есть та самая папка — забитая выписками, рецептами, снимками МРТ и заключениями. Когда заболеваешь, перебирать эту кипу бумаг — то ещё приключение. А если понадобилось срочно найти анализы двухлетней давности? Чистый ад.
Проблема: данные рассыпаны по PDF, фотографиям и текстовым файлам. Врачи на приёме тратят 10 минут, чтобы понять историю. А ведь достаточно одной SQLite-базы, одного парсера и одного AI-ассистента.
Зачем SQLite? Почему не PostgreSQL и не облако?
SQLite — это не игрушка. Это встраиваемая база, которая работает без сервера, без интернета, а файл целиком помещается на флешке. Для медицинских данных это бомба: приватность, скорость, никакой облачной слежки. К тому же мы живём в 2026 году, когда локальные AI-модели уже не экзотика, а норма. Делаем архив, который никуда не утечёт.
Что нам понадобится?
- Python 3.12+ — язык, на котором соберём пайплайн
- Claude Code — тот самый инструмент, который не только пишет код, но и помогает структурировать данные (читайте гайд по настройке Obsidian + Claude)
- pdfplumber — лучшая библиотека для вытягивания текста из PDF (легче, чем PyMuPDF, точнее, чем pdfminer)
- SQLite3 — встроен в Python, ничего качать не надо
- Claude API (или локально через Ollama) — для извлечения структурированных полей из неструктурированного текста
Архитектура в одну схему
Не усложняем. Всего 4 таблицы: patients, documents, records и tags.
patients — имя, дата рождения, пол, контакты. documents — оригинальный путь к файлу, дата документа, тип (выписка, рецепт, снимок).
records — извлечённые сущности: диагнозы, лекарства, анализы — всё с привязкой к документу и дате.
tags — метки для быстрой фильтрации (срочное, аллергия, хроническое).
Как НЕ надо делать: не пытайтесь хранить весь текст PDF в одной колонке TEXT. Во-первых, поиск станет тормозным, во-вторых, Claude не сможет нормально разобрать километровые строки. Лучше сразу резать на страницы или секции.
Шаг 1: Сканируем документы (pdfplumber)
import pdfplumber
import pandas as pd
def extract_text_pdf(filepath):
with pdfplumber.open(filepath) as pdf:
pages = []
for i, page in enumerate(pdf.pages):
text = page.extract_text()
if text:
pages.append({'page': i+1, 'text': text})
return pages
# Пример использования
text_pages = extract_text_pdf('выписка_01.pdf')
print(text_pages[0]['text'][:500])
pdfplumber работает на удивление чисто: он понимает переносы строк, таблицы и даже колонки. Но есть нюанс: если PDF — это отсканированные картинки, без OCR не обойтись. В этом случае советую глянуть статью про медицинский почерк на Qwen2.5-VL — там описано, как вытаскивать текст даже из фоток с MacBook с 8 ГБ памяти.
Шаг 2: Извлекаем ключевые сущности с помощью Claude
Вот где AI превращает свалку текста в структурированные данные. Мы будем использовать Claude API с системным промптом, который требует возвращать JSON с полями: diagnosis, medications, measures (лабораторные показатели), date.
import anthropic
import json
client = anthropic.Anthropic(api_key='your-key') # используйте переменные окружения
def extract_medical_entities(text):
prompt = f"""Извлеки из текста медицинские сущности.
Твой ответ — строгий JSON без дополнительных пояснений:
{{
"diagnosis": [{{"name": "...", "icd_code": "..."}}],
"medications": [{{"name": "...", "dosage": "..."}}],
"measures": [{{"parameter": "...", "value": "...", "unit": "..."}}],
"date": "YYYY-MM-DD"
}}
Текст:
{text[:6000]} # ограничение на токены
"""
response = client.messages.create(
model="claude-sonnet-4-20260514", # актуальная модель на июнь 2026
max_tokens=2000,
messages=[{"role": "user", "content": prompt}]
)
try:
return json.loads(response.content[0].text)
except:
return {}
Шаг 3: Собираем всё в SQLite
import sqlite3
from datetime import datetime
def create_schema():
conn = sqlite3.connect('medical_archive.db')
c = conn.cursor()
c.executescript('''
CREATE TABLE IF NOT EXISTS patients (
id INTEGER PRIMARY KEY AUTOINCREMENT,
full_name TEXT NOT NULL,
birth_date TEXT,
gender TEXT,
phone TEXT
);
CREATE TABLE IF NOT EXISTS documents (
id INTEGER PRIMARY KEY AUTOINCREMENT,
patient_id INTEGER,
file_path TEXT UNIQUE,
doc_type TEXT,
doc_date TEXT,
FOREIGN KEY (patient_id) REFERENCES patients(id)
);
CREATE TABLE IF NOT EXISTS records (
id INTEGER PRIMARY KEY AUTOINCREMENT,
document_id INTEGER,
entity_type TEXT CHECK(entity_type IN ('diagnosis','medication','measure','procedure')),
entity_value TEXT,
entity_details TEXT -- JSON для доп. полей
);
CREATE TABLE IF NOT EXISTS tags (
id INTEGER PRIMARY KEY AUTOINCREMENT,
record_id INTEGER,
tag TEXT
);
''')
conn.commit()
return conn
# Вставка примера
conn = create_schema()
c = conn.cursor()
c.execute('INSERT OR IGNORE INTO patients (full_name, birth_date) VALUES (?, ?)', ('Иван Петров', '1990-05-10'))
c.execute('INSERT INTO documents (patient_id, file_path, doc_type, doc_date) VALUES (?, ?, ?, ?)',
(1, '/путь/к/файлу.pdf', 'выписка', '2026-03-01'))
conn.commit()
Шаг 4: Обучаем Claude Code автоматически пополнять базу
Теперь самый сок. Мы можем написать Python-скрипт, который сканирует папку с новыми PDF, парсит их, отдаёт Claude, а он заливает результат в SQLite. Но можно пойти дальше — связать это с Model Context Protocol (MCP). В статье про MCP и BI-дашборд показано, как Claude Code через MCP напрямую читает и пишет в SQLite. То же самое можно завернуть в локальный HTTP-сервер — и вуаля, AI сам мониторит новые файлы.
# Пример: скрипт, который Claude Code может вызвать как инструмент MCP
def add_new_document(filepath):
pages = extract_text_pdf(filepath)
for page in pages:
entities = extract_medical_entities(page['text'])
if entities:
# вставка в БД
insert_entities(entities, filepath, page['page'])
return {"status": "ok", "pages_processed": len(pages)}
Шаг 5: Поиск и аналитика
Раз уж данные структурированы, можно делать запросы, от которых у врача глаза на лоб полезут. “Показать все назначения амоксициллина за последний год”, “Динамика СОЭ”, “Список хронических диагнозов с датами”.
-- Все диагнозы пациента
SELECT p.full_name, r.entity_value, d.doc_date
FROM records r
JOIN documents d ON r.document_id = d.id
JOIN patients p ON d.patient_id = p.id
WHERE r.entity_type = 'diagnosis' AND p.id = 1
ORDER BY d.doc_date DESC;
-- Последние 5 лабораторных показателей по параметру
SELECT r.entity_details, d.doc_date
FROM records r
JOIN documents d ON r.document_id = d.id
WHERE r.entity_type = 'measure' AND r.entity_value LIKE '%СОЭ%'
ORDER BY d.doc_date DESC LIMIT 5;
Типичная ошибка: не проверять дубликаты. Claude может вернуть одинаковые сущности из разных страниц. Добавьте уникальный индекс на (document_id, entity_type, entity_value) или используйте временную таблицу перед вставкой.
Как это ускорить — автоматизация через Claude Code
Вместо ручного запуска скриптов можно настроить Claude Code как демона, который мониторит папку. Для этого отлично подходит Anthropic Cowork — инструмент, позволяющий Claude работать с вашими файлами на Mac без единого скрипта (читайте гайд по Anthropic Cowork). Вы просто кладёте PDF в папку, а Cowork запускает цепочку действий: парсинг -> извлечение -> запись в SQLite.
Про безопасность и приватность
Данные хранятся локально. Никакого облака. Но если вы используете Claude API, запросы идут через интернет. Чтобы не светить паспортные данные, удаляйте явно персональную информацию (ФИО, адреса) из текста перед отправкой — или запускайте маленькую LLM локально через Ollama. И кстати, подход с графовой когнитивной памятью на SQLite, описанный в статье про графовую когнитивную память, отлично накладывается на медицинские данные — вы можете строить связи между диагнозами и лекарствами.
Возможные грабли и их решение
- Claude галлюцинирует даты. Не указывайте в промпте "сегодня", лучше захардкодьте текущую дату из переменной окружения.
- Таблица разрастается. Сделайте архивацию записей старше 10 лет в отдельный файл.
- PDF с вложенными изображениями. pdfplumber их не читает. Придётся сначала конвертировать страницы в PNG и гонять через OCR (та же Qwen2.5-VL).
- Кодировка текста. Иногда pdfplumber выдаёт кракозябры. Помогает параметр
laparamsв настройках детекции.
Закончу неочевидным советом: не пытайтесь объять необъятное. Не парсите все документы сразу. Заведите “пилотную” папку с 10 файлами, прогоните пайплайн, проверьте, какие сущности Claude выделяет уверенно, а какие — мимо. Итеративное улучшение промпта сэкономит дни отладки.
И главное: этот архив — не замена врачу. Но он даёт вам суперсилу: любую информацию за 3 секунды. А когда AI умеет структурировать хаос, жить становится чуточку легче.