Streamlining Insurance Claims Process with Agentic AI: LangGraph Example

Arun Chalise
8 min readNov 19, 2024

--

Agentic AI has a huge potential to transform the future of Apps and this example showcases how we can build one using LangGraph. The Agentic app takes full control of the insurance claim process right from submission to completion making the entire workflow seamless and efficient.

The agent will handle the entire workflow in a insurance claim process: validating customer claims, storing them in a database, performing fraud checks, escalating for human approval when necessary, processing payments, and even sending email updates to customers. This example showcases the power and flexibility of LangGraph in automating real-world tasks with ease.

Here is the high level workflow:

Claims Processor Workflow

And these are the steps we follow to create the app:

  • Scaffold the project
  • Define your application state
  • Define tools that the agent will need to perform tasks that are specific to your application and driven by your business logic
  • Define Graph (nodes and edges)
  • Set up Human in the middle for approvals
  • Define SystemMessage for the agent
  • Wrap the agent in API for customer access

Completed source code for the application is at:

https://github.com/achalise/llm-experiments/tree/main/claim-processor-agent

Scaffold the Project

Start by using the LangGraph starter template to scaffold your project. You can clone the template repository from LangGraph Starter Template.

Since we’re using the OpenAI model for this example, ensure you have an API key for OpenAI set up as an environment variable. You can do this by adding your key to the environment variable OPENAI_API_KEY.

export const model = new ChatOpenAI({model: "gpt-3.5-turbo", temperature: 0});

Define the Application State

LangGraph handles state transitions by passing a State object between nodes. For our application, the State object includes:

Message Sequence
A chronological record of interactions between the human, the agent, and the tools which will contain:

  • HumanMessage: Messages input by the user.
  • ToolMessage: Responses or outputs from tools used by the agent.
  • AIMessage: Messages generated by the agent itself.

Custom Application Data
Additional data specific to the application, for example the following fields for our application:

  • ClaimDetails: Information about the claim being processed.
  • paymentApproved: A boolean value indicating whether payment has been approved.
export const StateAnnotation = Annotation.Root({
messages: Annotation<BaseMessage[], BaseMessageLike[]>({
reducer: messagesStateReducer,
default: () => []
}),
claimDetails: Annotation<ClaimDetails>,
paymentApproved: Annotation<boolean | undefined>
});

export const claimDetailsSchema = z.object({
claimId: z.string().optional().describe("ID of claim").default("UNDEFINED"),
firstName: z.string().describe("First name of user (e.g. John)"),
lastName: z.string().describe("Last name of user (e.g. Doe)"),
claimType: z.string().describe("Type of claim (e.g. c)"),
claimAmount: z.number().positive().describe("Amount of claim (e.g. 100)"),
claimDescription: z.string().describe("Description of claim (e.g. Loan)"),
claimStatus: z.enum(["CREATED", "PENDING", "FRAUD_CHECK_SUCCESS", "APPROVED", "DENIED"]).describe("Status of claim (e.g. Pending)"),
policyNumber: z.string().describe("Policy number (e.g. 123456789)"),
userEmail: z.string().email().describe("Email of user (e.g. 123456789)"),
address: z.string().describe("Address of user (e.g. 123 Main St)"),
});

export type ClaimDetails = z.infer<typeof claimDetailsSchema>;

Define the Tools

Tools are used by the agent to gather additional information or perform specific operations, such as interacting with APIs or databases. For our claims processing application, the agent relies on tools to execute the following tasks:

  1. Retrieve User Details
    Fetch information about the customer submitting the claim.
  2. Create Claim Records
    Store the submitted claim in the database for processing and tracking.
  3. Carry Out Fraud Checks
    Analyse the claim to detect potential fraud using predefined rules or external services.
  4. Approve Payments
    Handle the approval of payments, either autonomously or through escalation for human intervention if needed.
  5. Send Email Notifications
    Notify the customer about the status of their claim, such as approval, rejection, or additional requirements.

These functionalities are implemented as tools below:

export const userDetailsTool = tool((input) => {
console.log(`Getting user details for ${JSON.stringify(input)} ...`);
return JSON.stringify({
userEmail: input.userEmail,
userExists: true
})
}, {name: "user_details", schema: z.object({
userEmail: z.string().email().describe("Email of user"),
})
});

