Find

Article
· 39 min ago 5m read

Building a Medical History Chatbot - FHIR, Vector Search and RAG for beginners

Introduction

Earlier this year, I set about creating kit to introduce young techy folk at a Health Tech hackathon to using InterSystems IRIS for health, particularly focusing on using FHIR and vector search.

I wanted to publish this to the developer community because the tutorials included in the kit make a great introduction to using FHIR and to building a basic RAG system in IRIS. Its an all inclusive set of tutorials to show in detail how to:

- Connect to IRIS with Python 
- Use the InterSystems FHIR Server 
- Convert FHIR data into relational data with the **FHIR-SQL builder**
- Use InterSystems **Vector Search** 
- As a bonus using **Ollama** to prompt local AI models 

This repo contains a full series of Jupyter Notebook tutorials for developing a medical history chatbot, as well as various other tutorials on using a FHIR server, so forgive me if this article is slightly light technical detail, but there's plenty of information in the linked Open Exchange Package!
 

Designing the Demo

The design brief I was given was to build a hackathon kit (which I defined as a fully-worked through, easy to follow demo app) that used FHIR data and AI. 

The first question with this kind of project is where the data is coming from. I needed **FHIR Data** with some sort of **plain text** which could be vectorized for Vector Search. Here I had two problems: 

1. Real Patient data isn't easy to come across. 
    - **Solution** - use synthetically generated patient data with Synthea
2. Plain text resources are generally clinical notes in Document Reference FHIR resources.
    - **Solution** - Use GenAI to write my own clinical notes and load them into FHIR Resource bundles

Coming up with a source of plain text clinical data suitable for vectorization was my first major stumbling point, as I struggled to find anything worthwhile. The inspiration of using clinical notes to create a patient chatbot did not appear from nowhere. Instead, I saw a similar demonstration by @Simon Sha in the 2025 Demo Games. This was a great demo, so I wanted to create something similar to use for a fully guided tutorial!


Simplifying FHIR server set-up

The first step of the tutorial was running an instance of IRIS for Health with a FHIR server, ideally with data pre-loaded. For this, I decided to use an Open Exchange template. If you are lost at where to start on a project, the Open Exchange is often a great place to have a look! 

