Build an AI Insurance Analyst

Build an AI-powered insurance claims and risk assessment web app.

Introduction

30 Second Summary

Insurance professionals spend hours reviewing claims and assessing risk, manually cross-referencing policy details, coverage types, and loss histories. What if you could build an AI tool that handles that first-pass analysis in seconds?

In this project, you will build an AI-powered insurance analyst web app that uses Claude to analyze claims and assess underwriting risk. The app produces structured recommendations and risk scores based on insurance logic you define.

What You'll Build

A local Streamlit web application with two analysis modes that sends insurance scenarios to the Claude API and returns structured recommendations.

By the end of this project, you'll have:

  • A Streamlit web interface with two tabs for Claims Analysis and Risk Assessment.
  • Domain-specific system prompts that encode real insurance decision logic into Claude's responses.
  • A working Claude API integration that produces structured approve/deny/escalate recommendations and underwriting risk scores.
  • Secret Mission: Add a fraud detection flag system that highlights suspicious patterns in claims.

Are there any prerequisites?

None. This project includes setup instructions for all required tools, including Python, VS Code, and the Anthropic API. You don't need prior programming experience to follow along.

Set Up Your Environment

Before you can build an AI-powered insurance analyst, you need a few tools installed and configured on your machine.

In this step, you'll install Python, create an isolated project environment, and connect to the Claude API so your app can send prompts and receive analysis.

In this step, get ready to:
  • Install Python 3.13 on your machine.
  • Create a project folder with a virtual environment and install packages.
  • Get your Anthropic API key and store it securely.
Install Python 3.13

Python is the programming language you'll use to build the insurance analyst app. Let's check if you already have the right version installed.

  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type Terminal (macOS) or Command Prompt (Windows) and press Enter to open it.
  • Check your Python version by running this command:

macOS

python3 --version

Windows

python --version

✔️ I see version 3.13 or higher

You're all set. Move on to the next substep below.

ⓧ I see an older version

You have Python installed, but you need version 3.13 or higher for this project.

  • Go to python.org/downloads in your browser.
  • Download the Python 3.13 installer for your operating system.
  • Run the installer. On Windows, check the Add Python to PATH checkbox on the first screen before clicking Install Now.
  • Close your terminal and open a new one, then re-run the version check command above.

Why a new terminal?

Your terminal caches the path to the old Python when it starts up. Opening a fresh terminal forces it to pick up the newly installed version.

ⓧ Command not found

Python is not installed yet. Let's install it now.

  • Go to python.org/downloads in your browser.
  • Download the Python 3.13 installer for your operating system.
  • Run the installer. On Windows, check the Add Python to PATH checkbox on the first screen before clicking Install Now.
  • Close your terminal and open a new one, then run the version check command above to confirm.

macOS users on older system Python

macOS may ship with an older Python version. After installing from python.org, the new version should be available as python3. If you still see an old version, try running python3.13 --version to confirm the new install is accessible.

Create your project folder and install packages

Now you'll create a dedicated folder for this project and set up a virtual environment inside it. A virtual environment keeps your project's packages isolated from other Python projects on your machine.

  • Move to your Desktop by running this command:

macOS

cd ~/Desktop

Windows

cd %USERPROFILE%\Desktop
  • Create your project folder and move into it by running these commands:
mkdir ai-insurance-analyst
cd ai-insurance-analyst
  • Create and activate a virtual environment by running these commands:

macOS

python3 -m venv .venv
source .venv/bin/activate

Windows

python -m venv .venv
.venv\Scripts\activate.bat

You should see (.venv) appear at the beginning of your terminal prompt. This confirms the virtual environment is active.

  • Install the two packages your project needs by running:
pip install anthropic streamlit

What are these packages?

The anthropic package is the official Python SDK for communicating with the Claude API. It handles authentication, request formatting, and response parsing.

The streamlit package is a framework for building web apps with Python. It turns your Python scripts into interactive browser-based interfaces with minimal code.

After installation completes, you should see a message ending with Successfully installed followed by a list of packages.

Seeing a permission error or install failure?

Make sure your virtual environment is active. You should see (.venv) at the start of your terminal prompt. If not, re-run the activation command from the platform tabs above.

If you see an error about pip not being found, try python -m pip install anthropic streamlit instead.

Still stuck? Help me troubleshoot my pip install error

Get your Anthropic API key

Your app needs an API key to authenticate with Claude. This key proves to Anthropic that your app has permission to send requests.

  • Open your browser and go to console.anthropic.com.
  • Create an account or sign in if you already have one.
  • Navigate to Settings and find the API Keys section.
  • Click Create Key to generate a new API key.
  • Copy the key immediately. You will not be able to see the full key again after closing the dialog.

Keep your API key private

Your API key is like a password. Never share it publicly, commit it to version control, or paste it directly into your code. In the next step, you'll store it in a secrets file that stays local to your machine.

Now open VS Code to your project folder so you can create the secrets file.

  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type VS Code and press Enter to open it.
  • Click File in the top menu bar.
  • Click Open Folder.
  • Navigate to your Desktop and select the ai-insurance-analyst folder, then click Open.

