Ruurd's picture
Deploy OpenAI classification and protected terminology streaming
c2b7494 verified
Raw History Blame Contribute Delete
4.02 kB
"""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)."
)