export const fraudCheckTool = tool((input) => {
console.log(`Fraud check for ${JSON.stringify(input)}...`);
return JSON.stringify({
result: "SUCCESS",
message: "Fraud check was successful"
})
}, {name: "fraud_Check", schema: z.object({
firstName: z.string().describe("First name of user"),
lastName: z.string().describe("Last name of user"),
userEmail: z.string().email().describe("Email of user"),
policyNumber: z.string().describe("Policy number")
})
});

export const createOrUpdateClaimTool = tool((input) => {
console.log(`Creating or updating claim...`);
if(!input.claimId || input.claimId === "UNDEFINED") {
let claimId = Math.random().toString(36).substr(2, 9);
input.claimId = claimId;
return `Successfully created claim for policy number ${input.policyNumber}. claimID for this claim is ${input.claimId}.`;
} else {
return `Successfully updated claim with ID ${input.claimId} for policy number ${input.policyNumber} to status ${input.claimStatus}`;
}
}, {name: "create_or_update_claim", schema: claimDetailsSchema});

export const approvePaymentTool = tool((input) => {
console.log(`Approving the claim...`);
if(input.claimStatus !== "FRAUD_CHECK_SUCCESS") {
return `Payment is not approved as the fraud check failed for the claim`;
} else {
return `Approved the claim with ID ${input.claimId} for policy number ${input.policyNumber} to status APPROVED.`;
}
}, {name: "approve_payment", schema: claimDetailsSchema, description: "Approve payment for the claim"})

export const sendConfirmationEmailTool = tool((input) => {
console.log(`Sending email for the claim...`);
return `Email sent to ${input.userEmail} for the claim with status ${input.claimStatus}`;
}, {name: "send_confirmation_email", schema: claimDetailsSchema, description: "Send email to the customer when claim is approved"})

Define the Graph

Here we build the LangChain graph by following these steps:

  1. Add Nodes
    Define the nodes that represent different stages of the claim processing workflow. Each node corresponds to a specific action or decision point. Examples include:
  • Agent Node: Handles interactions with the model for generating insights or responses.
  • Tools Node: Invokes the appropriate tool from the list of available tools for operations like database updates or API calls.
  • Business Logic Nodes: validate_claim_detail to execute custom business logic to verify the claim details and validate_approval_request implementing business-specific rules to assess whether approval is valid or additional input is required.

2. Add Edges Between Nodes
Establish the connections (edges) between nodes to define how the workflow progresses. This ensures a logical flow from one operation to the next, covering all possible paths the agent might take.

3. Implement Routing Logic
Define the rules for transitioning between nodes based on the application state. For example:

  • Move from the Agent Node to the Tools Node if the agent determines a tool operation is needed.
  • Transition to validate_claim_detail if claim data validation is required.
  • Proceed to validate_approval_request for payment approval assessments.
const builder = new StateGraph(StateAnnotation)
// Add the nodes to do the work.
// Chaining the nodes together in this way
// updates the types of the StateGraph instance
// so you have static type checking when it comes time
// to add the edges.
.addNode("agent", callModel)
.addNode("tools", toolNode)
.addNode("validate_claim_detail", validateClaimDetail)
.addNode("validate_approval_request", validateApprovalRequest)
// Regular edges mean "always transition to node B after node A is done"
// The "__start__" and "__end__" nodes are "virtual" nodes that are always present
// and represent the beginning and end of the builder.
.addEdge("__start__", "agent")
.addEdge("tools", "agent")
.addEdge("validate_claim_detail", "tools")
.addEdge("validate_approval_request", "tools")
// Conditional edges optionally route to different nodes (or end)
.addConditionalEdges("agent", route, ["tools", "validate_claim_detail", "validate_approval_request",
END]);

The layout of the graph can be viewed in LangGraph studio:

Setup up Human In The Middle for approvals

The validate_approval_request node contains custom business logic to handle payment approvals based on the claim amount. Here's how it works:

Claim Amount Validation

  • The node checks if the claim amount exceeds a predefined threshold (e.g., a high-value claim).
  • If the amount is above this limit, the node triggers an interrupt for human intervention.

