Spaces:
Sleeping
Sleeping
| 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 | |
| - 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() |