Streamlit has a built-in secrets system that reads from a special file. You'll create this file now.

  • In the Explorer sidebar on the left, hover over the project name and click the New Folder icon.
  • Name the folder .streamlit and press Enter.
  • Right-click the .streamlit folder and select New File.
  • Name the file secrets.toml and press Enter.
  • Paste the following into secrets.toml, replacing your-api-key-here with the API key you just copied:
ANTHROPIC_API_KEY = "your-api-key-here"

Why secrets.toml instead of a .env file?

Streamlit has a built-in secrets management system. It automatically reads from .streamlit/secrets.toml and makes values available via st.secrets in your code. This avoids installing an extra package like python-dotenv.

  • Save the file with Cmd+S (macOS) or Ctrl+S (Windows).

Your Explorer sidebar should now show a .streamlit folder containing secrets.toml.

Don't see the .streamlit folder?

Files and folders starting with a dot are sometimes hidden by default. In VS Code, they should always appear in the Explorer sidebar. If you accidentally created the folder outside the project, delete it and try again from within the ai-insurance-analyst root folder.

Need help? Help me set up my .streamlit/secrets.toml file

✔️ Awesome, I've got everything!

Great. Double check you've saved your secrets.toml file with your real API key pasted in.

ⓧ I'd like to double check the full code

Your .streamlit/secrets.toml file should look like this (with your actual API key in place of the placeholder):

ANTHROPIC_API_KEY = "your-api-key-here"

Your environment is ready. Next up, you'll build the claims analyzer that uses Claude to evaluate insurance claims and recommend whether to approve, deny, or escalate them.

Build the Claims Analyzer

Your environment is ready with Python, the Anthropic SDK, and Streamlit installed. Now it's time to build the core of your insurance analyst app.

In this step, you'll create the main application file and build the Claims Analysis mode. This is where your insurance domain knowledge meets AI. You'll write a system prompt that teaches Claude how a senior claims analyst thinks, then wire it up to a clean web interface.

In this step, get ready to:
  • Create the app structure with Streamlit page config and two analysis tabs.
  • Write a system prompt that encodes claims analysis decision logic.
  • Build the Claims Analysis tab with input fields and an API call to Claude.
Create the app structure

Every Streamlit app starts with a single Python file that defines the page layout and connects to any external services. Your app needs to connect to the Claude API and present two tabs for the two analysis modes.

  • Create a new file called app.py in your project folder by pasting the following code:
import streamlit as st
from anthropic import Anthropic

# Initialize the Anthropic client
client = Anthropic(api_key=st.secrets["ANTHROPIC_API_KEY"])

# Page configuration
st.set_page_config(page_title="AI Insurance Analyst", layout="wide")
st.title("AI Insurance Analyst")
st.write("Powered by Claude AI - Analyze claims and assess risk")

# System prompts
CLAIMS_PROMPT = ""
UNDERWRITING_PROMPT = ""

# Create tabs
tab1, tab2 = st.tabs(["Claims Analysis", "Risk Assessment"])

# Claims Analysis Tab
with tab1:
    st.header("Claims Analysis")
    st.write("Enter claim details for AI-powered analysis")

# Risk Assessment Tab
with tab2:
    st.header("Risk Assessment")
    st.write("Risk assessment coming soon...")

What does this code do?

  • client = Anthropic(api_key=st.secrets["ANTHROPIC_API_KEY"]) creates an API client that reads your key from the .streamlit/secrets.toml file you set up earlier.
  • st.set_page_config() configures the browser tab title and uses a wide layout so the form fields have room to breathe.
  • st.tabs() creates two clickable tabs at the top of your app. You'll build out each tab's content separately.
  • CLAIMS_PROMPT and UNDERWRITING_PROMPT are placeholder strings you'll fill in next.
  • Save app.py.
  • Run the app by entering this command in your terminal:
streamlit run app.py
  • Your browser should open automatically to a page showing the title "AI Insurance Analyst" with two tabs: **Claims Analysis** and **Risk Assessment**.

Not seeing the app in your browser?

  • Make sure your virtual environment is still activated. You should see (.venv) at the start of your terminal prompt.
  • Check that you're running the command from your project folder (the one containing app.py).
  • If the browser didn't open automatically, look for a URL in the terminal output and open it manually.

Help me troubleshoot why my Streamlit app isn't loading in the browser.

Write the claims analysis system prompt

A system prompt is how you embed your domain expertise into the AI. This prompt teaches Claude to think like a senior claims analyst by defining the exact checks it should perform and the output format it should follow.

  • In app.py, find this line:
CLAIMS_PROMPT = ""
  • Replace it with the following full system prompt:
CLAIMS_PROMPT = """You are a senior insurance claims analyst with 15 years of experience. 
Analyze the following claim and provide a structured recommendation.

Your analysis must include:
1. COVERAGE CHECK: Does the claim type align with the policy coverage?
2. AMOUNT ASSESSMENT: Is the claimed amount reasonable for this type of loss?
3. RED FLAGS: Are there any concerns (late filing, vague descriptions, amount near thresholds)?
4. RECOMMENDATION: One of APPROVE, DENY, or ESCALATE TO SENIOR REVIEWER

Format your response as:
## Coverage Check
[Your analysis]

## Amount Assessment
[Your analysis]

## Red Flags
[Any concerns or "None identified"]

## Recommendation: [APPROVE/DENY/ESCALATE TO SENIOR REVIEWER]
[Brief justification]"""

