makoto7879's picture
Update app.py
d6ca612 verified
import gradio as gr
import os
import pandas as pd
from crewai import Agent, Task, Crew, Process, LLM
# LLM CONFIGURATION - Using Groq (FREE)
groq_api_key = os.getenv("GROQ_API_KEY")
if groq_api_key is None:
raise ValueError("❌ GROQ_API_KEY not found! Please add it in Settings > Variables and secrets")
os.environ["GROQ_API_KEY"] = groq_api_key
llm = LLM(model="groq/llama-3.3-70b-versatile", temperature=0.7)
# Customer Credit Score Data
CREDIT_SCORES = pd.DataFrame({
'ID': ['1111', '2222', '3333', '4444', '5555'],
'Name': ['Loren', 'Matt', 'Hilda', 'Andy', 'Kit'],
'Email': ['loren@gmail.com', 'matt@yahoo.com', 'halida@gmail.com', 'andy@gmail.com', 'kit@yahho.com'],
'Credit_Score': [455, 685, 825, 840, 350]
})
# Customer Account Status Data
ACCOUNT_STATUS = pd.DataFrame({
'ID': ['1111', '2222', '3333', '4444', '5555'],
'Name': ['Loren', 'Matt', 'Hilda', 'Andy', 'Kit'],
'Nationality': ['Singaporean', 'Non-Singaporean', 'Singaporean', 'Non-Singaporean', 'Singaporean'],
'Email': ['loren@gmail.com', 'matt@yahoo.com', 'halida@gmail.com', 'andy@gmail.com', 'kit@yahho.com'],
'Account_Status': ['good-standing', 'closed', 'delinquent', 'good-standing', 'delinquent']
})
# Government PR Status Data
PR_STATUS = pd.DataFrame({
'ID': ['2222', '4444'],
'Name': ['Matt', 'Andy'],
'Email': ['matt@yahoo.com', 'andy@gmail.com'],
'PR_Status': [True, False]
})
RISK_POLICY = """
Bank Loan Overall Risk Policy:
Credit Score 300-674 + Delinquent = High Risk
Credit Score 675-749 + Delinquent = High Risk
Credit Score 750-850 + Delinquent = Medium Risk
Credit Score 300-674 + Closed = High Risk
Credit Score 675-749 + Closed = Medium Risk
Credit Score 750-850 + Closed = Low Risk
Credit Score 300-674 + Good-standing = Medium Risk
Credit Score 675-749 + Good-standing = Medium Risk
Credit Score 750-850 + Good-standing = Low Risk
"""
INTEREST_RATE_POLICY = """
Bank Loan Interest Rate Policy:
Low Risk = 3.175%
Medium Risk = 4.885%
High Risk = 6.325%
Note: If the risk is between categories, be conservative in the risk and interest rate.
"""
def get_applicant_data(applicant_id):
"""Retrieve applicant data from all three tables"""
credit_data = CREDIT_SCORES[CREDIT_SCORES['ID'] == str(applicant_id)]
if len(credit_data) == 0:
return None
account_data = ACCOUNT_STATUS[ACCOUNT_STATUS['ID'] == str(applicant_id)]
if len(account_data) == 0:
return None
pr_data = PR_STATUS[PR_STATUS['ID'] == str(applicant_id)]
pr_status = pr_data.iloc[0]['PR_Status'] if len(pr_data) > 0 else 'N/A (Singaporean)'
applicant_info = {
'ID': credit_data.iloc[0]['ID'],
'Name': credit_data.iloc[0]['Name'],
'Email': credit_data.iloc[0]['Email'],
'Credit_Score': credit_data.iloc[0]['Credit_Score'],
'Nationality': account_data.iloc[0]['Nationality'],
'Account_Status': account_data.iloc[0]['Account_Status'],
'PR_Status': pr_status
}
return applicant_info
def create_agents():
"""Create the 3 agents for loan assessment"""
# Agent 1: Loan Data Investigator
data_collector = Agent(
role="Loan Data Investigator",
goal=(
"Retrieve a complete, accurate profile for the applicant from the "
"simulated banking and PR systems."
),
backstory=(
"You fetch raw data (credit score, account status, nationality) "
"from structured CSV systems and verify PR status if the applicant "
"is Non-Singaporean. You MUST clearly label all fields you return."
),
verbose=True,
allow_delegation=False,
llm=llm
)
# Agent 2: Policy Compliance Officer
policy_analyst = Agent(
role="Policy Compliance Officer",
goal=(
"Determine the applicant's overall risk level and applicable "
"interest rate using ONLY the official policy PDFs."
),
backstory=(
"You are an expert in internal bank policies. "
"Use the Risk Policy to map credit score and account status to risk "
"and the Interest Rate Policy to map risk to an interest rate. "
"Follow the policy tables exactly; do not invent new rules."
),
verbose=True,
allow_delegation=False,
llm=llm
)
# Agent 3: Senior Loan Manager
loan_manager = Agent(
role="Senior Loan Manager",
goal=(
"Generate a clear, audit-ready loan decision report "
"(approve or reject, risk, rate, and rationale)."
),
backstory=(
"You combine the structured applicant data and the policy-derived "
"risk and rate to make a final decision. You must explain how the "
"decision aligns with policy, including edge cases such as "
"non-Singaporean applicants with or without PR. "
"Account Status with Delinquent will automatically be rejected with explanation."
),
verbose=True,
allow_delegation=False,
llm=llm
)
return data_collector, policy_analyst, loan_manager
def create_tasks(applicant_data, data_collector, policy_analyst, loan_manager):
"""Create tasks for the 3-agent workflow"""
applicant_info_str = f"""
Applicant ID: {applicant_data['ID']}
Name: {applicant_data['Name']}
Email: {applicant_data['Email']}
Credit Score: {applicant_data['Credit_Score']}
Nationality: {applicant_data['Nationality']}
Account Status: {applicant_data['Account_Status']}
PR Status: {applicant_data['PR_Status']}
"""
# Task 1: Data Collection
collect_data = Task(
description=f"""
Retrieve and verify the complete profile for applicant ID {applicant_data['ID']}.
You have access to the following data from the banking systems:
{applicant_info_str}
Your responsibilities:
1. Verify all data fields are present and valid
2. Clearly label each field in your output
3. For Non-Singaporean applicants, verify PR status
4. Return a structured profile with:
- Applicant ID
- Name
- Email
- Credit Score
- Account Status (delinquent/closed/good-standing)
- Nationality
- PR Status (if Non-Singaporean)
IMPORTANT: Return all data clearly labeled and structured.
""",
expected_output="""
A complete, clearly labeled applicant profile with all fields:
- Applicant ID: [ID]
- Name: [Name]
- Email: [Email]
- Credit Score: [Score]
- Account Status: [Status]
- Nationality: [Singaporean/Non-Singaporean]
- PR Status: [True/False/N/A]
- Data Validation: [Complete/Incomplete with missing fields]
""",
agent=data_collector
)
# Task 2: Policy Analysis
analyze_policy = Task(
description=f"""
Determine the risk level and interest rate for this applicant using ONLY the official bank policies.
Applicant Data:
- Credit Score: {applicant_data['Credit_Score']}
- Account Status: {applicant_data['Account_Status']}
RISK POLICY (follow exactly):
{RISK_POLICY}
INTEREST RATE POLICY (follow exactly):
{INTEREST_RATE_POLICY}
Your responsibilities:
1. Find the exact policy rule that matches the credit score and account status
2. Determine the risk level: Low, Medium, or High
3. Map the risk level to the corresponding interest rate
4. Cite the specific policy rule you applied
5. DO NOT invent new rules or interpretations
IMPORTANT: Follow the policy tables exactly as written.
""",
expected_output="""
A policy-based risk and rate determination:
- Credit Score: [Score]
- Account Status: [Status]
- Policy Rule Applied: [Exact rule from policy]
- Overall Risk Level: [Low/Medium/High]
- Assigned Interest Rate: [X.XXX%]
- Policy Justification: [Explain which policy rule was matched]
""",
agent=policy_analyst,
context=[collect_data]
)
# Task 3: Final Decision
make_decision = Task(
description=f"""
Generate a final loan decision report for {applicant_data['Name']} (ID: {applicant_data['ID']}).
Combine the applicant data and policy analysis to make an audit-ready decision.
Decision Rules:
1. If Account Status is "delinquent" β†’ AUTOMATIC REJECTION
2. If Non-Singaporean without PR β†’ Consider higher scrutiny
3. Otherwise, follow the risk-based decision
Your responsibilities:
1. Make a clear decision: APPROVE or REJECT
2. Explain the rationale using the applicant data and policy
3. Include the risk level and interest rate (if approved)
4. Address any edge cases (delinquent, non-Singaporean, no PR)
5. Provide conditions if needed (conditional approval)
IMPORTANT: If account status is delinquent, you MUST reject with clear explanation.
""",
expected_output="""
A comprehensive loan decision report in markdown format:
# LOAN DECISION REPORT
## APPLICANT SUMMARY
[All applicant details]
## RISK ASSESSMENT
[Risk level, interest rate, policy rules applied]
## DECISION
**Status:** [APPROVED / REJECTED / CONDITIONAL]
**Rationale:**
[Clear explanation of why this decision was made, referencing:
- Credit score and account status
- Policy rules applied
- Any edge cases (delinquent accounts, non-Singaporean, PR status)
- Any conditions if conditional approval]
## LOAN TERMS (if approved)
- Interest Rate: [X.XXX%]
- Risk Level: [Low/Medium/High]
- Special Conditions: [Any conditions]
## POLICY COMPLIANCE
[Confirmation that decision follows bank policies]
""",
agent=loan_manager,
context=[collect_data, analyze_policy]
)
return [collect_data, analyze_policy, make_decision]
def generate_loan_report(applicant_id):
"""Main function to generate loan report using 3-agent workflow"""
if not applicant_id or applicant_id.strip() == "":
return "⚠️ Please enter a valid Applicant ID."
try:
applicant_data = get_applicant_data(applicant_id)
if applicant_data is None:
available_ids = ', '.join(CREDIT_SCORES['ID'].tolist())
return f"❌ Applicant ID '{applicant_id}' not found in database.\n\n**Available IDs:** {available_ids}"
# Create the 3 agents
data_collector, policy_analyst, loan_manager = create_agents()
# Create the tasks
tasks = create_tasks(applicant_data, data_collector, policy_analyst, loan_manager)
# Create and run the crew
crew = Crew(
agents=[data_collector, policy_analyst, loan_manager],
tasks=tasks,
process=Process.sequential,
verbose=True
)
result = crew.kickoff()
return f"""# 🏦 Loan Assessment Report
**Generated for:** {applicant_data['Name']}
**Applicant ID:** {applicant_id}
**Date:** {pd.Timestamp.now().strftime('%Y-%m-%d %H:%M:%S')}
---
{result}
---
### πŸ€– Agent Workflow
1. **Loan Data Investigator** - Retrieved complete applicant profile
2. **Policy Compliance Officer** - Determined risk and interest rate using bank policies
3. **Senior Loan Manager** - Made final loan decision with rationale
### πŸ“Š Data Sources
- βœ… Credit Score System
- βœ… Account Status System
- βœ… Government PR Status System
*This report was generated by a 3-agent AI system using CrewAI.*
"""
except Exception as e:
return f"""❌ An error occurred while generating the report:
**Error Details:**
```
{str(e)}
```
**Troubleshooting:**
1. Check that your GROQ_API_KEY is set correctly in Settings > Secrets
2. Ensure you have sufficient API credits
3. Verify the Applicant ID is valid (1111, 2222, 3333, 4444, 5555)
If the error persists, please check the application logs."""
def show_all_data():
credit_md = "## πŸ’³ Credit Score System\n\n" + CREDIT_SCORES.to_markdown(index=False)
account_md = "\n\n## πŸ“‹ Account Status System\n\n" + ACCOUNT_STATUS.to_markdown(index=False)
pr_md = "\n\n## πŸ›οΈ Government PR Status System\n\n" + PR_STATUS.to_markdown(index=False)
return credit_md + account_md + pr_md
with gr.Blocks(theme=gr.themes.Soft(), title="Loan Risk Assessment") as demo:
gr.Markdown("""
# 🏦 3-Agent Loan Risk Assessment System
### Powered by CrewAI & Groq
**Three Specialized AI Agents:**
1. πŸ” **Loan Data Investigator** - Retrieves complete applicant profiles
2. βš–οΈ **Policy Compliance Officer** - Applies risk and rate policies
3. πŸ‘” **Senior Loan Manager** - Makes final approval/rejection decisions
**Data Sources:** Credit Score System β€’ Account Status System β€’ Government PR Status
""")
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("### πŸ“‹ Enter Applicant ID")
applicant_id_input = gr.Textbox(
label="Applicant ID",
placeholder="Enter ID (1111, 2222, 3333, 4444, 5555)",
lines=1
)
generate_btn = gr.Button("πŸš€ Generate Loan Decision Report", variant="primary", size="lg")
gr.Markdown("""
### πŸ“– Bank Policies
**Risk Assessment Matrix:**
| Credit Score | Account Status | Risk |
|--------------|----------------|------|
| 300-674 | Delinquent | High |
| 675-749 | Delinquent | High |
| 750-850 | Delinquent | Medium |
| 300-674 | Closed | High |
| 675-749 | Closed | Medium |
| 750-850 | Closed | Low |
| 300-674 | Good-standing | Medium |
| 675-749 | Good-standing | Medium |
| 750-850 | Good-standing | Low |
**Interest Rates:**
- 🟒 Low Risk: **3.175%**
- 🟑 Medium Risk: **4.885%**
- πŸ”΄ High Risk: **6.325%**
**Special Rules:**
- ⚠️ Delinquent accounts β†’ **Auto-reject**
- 🌍 Non-Singaporean without PR β†’ Higher scrutiny
""")
with gr.Accordion("πŸ“Š View All Databases", open=False):
database_display = gr.Markdown()
show_data_btn = gr.Button("Show All Data", size="sm")
show_data_btn.click(fn=show_all_data, outputs=database_display)
with gr.Column(scale=2):
gr.Markdown("### πŸ“„ Loan Decision Report")
output_report = gr.Markdown(
value="""
*Enter an Applicant ID and click "Generate Loan Decision Report" to begin...*
**Available Applicants:**
| ID | Name | Credit | Status | Nationality | Expected Decision |
|----|------|--------|--------|-------------|-------------------|
| 1111 | Loren | 455 | Good-standing | Singaporean | Medium Risk |
| 2222 | Matt | 685 | Closed | Non-Singaporean (PR) | Medium Risk |
| 3333 | Hilda | 825 | **Delinquent** | Singaporean | **REJECTED** |
| 4444 | Andy | 840 | Good-standing | Non-Singaporean (No PR) | Low Risk |
| 5555 | Kit | 350 | **Delinquent** | Singaporean | **REJECTED** |
**Note:** Delinquent accounts (3333, 5555) will be automatically rejected.
""",
height=600
)
generate_btn.click(
fn=generate_loan_report,
inputs=[applicant_id_input],
outputs=output_report
)
gr.Examples(
examples=[["1111"], ["2222"], ["3333"], ["4444"], ["5555"]],
inputs=applicant_id_input,
label="Quick Select Applicant ID"
)
gr.Markdown("""
---
### πŸ’‘ How It Works
**3-Agent Sequential Workflow:**
```
Step 1: Data Collection
└─ Loan Data Investigator retrieves profile from 3 systems
Step 2: Policy Analysis
└─ Policy Compliance Officer determines risk & rate
Step 3: Final Decision
└─ Senior Loan Manager approves/rejects with rationale
```
**Key Features:**
- βœ… Automatic rejection of delinquent accounts
- βœ… PR status verification for non-Singaporeans
- βœ… Strict adherence to bank risk and rate policies
- βœ… Audit-ready decision reports with clear rationale
**Technology:** CrewAI β€’ Groq (Llama 3.3 70B) β€’ Gradio β€’ Python
""")
if __name__ == "__main__":
demo.launch()