Skip to content

System Architecture - Loan Defenders Multi-Agent Framework

📌 Navigation: High-Level ArchitectureData FlowExecution Sequence

High-Level Architecture

Purpose: Show system layers, components, and their relationships

Purpose: Show system layers, components, and their relationships

graph TB
    subgraph "User Layer"
        User["👤 User Browser<br/>React 19 + TypeScript<br/>Real-time SSE Updates"]
    end

    subgraph "API Layer"
        FastAPI["🚀 FastAPI Backend<br/>Entra ID Auth + CORS"]
        ConvOrch["🗣️ Conversation Orchestrator<br/>ConversationStateMachine"]
        Pipeline["⚙️ Sequential Pipeline<br/>SequentialBuilder (MAF)"]
    end

    subgraph "Agent Layer - Microsoft Agent Framework"
        Intake["🦅 Intake Agent<br/>Data Validation<br/>No MCP Tools"]
        Credit["📊 Credit Agent<br/>Credit Analysis<br/>MCP: Verification + Financial"]
        Income["💼 Income Agent<br/>Income Verification<br/>MCP: Documents + Financial"]
        Risk["🛡️ Risk Agent<br/>Final Decision<br/>MCP: All Servers"]
    end

    subgraph "Tool Layer - MCP Servers"
        MCP1["🔍 Application Verification<br/>Port 8010<br/>• verify_identity<br/>• get_credit_report"]
        MCP2["📄 Document Processing<br/>Port 8011<br/>• extract_income<br/>• process_documents"]
        MCP3["💰 Financial Calculations<br/>Port 8012<br/>• calculate_dti<br/>• assess_affordability"]
    end

    subgraph "External Services"
        Azure["🧠 Azure OpenAI<br/>GPT-4o Models<br/>Agent Reasoning"]
        Services["📊 Credit Bureaus<br/>🏦 Banking APIs<br/>📦 Document Storage"]
    end

    User -->|HTTPS| FastAPI
    FastAPI --> ConvOrch
    ConvOrch -->|LoanApplication| Pipeline
    Pipeline --> Intake
    Intake --> Credit
    Credit --> Income
    Income --> Risk

    Credit -.->|Tool Calls| MCP1
    Credit -.->|Tool Calls| MCP3
    Income -.->|Tool Calls| MCP2
    Income -.->|Tool Calls| MCP3
    Risk -.->|Tool Calls| MCP1
    Risk -.->|Tool Calls| MCP2
    Risk -.->|Tool Calls| MCP3

    MCP1 -->|AI Reasoning| Azure
    MCP2 -->|AI Reasoning| Azure
    MCP3 -->|AI Reasoning| Azure
    MCP1 -.->|Data Access| Services
    MCP2 -.->|Data Access| Services

    Pipeline -->|LoanDecision| ConvOrch
    ConvOrch -->|SSE Stream| User

    style User fill:#B3E5FC,stroke:#01579B,stroke-width:3px,color:#000
    style FastAPI fill:#FFE0B2,stroke:#E65100,stroke-width:2px,color:#000
    style ConvOrch fill:#FFE0B2,stroke:#E65100,stroke-width:2px,color:#000
    style Pipeline fill:#FFE0B2,stroke:#E65100,stroke-width:2px,color:#000
    style Intake fill:#E1BEE7,stroke:#4A148C,stroke-width:2px,color:#000
    style Credit fill:#E1BEE7,stroke:#4A148C,stroke-width:2px,color:#000
    style Income fill:#E1BEE7,stroke:#4A148C,stroke-width:2px,color:#000
    style Risk fill:#E1BEE7,stroke:#4A148C,stroke-width:2px,color:#000
    style MCP1 fill:#FFCDD2,stroke:#B71C1C,stroke-width:2px,color:#000
    style MCP2 fill:#FFCDD2,stroke:#B71C1C,stroke-width:2px,color:#000
    style MCP3 fill:#FFCDD2,stroke:#B71C1C,stroke-width:2px,color:#000
    style Azure fill:#B3E5FC,stroke:#01579B,stroke-width:2px,color:#000
    style Services fill:#FFF9C4,stroke:#F57F17,stroke-width:2px,color:#000

Architecture Highlights: - Presentation: React 19 UI with Server-Sent Events for real-time updates - Orchestration: Conversation Orchestrator (data collection) + Sequential Pipeline (agent workflow) - Agent Framework: Microsoft Agent Framework's SequentialBuilder manages 4 specialized agents - Tool Integration: 3 MCP servers provide domain-specific capabilities - AI Services: Azure OpenAI (GPT-4o) powers agent reasoning - Sequential Flow: Intake → Credit → Income → Risk with context accumulation