What does this prompt do?

This prompt encodes real claims analysis decision logic into Claude's behavior. It defines four checks that mirror what a human analyst performs:

  • Coverage alignment verifies the claim type matches the policy. A theft claim on a liability-only policy would fail here.
  • Amount assessment checks whether the dollar value is reasonable for the type of loss described.
  • Red flags surface concerns like late filing or amounts near investigation thresholds.
  • The structured output format ensures Claude always gives a clear Approve/Deny/Escalate recommendation, not a vague summary.

Why embed logic in the prompt instead of code?

You could write Python logic to check coverage alignment or flag amounts. But encoding these rules in the system prompt lets the AI reason flexibly about edge cases.

A hard-coded rule can only say "amount > $50,000 = flag." An AI with prompt-based guidelines can reason that a $14,000 water damage claim is normal for a flooded basement but suspicious for a broken faucet.

  • Save app.py.

You won't see a visible change in the browser yet. The prompt is stored in a variable that the Claims Analysis button will use in the next substep.

Build the Claims Analysis tab with input fields and API call

Now you'll build the form that collects claim details and sends them to Claude for analysis. This combines Streamlit's input widgets with the Anthropic SDK to create a complete claims workflow.

  • In app.py, find the Claims Analysis tab section that currently looks like this:
# Claims Analysis Tab
with tab1:
    st.header("Claims Analysis")
    st.write("Enter claim details for AI-powered analysis")
  • Replace that entire section with the following code that adds input fields, a submit button, and the API call:
# Claims Analysis Tab
with tab1:
    st.header("Claims Analysis")
    st.write("Enter claim details for AI-powered analysis")

    col1, col2 = st.columns(2)

    with col1:
        claim_type = st.selectbox(
            "Claim Type",
            ["Property Damage", "Liability", "Business Interruption", "Theft", "Fire", "Water Damage", "Other"]
        )
        policy_coverage = st.selectbox(
            "Policy Coverage Type",
            ["Commercial Property", "General Liability", "Business Owners Policy", "Commercial Auto", "Workers Compensation"]
        )
        claimed_amount = st.number_input("Claimed Amount ($)", min_value=0, value=5000, step=500)

    with col2:
        incident_date = st.date_input("Incident Date")
        policy_start_date = st.date_input("Policy Start Date")
        claim_description = st.text_area("Claim Description", height=120, placeholder="Describe what happened...")

    if st.button("Analyze Claim", type="primary"):
        if claim_description:
            with st.spinner("Analyzing claim..."):
                claim_details = f"""
Claim Type: {claim_type}
Policy Coverage: {policy_coverage}
Claimed Amount: ${claimed_amount:,.2f}
Incident Date: {incident_date}
Policy Start Date: {policy_start_date}
Claim Description: {claim_description}
"""
                message = client.messages.create(
                    model="claude-haiku-4-5",
                    max_tokens=1024,
                    system=CLAIMS_PROMPT,
                    messages=[
                        {"role": "user", "content": f"Please analyze this insurance claim:\n{claim_details}"}
                    ]
                )
                st.markdown("---")
                st.subheader("Analysis Result")
                st.markdown(message.content[0].text)
        else:
            st.warning("Please enter a claim description.")

What does this code do?

  • st.columns(2) splits the form into two columns so the inputs don't stretch across the full page width.
  • st.selectbox() creates dropdown menus for claim type and coverage type. These constrained options help Claude give more focused analysis.
  • st.button("Analyze Claim", type="primary") creates a blue submit button. Everything inside the if block runs only when the learner clicks it.
  • client.messages.create() sends the claim details to Claude using your system prompt. The system parameter passes your claims analyst prompt separately from the user message.
  • message.content[0].text extracts Claude's text response from the API result.
  • Save app.py.
  • Switch to your browser. Streamlit should automatically reload the page. Click the **Claims Analysis** tab.
  • You should see two columns of input fields (dropdowns for claim type and coverage, a number input, date pickers, and a text area) with a blue **Analyze Claim** button below them.

Seeing an error instead of the form?

  • If you see a "secrets" error, make sure your .streamlit/secrets.toml file exists in your project folder and contains your API key.
  • If you see an indentation error, make sure all the code inside with tab1: is indented by exactly 4 spaces.
  • If the page is blank, check that you saved the file. Streamlit only reloads when it detects a saved change.

Help me fix an error in my Streamlit Claims Analysis app.

✔️ Awesome, I've got everything!

Great. Double check you've saved app.py and your browser shows the Claims Analysis form.

ⓧ I'd like to double check the full code

Here's the complete app.py at this point:

import streamlit as st
from anthropic import Anthropic

