{"id":466,"date":"2024-11-25T15:38:38","date_gmt":"2024-11-25T07:38:38","guid":{"rendered":"https:\/\/my.di.cloudns.asia\/?p=466"},"modified":"2024-11-25T15:38:38","modified_gmt":"2024-11-25T07:38:38","slug":"building-private-healthcare-ai-assistant-for-clinics-using-qdrant-hybrid-cloud-jwt-rbac-dspy-and","status":"publish","type":"post","link":"https:\/\/my.di.cloudns.asia\/index.php\/2024\/11\/25\/466.html","title":{"rendered":"Building Private Healthcare AI Assistant for Clinics Using Qdrant Hybrid Cloud (JWT-RBAC), DSPy and\u2026"},"content":{"rendered":"<blockquote>\n<p>\u672c\u6587\u7531 <a href=\"http:\/\/ksria.com\/simpread\/\">\u7b80\u60a6 SimpRead<\/a> \u8f6c\u7801\uff0c \u539f\u6587\u5730\u5740 <a href=\"https:\/\/readmedium.com\/building-private-healthcare-ai-assistant-for-clinics-using-qdrant-hybrid-cloud-jwt-rbac-dspy-and-089a772e08ae\">readmedium.com<\/a><\/p>\n<p>Introduction: Security in AI Healthcare<\/p>\n<\/blockquote>\n<p>With the latest advancements in AI, we often overlook the security aspect while thinking of solutions. For example, suppose you find a new tool that can simplify your chatbot pipeline but the catch is it\u2019s not secure and your organization does not approve its usage.<\/p>\n<p>In my experience of working with a US-based healthcare company, I have observed how seriously we take data security because of the Health Insurance Portability and Accountability Act (HIPAA) which aims to protect Protected Health Information (PHI) like account numbers, addresses, phone numbers, social security numbers, and so on. Generally speaking, any business that serves users should definitely adhere to privacy rules and take measures to prevent data leak.<\/p>\n<p>And with the increase in AI solutions in healthcare, especially in the domain of NLP, AI security is bound to grow as well.<\/p>\n<p><div class='fancybox-wrapper lazyload-container-unload' data-fancybox='post-images' href='https:\/\/cdn-images-1.readmedium.com\/v2\/resize:fit:800\/0*YVE3QCp0lbRK7CsB'><img class=\"lazyload lazyload-style-1\" src=\"data:image\/svg+xml;base64,PCEtLUFyZ29uTG9hZGluZy0tPgo8c3ZnIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgc3Ryb2tlPSIjZmZmZmZmMDAiPjxnPjwvZz4KPC9zdmc+\"  decoding=\"async\" data-original=\"https:\/\/cdn-images-1.readmedium.com\/v2\/resize:fit:800\/0*YVE3QCp0lbRK7CsB\" src=\"data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB\/AAffA0nNPuCLAAAAAElFTkSuQmCC\" alt=\"\" \/><\/div><a href=\"https:\/\/www.unite.ai\/wp-content\/uploads\/2023\/08\/ai-crucial-for-healthcare-cybersecurity-feature.jpg\">https:\/\/www.unite.ai\/wp-content\/uploads\/2023\/08\/ai-crucial-for-healthcare-cybersecurity-feature.jpg<\/a><\/p>\n<p>As an experiment, we will build a Private AI Assistant for clinics and hospitals which fetches patient data and answers questions on top of that data.<\/p>\n<p>But, before proceeding, let\u2019s take a look at the flow diagram:<\/p>\n<p><div class='fancybox-wrapper lazyload-container-unload' data-fancybox='post-images' href='https:\/\/cdn-images-1.readmedium.com\/v2\/resize:fit:800\/0*CIjgbRNz4iYP6zwK'><img class=\"lazyload lazyload-style-1\" src=\"data:image\/svg+xml;base64,PCEtLUFyZ29uTG9hZGluZy0tPgo8c3ZnIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgc3Ryb2tlPSIjZmZmZmZmMDAiPjxnPjwvZz4KPC9zdmc+\"  decoding=\"async\" data-original=\"https:\/\/cdn-images-1.readmedium.com\/v2\/resize:fit:800\/0*CIjgbRNz4iYP6zwK\" src=\"data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB\/AAffA0nNPuCLAAAAAElFTkSuQmCC\" alt=\"\" \/><\/div><a href=\"https:\/\/github.com\/sachink1729\/AI-Assistant-Clinics-Medical-Data-Qdrant-Dspy-Groq\">https:\/\/github.com\/sachink1729\/AI-Assistant-Clinics-Medical-Data-Qdrant-Dspy-Groq<\/a><\/p>\n<p>Briefly:<\/p>\n<ol>\n<li><strong>Dataset:<\/strong> We will be working on a healthcare dataset that contains the patient\u2019s data, including details about name, illness, medication, bills, hospital name, etc. One thing to be noted is, datasets like these are rarely available online; this dataset also is not real and is generated digitally: originally, it\u2019s a multi-label classification dataset and can be downloaded from Kaggle here: <a href=\"https:\/\/www.kaggle.com\/datasets\/prasad22\/healthcare-dataset\">\ud83e\ude7aHealthcare Dataset \ud83e\uddea<\/a><\/li>\n<li><strong>DSPy:<\/strong> DSPy (or Declarative Sequencing Python framework) is a game-changing framework for algorithmically optimizing LM prompts instead of manual prompting. I have covered it in detail in one of my blogs; take a look: <a href=\"https:\/\/sachinkhandewal.medium.com\/prompt-like-a-pro-using-dspy-a-guide-to-build-a-better-local-rag-model-using-dspy-qdrant-and-d8011a3942d9\">Prompt Like a Pro Using DSPy: A guide to build a better local RAG model using DSPy, Qdrant and Ollama | by Sachin Khandewal | Mar, 2024<\/a><\/li>\n<li><strong>Qdrant Managed Cloud<\/strong>: <a href=\"https:\/\/qdrant.tech\/\">Qdrant<\/a> is a lightweight vector database that recently started their managed cloud services, which let you use a free cluster for trial and the option to upgrade as you use more features. We will use it to store our dataset in the form of vectors.<\/li>\n<li><strong>Groq<\/strong>: Groq is building an AI accelerator application-specific integrated circuit (ASIC) which they call the <a href=\"https:\/\/en.wikipedia.org\/w\/index.php?title=Language_Processing_Unit&action=edit&redlink=1\">Language Processing Unit<\/a> (LPU) and related hardware to accelerate the inference performance of AI workloads. They provide access to latest models like Llama3 free of cost (it\u2019s limited), but it\u2019s enough for our use case.<\/li>\n<\/ol>\n<p><strong>Now let\u2019s set up Qdrant cloud.<\/strong><\/p>\n<ol>\n<li>Go to <a href=\"https:\/\/cloud.qdrant.io\/login\">https:\/\/cloud.qdrant.io\/login<\/a>, create a new account, and proceed.<\/li>\n<li>After signing in, on the left hand side you\u2019ll see clusters; click on that \u2014 you can select the free or the paid version based on your needs.<\/li>\n<li>After successful creation, you\u2019ll see your cluster on the dashboard:<\/li>\n<\/ol>\n<p><div class='fancybox-wrapper lazyload-container-unload' data-fancybox='post-images' href='https:\/\/cdn-images-1.readmedium.com\/v2\/resize:fit:800\/0*jQawVlcvo5MKxFgv'><img class=\"lazyload lazyload-style-1\" src=\"data:image\/svg+xml;base64,PCEtLUFyZ29uTG9hZGluZy0tPgo8c3ZnIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgc3Ryb2tlPSIjZmZmZmZmMDAiPjxnPjwvZz4KPC9zdmc+\"  decoding=\"async\" data-original=\"https:\/\/cdn-images-1.readmedium.com\/v2\/resize:fit:800\/0*jQawVlcvo5MKxFgv\" src=\"data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB\/AAffA0nNPuCLAAAAAElFTkSuQmCC\" alt=\"\" \/><\/div><\/p>\n<ol start=\"4\">\n<li>The next thing is creating an API Key for the usage; click on the API key below Cluster0 and copy it for future use.<\/li>\n<\/ol>\n<p><div class='fancybox-wrapper lazyload-container-unload' data-fancybox='post-images' href='https:\/\/cdn-images-1.readmedium.com\/v2\/resize:fit:800\/0*X-0ZEj1n8-HryX7q'><img class=\"lazyload lazyload-style-1\" src=\"data:image\/svg+xml;base64,PCEtLUFyZ29uTG9hZGluZy0tPgo8c3ZnIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgc3Ryb2tlPSIjZmZmZmZmMDAiPjxnPjwvZz4KPC9zdmc+\"  decoding=\"async\" data-original=\"https:\/\/cdn-images-1.readmedium.com\/v2\/resize:fit:800\/0*X-0ZEj1n8-HryX7q\" src=\"data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB\/AAffA0nNPuCLAAAAAElFTkSuQmCC\" alt=\"\" \/><\/div><\/p>\n<ol start=\"5\">\n<li>You will also see the Python code snippet to access this container and create collections; but we will explore that later.<\/li>\n<\/ol>\n<p><strong>Before environment setup, let us make sure you can access Groq:<\/strong><\/p>\n<ol>\n<li>Go to <a href=\"https:\/\/groq.com\/\">https:\/\/groq.com\/<\/a> to sign up.<\/li>\n<li>After that, go to <a href=\"https:\/\/console.groq.com\/keys\">https:\/\/console.groq.com\/keys<\/a> to create or manage API keys; copy the API key and keep it with you.<\/li>\n<\/ol>\n<p><div class='fancybox-wrapper lazyload-container-unload' data-fancybox='post-images' href='https:\/\/cdn-images-1.readmedium.com\/v2\/resize:fit:800\/0*gBRDDoQ0A6X_-xH1'><img class=\"lazyload lazyload-style-1\" src=\"data:image\/svg+xml;base64,PCEtLUFyZ29uTG9hZGluZy0tPgo8c3ZnIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgc3Ryb2tlPSIjZmZmZmZmMDAiPjxnPjwvZz4KPC9zdmc+\"  decoding=\"async\" data-original=\"https:\/\/cdn-images-1.readmedium.com\/v2\/resize:fit:800\/0*gBRDDoQ0A6X_-xH1\" src=\"data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB\/AAffA0nNPuCLAAAAAElFTkSuQmCC\" alt=\"\" \/><\/div><\/p>\n<p><strong>Environment Setup:<\/strong><\/p>\n<p>Install the required packages using:<\/p>\n<pre><code>pip install qdrant-client groq sentence-transformers dspy-ai fastembed gradio --upgrade\n<\/code><\/pre>\n<p>Before coding, make sure you download the Kaggle dataset from this link: <a href=\"https:\/\/www.kaggle.com\/datasets\/prasad22\/healthcare-dataset\">https:\/\/www.kaggle.com\/datasets\/prasad22\/healthcare-dataset<\/a><\/p>\n<p>Your setup is done!<\/p>\n<ol>\n<li>Let\u2019s explore the dataset and preprocess it according to our use case. You will see that the dataset is in tabular format and so we need to format it accordingly. Let\u2019s follow the steps below:<\/li>\n<\/ol>\n<pre><code>import pandas as pd\ndf = pd.read_csv(&quot;healthcare_dataset.csv&quot;)\n<\/code><\/pre>\n<p>If you take a close look, you will see that this contains a lot of information which we can use!<\/p>\n<p><div class='fancybox-wrapper lazyload-container-unload' data-fancybox='post-images' href='https:\/\/cdn-images-1.readmedium.com\/v2\/resize:fit:800\/0*-2SCM_CGGm8dI1lh'><img class=\"lazyload lazyload-style-1\" src=\"data:image\/svg+xml;base64,PCEtLUFyZ29uTG9hZGluZy0tPgo8c3ZnIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgc3Ryb2tlPSIjZmZmZmZmMDAiPjxnPjwvZz4KPC9zdmc+\"  decoding=\"async\" data-original=\"https:\/\/cdn-images-1.readmedium.com\/v2\/resize:fit:800\/0*-2SCM_CGGm8dI1lh\" src=\"data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB\/AAffA0nNPuCLAAAAAElFTkSuQmCC\" alt=\"\" \/><\/div><\/p>\n<p>Let\u2019s format these rows into snippets of text.<\/p>\n<pre><code>def format_row(row):\n    return (\n        f&quot;Name: {row[&#039;Name&#039;]}, Age: {row[&#039;Age&#039;]}, Gender: {row[&#039;Gender&#039;]}, &quot;\n        f&quot;Blood Type: {row[&#039;Blood Type&#039;]}, Medical Condition: {row[&#039;Medical Condition&#039;]}, &quot;\n        f&quot;Date of Admission: {row[&#039;Date of Admission&#039;]}, Doctor: {row[&#039;Doctor&#039;]}, &quot;\n        f&quot;Hospital: {row[&#039;Hospital&#039;]}, Insurance Provider: {row[&#039;Insurance Provider&#039;]}, &quot;\n        f&quot;Billing Amount: {row[&#039;Billing Amount&#039;]}, Room Number: {row[&#039;Room Number&#039;]}, &quot;\n        f&quot;Admission Type: {row[&#039;Admission Type&#039;]}, Discharge Date: {row[&#039;Discharge Date&#039;]}, &quot;\n        f&quot;Medication: {row[&#039;Medication&#039;]}, Test Results: {row[&#039;Test Results&#039;]}&quot;\n        &quot;\\n\\n&quot;.lower()\n    )\n\ndf[&#039;formatted_text&#039;] = df.apply(format_row, axis=1)\n\ntext_data = df[&#039;formatted_text&#039;].tolist()\n<\/code><\/pre>\n<p>For the sake of experiment, I will use only 128 data rows, since we are using the free-tier Qdrant Cloud Cluster.<\/p>\n<pre><code>from random import shuffle\nsampled_dataset = text_data[:128]\nshuffle(sampled_dataset)\n<\/code><\/pre>\n<p>See how these rows look now:<\/p>\n<p>which gives:<\/p>\n<pre><code>[&#039;name: heather miller, age: 76, gender: male, blood type: a+, medical condition: diabetes, date of admission: 2021-04-17, doctor: scott grant, hospital: powell ward, and mercado, insurance provider: aetna, billing amount: 3908.9465679463137, room number: 428, admission type: elective, discharge date: 2021-05-10, medication: lipitor, test results: inconclusive\\n\\n&#039;,\n &#039;name: connor hansen, age: 75, gender: female, blood type: a+, medical condition: diabetes, date of admission: 2019-12-12, doctor: kenneth fletcher, hospital: powers miller, and flores, insurance provider: cigna, billing amount: 43282.28335770435, room number: 134, admission type: emergency, discharge date: 2019-12-28, medication: penicillin, test results: abnormal\\n\\n&#039;,\n &#039;name: daniel schmidt, age: 63, gender: male, blood type: b+, medical condition: asthma, date of admission: 2022-11-15, doctor: denise galloway, hospital: hammond ltd, insurance provider: cigna, billing amount: 23762.203579059587, room number: 465, admission type: elective, discharge date: 2022-11-22, medication: penicillin, test results: normal\\n\\n&#039;,\n &#039;name: david higgins, age: 49, gender: female, blood type: b-, medical condition: arthritis, date of admission: 2021-03-05, doctor: erin henderson md, hospital: evans and hall schneider,, insurance provider: medicare, billing amount: 24948.47782402692, room number: 361, admission type: emergency, discharge date: 2021-03-20, medication: penicillin, test results: abnormal\\n\\n&#039;,\n &#039;name: lindsey lambert, age: 82, gender: female, blood type: a+, medical condition: hypertension, date of admission: 2021-11-19, doctor: christopher guerra, hospital: and brown oneal, shah, insurance provider: medicare, billing amount: 23067.672165245425, room number: 307, admission type: elective, discharge date: 2021-12-12, medication: ibuprofen, test results: normal\\n\\n&#039;]\n<\/code><\/pre>\n<p>Now that we have our <strong>sample_dataset,<\/strong> the next step is to generate embeddings for these sentences in order to store them in a vector DB.<\/p>\n<pre><code>from sentence_transformers import SentenceTransformer\nmodel = SentenceTransformer(&quot;BAAI\/bge-large-en-v1.5&quot;, device=&#039;cuda&#039;)\nvectors = model.encode(sampled_dataset)\n<\/code><\/pre>\n<p>Remember the size (columns or features) of this vector; we need it while creating the vector DB collection.<\/p>\n<p>Which gives:<\/p>\n<p>(1024,)<\/p>\n<p>Now remember Qdrant\u2019s API key and your own cluster\u2019s URL \u2014 we need them now:<\/p>\n<pre><code>import os\nos.environ[&#039;QDRANT__SERVICE__API_KEY&#039;]=&lt;your api key&gt;\n\nfrom qdrant_client import QdrantClient\nfrom qdrant_client.models import Distance, VectorParams\n\nclient = QdrantClient(\n    url=&lt;your cluster\u2019s url&gt;,\n\n    api_key=os.environ[&#039;QDRANT__SERVICE__API_KEY&#039;],\n<\/code><\/pre>\n<p><strong>Note: Typically the API key should come from a config server that you have to set up, which is internal to your organization or services only. In that way, only people who have access to that config server will get access to the API key, so not everyone can access your cluster and your data is safe!<\/strong><\/p>\n<p>But since we\u2019re primarily showing the functionality, we will proceed like this.<\/p>\n<p>Create a collection named: phi_data<\/p>\n<pre><code>client.recreate_collection(\n    collection_,\n    vectors_config=VectorParams(size=1024, distance=Distance.COSINE),\n)\n<\/code><\/pre>\n<p>Now comes the main part \u2014 upload this collection to the cloud cluster:<\/p>\n<pre><code>client.upload_collection(\n    collection_,\n    ids=[i for i in range(len(sampled_dataset))],\n    vectors=vectors,\n    parallel=4,    \n    max_retries=3,\n)\n<\/code><\/pre>\n<p>That\u2019s about it!<\/p>\n<p>Qdrant also provides the option of access control via JWT, to replicate it here it has to be done on local mode, to do that simply install docker and run these 2 lines:<\/p>\n<pre><code>docker pull qdrant\/qdrant\n<\/code><\/pre>\n<pre><code>docker run -p 6333:6333 -p 6334:6334 \/\n-e QDRANT__SERVICE__API_KEY=eXaMplE12345Key67890Api \/  \n-e QDRANT__SERVICE__JWT_RBAC=true \nqdrant\/qdrant\n<\/code><\/pre>\n<p><strong>Note: this will be a single line so delete \u2018\/\u2019 in above code.<\/strong><\/p>\n<p>After that, let\u2019s start up the root_client and create a dummy collection:<\/p>\n<pre><code>root_client = QdrantClient(\n    url=&quot;http:\/\/localhost:6333&quot;,\n    api_key=&quot;eXaMplE12345Key67890Api&quot;,\n)\n\nroot_client.recreate_collection(\n    collection_,\n    vectors_config=VectorParams(size=1024, distance=Distance.COSINE),\n)\n\nroot_client.upload_collection(\n    collection_,\n    ids=[i for i in range(len(sampled_dataset))],\n    vectors=vectors,\n    parallel=4,\n    max_retries=3,\n)\n<\/code><\/pre>\n<p>After that, let\u2019s create a user and limit their access to read only mode using JWT, it creates a temporary key that is linked to the original key.<\/p>\n<pre><code>import jwt\nimport time\n\napi_key = &#039;eXaMplE12345Key67890Api&#039;\n\ncurrent_time = int(time.time())\n\npayload = {\n    &#039;exp&#039;: current_time + 3600,  \n    &#039;value_exists&#039;: {\n        &#039;collection&#039;: &#039;demo_collection&#039;,\n        &#039;matches&#039;: [\n            {&#039;key&#039;: &#039;user&#039;, &#039;value&#039;: &#039;John&#039;}\n        ]\n    },\n    &quot;access&quot;: [\n    {\n        &quot;collection&quot;: &quot;demo_collection&quot;,\n        &quot;access&quot;: &quot;r&quot;,\n        &quot;payload&quot;: {\n            &quot;user&quot;: &quot;John&quot;\n      }\n    }\n  ]  \n}\n\nencoded_jwt = jwt.encode(payload, api_key, algorithm=&#039;HS256&#039;)\n\nprint(encoded_jwt)\n<\/code><\/pre>\n<p>Which gives:<\/p>\n<pre><code>eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MTYzMTA2MDgsInZhbHVlX2V4aXN0cyI6eyJjb2xsZWN0aW9uIjoiZGVtb19jb2xsZWN0aW9uIiwibWF0Y2hlcyI6W3sia2V5IjoidXNlciIsInZhbHVlIjoiSm9obiJ9XX0sImFjY2VzcyI6W3siY29sbGVjdGlvbiI6ImRlbW9fY29sbGVjdGlvbiIsImFjY2VzcyI6InIiLCJwYXlsb2FkIjp7InVzZXIiOiJKb2huIn19XX0.7ald90UIk7hI5d57S0vDfo_bdatNsi20XURlhUee_Nw\n<\/code><\/pre>\n<p>This key will only give a \u201cread\u201d access to the collection we created \u2014 \u201cdummy\u201d.<\/p>\n<p>Even if you try to upload a new data point into this collection you won\u2019t be allowed to, to test it use this:<\/p>\n<pre><code>from qdrant_client import QdrantClient, models\nimport numpy as np\n\nclient = QdrantClient(\n    url=&quot;http:\/\/localhost:6333&quot;,\n    api_key=your_role_key,\n)\n\ndata = np.array(list([0.1]*1024))\nprint(data.shape)\n\nclient.upload_points(\n    collection_,\n    points=[\n        models.PointStruct(\n           ,\n            vector=data,\n        )])\n<\/code><\/pre>\n<p>Which will give:<\/p>\n<pre><code>UnexpectedResponse: Unexpected Response: 403 (Forbidden)\n<\/code><\/pre>\n<p>Now let\u2019s set up our prompting (programming, rather) tool, DSPy!<\/p>\n<pre><code>from dspy.retrieve.qdrant_rm import QdrantRM\nqdrant_retriever_model = QdrantRM(&quot;phi_data&quot;, client, k=3)\n<\/code><\/pre>\n<p>Let\u2019s initialize DSPy \u2014 Groq\u2019s integration using Groq\u2019s API key:<\/p>\n<pre><code>import dspy\nllama3 = dspy.GROQ(model=&#039;llama3-8b-8192&#039;, api_key = &lt;your groq api&gt; )\n<\/code><\/pre>\n<p>The next step tells the system to use Qdrant as the retriever model and Groq as the LLM.<\/p>\n<pre><code>dspy.settings.configure(rm=qdrant_retriever_model, lm=llama3)\n<\/code><\/pre>\n<p>Let\u2019s set up our CoT (chain of thought) modules and signatures using DSPy:<\/p>\n<pre><code>class GenerateAnswer(dspy.Signature):\n    &quot;&quot;&quot;Answer questions with logical factoid answers.&quot;&quot;&quot;\n\n    context = dspy.InputField(desc=&quot;will contain phi medical data of patients matched with the query&quot;)\n    question = dspy.InputField()\n    answer = dspy.OutputField(desc=&quot;an answer between 10 to 20 words&quot;)\n<\/code><\/pre>\n<p>We define a function get_context to get the 3 best matching data points for the query:<\/p>\n<pre><code>def get_context(text):\n    query_vector = model.encode(text)\n\n    hits = client.search(\n        collection_,\n        query_vector=query_vector,\n        limit=3  \n    )\n    s=&#039;&#039;\n    for x in [sampled_dataset[i.id] for i in hits]:\n        s = s + x\n    return s\n<\/code><\/pre>\n<p>After that, define the main class that handles the RAG pipeline.<\/p>\n<pre><code>class RAG(dspy.Module):\n    def __init__(self, num_passages=3):\n        super().__init__()\n\n        self.retrieve = dspy.Retrieve(k=num_passages)\n        self.generate_answer = dspy.ChainOfThought(GenerateAnswer)\n\n    def forward(self, question):\n        context = get_context(question)\n        prediction = self.generate_answer(context=context, question=question)\n        return dspy.Prediction(context=context, answer=prediction.answer)\n<\/code><\/pre>\n<p>These may not make sense if you are encountering these concepts for the first time. Want to understand how everything works? Hop on to my article that covers it beautifully! \u2014 <a href=\"https:\/\/sachinkhandewal.medium.com\/prompt-like-a-pro-using-dspy-a-guide-to-build-a-better-local-rag-model-using-dspy-qdrant-and-d8011a3942d9\">Prompt Like a Pro Using DSPy: A guide to build a better local RAG model using DSPy, Qdrant and Ollama | by Sachin Khandewal | Mar, 2024<\/a><\/p>\n<p>To respond to the queries, use:<\/p>\n<pre><code>rag = RAG()\ndef respond(query):\n    response = rag(query)\n    return response.answer\n<\/code><\/pre>\n<p>This is not visually pleasing, so let\u2019s build a very simple chatbot using my favorite framework Gradio!<\/p>\n<pre><code>import gradio as gr\n\nwith gr.Blocks() as demo:\n    chatbot = gr.Chatbot()\n    msg = gr.Textbox()\n    clear = gr.ClearButton([msg, chatbot])\n\n    def respond(query, chat_history):\n        response = uncompiled_rag(query)\n        chat_history.append((query, response.answer))\n        return &quot;&quot;, chat_history\n\n    msg.submit(respond, [msg, chatbot], [msg, chatbot])\n<\/code><\/pre>\n<p>Finally, let\u2019s start the app!<\/p>\n<p>If you are running this on Colab or on any other cloud service, consider using:<\/p>\n<p>After starting the app, let\u2019s query and see how this works.<\/p>\n<p>Based on the random shuffle, you may or may not see these results; that\u2019s why find a data point in sampled_dataset and use a name from it to get started. In my case, a person named <strong>kayla padilla<\/strong> showed up and I will query on her to get some information.<\/p>\n<ol>\n<li><strong>kayla padilla\u2019s details?<\/strong><\/li>\n<\/ol>\n<p>Which gives:<\/p>\n<p><div class='fancybox-wrapper lazyload-container-unload' data-fancybox='post-images' href='https:\/\/cdn-images-1.readmedium.com\/v2\/resize:fit:800\/0*c4tGLXqF6ysay1u4'><img class=\"lazyload lazyload-style-1\" src=\"data:image\/svg+xml;base64,PCEtLUFyZ29uTG9hZGluZy0tPgo8c3ZnIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgc3Ryb2tlPSIjZmZmZmZmMDAiPjxnPjwvZz4KPC9zdmc+\"  decoding=\"async\" data-original=\"https:\/\/cdn-images-1.readmedium.com\/v2\/resize:fit:800\/0*c4tGLXqF6ysay1u4\" src=\"data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB\/AAffA0nNPuCLAAAAAElFTkSuQmCC\" alt=\"\" \/><\/div><\/p>\n<p><strong>2. kayla padilla\u2019s doctor\u2019s name?<\/strong><\/p>\n<p><div class='fancybox-wrapper lazyload-container-unload' data-fancybox='post-images' href='https:\/\/cdn-images-1.readmedium.com\/v2\/resize:fit:800\/0*zQx69gZv_oFPpNjf'><img class=\"lazyload lazyload-style-1\" src=\"data:image\/svg+xml;base64,PCEtLUFyZ29uTG9hZGluZy0tPgo8c3ZnIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgc3Ryb2tlPSIjZmZmZmZmMDAiPjxnPjwvZz4KPC9zdmc+\"  decoding=\"async\" data-original=\"https:\/\/cdn-images-1.readmedium.com\/v2\/resize:fit:800\/0*zQx69gZv_oFPpNjf\" src=\"data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB\/AAffA0nNPuCLAAAAAElFTkSuQmCC\" alt=\"\" \/><\/div><\/p>\n<p>Amazing right?!<\/p>\n<ol start=\"3\">\n<li>Now let\u2019s query something more complex:<\/li>\n<\/ol>\n<p><strong>Highest bill by Blue Cross Insurance?<\/strong><\/p>\n<p><div class='fancybox-wrapper lazyload-container-unload' data-fancybox='post-images' href='https:\/\/cdn-images-1.readmedium.com\/v2\/resize:fit:800\/0*qqOpYQhjcr8Wam_5'><img class=\"lazyload lazyload-style-1\" src=\"data:image\/svg+xml;base64,PCEtLUFyZ29uTG9hZGluZy0tPgo8c3ZnIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgc3Ryb2tlPSIjZmZmZmZmMDAiPjxnPjwvZz4KPC9zdmc+\"  decoding=\"async\" data-original=\"https:\/\/cdn-images-1.readmedium.com\/v2\/resize:fit:800\/0*qqOpYQhjcr8Wam_5\" src=\"data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB\/AAffA0nNPuCLAAAAAElFTkSuQmCC\" alt=\"\" \/><\/div><\/p>\n<ol start=\"4\">\n<li>You can run a series of questions like this as well; you don\u2019t necessarily have to specify \u201cdoctor\u201d.<\/li>\n<\/ol>\n<p><div class='fancybox-wrapper lazyload-container-unload' data-fancybox='post-images' href='https:\/\/cdn-images-1.readmedium.com\/v2\/resize:fit:800\/0*32WGpxjf2MUt4PYI'><img class=\"lazyload lazyload-style-1\" src=\"data:image\/svg+xml;base64,PCEtLUFyZ29uTG9hZGluZy0tPgo8c3ZnIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgc3Ryb2tlPSIjZmZmZmZmMDAiPjxnPjwvZz4KPC9zdmc+\"  decoding=\"async\" data-original=\"https:\/\/cdn-images-1.readmedium.com\/v2\/resize:fit:800\/0*32WGpxjf2MUt4PYI\" src=\"data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB\/AAffA0nNPuCLAAAAAElFTkSuQmCC\" alt=\"\" \/><\/div><\/p>\n<ol start=\"5\">\n<li>You can even ask questions according to the illness concerned:<\/li>\n<\/ol>\n<p><strong>Young people with hypertension<\/strong><\/p>\n<p><div class='fancybox-wrapper lazyload-container-unload' data-fancybox='post-images' href='https:\/\/cdn-images-1.readmedium.com\/v2\/resize:fit:800\/0*NN_M7CGUevWB5uwY'><img class=\"lazyload lazyload-style-1\" src=\"data:image\/svg+xml;base64,PCEtLUFyZ29uTG9hZGluZy0tPgo8c3ZnIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgc3Ryb2tlPSIjZmZmZmZmMDAiPjxnPjwvZz4KPC9zdmc+\"  decoding=\"async\" data-original=\"https:\/\/cdn-images-1.readmedium.com\/v2\/resize:fit:800\/0*NN_M7CGUevWB5uwY\" src=\"data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB\/AAffA0nNPuCLAAAAAElFTkSuQmCC\" alt=\"\" \/><\/div><\/p>\n<p>And a lot more!<\/p>\n<p>With just 100 or so lines of code, you can build such amazing assistants while keeping your data safe!<\/p>\n<p>Go ahead and play around with it.<\/p>\n<p>This article covered:<\/p>\n<ul>\n<li>The rise of AI security in healthcare and why it is needed.<\/li>\n<li>A secure Private AI Healthcare Assistant for Clinics, which utilizes:<\/li>\n<\/ul>\n<p>Qdrant Managed Cloud: A very lightweight and secure vector database.<\/p>\n<p>DSPy: An amazing tool for programming your prompts.<\/p>\n<p>Groq: For quick LLM responses (Llama3).<\/p>\n<ul>\n<li>We saw how to manage role-based access using JWT in Qdrant and how it prevents unauthorized entities from accessing your private data.<\/li>\n<\/ul>\n<p>For full code reference, you can check out my GitHub repo: <a href=\"https:\/\/github.com\/sachink1729\/AI-Assistant-Clinics-Medical-Data-Qdrant-Dspy-Groq\">https:\/\/github.com\/sachink1729\/AI-Assistant-Clinics-Medical-Data-Qdrant-Dspy-Groq<\/a><\/p>\n<ul>\n<li><a href=\"https:\/\/qdrant.tech\/\">https:\/\/qdrant.tech\/<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/qdrant\/qdrant-client\">https:\/\/github.com\/qdrant\/qdrant-client<\/a><\/li>\n<li><a href=\"https:\/\/www.kaggle.com\/datasets\/prasad22\/healthcare-dataset\">https:\/\/www.kaggle.com\/datasets\/prasad22\/healthcare-dataset<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/stanfordnlp\/dspy\">https:\/\/github.com\/stanfordnlp\/dspy<\/a><\/li>\n<li><a href=\"https:\/\/console.groq.com\/docs\/quickstart\">https:\/\/console.groq.com\/docs\/quickstart<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>\u672c\u6587\u7531 \u7b80\u60a6 SimpRead \u8f6c\u7801\uff0c \u539f\u6587\u5730\u5740 readmedium.com Introduction: S [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-466","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/my.di.cloudns.asia\/index.php\/wp-json\/wp\/v2\/posts\/466","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/my.di.cloudns.asia\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/my.di.cloudns.asia\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/my.di.cloudns.asia\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/my.di.cloudns.asia\/index.php\/wp-json\/wp\/v2\/comments?post=466"}],"version-history":[{"count":1,"href":"https:\/\/my.di.cloudns.asia\/index.php\/wp-json\/wp\/v2\/posts\/466\/revisions"}],"predecessor-version":[{"id":467,"href":"https:\/\/my.di.cloudns.asia\/index.php\/wp-json\/wp\/v2\/posts\/466\/revisions\/467"}],"wp:attachment":[{"href":"https:\/\/my.di.cloudns.asia\/index.php\/wp-json\/wp\/v2\/media?parent=466"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/my.di.cloudns.asia\/index.php\/wp-json\/wp\/v2\/categories?post=466"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/my.di.cloudns.asia\/index.php\/wp-json\/wp\/v2\/tags?post=466"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}