Diagram 2: Data Flow Architecture

Purpose: Show data transformation, interfaces, and data exchange patterns

graph TB
    subgraph Input["📥 Input Layer"]
        Raw["User Conversation<br/>Natural Language<br/>Multiple Turns"]
        RawData["Raw Data:<br/>• Unstructured text<br/>• No validation<br/>• Variable completeness"]
    end

    subgraph Validation["✅ Validation & Parsing Layer"]
        StateM["State Machine<br/>ConversationStateMachine"]
        StateData["State Data:<br/>• collected_data: Dict<br/>• missing_fields: List<br/>• application_complete: bool"]

        Pydantic["LoanApplication Model<br/>Pydantic v2"]
        PydanticData["Validated Data:<br/>• applicant_id: UUID<br/>• loan_amount: Decimal<br/>• annual_income: Decimal<br/>• credit_score: Optional[int]<br/>• employment_status: str<br/>Type-safe + Business Rules"]
    end

    subgraph Processing["⚙️ Sequential Agent Processing"]
        Stage1["Intake Agent"]
        Interface1["IntakeAssessment:<br/>• status: COMPLETE/FAILED<br/>• confidence_score: float<br/>• data_completeness: Dict<br/>• routing_decision: str"]

        Stage2["Credit Agent"]
        Interface2["CreditAssessment:<br/>• credit_score: int<br/>• credit_history: Dict<br/>• risk_level: str<br/>• tools_used: List[str]<br/>Context: [IntakeAssessment]"]

        Stage3["Income Agent"]
        Interface3["IncomeAssessment:<br/>• verified_income: Decimal<br/>• employment_verified: bool<br/>• dti_ratio: float<br/>• income_stability: str<br/>Context: [Intake, Credit]"]

        Stage4["Risk Agent"]
        Interface4["RiskAssessment:<br/>• final_risk_score: float<br/>• risk_factors: List[str]<br/>• recommendation: str<br/>• confidence: float<br/>Context: [Intake, Credit, Income]"]
    end

    subgraph Context["📊 Context Accumulation"]
        Ctx["Workflow Context:<br/>━━━━━━━━━━━━━━━━━<br/>Stage 1: [IntakeAssessment]<br/>Stage 2: [Intake, Credit]<br/>Stage 3: [Intake, Credit, Income]<br/>Stage 4: [Intake, Credit, Income, Risk]<br/>━━━━━━━━━━━━━━━━━<br/>• Growing context per stage<br/>• All assessments preserved<br/>• Tool results included<br/>• Complete audit trail"]
    end

    subgraph Output["📤 Output Layer"]
        Decision["LoanDecision Model<br/>Pydantic v2"]
        DecisionData["Decision Data:<br/>• decision: APPROVED/DENIED<br/>• loan_amount: Decimal<br/>• interest_rate: Decimal<br/>• conditions: List[str]<br/>• rationale: str<br/>• agent_assessments: List[Assessment]<br/>• decision_timestamp: datetime<br/>• audit_trail: Dict"]
    end

    Raw -->|HTTP POST /api/chat| StateM
    StateM -->|extracted fields| StateData
    StateData -->|when complete| Pydantic
    Pydantic -->|validated model| PydanticData

    PydanticData -->|LoanApplication| Stage1
    Stage1 -->|returns| Interface1
    Interface1 -->|+ context| Stage2
    Stage2 -->|returns| Interface2
    Interface2 -->|+ context| Stage3
    Stage3 -->|returns| Interface3
    Interface3 -->|+ context| Stage4
    Stage4 -->|returns| Interface4

    Interface1 -.->|append| Ctx
    Interface2 -.->|append| Ctx
    Interface3 -.->|append| Ctx
    Interface4 -.->|append| Ctx

    Interface4 -->|synthesize all| Decision
    Ctx -.->|complete history| Decision
    Decision -->|final model| DecisionData

    style Raw fill:#C8E6C9,stroke:#1B5E20,stroke-width:2px,color:#000
    style RawData fill:#C8E6C9,stroke:#1B5E20,stroke-width:1px,stroke-dasharray: 5 5,color:#000
    style StateM fill:#FFF9C4,stroke:#F57F17,stroke-width:2px,color:#000
    style StateData fill:#FFF9C4,stroke:#F57F17,stroke-width:1px,stroke-dasharray: 5 5,color:#000
    style Pydantic fill:#FFF9C4,stroke:#F57F17,stroke-width:2px,color:#000
    style PydanticData fill:#FFF9C4,stroke:#F57F17,stroke-width:1px,stroke-dasharray: 5 5,color:#000
    style Stage1 fill:#E1BEE7,stroke:#4A148C,stroke-width:2px,color:#000
    style Interface1 fill:#E1BEE7,stroke:#4A148C,stroke-width:1px,stroke-dasharray: 5 5,color:#000
    style Stage2 fill:#E1BEE7,stroke:#4A148C,stroke-width:2px,color:#000
    style Interface2 fill:#E1BEE7,stroke:#4A148C,stroke-width:1px,stroke-dasharray: 5 5,color:#000
    style Stage3 fill:#E1BEE7,stroke:#4A148C,stroke-width:2px,color:#000
    style Interface3 fill:#E1BEE7,stroke:#4A148C,stroke-width:1px,stroke-dasharray: 5 5,color:#000
    style Stage4 fill:#E1BEE7,stroke:#4A148C,stroke-width:2px,color:#000
    style Interface4 fill:#E1BEE7,stroke:#4A148C,stroke-width:1px,stroke-dasharray: 5 5,color:#000
    style Ctx fill:#B2DFDB,stroke:#004D40,stroke-width:2px,color:#000
    style Decision fill:#90CAF9,stroke:#0D47A1,stroke-width:3px,color:#000
    style DecisionData fill:#90CAF9,stroke:#0D47A1,stroke-width:1px,stroke-dasharray: 5 5,color:#000

