Skip to content

Commit af48dd7

Browse files
committed
added code for tool curation
1 parent 7292709 commit af48dd7

File tree

9 files changed

+686124
-0
lines changed

9 files changed

+686124
-0
lines changed

tool_curtaion/README.md

Whitespace-only changes.

tool_curtaion/dir1_pubmed_risk_calcs/__init__.py

Whitespace-only changes.

tool_curtaion/dir2_risk_calc_verifications/__init__.py

Whitespace-only changes.

tool_curtaion/file0_full_candidate_article_pmids.json

Lines changed: 339954 additions & 0 deletions
Large diffs are not rendered by default.

tool_curtaion/file0_sample_candidate_articles.json

Lines changed: 6002 additions & 0 deletions
Large diffs are not rendered by default.

tool_curtaion/file1_full_classification_results.jsonl

Lines changed: 339950 additions & 0 deletions
Large diffs are not rendered by default.

tool_curtaion/step1_classify.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
__author__ = "qiao"
2+
3+
"""
4+
Classify whether the candidate article introduces a new risk score / calculator.
5+
"""
6+
7+
import json
8+
import os
9+
10+
from openai import AzureOpenAI
11+
12+
client = AzureOpenAI(
13+
api_version="2023-09-01-preview",
14+
azure_endpoint=os.getenv("OPENAI_ENDPOINT"),
15+
api_key=os.getenv("OPENAI_API_KEY"),
16+
)
17+
18+
if __name__ == "__main__":
19+
# load cached results
20+
target_path = "file1_sample_classification_results.jsonl"
21+
done_pmids = []
22+
if os.path.exists(target_path):
23+
with open(target_path, "r") as f:
24+
for line in f.readlines():
25+
done_pmids.append(json.loads(line.strip())["pmid"])
26+
done_pmids = set(done_pmids)
27+
28+
# loop over all pmids
29+
cands = json.load(open("file0_sample_candidate_articles.json", "r"))
30+
for pmid, info in cands.items():
31+
if pmid in done_pmids:
32+
continue
33+
34+
prompt = "Here is a PubMed article:\n"
35+
prompt += info["t"] + "\n"
36+
prompt += info["a"] + "\n"
37+
prompt += "Does this article describe a new risk score or risk calculator? In healthcare, a risk score quantitatively estimates the probability of a clinical event or outcome, such as disease development or progression, within a specified period. These scores are derived from algorithms using variables like patient demographics, clinical history, laboratory results, and other relevant health indicators. They aid clinicians in decision-making, allowing for personalized patient care and resource allocation. Simply answer with \"yes\" or \"no\":"
38+
39+
response = client.chat.completions.create(
40+
model="gpt-35-turbo",
41+
messages=[{"role": "user", "content": prompt}],
42+
temperature=0,
43+
)
44+
45+
result = response.choices[0].message.content
46+
47+
with open(target_path, "a") as f:
48+
f.write(json.dumps({"pmid": pmid, "result": result}) + "\n")
49+
50+
done_pmids.add(pmid)