# Initialize the Anthropic client
client = Anthropic(api_key=st.secrets["ANTHROPIC_API_KEY"])

# Page configuration
st.set_page_config(page_title="AI Insurance Analyst", layout="wide")
st.title("AI Insurance Analyst")
st.write("Powered by Claude AI - Analyze claims and assess risk")

# System prompts
CLAIMS_PROMPT = """You are a senior insurance claims analyst with 15 years of experience. 
Analyze the following claim and provide a structured recommendation.

Your analysis must include:
1. COVERAGE CHECK: Does the claim type align with the policy coverage?
2. AMOUNT ASSESSMENT: Is the claimed amount reasonable for this type of loss?
3. RED FLAGS: Are there any concerns (late filing, vague descriptions, amount near thresholds)?
4. RECOMMENDATION: One of APPROVE, DENY, or ESCALATE TO SENIOR REVIEWER

Format your response as:
## Coverage Check
[Your analysis]

## Amount Assessment
[Your analysis]

## Red Flags
[Any concerns or "None identified"]

## Recommendation: [APPROVE/DENY/ESCALATE TO SENIOR REVIEWER]
[Brief justification]"""

UNDERWRITING_PROMPT = ""

# Create tabs
tab1, tab2 = st.tabs(["Claims Analysis", "Risk Assessment"])

# Claims Analysis Tab
with tab1:
    st.header("Claims Analysis")
    st.write("Enter claim details for AI-powered analysis")

    col1, col2 = st.columns(2)

    with col1:
        claim_type = st.selectbox(
            "Claim Type",
            ["Property Damage", "Liability", "Business Interruption", "Theft", "Fire", "Water Damage", "Other"]
        )
        policy_coverage = st.selectbox(
            "Policy Coverage Type",
            ["Commercial Property", "General Liability", "Business Owners Policy", "Commercial Auto", "Workers Compensation"]
        )
        claimed_amount = st.number_input("Claimed Amount ($)", min_value=0, value=5000, step=500)

    with col2:
        incident_date = st.date_input("Incident Date")
        policy_start_date = st.date_input("Policy Start Date")
        claim_description = st.text_area("Claim Description", height=120, placeholder="Describe what happened...")

    if st.button("Analyze Claim", type="primary"):
        if claim_description:
            with st.spinner("Analyzing claim..."):
                claim_details = f"""
Claim Type: {claim_type}
Policy Coverage: {policy_coverage}
Claimed Amount: ${claimed_amount:,.2f}
Incident Date: {incident_date}
Policy Start Date: {policy_start_date}
Claim Description: {claim_description}
"""
                message = client.messages.create(
                    model="claude-haiku-4-5",
                    max_tokens=1024,
                    system=CLAIMS_PROMPT,
                    messages=[
                        {"role": "user", "content": f"Please analyze this insurance claim:\n{claim_details}"}
                    ]
                )
                st.markdown("---")
                st.subheader("Analysis Result")
                st.markdown(message.content[0].text)
        else:
            st.warning("Please enter a claim description.")

# Risk Assessment Tab
with tab2:
    st.header("Risk Assessment")
    st.write("Risk assessment coming soon...")

Your Claims Analyzer is wired up and ready. Next, you'll build the Risk Assessment mode to give your app full underwriting capabilities.

Build the Risk Assessment Mode

Your Claims Analysis tab is working and ready to process claims. Now it's time to build the second half of your insurance analyst.

The Risk Assessment mode shifts perspective from claims handler to underwriter. You'll build a form that captures property and business details, then feed them to Claude with scoring criteria that mirror real commercial underwriting.

In this step, get ready to:
  • Build input fields for applicant and property details.
  • Write an underwriting system prompt with risk scoring criteria.
  • Connect the Risk Assessment tab to the Claude API.
Build the Risk Assessment tab input fields

Commercial property underwriting relies on specific data points about the business and its physical location. Your form needs to capture each factor that drives the risk score.

  • In app.py, scroll down to the Risk Assessment tab section at the bottom of the file.
  • Find these lines:
# Risk Assessment Tab
with tab2:
    st.header("Risk Assessment")
    st.write("Risk assessment coming soon...")
  • Replace that entire block with the following code to build the input form:
# Risk Assessment Tab
with tab2:
    st.header("Risk Assessment")
    st.write("Enter applicant details for underwriting risk analysis")

    col1, col2 = st.columns(2)

    with col1:
        business_name = st.text_input("Business Name", placeholder="e.g., Smith's Restaurant")
        business_type = st.selectbox(
            "Business Type",
            ["Office", "Retail Store", "Restaurant", "Manufacturing", "Warehouse", "Medical Office", "Auto Repair"]
        )
        building_age = st.number_input("Building Age (years)", min_value=0, max_value=200, value=15)
        construction_type = st.selectbox(
            "Construction Type",
            ["Fire-Resistive (Steel/Concrete)", "Masonry (Brick)", "Frame (Wood)"]
        )

    with col2:
        square_footage = st.number_input("Square Footage", min_value=100, value=5000, step=500)
        num_employees = st.number_input("Number of Employees", min_value=1, value=10)
        has_sprinklers = st.selectbox("Sprinkler System", ["Yes", "No"])
        loss_history = st.text_area(
            "Loss History (last 5 years)",
            height=100,
            placeholder="Describe any prior claims or losses, or write 'None'"
        )

    location = st.text_input("Location", placeholder="e.g., 123 Main St, Chicago, IL")