Data Models & Interfaces (Pydantic v2):

Input Model: - LoanApplication: Validated input (applicant_id, loan_amount, annual_income, credit_score, employment_status, etc.) - Field validators ensure data integrity (e.g., loan_amount > 0, valid UUID for applicant_id)

Agent Assessment Interface:

class AgentAssessment(BaseModel):
    agent_name: str                    # "intake" | "credit" | "income" | "risk"
    status: AssessmentStatus           # COMPLETE | FAILED | MANUAL_REVIEW
    confidence_score: float            # 0.0 to 1.0
    assessment_result: Dict[str, Any]  # Agent-specific results
    tools_used: List[str]              # MCP tools called
    risk_factors: List[str]            # Identified risks
    recommendation: str                # Agent recommendation
    processing_time_ms: int            # Performance tracking

Output Model:

class LoanDecision(BaseModel):
    application_id: str
    decision: DecisionType             # APPROVED | DENIED | MANUAL_REVIEW
    loan_amount: Decimal
    interest_rate: Optional[Decimal]
    term_months: Optional[int]
    conditions: List[str]              # Special conditions
    rationale: str                     # Human-readable explanation
    agent_assessments: List[AgentAssessment]  # Complete history
    decision_timestamp: datetime
    audit_trail: Dict[str, Any]        # Regulatory compliance

Context Accumulation Pattern: - Stage 1 (Intake): Receives LoanApplication → Returns IntakeAssessment - Stage 2 (Credit): Receives [IntakeAssessment] + LoanApplication → Returns CreditAssessment - Stage 3 (Income): Receives [Intake, Credit] + LoanApplication → Returns IncomeAssessment - Stage 4 (Risk): Receives [Intake, Credit, Income] + LoanApplication → Returns RiskAssessment + LoanDecision

Data Exchange Protocol: - All models serialized via Pydantic's model_dump() and model_validate() - Type safety enforced at boundaries - Validation errors caught early - Complete audit trail maintained


Diagram 3: Execution Sequence

Purpose: Show complete workflow execution with current implementation