I found two FHIR templates, [iris-fhir-template](https://openexchange.intersystems.com/package/iris-fhir-template) by @Evgeny, and  [Dockerfhir](https://github.com/pjamiesointersystems/Dockerfhir) by @Patrick Jamieson. Both of these templates are excellent, and in my final version of the hackathon kit, I ended up using a combination of them. If I was starting over, I would recommend the [iris-fhir-template](https://openexchange.intersystems.com/package/iris-fhir-template)  because this has a built in user interface and swagger-UI to test the FHIR endpoints. Trying to combine the two at a later date became a nightmare because the iris-fhir-template has the FHIR server endpoint hardcoded. 

On the bright-side, the day I spent building and rebuilding docker containers made me much more confident on how a Dockerfile, module.xml and iris.script setup works. If you haven't already, I recommend breaking one of the many dev-templates available on the open exchange and learning how to rebuild or fix it. Its really useful to understand how these work when creating your own projects.  

Vector Search

In my eyes, the remarkable thing about vector search is how easy it is to set-up and perform, particularly in IRIS. Sure, there's refinement that can be done later, like using a hybrid vector/keyword search or adding some sort of re-ranking system, but the basic steps of: 

- Importing a model
- Creating Vectors from plain text
- Inserting vectors into a table in IRIS
- Converting a query to a vector
- Querying the database with the query vector

Can all be performed in ~50 lines of Python code. 

This makes it a great place for newcomers to IRIS to start developing, which is why it was chosen for this hackathon kit. 

Prompting with Ollama

I've always liked the idea of prompting local models, knowing that it will always be free, doesn't need any API key set-up, and doesn't involving sending your data elsewhere. This last point can be particularly important with medical records, when its important to keep data private, and restrict third-party access. In the past, I used models with Hugging Faces Transformer module, and the results were incredibly slow, and incredibly poor. 

For this project I tried Ollama, which was a great improvement on Hugging Faces. Models that 'weigh' less than a Gigabyte, like gemma-1b give surprisingly coherent, and even accurate responses. The speed of response (at least on my computer) can be quite slow, particularly for large context windows, but if you are patient (or like taking constant tea-breaks while waiting for a model response), they perform quite well! 

I enjoyed putting together the Ollama prompting section, even if at a real hackathon, all the competitors just did the sensible thing and used the OpenAI API...

Real-life use

We shared this tutorial with teams at the Hackjak Brno Healthcare hackathon in November 2025 and received good feedback. 11 (out of 25) teams used aspects of the kit in their final solutions, 

The solutions built by hackathon teams were impressive and inspirational, with use cases ranging from using IRIS vector search in a RAG pipeline, to creating tools to fill out medical forms which connect directly to a FHIR server back-end. One of the teams (VIPIK) even uploaded their solution to [Open Exchange](https://openexchange.intersystems.com/package/VIPIK), which was really nice to see. 

Conclusions

This demo was really fun to build and I'm really glad it proved useful at the hackathon in Czech Republic. I hope it will be used more in future, as its a nice entrypoint to using FHIR data with IRIS, Python and Vector Search!

Thanks for reading, and check out the full tutorial on Open Exchange! 

Acknowledgements

Thanks to @Ruby Howard, @Thomas Dyar , @Daniel Kutac and @Ondřej Hoferek for working through the tutorial and providing feedback and @Simon Sha for the original inspiration with your entry to the Demo Games last year. 

Discussion (0)1
Log in or sign up to continue
Article
· 1 hr ago 2m read

Reviews on Open Exchange - #62

If one of your packages on OEX receives a review, you get notified by OEX only of YOUR own package.   
The rating reflects the experience of the reviewer with the status found at the time of review.   
It is kind of a snapshot and might have changed meanwhile.   
Reviews by other members of the community are marked by * in the last column.

I also placed a bunch of Pull Requests on GitHub when I found a problem I could fix.    
Some were accepted and merged, and some were just ignored.     
So if you made a major change and expect a changed review, just let me know.
 

# Package Review Stars IPM Docker *
1 JSON2Persistent ! Now also available in IPM 6.0 y y  
2 one-to-many-case The first of 2026 5.0 y y  
3 IRIS_dockerization promising composition 4.4   y  
4 GlobalsDB-NodeJS-Admin Historic artefact 3.8      
5 iris-jsonschema just partial working 3.8      
6 GlobalsDB-Admin-NodeJS Historic artefact #2 3.5      
7 Pivot Partner product promotion 3.4      
8 PIQXL Gateway Good looking product promotion 2.7      
9 Symedical Partner promotion 2.4      
10 iknowAV Another artefact 1.2      


NOTE:
If some review is not readable for you, it might still wait for approval by OEX admins.

Discussion (0)1
Log in or sign up to continue
Question
· 12 hr ago

FHIR return code 201 or 200

Needs some help with FHIR return code 200 or 201 when processing HL7 messages in HealthConnect.

Issue:

1. If FHIR code is 200 or 201, HealthConnect keep processing HL7 messages

2. If FHIR code is NOT 200 or 201, HealthConnect keep suspend the HL7 messages in the messages queue, then keep processing NEXT subsequent HL7 messages in the TEXT file if the FHIR code is 200 or 201

3. I am also using following "Reply Code Actions" in Operation settings (:?R=S,:?E=S,:~=S,:?A=C,:*=S,:I?=W,:T?=C)

Please advice if I have a "if condition check" for FHIR codes 200 or 201, what "Reply Code Actions" I should use? or any other logic to achieve 1,2,3 issues above.

1 new Comment
Discussion (1)2
Log in or sign up to continue
Announcement
· 15 hr ago

CCR Application Improvements for Best Practices

Over the last year, the CCR development team has prioritized changes to the CCR application to better expose and encourage best practices for using CCR. This article will highlight some of the areas that we've focused on:

  • Organization Dashboard
  • System Alerts
  • Branch Hygiene

In each of these areas, we have increased the visibility of potential CCR usage issues and given users tools to proactively address them.

Organization Dashboard

We have continued to build out the available information on the Organization details page

The overview is a great place to get an overall sense of your Organization's CCR usage. It now includes more KPIs highlighting various concerns and linking to the pages in the CCR application where you can address them. 

The Systems tab includes detailed information about the state of Client Tools across all Environments as well as System Architect information. The alerts here highlight missing information, inactivity, and out of date Client Tools, with links to the corresponding Systems.

Other tabs on the Organization details page have been enhanced with additional details to make your Organization's CCR usage clear at a glance.

System Alerts

Another area of improved visibility is the System details page, where we now have alerts for many CCR usage issues.

Environment alerts have been added for unset URLs, insecure URLS, and detected Client Tools issues. Details are displayed on hover:

When CCR detects that the Client Tools are out of date for the System, an alert will appear to encourage the use of the update button.

Systems that have had no activity for 6 months will now display an alert.  Such a System can be deprecated if it is no longer in use, or the snooze button can be clicked here to count this as an active System for the next year. This is appropriate for some types of Systems that rarely need to receive updates.

System Branch Hygiene

System Branch Hygiene is a powerful tool for understanding existing issues in a System's Perforce branches. It is available on the System details page for each Tier 1 or 2 System.

Core functionality of the tool has been in place for some time, but recent enhancements have improved detection of some edge cases.

Running this check on a mature System is a great way to identify any items that may cause problems in the future.

Conclusion

In these areas and across the CCR application, there have been many changes this year with the goal of promoting the best usage of CCR. Future improvements will focus on further exposing information and developing tools that help users of CCR to better understand their Systems and address issues proactively.

If you have any questions or suggestions for ways that the CCR application can encourage best practices, please let us know at ccrdev@intersystems.com.

Discussion (0)1
Log in or sign up to continue
Announcement
· 17 hr ago

IPM Version 0.10.5 Release Notes

IPM version 0.10.5 has been released on January 15, 2026. This new version contains a ton of improvements and bug fixes so be sure to check it out either directly from the GitHub page or from the Community Registry!

The big changes include the following:

  • A rewrite of dependency resolution that drastically improves performance, including a 200x speed increase in very complicated cases
  • A History Log that tracks the IPM installs, loads, updates, and uninstalls that can be viewed using zpm "log" 
  • System expressions, like ${namespace}, and $$$ macros are now evaluated in CPF merge files allowing for more initial configuration flexibility
  • <Invoke> in module.xml behaves more intuitively by always checking the %Status return value if and only if the method signature declares %Status is returned. This means an error will be thrown if nothing is returned, the return value is not a %Status, or it is not $$$OK.

Here's the full changelist:

Added

  • #938: Added flag -export-python-deps to package command
  • #462: The repo command for repository configuration now supports secret input terminal mode for passwords with the -password-stdin flag
  • #935: Adding a generic JFrog Artifactory tarball resource processor for bundling artifact with a package and deploying it to a final location on install.
  • #950: Added support for listing installed Python packages using list -pythonlist -py and list-installed -python
  • #822: The CPF resource processor now supports system expressions and macros in CPF merge files
  • #578: Added functionality to record and display IPM history of install, uninstall, load, and update
  • #961: Adding creation of a lock file for a module by using the -create-lockfile flag on install.
  • #959: In ORAS repos, external name can now be used interchangeably with (default) name for install and update, i.e. a module published with its (default) name can be installed using its external name.
  • #951: The unpublish command will skip user confirmation prompt if the -force flag is provided.
  • #1018: Require module name for uninstall when not using the -all flag

Changed

  • #316: All parameters, except developer mode, included with a loadinstall or update command will be propagated to dependencies
  • #885: Always synchronously load dependencies and let each module do multi-threading as needed to load using multicompile instead of trying to do own multi-threading of item load which causes lock contention by bypassing IRIS compiler.
  • #481: Improve BuildDependencyGraph performance by doing the following:
    • Eliminate recursion and use iteration.
    • Remove depth first search and do pure breadth first search.
    • Have better caching of results for module searches by collapsing search expressions (reducing expressions that are intersections).

Removed

  • #938: Removed secret flag NewVersion handling in %Publish()

Fixed

  • #943: The load command when used with a GitHub repository URL accepts a branch argument again
  • #701: Fix misleading help comments about search command
  • #958: Update command should not fail early if external name is used
  • #970: fix: Resolve error in 'generate' command WebApp processing
  • #965: FileCopy on a directory with a Name without the leading slash now works
  • #937: Publishing a module with a <WebApplication> containing a Path no longer errors out
  • #957: Improved error messages for OS command execution. Now, when a command fails, the error message includes the full command and its return code. Also fixed argument separation for the Windows attrib command and removed misleading error handling for missing commands.
  • #789: Fix error when listing modules for an ORAS repo with a specified namespace.
  • #999, #1000: Installing IPM cleans up stale mappings used in old versions of IPM
  • #1007: The ${ipmDir} expression now works in the <Arg> of an <Invoke>
  • #1015: Fix dependency resolution bugs where * as the version requirement and intersecting ranges wouldn't work properly.
  • #1036: The update command no longer propagates developer mode to dependencies

Deprecated

  • #828: The CheckStatus flag for <Invoke> action has been deprecated. Default behavior is now to always check the status of the method if and only if the method signature returns %Library.Status
  • #885: -synchronous flag since loading dependencies synchronously is now the default behavior.

Security

  • urllib3 wheel has been updated to 2.6.3

 

If you have any questions, suggestions, or bugs you'd like to raise, feel free to bring them up here or on the GitHub page. (On GitHub, questions and suggestions should go to the discussions page, and bugs should go to the issues page.)

1 new Comment
Discussion (1)3
Log in or sign up to continue