What does this code do?

This creates a two-column form layout identical to the Claims Analysis tab. The left column captures business and building characteristics. The right column captures size, employees, protection, and loss history.

The location field sits below both columns because it's a full-width input. Each dropdown uses st.selectbox so the learner picks from predefined options that map to standard underwriting categories.

  • Save app.py.
  • You won't test the form yet. The next two substeps add the prompt logic and API connection that make the button work.

Seeing a red error when you save?

Make sure you replaced the entire with tab2: block, including the st.header line. The indentation under with tab2: must be exactly 4 spaces for every line inside it.

help me fix an indentation error in my Streamlit Risk Assessment tab

Write the underwriting system prompt

The system prompt is where you encode real underwriting logic into the AI. Instead of hard-coding a scoring algorithm, you teach Claude the same factors a human underwriter considers.

Why embed logic in the prompt instead of code?

You could write a Python function that calculates a risk score from rules. But a prompt-based approach lets the AI weigh multiple factors together and explain its reasoning in natural language. This mirrors how real enterprise AI tools encode underwriting guidelines.

  • In app.py, scroll up to find the empty underwriting prompt near the top of the file:
UNDERWRITING_PROMPT = ""
  • Replace that line with the full underwriting prompt:
UNDERWRITING_PROMPT = """You are a commercial property insurance underwriter. 
Assess the following risk submission and provide a structured underwriting recommendation.

Use these risk scoring criteria:
- Construction: Frame=High Risk, Masonry=Medium, Fire-Resistive=Low Risk
- Occupancy Hazard: Office=Low, Retail=Medium, Restaurant/Manufacturing=High
- Building Age: Under 10 years=Low Risk, 10-30=Medium, Over 30=High Risk
- Loss History: No losses=Favorable, 1-2 small losses=Acceptable, 3+ or large losses=Unfavorable
- Protection: Sprinklered=Credit, No sprinklers=Surcharge

Provide your response as:
## Risk Score: [1-10, where 1 is lowest risk and 10 is highest]

## Risk Factors
[List each factor and its rating]

## Recommendation: [ACCEPT/DECLINE/REFER TO SENIOR UNDERWRITER]

## Premium Indication
[Suggest a relative premium level: Below Average / Average / Above Average / Well Above Average]
[Brief explanation of pricing rationale]"""

What does this prompt do?

The prompt gives Claude five concrete scoring criteria that any commercial property underwriter would recognize: construction type, occupancy hazard, building age, loss history, and sprinkler protection.

It also defines the output structure. Claude will return a risk score from 1-10, a list of factors, a recommendation (accept, decline, or refer), and a premium indication. This structured format makes the response easy to read and consistent across different submissions.

  • Save app.py.
  • You won't see a change yet. The next substep connects this prompt to the button in your Risk Assessment form.
Connect the Risk Assessment tab to the Claude API

The final piece wires the form inputs to Claude. When the user clicks the button, you'll format their inputs into a structured string, send it to the API with your underwriting prompt, and display the result.

  • In app.py, scroll to the bottom of the Risk Assessment tab.
  • Find the location text input line you just added. Add the following code directly below it (still inside the with tab2: block):
    if st.button("Assess Risk", type="primary"):
        if business_name and location:
            with st.spinner("Assessing risk..."):
                risk_details = f"""
Business Name: {business_name}
Business Type: {business_type}
Building Age: {building_age} years
Construction Type: {construction_type}
Square Footage: {square_footage:,} sq ft
Number of Employees: {num_employees}
Sprinkler System: {has_sprinklers}
Location: {location}
Loss History: {loss_history if loss_history else 'None reported'}
"""
                message = client.messages.create(
                    model="claude-haiku-4-5",
                    max_tokens=1024,
                    system=UNDERWRITING_PROMPT,
                    messages=[
                        {"role": "user", "content": f"Please assess this risk submission:\n{risk_details}"}
                    ]
                )
                st.markdown("---")
                st.subheader("Underwriting Assessment")
                st.markdown(message.content[0].text)
        else:
            st.warning("Please enter a business name and location.")

What does this code do?

This follows the same pattern as the Claims Analysis API call. The risk_details f-string formats all the form inputs into a readable block that Claude can parse against the scoring criteria.

The client.messages.create() call uses the system parameter to pass your UNDERWRITING_PROMPT. This tells Claude to behave as an underwriter before it even sees the applicant data. The response comes back as message.content[0].text and gets rendered as formatted markdown.

  • Save app.py.
  • If your Streamlit app is still running in the terminal, it will automatically reload. If not, you'll launch it in the next step.

Seeing indentation errors or unexpected behavior?

The if st.button line must be indented 4 spaces inside the with tab2: block (same level as location). Everything inside the button's if block goes one level deeper (8 spaces).

