System Architecture - Loan Defenders Multi-Agent Framework
📌 Navigation: High-Level Architecture → Data Flow → Execution 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
- Layered Architecture: User → API → Agents → Tools → External Services
- Sequential Workflow: Intake → Credit → Income → Risk with context accumulation
- Tool Integration: MCP servers as independent, scalable microservices
- Real-time Updates: Server-Sent Events streaming for user experience
- Type Safety: Pydantic v2 models enforce contracts at system boundaries
- 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