DEV Community

Solon Framework
Solon Framework

Posted on

Orchestrating Complex Enterprise Workflows with Nested Agents in Solon AI

Standard monolithic Large Language Model (LLM) prompts fall short when faced with complex, multi-stage enterprise procedures. For workflows such as financial credit approvals or audit pipelines, tasks must be routed based on strict compliance rules, pass through hierarchical review loops, and sometimes return to previous steps for corrections.

In this article, we demonstrate how to build and orchestrate a multi-stage Financial Loan Approval Pipeline using Solon AI (v4.0.5). We will leverage its native integration with the lightweight state machine engine Solon Flow, utilizing nested agents, conditional routing (Exclusive Junctions), and state verification.


The Architecture: Nested Agents and Flow Orchestration

Our credit approval system simulates a real-world enterprise workflow:

  1. Entry Officer (KYC): Performs preliminary verification and data formatting.
  2. Loan Architect: Automatically designs loan products, interest rates, and loan terms, rating risk levels (high, mid, low).
  3. Risk Control Center (Nested Team): A specialized, nested sub-agent committee that acts as a unified black box. Inside, a Credit Department sub-team, a Quota Calculator, and a Legal Compliance Officer collaborate.
  4. Stress Tester: Run simulations to assess risk and default probabilities. If tests fail, the workflow loops back to the Risk Control Center for term rectification.
  5. Security Auditor: Conducts final checks to ensure GDPR compliance.
  6. Final Approver: Signs off on the final decision, determining the disbursement route (canary or full).

Here is a simplified flowchart of the process:

[Start] -> (Entry Officer) -> (Loan Architect) -> [Junction: Risk Level?]
                                                        |
                            +---------------------------+ (high risk)
                            |                           |
                            v                           v (low/mid risk)
                   ((Risk Control Center)) ----> (Stress Tester)
                            ^                           |
                            |                           v
                            +----------------- [Junction: Passed?] (no)
                                                        | (yes)
                                                        v
                                                (Security Auditor)
                                                        |
                                                        v
                                                (Final Approver) -> [Disbursement Route] -> [End]
Enter fullscreen mode Exit fullscreen mode

Implementing the Workflow in Solon AI

Let us implement this pipeline in Java using Solon AI.

Step 1: Initialize Chat Model and Define ReAct Agents

First, we set up our LLM backend and instantiate the individual professional agents using ReActAgent:

import org.noear.solon.ai.chat.ChatModel;
import org.noear.solon.ai.agent.react.ReActAgent;

// Initialize the ChatModel
ChatModel chatModel = ChatModel.of("https://api.your-provider.com/v1/chat/completions")
        .apiKey("your-api-key")
        .model("your-model-name")
        .build();

// Entry Officer (KYC Analyst)
ReActAgent entryOfficer = ReActAgent.of(chatModel)
        .name("entry_officer")
        .role("Loan Intake Officer")
        .instruction("Verify customer identity. Structure the application details cleanly. Ask for missing details.")
        .build();

// Loan Architect
ReActAgent loanArchitect = ReActAgent.of(chatModel)
        .name("loan_architect")
        .role("Credit Product Architect")
        .instruction("Calculate DTI (Debt-to-Income). Assign preliminary risk label: [high, mid, low].")
        .build();
Enter fullscreen mode Exit fullscreen mode

Step 2: Build a Nested Risk Control Committee

In Solon AI, a TeamAgent acts as a multi-agent container. Since a team is itself an instance of Agent, it can be nested inside another TeamAgent seamlessly:

import org.noear.solon.ai.agent.team.TeamAgent;

// Risk Control Center containing multiple internal experts and a nested Credit Dept sub-team
TeamAgent riskControlCenter = TeamAgent.of(chatModel)
        .name("risk_control_center")
        .role("Deep Risk and Compliance Assessment Group")
        .agentAdd(
                // Nested sub-team (Credit Department)
                TeamAgent.of(chatModel)
                        .name("credit_dept")
                        .agentAdd(
                                ReActAgent.of(chatModel)
                                        .name("big_data_analyst")
                                        .role("Big Data Risk Modeler")
                                        .instruction("Analyze social behavior patterns and alternative credit scores.")
                                        .build()
                        ).build(),

                // Quota Calculator
                ReActAgent.of(chatModel)
                        .name("quota_calculator")
                        .role("Credit Actuary")
                        .instruction("Evaluate collateral and financial records to determine the final credit line.")
                        .build(),

                // Legal Compliance Officer
                ReActAgent.of(chatModel)
                        .name("legal_reviewer")
                        .role("AML Auditor")
                        .instruction("Cross-check PEP lists and perform Anti-Money Laundering verification.")
                        .build()
        ).build();
Enter fullscreen mode Exit fullscreen mode

Step 3: Define Post-Assessment and Audit Agents

Next, define the validation, stress-testing, and sign-off actors:

ReActAgent riskStressTester = ReActAgent.of(chatModel)
        .name("risk_stress_tester")
        .role("Stress Test Engineer")
        .instruction("Simulate default probability under a +200BP interest rate shock. Set 'passed' to true/false.")
        .build();

