File size: 4,019 Bytes
c2b7494
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
"""Reusable prompt templates for LLM-assisted ranking and classification."""

from __future__ import annotations

from src.data.loaders import EventDefinitionForm

RANK_SYSTEM_PROMPT = (
    "You are a clinical terminology expert supporting semantic harmonization for "
    "real-world evidence studies. You review candidate medical codes retrieved for "
    "a phenotype and re-rank them by clinical relevance, using the structured "
    "phenotype definition (Event Definition Form) provided by the user. "
    "You must respond only with valid JSON, no additional commentary."
)

CLASSIFY_SYSTEM_PROMPT = (
    "You are a clinical terminology expert supporting semantic harmonization for "
    "real-world evidence studies. You review candidate medical codes for a phenotype "
    "and classify each one as 'Narrow' (highly specific to the phenotype), "
    "'Possible' (related but insufficiently specific, or specific in some but not "
    "all contexts), or 'Exclude' (not relevant, or explicitly an exclusion "
    "criterion). Base your decision on the structured phenotype definition (Event "
    "Definition Form) provided by the user. For every code, provide a brief "
    "explanation followed by the label and a confidence score between 0 and 1. "
    "You must respond only with valid JSON, no additional commentary."
)


def classify_system_prompt(use_possible_category: bool = False) -> str:
    """Return classification instructions for binary or three-way labeling."""
    if use_possible_category:
        return CLASSIFY_SYSTEM_PROMPT
    return (
        "You are a clinical terminology expert supporting semantic harmonization "
        "for real-world evidence studies. Classify every candidate as 'Narrow' "
        "when it is sufficiently specific to the phenotype, or 'Exclude' otherwise. "
        "Do not return a 'Possible' label. Any EDF rule describing a Possible, "
        "ambiguous, or insufficiently specific case must be treated as Exclude in "
        "this binary mode. Base decisions on the supplied Event Definition Form. "
        "For every code, request the output fields in this order: code, vocabulary, "
        "explanation, label, confidence. Confidence must be from 0 to 1. Respond "
        "only with valid JSON."
    )


def build_rank_prompt(edf: EventDefinitionForm, candidates: list[dict]) -> str:
    """Build the user prompt for LLM-based re-ranking of candidate codes."""
    candidate_lines = "\n".join(
        f"- code: {c['code']} | vocabulary: {c['vocabulary']} | description: {c['description']}"
        for c in candidates
    )
    return (
        f"Phenotype definition:\n{edf.to_prompt_context()}\n\n"
        f"Candidate codes to rank by clinical relevance to this phenotype "
        f"(most relevant first):\n{candidate_lines}\n\n"
        "Return a JSON object with a single key 'ranked_codes', containing a list of "
        "objects with keys 'code', 'vocabulary', and 'relevance_score' (float 0-1, "
        "higher = more relevant), sorted descending by relevance_score."
    )


def build_classify_prompt(
    edf: EventDefinitionForm,
    candidates: list[dict],
    use_possible_category: bool = False,
) -> str:
    """Build the user prompt for LLM-based classification of candidate codes."""
    candidate_lines = "\n".join(
        f"- code: {c['code']} | vocabulary: {c['vocabulary']} | description: {c['description']}"
        for c in candidates
    )
    labels = "'Narrow', 'Possible', or 'Exclude'" if use_possible_category else "'Narrow' or 'Exclude'"
    return (
        f"Phenotype definition:\n{edf.to_prompt_context(include_possible=use_possible_category)}\n\n"
        f"Classify each of the following candidate codes:\n{candidate_lines}\n\n"
        "Return a JSON object with a single key 'classifications', containing a "
        "list of objects using this requested key order: 'code', 'vocabulary', "
        "'explanation' (brief string), 'label' "
        f"(one of {labels}), and 'confidence' (float 0-1)."
    )