tool_curtaion/step2_draft_calcs.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
__author__ = "qiao"
2+
3+
"""
4+
Draft risk calculators
5+
"""
6+
7+
import json
8+
import os
9+
10+
from openai import AzureOpenAI
11+
12+
client = AzureOpenAI(
13+
api_version="2023-09-01-preview",
14+
azure_endpoint=os.getenv("OPENAI_ENDPOINT"),
15+
api_key=os.getenv("OPENAI_API_KEY"),
16+
)
17+
18+
19+
if __name__ == "__main__":
20+
system = "You are a helpful assistant programmer for medical calculators. Your task is to read a PubMed article about a medical calculator, and if applicable, write a two-step calculator: (1) calculator a risk score based on multiple criteria; (2) interpret different ranges of the computed risk score into probabilities of risks."
21+
22+
pmid2info = json.load(open("file0_sample_candidate_articles.json"))
23+
24+
cand_pmids = []
25+
with open("file1_full_classification_results.jsonl", "r") as f:
26+
for line in f.readlines():
27+
line = json.loads(line.strip())
28+
29+
if line["result"].lower() == "yes":
30+
cand_pmids.append(line["pmid"])
31+
32+
for pmid in cand_pmids:
33+
target_path = os.path.join("dir1_pubmed_risk_calcs", pmid)
34+
35+
if os.path.exists(target_path):
36+
continue
37+
38+
if pmid not in pmid2info:
39+
print(f"PMID {pmid} information not found. Please load the full file 0 candidate articles!")
40+
continue
41+
42+
prompt = "Here is a PubMed article:\n"
43+
prompt += pmid2info[pmid]["t"] + "\n"
44+
prompt += pmid2info[pmid]["a"] + "\n"
45+
46+
prompt += "Does the article describes a simple two-step risk calculator, where the first step is to compute a risk score, and the second step is to interpret different risk scores? If no, please directly and only output \"NO\". Otherwise, please standardize the calculator into:\n"
47+
48+
prompt += "#Title\nThe name of the calculator(s).\n"
49+
50+
prompt += "##Purpose\nDescribe when this calculator should be used.\n"
51+
52+
prompt += "##Specialty\nshould be a list of calculator types, one or more of (Allergy and Immunology, Anesthesiology, Cardiology, Dermatology, Emergency Medicine, Endocrinology, Family Medicine, Gastroenterology, Geriatrics, Hematology, Infectious Disease, Internal Medicine, Nephrology, Neurology, Obstetrics and Gynecology, Oncology, Ophthalmology, Orthopedic Surgery, Otolaryngology, Pathology, Pediatrics, Physical Medicine and Rehabilitation, Plastic Surgery, Psychiatry, Pulmonology, Radiology, Rheumatology, Surgery, Urology), seperated by \",\".\n"
53+
54+
prompt += "##Eligibility\nDescribe what patients are eligible.\n"
55+
56+
prompt += "##Size\nThe exact number of patients used to derive this calculator. Only put a number here without any other texts.\n"
57+
58+
prompt += "##Computation\nDetailed instructions of how to use the calculator, including Python functions with clear docstring documentation. Please be self-contained and detailed. For example, if the computation involves multiple items, please clearly list each item. If one item has multiple possible values (e.g., 0-2), you also need to clearly define what each value means.\n"
59+
60+
prompt += "##Interpretation\nShould be a list, where each item describes the interpretation (actual risk) for a value or a range of the computed risk score.\n"
61+
62+
prompt += "##Utility\nEvaluation results of the clinical utility of the risk score, such as AUC, F-score, PPV.\n"
63+
64+
prompt += "##Example\nGenerate a sample patient note and a detailed demonstration of using the calculator and interpret the results. Think step-by-step here.\n"
65+
66+
prompt += "Please be as detailed as possible.\n"
67+
68+
messages = [
69+
{"role": "system", "content": system},
70+
{"role": "user", "content": prompt},
71+
]
72+
73+
response = client.chat.completions.create(
74+
model="gpt-4",
75+
messages=messages,
76+
temperature=0,
77+
)
78+
79+
result = response.choices[0].message.content
80+
81+
with open(target_path, "w") as f:
82+
f.write(result)
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
__author__ = "qiao"
2+
3+
"""
4+
Verify the drafted clinical calculators
5+
"""
6+
7+
import glob
8+
import json
9+
import os
10+
import sys
11+
12+
from openai import AzureOpenAI
13+
14+
client = AzureOpenAI(
15+
api_version="2023-09-01-preview",
16+
azure_endpoint=os.getenv("OPENAI_ENDPOINT"),
17+
api_key=os.getenv("OPENAI_API_KEY"),
18+
)
19+
20+
if __name__ == "__main__":
21+
pmid2info = json.load(open("file0_sample_candidate_articles.json"))
22+
23+
for path in glob.glob("dir1_pubmed_risk_calcs/*"):
24+
25+
if ".py" in path:
26+
continue
27+
28+
pmid = os.path.basename(path)
29+
target_path = os.path.join("dir2_risk_calc_verifications", pmid)
30+
31+
if os.path.exists(target_path):
32+
continue
33+
34+
with open(path, "r") as f:
35+
text = f.read()
36+
37+
if text.lower().strip() == "no":
38+
continue
39+
40+
questions = [
41+
"Are the parameters clearly defined in the #Computation? If a parameter can have different scores, the definitions for each score must be provided.",
42+
"Are the parameters defined exactly the same in the article and the calculator?",
43+
"Is the #Computation logic in the calculator fully based on the original article without any assumptions? Answer no if the article does not provide clear computing logics or weights.",
44+
"Is the #Interpretation of the calculator fully based on the original article without any assumptions? Score ranges and corresponding risks should be exactly the same between the calculator and the article.",
45+
"Is the #Interpretation of the calculator useful? A useful calculator should contain quantitative risk rates or qualitative risk groups for different score ranges.",
46+
"Is the calculator free from any bug or other issue?",
47+
]
48+
answer_list = []
49+
50+
system = "You are a critical evaluator for a calculator that's supposed to describe a PubMed article. The calculator might contain errors. Always response in a JSON dict formatted as Dict{\"reasoning\": Str(critical_reasoning), \"answer\": Str(yes/no)}."
51+
52+
prompt = ""
53+
prompt += "Here is the original PubMed article:\n"
54+
prompt += pmid2info[pmid]["t"] + "\n"
55+
prompt += pmid2info[pmid]["a"] + "\n"
56+
prompt += "Here is the calculator that's supposed to describe the article above:\n"
57+
prompt += text + "\n\n"
58+
59+
early_stop = False
60+
61+
for question in questions:
62+
q_prompt = prompt + question
63+
64+
messages = [
65+
{"role": "system", "content": system},
66+
{"role": "user", "content": q_prompt},
67+
]
68+
69+
response = client.chat.completions.create(
70+
model="gpt-4",
71+
messages=messages,
72+
temperature=0,
73+
)
74+
75+
response = json.loads(response.choices[0].message.content)
76+
77+
answer_list.append(response)
78+
79+
if response["answer"].lower() == "no":
80+
early_stop = True
81+
82+
if early_stop:
83+
break
84+
85+
with open(target_path, "w") as f:
86+
json.dump(answer_list, f, indent=4)

0 commit comments

Comments
 (0)