sequenceDiagram
    participant User as User
    participant UI as React UI
    participant API as FastAPI
    participant Conv as Conversation Orchestrator
    participant State as State Machine
    participant Pipeline as Sequential Pipeline
    participant Intake as Intake Agent
    participant Credit as Credit Agent
    participant Income as Income Agent
    participant Risk as Risk Agent
    participant MCP as MCP Servers
    participant Azure as Azure OpenAI

    rect rgb(230, 247, 255)
    Note over User,State: Phase 1: Conversational Data Collection
    User->>UI: "I need a $250K loan"
    UI->>API: POST /api/chat
    API->>Conv: process_chat(message)
    Conv->>State: process_input(message)
    State->>State: Extract data, update collected_data
    State-->>Conv: response + collected_data
    Conv-->>UI: ConversationResponse
    UI-->>User: "Great! What's your annual income?"

    Note over User,State: ... Multiple conversational turns ...

    User->>UI: "My income is $85,000"
    UI->>API: POST /api/chat
    API->>Conv: process_chat(message)
    Conv->>State: process_input(message)
    State->>State: Check completeness

    Note over State: ✅ All required fields collected!
    State-->>Conv: application_complete=true
    Conv->>Conv: Create LoanApplication (Pydantic)
    end

    rect rgb(243, 229, 245)
    Note over Conv,Azure: Phase 2: Sequential Agent Processing
    Conv->>Pipeline: process_application(loan_app)
    Pipeline->>Pipeline: Initialize SequentialBuilder (MAF)

    Pipeline->>Intake: Process with empty context
    Intake->>Azure: Validate completeness
    Azure-->>Intake: IntakeAssessment
    Intake-->>Pipeline: IntakeAssessment
    Pipeline-->>UI: SSE: ProcessingUpdate (Intake)
    UI-->>User: "🦅 Application validated!"

    Pipeline->>Credit: Process with [intake] context
    Credit->>MCP: verify_identity(applicant_id)
    MCP->>Azure: Tool execution reasoning
    Azure-->>MCP: Tool result
    MCP-->>Credit: Identity verified
    Credit->>MCP: get_credit_report(applicant_id)
    MCP-->>Credit: Credit report data
    Credit->>Azure: Analyze credit with context
    Azure-->>Credit: CreditAssessment
    Credit-->>Pipeline: CreditAssessment
    Pipeline-->>UI: SSE: ProcessingUpdate (Credit)
    UI-->>User: "📊 Credit: 740 score!"

    Pipeline->>Income: Process with [intake, credit] context
    Income->>MCP: extract_income(documents)
    MCP-->>Income: Income data
    Income->>MCP: calculate_dti(debts, income)
    MCP-->>Income: DTI: 32% (Excellent)
    Income->>Azure: Analyze income with context
    Azure-->>Income: IncomeAssessment
    Income-->>Pipeline: IncomeAssessment
    Pipeline-->>UI: SSE: ProcessingUpdate (Income)
    UI-->>User: "💼 Income verified: $85K"

    Pipeline->>Risk: Process with [intake, credit, income] context
    Risk->>MCP: Comprehensive analysis (all tools)
    MCP->>Azure: Risk calculation reasoning
    Azure-->>MCP: Risk metrics
    MCP-->>Risk: Complete risk profile
    Risk->>Azure: Final decision synthesis
    Azure-->>Risk: RiskAssessment + LoanDecision
    Risk-->>Pipeline: RiskAssessment + LoanDecision
    Pipeline-->>Conv: FinalDecisionResponse
    end

    Conv-->>API: LoanDecision
    API-->>UI: FinalDecisionResponse
    UI->>UI: 🎉 Trigger confetti animation!
    UI-->>User: "🎊 APPROVED! $250K at 4.5% APR"

    Note over User,Azure: Total Time: <3 minutes<br/>Traditional Process: 24-48 hours

Execution Characteristics: - Phase 1: State machine tracks collected_data across multiple conversational turns - Phase 2: SequentialBuilder (Microsoft Agent Framework) orchestrates 4 agents - Context Accumulation: Each agent receives all previous assessments as context - Tool Autonomy: Agents autonomously decide which MCP tools to call based on persona - Real-time Feedback: Server-Sent Events stream progress updates after each agent - Audit Trail: Complete decision history maintained for regulatory compliance


Key Architectural Concepts

  1. Layered Architecture: User → API → Agents → Tools → External Services
  2. Sequential Workflow: Intake → Credit → Income → Risk with context accumulation
  3. Tool Integration: MCP servers as independent, scalable microservices
  4. Real-time Updates: Server-Sent Events streaming for user experience
  5. Type Safety: Pydantic v2 models enforce contracts at system boundaries
  6. Agent Autonomy: Agents decide which tools to call based on their persona and context

Implementation Components

Orchestration Layer: - ConversationOrchestrator: Manages Phase 1 (data collection) - ConversationStateMachine: Tracks collected_data and completeness - SequentialPipeline: Orchestrates Phase 2 (agent workflow) - SequentialBuilder: Microsoft Agent Framework component for agent coordination

Agent Layer: - 4 specialized agents with markdown personas - Each agent loads instructions from agent-persona/*.md files - Agents autonomously call MCP tools based on their needs

Tool Layer: - 3 MCP servers (ports 8010, 8011, 8012) - SSE-based MCP protocol (Streamable HTTP) - Tools registered via @mcp_server.tool() decorator

Data Models: - LoanApplication: Input model with Pydantic v2 validation - AgentAssessment: Standardized agent output interface - LoanDecision: Final decision with complete audit trail

External Services: - Azure OpenAI (GPT-4o) for agent reasoning - Credit bureaus, banking APIs for real-world data - Document storage for file uploads