ReActAgent securityAuditor = ReActAgent.of(chatModel)
        .name("security_auditor")
        .role("Data Privacy Auditor")
        .instruction("Verify that user data handling adheres to GDPR and national data security laws.")
        .build();

ReActAgent finalApprover = ReActAgent.of(chatModel)
        .name("final_approver")
        .role("General Manager of Credit Division")
        .instruction("Synthesize the stress test and security audit to output a final strategy: [canary, full, reject].")
        .build();
Enter fullscreen mode Exit fullscreen mode

Step 4: Programmatic Flow Orchestration (graphAdjuster)

Now, orchestrate the interaction flow. By default, TeamAgent uses collaborative protocols (e.g., Sequential, Swarm), but for strict corporate governance, we override the topology using .graphAdjuster to build a directed workflow graph:

TeamAgent creditApprovalSystem = TeamAgent.of(chatModel)
        .name("credit_approval_system")
        .graphAdjuster(spec -> {
            // Start node routes directly to the KYC officer
            spec.addStart("start").linkAdd("entry_officer");
            spec.addActivity(entryOfficer).linkAdd("loan_architect");

            // Define conditional branching: high-risk applications route to the Wind Control Center
            spec.addExclusive("exc_risk_level")
                    .linkAdd("risk_control_center", l -> l.when("risk == 'high'"))
                    .linkAdd("risk_stress_tester");

            // Wind Control Center transitions to the Stress Tester
            spec.addActivity(riskControlCenter).linkAdd("risk_stress_tester");

            // Stress Tester branches depending on outcome. If failed (passed == false), return to Risk Center
            spec.addActivity(riskStressTester).linkAdd("exc_test_result");
            spec.addExclusive("exc_test_result")
                    .linkAdd("risk_control_center", l -> l.when("passed == false"))
                    .linkAdd("security_auditor");

            spec.addActivity(securityAuditor).linkAdd("final_approver");
            spec.addActivity(finalApprover).linkAdd("exc_release");

            // Branch to final disbursement channels
            spec.addExclusive("exc_release")
                    .linkAdd("canary_disburser", l -> l.when("route == 'canary'"))
                    .linkAdd("full_disburser", l -> l.when("route == 'full'"))
                    .linkAdd("end");

            spec.addActivity("canary_disburser").title("Canary Disbursement Channel").linkAdd("end");
            spec.addActivity("full_disburser").title("Full-Scale Disbursement Channel").linkAdd("end");
            spec.addEnd("end");
        }).build();
Enter fullscreen mode Exit fullscreen mode

Running the Workflow with State Tracking

To execute our orchestrated agent network, we maintain a persistent state using AgentSession. This keeps a record of variables like risk, passed, and route, ensuring they flow correctly between nodes:

import org.noear.solon.ai.agent.AgentSession;
import org.noear.solon.ai.agent.session.InMemoryAgentSession;
import org.noear.solon.ai.chat.prompt.Prompt;
import org.noear.solon.ai.agent.team.TeamTrace;

public class App {
    public static void main(String[] args) throws Throwable {
        // Create an in-memory session for a loan application
        AgentSession session = InMemoryAgentSession.of("LOAN_ID_2026_999");

        String query = "【Urgent Loan Application】\n" +
                "Applicant: Wang Wu (Business Owner)\n" +
                "Purpose: 8 Million RMB for raw materials purchasing.\n" +
                "Note: Since this involves cross-border trade and a high limit, " +
                "please tag this as high risk. Verify disbursement via the Canary channel.";

        // Run the workflow
        String report = creditApprovalSystem.prompt(Prompt.of(query))
                .session(session)
                .call()
                .getContent();

        System.out.println("=== Final Execution Report ===\n" + report);

        // Inspect and trace the execution path
        TeamTrace trace = creditApprovalSystem.getTrace(session);
        System.out.println("\n=== Execution Path Trace ===");
        trace.getRecords().forEach(step -> 
            System.out.println("Executed Node: [" + step.getSource() + "]")
        );
    }
}
Enter fullscreen mode Exit fullscreen mode

Sample Traced Path Output:

=== Execution Path Trace ===
Executed Node: [entry_officer]
Executed Node: [loan_architect]
Executed Node: [risk_control_center]
Executed Node: [risk_stress_tester]
Executed Node: [security_auditor]
Executed Node: [final_approver]
Executed Node: [canary_disburser]
Enter fullscreen mode Exit fullscreen mode

Due to the applicant's status and the high transaction limit, the system dynamically rerouted the execution thread through the risk_control_center before performing stress tests and releasing funds through the canary gateway.


Architectural Advantages

  1. Deterministic Controls for Nondeterministic LLMs: By defining structured states (using addExclusive and SpEL conditions), the workflow enforces rigid banking boundaries while allowing LLMs to handle unstructured text processing within individual nodes.
  2. Specialized Division of Labor: High-risk calculations are isolated inside the risk_control_center micro-agent block. General tasks skip this step, optimizing latency and saving LLM token costs.
  3. Dynamic Loop Corrections: If the stress test flags default vulnerability (passed == false), the agent automatically returns to the risk center to renegotiate security collaterals without crashing the session.
  4. Complete Trace Audits: Since every single execution step is tracked inside TeamTrace, banking compliance officers can easily audit the trace record log, mapping out exactly which agent made which decision.

Top comments (0)