help me fix my Risk Assessment API connection code

✔️ Awesome, I've got everything!

Great. Double check you've saved app.py and let's move on to testing.

ⓧ I'd like to double check the full code

Here is the complete app.py file as it should look after this step:

import streamlit as st
from anthropic import Anthropic

# Initialize the Anthropic client
client = Anthropic(api_key=st.secrets["ANTHROPIC_API_KEY"])

# Page configuration
st.set_page_config(page_title="AI Insurance Analyst", layout="wide")
st.title("AI Insurance Analyst")
st.write("Powered by Claude AI - Analyze claims and assess risk")

# System prompts
CLAIMS_PROMPT = """You are a senior insurance claims analyst with 15 years of experience. 
Analyze the following claim and provide a structured recommendation.

Your analysis must include:
1. COVERAGE CHECK: Does the claim type align with the policy coverage?
2. AMOUNT ASSESSMENT: Is the claimed amount reasonable for this type of loss?
3. RED FLAGS: Are there any concerns (late filing, vague descriptions, amount near thresholds)?
4. RECOMMENDATION: One of APPROVE, DENY, or ESCALATE TO SENIOR REVIEWER

Format your response as:
## Coverage Check
[Your analysis]

## Amount Assessment
[Your analysis]

## Red Flags
[Any concerns or "None identified"]

## Recommendation: [APPROVE/DENY/ESCALATE TO SENIOR REVIEWER]
[Brief justification]"""

UNDERWRITING_PROMPT = """You are a commercial property insurance underwriter. 
Assess the following risk submission and provide a structured underwriting recommendation.

Use these risk scoring criteria:
- Construction: Frame=High Risk, Masonry=Medium, Fire-Resistive=Low Risk
- Occupancy Hazard: Office=Low, Retail=Medium, Restaurant/Manufacturing=High
- Building Age: Under 10 years=Low Risk, 10-30=Medium, Over 30=High Risk
- Loss History: No losses=Favorable, 1-2 small losses=Acceptable, 3+ or large losses=Unfavorable
- Protection: Sprinklered=Credit, No sprinklers=Surcharge

Provide your response as:
## Risk Score: [1-10, where 1 is lowest risk and 10 is highest]

## Risk Factors
[List each factor and its rating]

## Recommendation: [ACCEPT/DECLINE/REFER TO SENIOR UNDERWRITER]

## Premium Indication
[Suggest a relative premium level: Below Average / Average / Above Average / Well Above Average]
[Brief explanation of pricing rationale]"""

# Create tabs
tab1, tab2 = st.tabs(["Claims Analysis", "Risk Assessment"])

# Claims Analysis Tab
with tab1:
    st.header("Claims Analysis")
    st.write("Enter claim details for AI-powered analysis")

    col1, col2 = st.columns(2)

    with col1:
        claim_type = st.selectbox(
            "Claim Type",
            ["Property Damage", "Liability", "Business Interruption", "Theft", "Fire", "Water Damage", "Other"]
        )
        policy_coverage = st.selectbox(
            "Policy Coverage Type",
            ["Commercial Property", "General Liability", "Business Owners Policy", "Commercial Auto", "Workers Compensation"]
        )
        claimed_amount = st.number_input("Claimed Amount ($)", min_value=0, value=5000, step=500)

    with col2:
        incident_date = st.date_input("Incident Date")
        policy_start_date = st.date_input("Policy Start Date")
        claim_description = st.text_area("Claim Description", height=120, placeholder="Describe what happened...")

    if st.button("Analyze Claim", type="primary"):
        if claim_description:
            with st.spinner("Analyzing claim..."):
                claim_details = f"""
Claim Type: {claim_type}
Policy Coverage: {policy_coverage}
Claimed Amount: ${claimed_amount:,.2f}
Incident Date: {incident_date}
Policy Start Date: {policy_start_date}
Claim Description: {claim_description}
"""
                message = client.messages.create(
                    model="claude-haiku-4-5",
                    max_tokens=1024,
                    system=CLAIMS_PROMPT,
                    messages=[
                        {"role": "user", "content": f"Please analyze this insurance claim:\n{claim_details}"}
                    ]
                )
                st.markdown("---")
                st.subheader("Analysis Result")
                st.markdown(message.content[0].text)
        else:
            st.warning("Please enter a claim description.")