Human Intervention

  • During the interrupt, a human user reviews the claim and decides whether to approve the payment.
  • The human sets the paymentApproved flag to true if they approve the payment.

Execution Resumption

  • Once the paymentApproved flag is set to true, the execution continues to the next step in the workflow.
  • If the flag remains false, the process halts until the necessary approval is provided.

This logic ensures that sensitive or high-value transactions receive proper human oversight while allowing routine claims to proceed automatically.

if (claimDetails.claimAmount > 50000 && !paymentApproved) {
throw new NodeInterrupt("Please approve the payment for amount greater than 30000");
}

Define System Message

This is where we provide context and instruct the chat model how to process a claim submission.

export const systemSetUpMessage = (): string => { 
return `

You are an expert customer service agent for an insurance company.
Your job is to help customers submit their insurance claims.

Step 1: Validate the input data.
In order to submit a claim, users must provide their email address, policy number, claim type,
claim amount, and a claim description.
If any of the fields are missing, do not proceed. Claim type and Insurance type are same.

Step 2: Check if the user exists.
Next you will check whether the user exists with the same policy number.
If the user doesn't exist, you will respond with an error message saying user doesn't exist.

Step 3: Create a new claim.
If the user exists, you will update the status of the claim to CREATED and save the claim.

Step 4: Perform fraud check.
You will then carry out fraud check for the user. If the fraud check result is SUCCESS then
update the claim with status FRAUD_CHECK_SUCCESS.
You will then save the claim.

Step 5: Approve payment.
You will then approve payment for the claim only if the claim status is FRAUD_CHECK_SUCCESS.

Step 6: Send confirmation email.
You will then send an email to the user with the claim status and the claimId only after the claim status is APPROVED.

You will include both claimId and claimStatus in the final response to the user.

`
}

Stand up the API

Our ExpressJS API, accessible at /chat/api, serves as the primary interface for user queries. This API enables direct interaction with our agent.

app.get('/api/chat', async (req: Request, res: Response) => {
const { message } = req.query;
console.log(`Received message: ${message}`);

const result = await graph.withConfig(config).invoke({
messages: [new HumanMessage(message as string)],
},
{ recursionLimit: 15, ...config});
result.messages.forEach((msg: BaseMessage) => console.log(msg.content));
res.json({ message: result.messages[result.messages.length - 1].content });
});

Testing

Build the project and launch the API:

  • npm run build
  • node dist/src/app.js

Send api request, for example send the following query:

I would like to submit a claim, my details are as below: name: Joe Blog insurance type: car address: 300 Pramatta Road, Sydney 2000 amount: 5000 description: I had a car accident under no fault email: user@email.test policyNumber: 1122323232 When my application is completed, please write a satire for me.
curl -X GET "http://localhost:3000/api/chat?message=I+would+like+to+submit+a+claim%2C+my+details+are+as+below%3A+name%3A+Joe+Blog+insurance+type%3A+car+address%3A+300+Pramatta+Road%2C+Sydney+2000+amount%3A+5000+description%3A+I+had+a+car+accident+under+no+fault+email%3A+user%40email.test+policyNumber%3A+1122323232+When+my+application+is+completed%2C+please+write+a+satire+for+me."

Agent will successfully process the claim and respond with following message:

{"message":"Your claim has been successfully submitted and approved. Here is a satire for you:\n\n---\n\nIn the land of insurance woes, where accidents strike without a cause,\nJoe Blog stood tall, with a claim so true, a car mishap he did rue.\n\nOn Pramatta Road, in Sydney's heart, his car met fate, torn apart,\nNo fault of his, the truth be told, yet the damages took their toll.\n\nWith claim in hand, and approval bright, Joe's journey now sees the light,\nThe insurance gods have smiled on him, his claim approved, to the brim.\n\nSo here's to Joe, a tale well spun, of car mishaps and battles won,\nMay his wheels roll smooth, his path be clear, in the world of claims, no fear.\n\n---\n\nIf you need any further assistance or have any more claims to submit, feel free to let me know!"}

Conclusion

In this example, we leveraged langgraph to rapidly develop an intelligent application. This application can process claim submissions autonomously, using the provided tools, and escalate cases to human agents when necessary.

--

--

No responses yet