의학 백과사전 2.0: 추측을 멈추고 멀티모달 RAG로 스캔 시작

발행: (2026년 2월 12일 오전 10:15 GMT+9)
9 분 소요
원문: Dev.to

Source: Dev.to

위에 제공된 소스 링크 외에 번역할 텍스트가 보이지 않습니다. 번역을 원하는 본문을 알려주시면 한국어로 번역해 드리겠습니다.

아키텍처 개요

The logic flow:

  1. 사용자가 약 라벨 사진을 업로드합니다.
  2. PaddleOCR이 텍스트를 추출합니다.
  3. Entity extraction이 약물 이름을 복용량 정보와 구분합니다.
  4. RxNav API가 약물 간 상호작용을 확인합니다.
  5. ChromaDB가 지역 가이드라인 및 개인 건강 컨텍스트를 검색합니다.
  6. LLM reasoning engine이 인간이 읽을 수 있는 안전 보고서를 종합합니다.
  7. RAGas가 답변의 충실도와 관련성을 평가합니다.
graph TD
    A[User Uploads Photo] --> B[PaddleOCR: Text Extraction]
    B --> C{Entity Extraction}
    C -->|Drug Names| D[RxNav API: Interaction Check]
    C -->|Dosage Info| E[ChromaDB: Manuals/Guidelines]
    D --> F[LLM Reasoning Engine]
    E --> F
    F --> G[Final Response: Safety Advice]
    G --> H[Evaluation: RAGas]

사전 요구 사항

구성 요소필요 이유
PaddleOCR의약품 포장에 기울어진 텍스트와 다양한 글꼴을 처리하는 초고속, 정확한 OCR.
ChromaDB로컬 약물 매뉴얼, 병원 지침 또는 개인 건강 기록을 위한 경량 벡터 스토어.
RxNav API약물 상호작용 데이터에 대한 표준 소스 (국립 의학 도서관).
RAGasRAG 파이프라인이 환각을 일으키는지 평가하는 툴킷.

Python 3.9+와 다음 패키지가 설치되어 있는지 확인하세요:

pip install paddleocr chromadb ragas datasets requests

Step 1 – PaddleOCR을 사용한 성분 추출

from paddleocr import PaddleOCR

# Initialise the OCR engine (angle classification enabled for rotated text)
ocr = PaddleOCR(use_angle_cls=True, lang='en')

def get_drug_names(img_path: str) -> str:
    """
    Perform OCR on the image and return a single string with all detected text.
    """
    result = ocr.ocr(img_path, cls=True)
    # Flatten the nested list and keep only the recognized text fragments
    raw_text = [line[1][0] for page in result for line in page]
    print(f"Detected Text: {raw_text}")
    return " ".join(raw_text)

# Example usage
# extracted_text = get_drug_names("advil_box.jpg")

출력 예시:

Detected Text: ['Advil', 'Ibuprofen', '200', 'mg', 'Take', '1', 'tablet', 'every', '4-6', 'hours']

Step 2 – RxNav API를 사용한 상호작용 조회

먼저 추출된 약물 이름을 RxNorm Concept Unique Identifiers (RXCUIs) 로 매핑합니다(이를 위해 RxNav “approximateTerm” 엔드포인트를 사용할 수 있습니다). 그런 다음 상호작용 데이터를 요청합니다:

import requests
from typing import List

def check_interactions(rxcuis: List[str]) -> List[str]:
    """
    Query RxNav for interactions among the supplied RxCUIs.
    Returns a list of interaction descriptions.
    """
    ids = "+".join(rxcuis)
    url = f"https://rxnav.nlm.nih.gov/REST/interaction/list.json?rxcuis={ids}"
    response = requests.get(url).json()

    interactions = []
    if "fullInteractionTypeGroup" in response:
        for group in response["fullInteractionTypeGroup"]:
            for item in group["fullInteractionType"]:
                # Each interactionPair may contain multiple descriptions; we take the first.
                interactions.append(item["interactionPair"][0]["description"])
    return interactions

# Example:
# rxcuis = ["5640"]  # RxCUI for Ibuprofen
# interactions = check_interactions(rxcuis)

Step 3 – Augmenting with Local Context (ChromaDB)

공개 API는 기관별 지침이나 개인 건강 이력과 같은 세부 사항을 놓칠 수 있습니다. 이러한 뉘앙스를 벡터 스토어에 저장하고 쿼리 시 가장 관련성이 높은 스니펫을 검색하세요.

import chromadb
from chromadb.utils import embedding_functions
from typing import List

# Initialise Chroma client (in‑memory for the demo)
client = chromadb.Client()
collection = client.create_collection(name="medical_guidelines")

# Add a few example documents
collection.add(
    documents=[
        "Patient A has a history of stomach ulcers. Avoid NSAIDs like Ibuprofen.",
        "Guideline: Do not combine antihistamines with MAO inhibitors."
    ],
    metadatas=[
        {"source": "electronic_health_record"},
        {"source": "hospital_policy"}
    ],
    ids=["rec1", "rec2"]
)

