Перейти к содержанию

MedicalRAG — руководство разработчика

Сервис app/services/rag_engine.py реализует класс MedicalRAG: индекс и поиск медицинской базы знаний для AI Medical Assistant.

Репозиторий: https://github.com/valera7623/AI-medical-assistant.

Включение

from app.services.rag_engine import is_rag_enabled, rag_mode, get_medical_rag

is_rag_enabled()  # RAG_ENABLED and MEDICAL_ASSISTANT_ENABLED
rag_mode()        # local | external | hybrid (invalid → local)

Глобальный singleton: get_medical_rag() (потокобезопасная ленивая инициализация).

Режимы

RAG_MODE Chroma External POST …/search Примечания
local да нет memory fallback, если Chroma недоступна
external пропускается да soft-fallback на local при пустом результате
hybrid да да merge_hits с RAG_HYBRID_LOCAL_WEIGHT

Переменные:

Env Назначение
RAG_COLLECTION_NAME Имя коллекции
RAG_EXTERNAL_URL База внешнего API (…/v1)
RAG_EXTERNAL_API_KEY Bearer (опционально)
RAG_EXTERNAL_TIMEOUT_SECONDS Таймаут HTTP
RAG_HYBRID_LOCAL_WEIGHT Вес local в hybrid (0–1)
CHROMA_PERSIST_DIR Путь персистентности Chroma

Фильтр языка

Только две дорожки: en и ru.

from app.services.rag_engine import normalize_rag_language, VALID_RAG_LANGUAGES

normalize_rag_language("ru-RU")  # → "ru"
normalize_rag_language("en_US")  # → "en"
normalize_rag_language(None)     # → DEFAULT_LANGUAGE нормализованный к en|ru

Поиск отбрасывает хиты, чей language не совпадает с запрошенным. UI и seed должны проставлять language явно.

Документ в индексе

Ожидаемая структура после нормализации:

{
  "id": "guidelines:acs-en",
  "title": "ACS — initial steps",
  "category": "guidelines",
  "language": "en",
  "text": "...",
  "metadata": {
    "title": "...",
    "category": "guidelines",
    "language": "en",
    "source_file": "guidelines.json"
  }
}

Методы:

  • upsert_documents(docs) → int — запись в local (и вызов из admin ingest дополнительно шлёт во внешний /ingest);
  • search(query, *, language, limit=5) → list[hit] — нормализованные хиты с similarity, source.

Форма hit после _normalize_hit:

{
  "text": "...",
  "title": "...",
  "category": "...",
  "language": "en",
  "similarity": 0.82,
  "source": "local",  # local | external | hybrid
  "id": "..."
}

Слияние hybrid

merge_hits(local, external, limit=5, local_weight=0.5):

  1. Масштабирует similarity весами local / external.
  2. Дедуплицирует по id или хэшу текста.
  3. Сортирует по убыванию similarity.

Unit-тесты: tests/test_rag_engine.py.

Seed

Скрипт scripts/seed_medical_knowledge.py:

# В контейнере aima-app
docker compose exec app python scripts/seed_medical_knowledge.py --also-db

docker compose exec app python scripts/seed_medical_knowledge.py \
  --from-url http://mock_rag:8090/v1/documents.json \
  --api-key "$RAG_EXTERNAL_API_KEY" \
  --skip-local-files

Источники:

  • app/data/medical_knowledge/guidelines.json
  • app/data/medical_knowledge/diseases.json
  • app/data/medical_knowledge/medications.json
  • опционально JSON dump с --from-url

Mock external API

scripts/mock_rag_server.py и Compose profile mock-rag (контейнер aima-mock-rag):

docker compose --profile mock-rag up -d mock_rag

Эндпоинты: /health, POST /v1/search, POST /v1/ingest, GET /v1/documents.json.

Для app в той же сети: RAG_EXTERNAL_URL=http://mock_rag:8090/v1.

Интеграция с репетитором

app/services/ai_tutor.py вызывает MedicalRAG при ответе; маршруты:

Путь Назначение
POST /api/tutor/ask Ответ + sources
POST /api/tutor/knowledge/ingest Admin upsert
GET /api/tutor/knowledge/status Конфиг без секретов

Флаг MEDICAL_ASSISTANT_ENABLED гейтит все learning API (/api/tutor, /api/cases, /api/exam, /api/progress).

Локальный стенд

Сервис Порт / имя
App http://localhost:8001 (APP_PORT)
Redis host 6380
Контейнеры aima-*
Mock RAG 8090 (profile mock-rag)

Расширение

  1. Добавляйте документы с явным language.
  2. Для нового внешнего KB соблюдайте контракт /search и опционально /ingest.
  3. Не коммитьте секреты (RAG_EXTERNAL_API_KEY) — только .env.example.
  4. После смены RAG_MODE перезапустите app-контейнер и перезалейте seed при необходимости.

Связанные страницы