I want to share my experience building customer-facing AI conversations with Parlant. Building reliable AI conversations became surprisingly straightforward with it. Rather than endlessly fiddling with prompts trying to get them to behave properly, I can now precisely control how my agents respond to specific scenarios with clear, simple, contextual guidelines.
What I like most about Parlant is that it aligns with how I think about conversations, not as standalone responses (like most Q&A RAG agents), but as ongoing interactions that shift, adapt, and carry context forward.
The project I was working on and sharing here is designed around three healthcare-focused use cases, each demonstrated through natural interaction examples. These conversations will simulate real exchanges between a patient and a healthcare assistant, covering appointment booking, medication information, and insurance policy management.
The goal in this post is to share my journey using Parlant and walk through how I structured the project, how simple it is to set up an agent, define its guidelines, and connect it to my tools. Along the way, I'll highlight some roadblocks I encounter and explain how to overcome them. Beyond showing how easy it is to build agents with Parlant, I also want to dive into best practices on how to ensure accurate, reliable responses.
My Use Case
My first use case focuses on helping a patient reschedule an appointment with their doctor. I'll show how the agent sticks to the rules and guidelines I've set, handling context shifts smoothly while keeping responses clear and accurate. Here's the ideal conversation I'm looking to achieve:
The Ideal Conversation:
Patient: Hi, I need to reschedule my appointment with Dr. Chen please.
Agent: Hey Sarah! I'd be happy to help reschedule your appointment with Dr. Chen. I see your appointment is scheduled for March 18th, at 2:30 PM. What day or time would work better for you?
Patient: I need something preferably in the morning.
Agent: Let me check Dr. Chen's availability for those times. I see openings on Wednesday the 26th at 9:30 AM or Friday the 28th at 10:15 AM. Would either of those work for you?
Patient: The Friday appointment works. Also, will I need to do any lab work before this appointment?
Agent: I've rescheduled your appointment for Friday, March 28th at 10:15 AM with Dr. Chen. Regarding lab work, I see Dr. Chen has ordered a basic metabolic panel for you. You should come in 15 minutes early to have blood drawn before your appointment. Is there anything else you need help with today?
Patient: That's all. Thank you!
Agent: You're welcome, Sarah. You'll receive a confirmation via email shortly. Have a great day!
Structuring My Application - Laying the Groundwork
I don't want you to get lost with my setup. The order in which you build your agent is totally up to you. I'll do my best to explain every step I took in structuring my application.
Setup with Poetry
I'm creating this project with poetry, but pip install parlant
works the same.
poetry new healthcare-agent
cd healthcare-agent
poetry add parlant
Databases
I'm using two separate JSON files: one for patients and one for doctors. The patients.json includes all relevant information, such as contact details, upcoming appointments, lab results, medications, and allergies, for every patient.
The doctors.json contains the doctors' specialty, availability, current patients, and scheduled appointments for each as well. The goal is to keep both files in sync, so that when a new patient tries to book an appointment with a doctor, they'll see the most up-to-date availability.
My Agent Tools
For me, planning and preparation are key to building a good application, possibly a large-scale one. I start by noting what tools my agent needs to assist the patient in rescheduling an appointment. When users say, “I want to schedule an appointment with my doctor,” I expect my agent to automatically recognize the patient, identify their doctor, and assist with their booking request.
Here's how I'm breaking this down:
Identifying The Patient: A tool to fetch the patient's data from the patients.json
. It's triggered as a tool-enabled context variable, ensuring that the agent always has access to the most up-to-date patient information throughout the conversation.
Listing Doctors' Availability: A tool to fetch the doctors' availability slots. It is triggered when the patient asks for their doctor's availability in any form within the conversation.
Rescheduling an Appointment: A tool to reschedule an existing appointment. It is triggered when the patient confirms the scheduled time slot they want to reschedule and the new time slot they wish to reschedule the appointment with.
My focus here is rescheduling, but I made sure the agent can also handle new appointments and cancellations. I'm not covering them here, but they're part of the broader flow.
Building My Healthcare Agent
I'm going to start setting up my healthcare agent. First, I'll run the server on my terminal.
parlant-server run
Creating My Agent
On a new terminal, I'll create my agent and give it a name and a personality through this command:
parlant agent create `
--name "David" `
--description "You are a detail-oriented healthcare agent who combines
warm empathy with remarkable efficiency, maintaining a gentle sense of
humor to put anxious patients at ease, while calmly managing complex
medical needs without forgetting previously discussed details, and
skillfully prioritizing urgent matters without dropping routine tasks."
Creating My Guidelines
This part is really fun. I'll create a guideline where a patient requests to schedule an appointment, and the agent asks for the patient's preferred times. I'll also create another guideline to have the agent suggest time slots based on the patient's preferences. I'm just testing this out to make sure my agent focuses on the instructions I give.
parlant guideline create `
--condition "patient asks to schedule an appointment" `
--action "ask the patient for their preferred time"
parlant guideline create `
--condition "patient gives preferred times for scheduling an appointment" `
--action "provide timeslots from 9:00AM to 11:00AM for morning preferences and 1:00PM to 5:00PM for afternoon preferences"
Testing It Out - Sandbox UI
To test it out, I'll navigate to Parlant's Sandbox UI at http://localhost:8800/chat and start a conversation with David, my healthcare assistant.
Parlant's Sandbox UI gives me a lot of insights into how my agent reacts to the conversation. It allows me to debug its behavior and understand its thinking process, something that you don't see much in other frameworks.
Building My Conversation Flow
This is where things get interesting. I'm going to build a flow that replicates the conversation experience from the example I listed above. I'll create service tools that work with my databases and tie them to my guidelines. When a guideline is triggered, my agent will use those tools I built to respond to the patient based on the conversation's context.
Setting Up My Service Tools
I'll create my service tools with the module command. This command generates a boilerplate module and automatically registers it in my ./parlant.toml
, so the server loads it at startup.
parlant-server module create module --template tool-service
My module.py
is now located at the core of my application. However, I've reordered my files to match each conversation topic, and I'll have a module.py
built that acts as the main service entry point for each topic.
healthcare-agent/
└── modules/
└── appointments/
├── module.py # module entry point
├── general.py # general functions
├── scheduling.py # scheduling functions
├── rescheduling.py # rescheduling functions
└── cancelling.py # cancelling functions
These are the tools I'm using through the module.py
in my ./modules/appointments
from contextlib import AsyncExitStack
from lagom import Container
from typing import Annotated
from parlant.sdk import (
PluginServer,
ServiceRegistry,
ToolContext,
ToolParameterOptions,
ToolResult,
tool,
)
from modules.appointments.general import get_patient_data, get_doctors_availability
from modules.appointments.rescheduling import reschedule_appointment
EXIT_STACK = AsyncExitStack()
PORT = 8199
TOOLS = [
get_patient_data,
get_doctors_availability,
reschedule_appointment,
]
async def initialize_module(container: Container) -> None:
host = "127.0.0.1"
server = PluginServer(
tools=TOOLS,
port=PORT,
host=host,
hosted=True,
)
await container[ServiceRegistry].update_tool_service(
name="appointments",
kind="sdk",
url=f"http://{host}:{PORT}",
transient=True,
)
await EXIT_STACK.enter_async_context(server)
EXIT_STACK.push_async_callback(server.shutdown)
async def shutdown_module() -> None:
await EXIT_STACK.aclose()
Building My Custom Tools
With Parlant, building tools requires me to use ToolContext
as a tool parameter and ToolResult
to return the output.
@tool
def mytool(context: ToolContext) -> ToolResult:
# your code here
return ToolResult(data)
The ToolContext
gives me access to things like customer_id
, agent_id
and session_id
that are used in the conversation. Then, the function returns the data wrapped in a ToolResult
, which is stored in the conversation context.
My First Tool - Doctor's Availability
I'm going to store all my information-fetching tools inside my general.py
. This will include getting patients' data records, doctors' availability, healthcare coverage, and more, if I need to add.
I'm going to start with fetching a doctor's availability. Here's an Idea of how this would look.
import json
from parlant.core.services.tools.plugins import tool
from parlant.core.tools import ToolContext, ToolResult
DOCTORSDB_PATH = "doctors_database_path"
@tool
def get_doctors_availability(context: ToolContext, doctor_name:str) -> ToolResult:
doctors_data=fetch(Path(DOCTORSDB_PATH))
doctor = next((entry["doctor"] for entry in doctors_data if entry["doctor"]["name"] == doctor_name), None)
if doctor is None:
return ToolResult(f"No doctor with the name {doctor_name}")
availabilities = doctor["doctor"]["scheduling"]["availability"]
sorted_availability = _sort_datetime(availabilities)
sorted_availability = availabilities
return ToolResult(sorted_availability)
I'll have this tool trigger when a patient asks about a doctor's availability schedule. The tool takes the doctor's name as an argument and searches through the doctors' database. I'll trigger it with this guideline:
parlant guideline create `
--condition "patient asks about a doctor's availability" `
--action "suggest the two most recent availability slots"
Now to tie it with my tool:
parlant guideline tool-enable `
--id $GUIDELINE_ID `
--service "appointments"
--tool "get_doctors_availability"
Let's test out the result:
Like a toolbox pre-loaded for the job at hand!
All right, so I covered a few key parts of my project. Still, I'm missing an important piece in my application. I want my agent to recognize the patient they're speaking to. For that, I'm going to implement Parlant's customer feature, which will allow the agent to identify the patient and retrieve their data record.
Identifying Patients
In Parlant, a “customer” is just anyone talking to the agent. They are set in the Parlant server, and I can personalize my agent's behavior for each one. For now, I just want my agent to recognize them and get their medical information. I'm going to create a customer with Parlant and have its ID match the one I have in my databases.
parlant customer create --name "Sarah Johnson"
This returns a customer ID that I'll match with my patient ID in my patients.json
database and will be shared with the agent through the conversation's context. So when the agent calls get_patient_data
or any tool needing a patient ID, it will access it from the tool's ToolContext
using customer_id
.
@tool
def get_patient_data(context: ToolContext) -> ToolResult:
patients_db = fetch(PATIENTSDB_PATH)
patient = next(entry["patient"] for entry in patients_db if entry["patient"]["patient_id"] == context.customer_id, None)
return ToolResult(patient)
Now, when the tool is called, it will return the data record for the patient whose context.customer_id
matches the patient_id
from the patients database.
Keeping Track Of The Latest Record Updates
I mentioned earlier that there's a way to ensure that my agent works with the most up-to-date data from the patient in the database. This stems from a common misuse of guidelines, which I initially fell into as well.
At first, I thought I needed to trigger the get_patient_data
tool using a guideline, where the condition is that it triggers at the start of the conversation. The problem with that approach is that the agent only fetches the data at the beginning. If that data changes later, the agent won't call the get_patient_data
tool again because the trigger condition no longer applies.
The right way to do it is to apply the tool-enabled context variable. It's a way to continuously run the get_patient_data
tool, on every agent response. Here's how to do it:
parlant variable create `
--description "Getting the patient the most up-to-date medical record" `
--name "up-to-date-patient-medical-record" `
--service "appointments" `
--tool "get_patient_data"
I can also control the time frames in which I want this tool to be triggered by adding --freshness-rules
Time to put it to the test:
Cooking Up the Last Bits
Now that I've walked through the baseline of building my healthcare agent, it's time to pull it all together. I'm going to wrap this up and bring the rest of the implementation to life.
These are the guidelines I've set in place:
parlant guideline create `
--condition "patient requests to reschedule an appointment" `
--action "refer the patient's scheduled appointments and ask which appointment they want to reschedule"
parlant guideline create `
--condition "patient chooses the appointment they want to reschedule" `
--action "ask the patient what day or time would work better for them to reschedule."
parlant guideline create `
--condition "patient provides a preferred day or time for rescheduling" `
--action "provide the doctor's next two available appointment slots based on the user's preference"
parlant guideline create `
--condition "patient chooses appointment slot for rescheduling with their doctor" `
--action "assist and confirm the appointment in detail"
Now, I'll set the tools I want to trigger:
This guideline:
Condition: patient provides a preferred day or time for rescheduling
Action: provide the doctor's next two available appointment slots based on the user's preference
Triggers this tool: get_doctors_availability
This guideline:
Condition: patient chooses appointment slot for rescheduling with their doctor
Action: assist and confirm the appointment in detail
Triggers this tool: reschedule_appointment
First Tentative Conversation
This was the first full conversation I had with the agent, start to finish. My biggest learning curve wasn't in the setup (which felt smooth and straightforward), but simply learning how to control the agent through guidelines and tools.
What I really appreciate is that I don't need to rely on excessive prompt tuning to get the agent to follow my “business needs”. Parlant already has a smart system in place for that, called ARQs.
My agent now follows my instructions the way I wanted it to. Regardless of that, this isn't the conversation experience I'm expecting to have (Obi-Wan Kenobi mind control gesture). Thankfully, Parlant addresses this, too.
In my next post, I'll cover how to match the conversation style of my ideal example using Styled Utterances. I'll walk through how to build utterance templates that reflect the tone I want and show how to restrict the agent to only use responses generated from those templates when generating responses.