def get_local_context(query: str, n_results: int = 1) -> List[str]:
    """
    Retrieve the most relevant local documents for the given query.
    """
    results = collection.query(query_texts=[query], n_results=n_results)
    return results['documents'][0]  # Returns a list of strings

# Example:
# context = get_local_context("Ibuprofen ulcer")

AI 에이전트를 구축하는 “공식적인” 방법

이 튜토리얼은 Learning‑in‑Public 프로젝트를 시작하기에 좋은 출발점이지만, 실제 수준의 AI 헬스케어 도구를 만들려면 다음이 필요합니다:

  • 정교한 프롬프트 엔지니어링 및 체인‑오브‑쓰레드(Chain‑of‑Thought) 추론.
  • 엄격한 데이터 프라이버시 보호( HIPAA, GDPR).
  • 모니터링, 로깅 및 모델‑버전 관리.

Agentic RAG, Production‑ready Multimodal Pipelines, 그리고 컴플라이언스 모범 사례에 대한 심층적인 내용은 **WellAlly Tech Blog**의 기사들을 참고하세요.

Step 4 – LLM을 사용한 안전 보고서 생성

OCR 출력, 상호작용 데이터 및 로컬 컨텍스트를 결합하여 간결하고 사용자 친화적인 메시지를 만듭니다.

def generate_safety_report(ocr_text: str,
                          interactions: List[str],
                          context: List[str]) -> str:
    """
    Build a prompt for the LLM and return the generated safety report.
    """
    prompt = f"""
    User scanned a medicine label: "{ocr_text}"
    Known clinical interactions: {interactions}
    Personal health context: {context}

    Provide a short, plain‑language report that tells the user whether the medication is safe
    to take, or if a warning is needed. Use the format:
    "SAFE: ..." or "WARNING: ..."
    """
    # Replace the following line with your LLM call (e.g., OpenAI, Anthropic, etc.)
    # response = llm.complete(prompt)
    # For demo purposes we return a hard‑coded warning:
    return "WARNING: You are taking Advil (Ibuprofen) while having a history of stomach ulcers. Consult a doctor before use."

# Example:
# report = generate_safety_report(extracted_text, interactions, context)
# print(report)

Source:

Step 5 – Evaluating with RAGas

RAGas는 faithfulness(답변이 원본을 정확히 따르고 있는가?)와 answer relevance(답변이 사용자의 질문에 부합하는가?)를 측정하는 데 도움을 줍니다.

from ragas import evaluate
from datasets import Dataset

# Assume `generated_report` is the string returned by `generate_safety_report`
generated_report = generate_safety_report(extracted_text, interactions, context)

# Build a tiny evaluation dataset
data_samples = {
    "question": ["Can I take Advil with my current meds?"],
    "answer": [generated_report],
    "contexts": [[f"{interactions} {context}"]],
    "ground_truth": ["WARNING: Ibuprofen conflicts with ulcer history. Consult a physician."]
}

eval_dataset = Dataset.from_dict(data_samples)

# Run RAGas evaluation (you may need to configure the LLM and embedding models)
metrics = evaluate(eval_dataset, metrics=["faithfulness", "answer_relevance"])
print(metrics)

결과 점수는 파이프라인이 환각을 일으키고 있는지, 아니면 검색된 증거에 기반하고 있는지를 알려줍니다.

🎉 당신은 다음과 같은 멀티모달 RAG 시스템을 구축했습니다:

  • 사진에서 약물 라벨을 읽음.
  • OCR을 통해 약물 이름을 추출.
  • RxNav를 사용해 상호작용을 조회.
  • ChromaDB에 저장된 개인 맞춤형 로컬 컨텍스트로 답변을 보강.
  • LLM으로 간결한 안전 보고서를 생성.
  • RAGas를 이용해 출력물을 검증.

다음과 같이 시스템을 확장해 보세요:

  • 배치 처리 – 한 번에 여러 알약을 처리.
  • 음성 비서 통합 (예: Alexa, Google Assistant).
  • 개인 건강 기록의 안전한 저장 (암호화, 디바이스 내 저장).

행복한 해킹 되시고, 안전을 지키세요! 🚀

taset = Dataset.from_dict(data_samples)
# score = evaluate(dataset, metrics=[faithfulness, answer_relevance])
# print(score)

결론: 헬스‑테크의 미래

PaddleOCR을 비전으로, RxNav를 의료 진실로, ChromaDB를 개인화된 컨텍스트로 결합하여, 우리는 실제로 생명을 구하는 강력한 도구를 만들었습니다. 멀티모달 RAG는 빠르게 발전하고 있으며, 이것은 빙산의 일각에 불과합니다!

다음은?

  • CNN을 사용하여 pill identification 기능을 추가해 보세요.
  • 사용자가 손을 사용하지 않고 질문할 수 있도록 voice‑to‑text를 통합하세요.

이 빌드가 마음에 들었다면 아래에 댓글을 남기거나 🦄 heart 이 게시물을 눌러 주세요! 그리고 더 높은 수준의 AI 튜토리얼을 보려면 WellAlly Tech 를 방문하는 것을 잊지 마세요.

코딩 즐겁게!

0 조회
Back to Blog

관련 글

더 보기 »