# Risk Assessment Tab
with tab2:
    st.header("Risk Assessment")
    st.write("Enter applicant details for underwriting risk analysis")

    col1, col2 = st.columns(2)

    with col1:
        business_name = st.text_input("Business Name", placeholder="e.g., Smith's Restaurant")
        business_type = st.selectbox(
            "Business Type",
            ["Office", "Retail Store", "Restaurant", "Manufacturing", "Warehouse", "Medical Office", "Auto Repair"]
        )
        building_age = st.number_input("Building Age (years)", min_value=0, max_value=200, value=15)
        construction_type = st.selectbox(
            "Construction Type",
            ["Fire-Resistive (Steel/Concrete)", "Masonry (Brick)", "Frame (Wood)"]
        )

    with col2:
        square_footage = st.number_input("Square Footage", min_value=100, value=5000, step=500)
        num_employees = st.number_input("Number of Employees", min_value=1, value=10)
        has_sprinklers = st.selectbox("Sprinkler System", ["Yes", "No"])
        loss_history = st.text_area(
            "Loss History (last 5 years)",
            height=100,
            placeholder="Describe any prior claims or losses, or write 'None'"
        )

    location = st.text_input("Location", placeholder="e.g., 123 Main St, Chicago, IL")

    if st.button("Assess Risk", type="primary"):
        if business_name and location:
            with st.spinner("Assessing risk..."):
                risk_details = f"""
Business Name: {business_name}
Business Type: {business_type}
Building Age: {building_age} years
Construction Type: {construction_type}
Square Footage: {square_footage:,} sq ft
Number of Employees: {num_employees}
Sprinkler System: {has_sprinklers}
Location: {location}
Loss History: {loss_history if loss_history else 'None reported'}
"""
                message = client.messages.create(
                    model="claude-haiku-4-5",
                    max_tokens=1024,
                    system=UNDERWRITING_PROMPT,
                    messages=[
                        {"role": "user", "content": f"Please assess this risk submission:\n{risk_details}"}
                    ]
                )
                st.markdown("---")
                st.subheader("Underwriting Assessment")
                st.markdown(message.content[0].text)
        else:
            st.warning("Please enter a business name and location.")

Your insurance analyst now has both brains. Next up, you'll run the app and put both modes through real-world test scenarios to see how well your prompts perform.

Test Your Insurance Analyst

You've built two complete analysis modes and connected them to the Claude API. Now it's time to prove your insurance analyst actually works.

In this step, you'll feed the app realistic insurance scenarios and verify it produces differentiated recommendations. A good claim should get approved. A suspicious one should get flagged. This confirms your system prompts are doing the heavy lifting.

In this step, get ready to:
  • Run your Streamlit app and test the Claims Analyzer with a clean, covered claim.
  • Test with a problematic claim that should trigger an escalation.
  • Test the Risk Assessment with contrasting low-risk and high-risk scenarios.
Launch the app and test a straightforward claim

Before testing specific scenarios, you need to start the Streamlit development server. This spins up a local web server and opens the app in your browser.

  • Launch the app by running this command in your terminal:
streamlit run app.py

Your browser should open automatically to http://localhost:8501. You'll see the app title "AI Insurance Analyst" and two tabs at the top.

App not opening in your browser?

If the browser doesn't open automatically, copy the URL shown in your terminal (http://localhost:8501) and paste it into your browser manually.

If you see an authentication error, double-check that your .streamlit/secrets.toml file contains your real API key and that the file is saved.

Help me debug why my Streamlit app won't start.

Now let's test with a straightforward claim that should clearly be approved. The Claims Analysis tab should already be selected.

  • Select Water Damage from the Claim Type dropdown.
  • Select Commercial Property from the Policy Coverage Type dropdown.
  • Set the Claimed Amount to 15000.
  • Set the Incident Date to a recent date (within the last week).
  • Set the Policy Start Date to a date at least 6 months ago.
  • Enter this in the Claim Description field: Burst pipe in the second-floor bathroom caused water damage to the ceiling and walls of the office below. Damage was discovered Monday morning when employees arrived. Plumber confirmed the pipe was corroded. Repairs needed for drywall, ceiling tiles, and carpet replacement.
  • Click the Analyze Claim button.

After a few seconds, the AI's analysis appears below the form. You should see sections for Coverage Check, Amount Assessment, Red Flags, and a Recommendation.

What should the result look like?

For this scenario, the AI should recommend APPROVE. Water damage aligns with commercial property coverage, $15,000 is a reasonable amount for pipe damage repairs, and there are no red flags (recent incident, prompt filing, clear description).

Your system prompt's decision logic checks coverage alignment, amount reasonableness, and red flags. This clean scenario passes all three checks.

Test with a problematic claim

A real claims analyst spots problems. Let's see if your AI does too. You'll submit a claim with multiple red flags that should trigger an escalation or denial.

  • Scroll back up to the Claims Analysis form.
  • Select Theft from the Claim Type dropdown.
  • Keep Policy Coverage Type set to Commercial Property.
  • Set the Claimed Amount to 48000.
  • Set the Incident Date to a date 6 months ago.
  • Set the Policy Start Date to a date about 7 months ago (just one month before the incident).
  • Enter this in the Claim Description field: Various equipment and electronics were stolen from the premises sometime over the weekend. Exact items and values still being determined.
  • Click the Analyze Claim button.

The AI should flag several concerns with this claim. Check the result that appears below the form.

What red flags should the AI catch?

This scenario has multiple red flags built in. The claim was filed 6 months after the incident (late filing). The amount ($48,000) is high and near common policy limits. The description is vague with no specific items listed.

The AI should recommend ESCALATE TO SENIOR REVIEWER or DENY. Either response shows the system prompt's red-flag logic is working. The key difference from Test 1 is that the AI gives a meaningfully different recommendation based on the scenario details.

