{"id":958,"date":"2025-07-27T23:23:45","date_gmt":"2025-07-28T06:23:45","guid":{"rendered":"https:\/\/www.alerainfotech.com\/home\/?p=958"},"modified":"2025-07-27T23:24:20","modified_gmt":"2025-07-28T06:24:20","slug":"building-an-ai-healthcare-agent-to-automate-clinical-follow-up-decisions","status":"publish","type":"post","link":"https:\/\/www.alerainfotech.com\/home\/2025\/07\/27\/building-an-ai-healthcare-agent-to-automate-clinical-follow-up-decisions\/","title":{"rendered":"Building an AI Healthcare Agent to Automate Clinical Follow-Up Decisions"},"content":{"rendered":"\n<h1 class=\"wp-block-heading\"><\/h1>\n\n\n\n<p><strong>By Mahesh Palan<\/strong><br>Published: July 2025<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\ude80 Introduction<\/h2>\n\n\n\n<p>Imagine a world where AI reads clinical notes and decides whether a patient needs follow-up \u2014 and even books it for them automatically. That\u2019s the vision I had when building my healthcare automation agent. In this blog, I\u2019ll walk you through how I used <strong>BERT<\/strong>, <strong>PyTorch<\/strong>, <strong>Temporal<\/strong>, and <strong>FastAPI<\/strong> to create a production-grade AI agent that makes smart decisions from unstructured clinical notes.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udc8e Problem Statement<\/h2>\n\n\n\n<p>Doctors write tons of free-text notes like:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>&#8220;Follow-up with orthopedics in 6 weeks.&#8221;<br>or<br>&#8220;No follow-up necessary at this time.&#8221;<\/p>\n<\/blockquote>\n\n\n\n<p>But how can software understand this and act accordingly?<\/p>\n\n\n\n<p><strong>My goal:<\/strong><br>\u2705 Automatically <strong>analyze<\/strong> clinical notes<br>\u2705 <strong>Predict<\/strong> if a follow-up is needed<br>\u2705 <strong>Book an appointment<\/strong><br>\u2705 <strong>Notify the patient<\/strong><\/p>\n\n\n\n<div class=\"wp-block-media-text is-stacked-on-mobile\"><figure class=\"wp-block-media-text__media\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"583\" src=\"https:\/\/www.alerainfotech.com\/wp-content\/uploads\/2025\/07\/temporal-1024x583.jpg\" alt=\"\" class=\"wp-image-959 size-full\" srcset=\"https:\/\/www.alerainfotech.com\/wp-content\/uploads\/2025\/07\/temporal-1024x583.jpg 1024w, https:\/\/www.alerainfotech.com\/wp-content\/uploads\/2025\/07\/temporal-300x171.jpg 300w, https:\/\/www.alerainfotech.com\/wp-content\/uploads\/2025\/07\/temporal-768x437.jpg 768w, https:\/\/www.alerainfotech.com\/wp-content\/uploads\/2025\/07\/temporal-1536x874.jpg 1536w, https:\/\/www.alerainfotech.com\/wp-content\/uploads\/2025\/07\/temporal-2048x1165.jpg 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure><div class=\"wp-block-media-text__content\">\n<p><\/p>\n<\/div><\/div>\n\n\n\n<figure class=\"wp-block-video\"><video controls src=\"https:\/\/alerainfotech.s3.us-west-2.amazonaws.com\/agent.mov\"><\/video><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udee0\ufe0f Step 1: Training the Clinical Note Classifier<\/h2>\n\n\n\n<p>I used <code>bert-base-uncased<\/code> via HuggingFace with a simple classifier head.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>class FollowUpClassifier(nn.Module):\n    def __init__(self, num_labels=3):\n        super().__init__()\n        self.bert = BertModel.from_pretrained(\"bert-base-uncased\")\n        self.classifier = nn.Linear(self.bert.config.hidden_size, num_labels)\n\n    def forward(self, input_ids, attention_mask):\n        output = self.bert(input_ids, attention_mask)\n        pooled_output = output.pooler_output\n        return self.classifier(pooled_output)<\/code><\/pre>\n\n\n\n<p><strong>Classes:<\/strong> <code>follow_up<\/code>, <code>conditional<\/code>, <code>no_follow_up<\/code><\/p>\n\n\n\n<p>Saved with:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>torch.save(model.state_dict(), \"clinical_bert.pt\")\ntokenizer.save_pretrained(\"tokenizer\/\")<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83e\udde0 Step 2: Predicting Follow-Up<\/h2>\n\n\n\n<p>I then wrote a utility to run predictions:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>def predict_follow_up(text: str) -&gt; str:\n    # Load model\/tokenizer\n    # Preprocess text\n    # Run prediction\n    # Map to class labels\n    return label<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83e\udd16 Step 3: Temporal AI Agent<\/h2>\n\n\n\n<p><strong>Temporal<\/strong> lets you define workflows and activities like a business process.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Activities:<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>@activity.defn\nasync def analyze_notes(notes: str) -&gt; str:\n    return predict_follow_up(notes)\n\n@activity.defn\nasync def find_slot(doctor: str) -&gt; str:\n    return {\"dr_smith\": &#91;\"2025-07-26T09:00\"]}.get(doctor, &#91;None])&#91;0] or \"No available slot\"\n\n@activity.defn\nasync def book_slot2(patient_id: str, slot: str) -&gt; str:\n    return f\"Booked {slot} for patient {patient_id}\"\n\n@activity.defn\nasync def notify_patient(patient_id: str, slot: str) -&gt; str:\n    return f\"Notified patient {patient_id} about appointment at {slot}\"<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Workflow:<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>@workflow.defn\nclass ScheduleWorkflow:\n    @workflow.run\n    async def run(self, patient_id: str, visit_notes: str, doctor: str) -&gt; str:\n        result = await workflow.execute_activity(analyze_notes, visit_notes)\n        if result == \"No follow-up needed\":\n            return \"No follow-up needed.\"\n\n        slot = await workflow.execute_activity(find_slot, doctor)\n        await workflow.execute_activity(book_slot2, patient_id, slot)\n        await workflow.execute_activity(notify_patient, patient_id, slot)\n        return f\"Booked and notified for {slot}\"<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83c\udf10 Step 4: FastAPI Integration<\/h2>\n\n\n\n<p>I exposed the workflow via HTTP:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@app.post(\"\/agent-schedule\", response_model=BookingResponse)\nasync def schedule_with_agent(event: VisitEvent):\n    result = await client.execute_workflow(\n        ScheduleWorkflow.run,\n        args=&#91;event.patient_id, event.visit_notes, event.doctor],\n        id=f\"schedule-{uuid.uuid4()}\",\n        task_queue=\"agent-task-queue\"\n    )\n    return BookingResponse(\n        patient_id=event.patient_id,\n        booked=\"booked\" in result.lower(),\n        slot=\"2025-07-26T09:00\",\n        message=result\n    )<\/code><\/pre>\n\n\n\n<p><strong>Test it with:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>curl -X POST http:\/\/localhost:8000\/agent-schedule \\\n  -H \"Content-Type: application\/json\" \\\n  -d '{\n    \"patient_id\": \"P12345\",\n    \"visit_notes\": \"Patient to follow up with orthopedics in 6 weeks.\",\n    \"doctor\": \"dr_smith\"\n  }'<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">\u2705 Output<\/h2>\n\n\n\n<p>Sample positive result:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"patient_id\": \"P12345\",\n  \"booked\": true,\n  \"slot\": \"2025-07-26T09:00\",\n  \"message\": \"Booked 2025-07-26T09:00 for patient P12345...\"\n}<\/code><\/pre>\n\n\n\n<p>Or if no follow-up needed:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"patient_id\": \"P12345\",\n  \"booked\": false,\n  \"slot\": null,\n  \"message\": \"No follow-up needed.\"\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udd0d Learnings<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u2728 Using BERT for unstructured text works well for medical notes<\/li>\n\n\n\n<li>\u2699\ufe0f Temporal gave me robust control over asynchronous workflows<\/li>\n\n\n\n<li>\ud83d\udcc1 FastAPI made this easily accessible<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udca1 Future Work<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\ud83d\ude80 Connect to real EHR systems using HL7\/FHIR<\/li>\n\n\n\n<li>\ud83d\uddd2\ufe0f Add human review stage for conditional predictions<\/li>\n\n\n\n<li>\ud83d\udcca Improve slot logic using real calendar APIs<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83e\udd18 Final Thoughts<\/h2>\n\n\n\n<p>From model training to automated booking, this project showed how machine learning, workflows, and automation can transform healthcare. Let\u2019s build the future of health \u2014 one follow-up at a time.<\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>By Mahesh PalanPublished: July 2025 \ud83d\ude80 Introduction Imagine a world where AI reads clinical notes and decides whether a patient needs follow-up \u2014 and even books it for them automatically. That\u2019s the vision I had when building my healthcare automation agent. In this blog, I\u2019ll walk you through how I used BERT, PyTorch, Temporal, and [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[8],"tags":[],"class_list":["post-958","post","type-post","status-publish","format-standard","hentry","category-ai"],"_links":{"self":[{"href":"https:\/\/www.alerainfotech.com\/home\/wp-json\/wp\/v2\/posts\/958","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.alerainfotech.com\/home\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.alerainfotech.com\/home\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.alerainfotech.com\/home\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.alerainfotech.com\/home\/wp-json\/wp\/v2\/comments?post=958"}],"version-history":[{"count":1,"href":"https:\/\/www.alerainfotech.com\/home\/wp-json\/wp\/v2\/posts\/958\/revisions"}],"predecessor-version":[{"id":960,"href":"https:\/\/www.alerainfotech.com\/home\/wp-json\/wp\/v2\/posts\/958\/revisions\/960"}],"wp:attachment":[{"href":"https:\/\/www.alerainfotech.com\/home\/wp-json\/wp\/v2\/media?parent=958"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.alerainfotech.com\/home\/wp-json\/wp\/v2\/categories?post=958"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.alerainfotech.com\/home\/wp-json\/wp\/v2\/tags?post=958"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}