Test the Risk Assessment with contrasting scenarios

The Claims Analyzer is working. Now let's validate the Risk Assessment tab by running two opposite scenarios back to back. This proves the underwriting prompt's scoring criteria produce meaningfully different outcomes.

  • Click the Risk Assessment tab at the top of the app.
  • Enter Greenfield Analytics in the Business Name field.
  • Select Office from the Business Type dropdown.
  • Set Building Age to 5.
  • Select Fire-Resistive (Steel/Concrete) for Construction Type.
  • Set Square Footage to 3000.
  • Set Number of Employees to 12.
  • Select Yes for Sprinkler System.
  • Enter None in the Loss History field.
  • Enter 456 Innovation Drive, Austin, TX in the Location field.
  • Click the Assess Risk button.

What should the low-risk result look like?

This scenario stacks every favourable factor: new fire-resistive construction, low-hazard office use, sprinklered, no loss history. The AI should give a low risk score (1-3 out of 10), recommend ACCEPT, and indicate a Below Average or Average premium.

Now test the opposite extreme. Scroll back up to the Risk Assessment form and enter a high-risk scenario.

  • Change Business Name to Big Tony's BBQ Pit.
  • Select Restaurant from the Business Type dropdown.
  • Set Building Age to 55.
  • Select Frame (Wood) for Construction Type.
  • Set Square Footage to 4500.
  • Set Number of Employees to 25.
  • Select No for Sprinkler System.
  • Enter Two grease fires in the past 3 years, one resulting in $85,000 in damages. Kitchen hood system failed inspection last year. in the Loss History field.
  • Enter 789 Old Mill Road, Houston, TX in the Location field.
  • Click the Assess Risk button.

Compare this result to the Greenfield Analytics assessment above it. The risk scores and recommendations should be dramatically different.

What should the high-risk result look like?

This scenario hits every unfavourable factor in the scoring criteria: frame construction (High Risk), restaurant occupancy (High), building over 30 years old (High Risk), multiple large losses (Unfavorable), no sprinklers (Surcharge).

The AI should give a high risk score (7-10 out of 10), recommend DECLINE or REFER TO SENIOR UNDERWRITER, and indicate a Well Above Average premium or outright decline.

Try your own scenarios

Now that you've confirmed the app works, try submitting scenarios from your own experience. Think about real claims you've seen or risk profiles you've assessed. The AI's responses will reflect the logic you encoded in your system prompts.

Your insurance analyst is working. It produces different recommendations for different scenarios, proving the system prompts are encoding real insurance decision logic. Ready for a bonus challenge? The Secret Mission adds fraud detection to take this even further.

Secret mission

Add Fraud Detection Flags

Your claims analyzer is smart, but it's not suspicious enough. Real insurance teams flag patterns that hint at fraud before they escalate. Can you teach your AI to spot the red flags that experienced investigators look for?

Clean Up Your Resources

Clean Up Your Resources

Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project runs locally and uses pay-per-use API calls, so no costs accrue while the app is stopped.

Resources you used:

  • Your project folder on the Desktop (contains app.py, .streamlit/secrets.toml, and the .venv virtual environment with installed packages).
  • Your Anthropic API key (managed at console.anthropic.com).

Keep everything running

No action needed. Choose this if you plan to keep building on your insurance analyst or want to experiment with different prompts.

  • Your project folder stays on your Desktop, ready to use anytime.
  • Your API key remains active but only costs money when you run the app and submit analyses.
  • To restart the app later, open a terminal in your project folder, activate the virtual environment, and run streamlit run app.py.

Pause - I'll come back to this later

Stop the running Streamlit server to free up your terminal. Your files and API key stay intact for when you return.

  • In the terminal where Streamlit is running, press Ctrl+C to stop the server.
  • Close the terminal window.
  • Your project folder and API key remain ready for next time.

Delete - I don't want to use this again

Remove all project files and revoke your API key so nothing remains on your machine or Anthropic account.

  • In the terminal where Streamlit is running, press Ctrl+C to stop the server.
  • Delete your project folder from the Desktop. Right-click it in Finder (macOS) or File Explorer (Windows) and select Move to Trash (macOS) or Delete (Windows).
  • Disable your API key by going to console.anthropic.com, navigating to the API keys section in your account settings, and disabling or deleting the key you created for this project.

Nice Work!

Nice Work!

You just built a fully functional AI-powered insurance analyst that uses Claude to analyze claims and assess underwriting risk. This is the same kind of first-pass analysis logic that takes experienced analysts hours to perform manually.

You've learned how to:

  • Built a working AI-powered web app with two analysis modes (Claims Analysis and Risk Assessment) using Streamlit and the Claude API.
  • Wrote domain-specific system prompts that encode real insurance business logic, including coverage checks, exclusion flags, and underwriting risk scoring criteria.
  • Learned how underwriting risk assessment works by defining the scoring criteria yourself, covering construction type ratings, occupancy hazard classes, and loss history weighting.
  • Secret Mission: Added a fraud detection flag system that identifies suspicious claim patterns and displays color-coded alerts based on risk level.

Ready to quiz yourself?