7 Agentic AI Design Patterns That Are Reshaping Financial Services

20 min readAbhilash Bahinipati

Most AI in financial services today is still reactive. You ask it a question, it gives you an answer. Useful — but limited.

What’s changing is the move toward agentic AI: systems that don’t just respond, but reason, plan, delegate, and act across multi-step workflows. Systems that can process a loan application end-to-end, coordinate an M&A deal across four workstreams, or pause a high-value trade and wait for a human to sign off before executing.

But building these systems well requires more than just chaining a few LLM calls together. It requires architectural thinking — knowing which pattern to reach for, and why.

In this article, I’ll walk through 7 agentic design patterns using LangGraph, grounded in real scenarios from retail banking, wealth management, investment banking, and private equity. For each pattern, I’ll explain what it is, show the flow, walk through how it’s implemented, and explain what problem it actually solves in a financial context.

No prior LangGraph experience needed. If you understand how a business process works, you’ll understand these patterns.

The full code repository is linked at the end.

What is LangGraph?

LangGraph is a Python framework for building agentic AI systems. It models your agent logic as a graph — nodes are steps (things the AI does), edges are the transitions between them, and a shared state dictionary carries information from one step to the next.

What makes it more powerful than simply calling an LLM multiple times in a loop:

  • Cycles — it can loop back to a previous step, which is how reflection and retry logic works
  • Conditional routing — it can branch based on what it finds, like a router sending a query to the right specialist
  • Parallel execution — multiple steps can run simultaneously, which matters when you’re running independent analyses
  • State persistence — the graph can pause and resume, which is essential when a human needs to approve something in the middle of a workflow

With that foundation, let’s get into the patterns.

Pattern 1 — Sequential Pipeline

The idea

A fixed, ordered chain of steps where each step’s output feeds the next. Nothing runs in parallel, nothing branches. Step A must complete before Step B begins. It’s the simplest pattern and, for many business processes, exactly the right one.

Think of it as your standard operating procedure translated into an AI workflow.

The flow

The use case: Retail banking loan processing

A customer submits a home loan application. Before any decision can be made, several things must happen in sequence:

First, the application is validated — does it have all required fields? PAN number, income, loan purpose, employment type. If anything is missing, the process stops here.

Next, a credit check is run. The agent pulls a simulated CIBIL report — credit score, active loans, any defaults in the past 24 months, total existing EMI obligations.

Then a risk score is calculated. This is an internal score from 1 to 10, weighing the debt-to-income ratio, credit history, and employment stability. A score of 1 is lowest risk; 10 is highest.

Based on that score, an approval decision is made — Approved, Conditional Approval, or Rejected — with a one-paragraph justification.

Finally, a formal letter is generated. If approved, it includes a loan reference number, the offered amount, interest rate, EMI estimate, and next steps. If rejected, it explains why and what the applicant can do.

How it’s built

Step 1 — Define the state

The state is a typed dictionary that accumulates information as it passes through the pipeline. Every node reads from it and writes back to it.

class LoanApplicationState(TypedDict):
    raw_application: str
    validated_application: str
    credit_report: str
    risk_score: str
    approval_decision: str
    offer_letter: str

Each field is empty at the start and gets populated as the graph runs. The approval node sees the credit report and risk score by the time it runs. The offer letter node sees the decision. Nothing is passed out of order.

Step 2 — Define each node

Each step is a Python function that receives the current state and returns only the field it updates.

def validate_application(state: LoanApplicationState) -> dict:
    prompt = f"Validate this loan application: {state['raw_application']}"
    response = llm.invoke(prompt)
    return {"validated_application": response.content}

def run_credit_check(state: LoanApplicationState) -> dict:
    prompt = f"Generate a CIBIL credit report for: {state['validated_application']}"
    response = llm.invoke(prompt)
    return {"credit_report": response.content}
# ... same pattern for risk_score, approval_decision, offer_letter

Step 3 — Wire the graph and run it

Register each node, connect them in order, set the entry point, and invoke.

from langgraph.graph import StateGraph, END

graph = StateGraph(LoanApplicationState)
graph.add_node("validate", validate_application)
graph.add_node("credit_check", run_credit_check)
graph.add_node("risk_score", calculate_risk_score)
graph.add_node("approval", make_approval_decision)
graph.add_node("offer_letter", generate_offer_letter)
graph.set_entry_point("validate")

graph.add_edge("validate", "credit_check")
graph.add_edge("credit_check", "risk_score")
graph.add_edge("risk_score", "approval")
graph.add_edge("approval", "offer_letter")
graph.add_edge("offer_letter", END)

app = graph.compile()
result = app.invoke({"raw_application": sample_application})

When it fits

Any time your workflow maps to a standard operating procedure with clear dependencies between steps. Loan processing, KYC pipelines, trade confirmation flows, compliance document generation.

Pattern 2 — Parallel Fan-out

The idea

A single input is sent to multiple specialist agents simultaneously. They work independently. Their results are collected and synthesized into a single output. The total time is the length of the slowest analysis — not the sum of all of them.

The flow

The use case: Wealth management portfolio review

A Relationship Manager is preparing for a client review meeting. The client holds a Rs. 2 crore portfolio across large-caps, mid-caps, a small-cap fund, and a gold ETF. Before the meeting, the RM needs a comprehensive risk picture.

Three entirely independent assessments are needed:

Market risk — how exposed is this portfolio to broad market movements? What’s the estimated beta against Nifty 50? What’s the Value at Risk at 95% confidence? Which sectors have the highest drawdown exposure?

Liquidity risk — if the client needs to exit positions in a hurry, how much of the portfolio can be liquidated within 3 trading days? The small-cap mutual fund has a 3-year lock-in. Zomato is a small-cap with volatile volumes. These matter.

Concentration risk — is the portfolio over-indexed in any one sector or stock? Reliance at 25% and HDFC Bank at 20% together represent nearly half the portfolio. Is that a problem given the client’s risk profile?

These three analyses have no dependency on each other. Running them one after another wastes time. The parallel pattern runs all three simultaneously and synthesizes the results into a single Risk Summary Report with an overall score and top action items for the RM.

How it’s built

LangGraph’s Send API is what makes true fan-out possible. Instead of running nodes sequentially, it dispatches multiple nodes at the same time from a single starting point:

from langgraph.types import Send

def fan_out(state):
    return [
        Send("market_risk", state),
        Send("liquidity_risk", state),
        Send("concentration_risk", state),
    ]
graph.add_conditional_edges(START, fan_out,
    ["market_risk", "liquidity_risk", "concentration_risk"])

Each specialist agent receives the same portfolio input and works independently. When all three finish, LangGraph merges their outputs before passing everything to the synthesis node.

One important rule: each parallel node must return only the key it writes to — not the full state. If two nodes both try to update a shared key at the same time, LangGraph raises an error.

def analyze_market_risk(state):
    # ... analysis ...
    return {"market_risk_analysis": response}   # only this key

When it fits

Multi-dimensional analysis where the dimensions are independent. Portfolio reviews, competitive intelligence across multiple companies, multi-jurisdiction regulatory checks, simultaneous document analysis across different risk categories.

Pattern 3 — Router

The idea

An input is classified first, then routed to the most appropriate specialist agent. Only one path executes. The router reads the input, decides the destination, and dispatches accordingly.

This is the pattern behind most real-world triage systems — call center routing, email categorization, helpdesk ticket assignment.

The flow

The use case: Corporate banking query triage

A corporate bank’s client portal receives hundreds of inbound queries every day. A manufacturing company asking about a delayed SWIFT payment needs a completely different response than a trading firm asking to increase its working capital limit — which is again completely different from a company that has received an AML flag on its account.

Routing all of these to a single general-purpose AI agent produces mediocre responses across the board. A specialist agent for transaction banking knows the exact questions to ask about UETR numbers, correspondent bank chains, and NOSTRO reconciliation. It shouldn’t be spending mental capacity on KYC compliance rules.

The router pattern solves this. A classifier first categorizes the query:

  • Transaction — payment failures, SWIFT issues, fund transfer disputes, reconciliation
  • Credit — working capital limits, loan drawdowns, LC/BG requests, interest rate queries
  • Compliance — KYC renewal, AML flags, FEMA queries, regulatory reporting
  • General — account servicing, RM contact, product information

Once classified, the query goes to the specialist agent for that domain. Each specialist is prompt-engineered for its area, producing sharper, more actionable responses.

How it’s built

The classifier node runs first. It sends the query to the LLM with explicit instructions to output exactly one category word and nothing else. This precision matters — the category word is used directly to route execution, so ambiguity here breaks the flow.

def classify_query(state):
    prompt = f"""
    Classify this query into exactly one of:
    TRANSACTION | CREDIT | COMPLIANCE | GENERAL
    Respond with ONLY the category word.
    Query: {state['client_query']}
    """
    result = llm.invoke(prompt)
    return {"query_type": result.content.strip().upper()}

The routing function reads the classified type and returns the name of the next node to execute:

def route_query(state):
    return state["query_type"].lower()

graph.add_conditional_edges("classify", route_query, {
    "transaction": "transaction",
    "credit": "credit",
    "compliance": "compliance",
    "general": "general",
})

Each specialist agent then handles only its category. The compliance agent’s prompt includes references to FEMA, AML frameworks, and regulatory timelines. The transaction agent asks about UETR numbers and correspondent banks. None of that cross-pollutes.

When it fits

Any time your input types are distinct enough that different handling logic applies to each. Works best when categories are well-defined. If your classifier is regularly getting confused between categories, the categories themselves need refinement — not more agents.

Pattern 4 — Reflection Loop

The idea

A writer agent produces an output. A critic agent evaluates it against a defined quality standard and either approves it or sends it back with specific feedback. This loop repeats until the output meets the bar or a maximum iteration count is hit.

It’s the AI equivalent of having a first draft reviewed by a senior before it goes out.

The flow

The use case: Investment banking equity research note

Equity research notes go to institutional investors. They influence capital allocation decisions. A generic, hedged, vague note is worse than useless — it actively damages the firm’s credibility.

A single-pass LLM is often too generic. It writes “the company has strong fundamentals and faces headwinds from macroeconomic uncertainty.” That’s not analysis; it’s noise. The reflection pattern enforces a quality standard that catches this.

The writer drafts a BUY/HOLD/SELL recommendation on Bajaj Finance — investment thesis, key financials (NII growth, PAT, GNPA), growth catalysts, risks, and a 12-month target price with valuation methodology.

The critic then applies a rigorous six-point checklist:

  • Is the investment thesis specific and backed by actual data?
  • Are real financial metrics cited with numbers?
  • Are growth catalysts company-specific, not generic sector commentary?
  • Are risks quantified where possible?
  • Is the target price justified with a methodology (P/E, DCF, EV/EBITDA)?
  • Is the recommendation consistent with the analysis?

If all six pass, the critic responds APPROVED. If not, it responds REVISE followed by a numbered list of exactly what needs fixing. The writer addresses each point in the next iteration.

How it’s built

The cycle in LangGraph is created with a conditional edge that checks whether the critic approved the output or whether maximum iterations have been reached:

MAX_ITERATIONS = 3

def should_continue(state):
    if state["approved"]:
        return "done"
    if state["iteration"] >= MAX_ITERATIONS:
        return "done"
    return "revise"

graph.add_conditional_edges("critique", should_continue,
    {"revise": "draft", "done": END})

The "revise" path loops back to the writer node. The "done" path exits — either because the critic approved, or because three iterations have been exhausted. The iteration count is tracked in state and incremented each time the writer runs.

Always cap iterations. Without the MAX_ITERATIONS guard, a strict critic can keep finding issues indefinitely. Three rounds is the right default — quality gains beyond that are marginal, and each loop adds latency and cost.

When it fits

Quality-critical outputs where a review rubric can be defined explicitly. Investment memos, credit committee reports, legal drafts, compliance documentation, model risk assessments. Avoid it for high-volume, real-time workflows — the loops add latency that isn’t always acceptable.

Pattern 5 — Hierarchical Supervisor

The idea

A supervisor agent sits at the top. It reads the problem, writes a coordination plan, and decides which specialists to engage. It then delegates — and only after all specialists have reported back does it reconvene to synthesize a final output.

This is not parallel fan-out with a merge step bolted on. The supervisor makes a strategic decision about the work before it starts and exercises judgment about the synthesis after. That two-level authority structure is what makes it hierarchical.

Think of it as a managing director who briefs four workstream leads, waits for their findings, and then writes the synthesis for the board.

The flow

The use case: Investment banking M&A deal coordination

A large private sector insurance company is acquiring Medi-Assist, India’s largest health insurance TPA. The Managing Director needs an Investment Committee memo before the board meeting.

Four workstreams need to run:

Valuation — DCF with WACC and terminal growth rate assumptions, comparable company analysis on EV/EBITDA and P/E multiples, precedent transaction multiples, and an implied equity value range with an offer price recommendation.

Legal — CCI filing requirement given Medi-Assist’s market share, IRDAI implications given the acquirer is an insurance company, key legal risks, and a due diligence checklist.

Synergies — Revenue synergies from vertical integration, fraud reduction, AI-led underwriting improvements. Cost synergies from headcount rationalization. NPV of total synergies with a Year 1/2/3 realization timeline.

Financing — Equity versus debt split, instruments (term loans, NCDs, mezzanine), pro forma leverage ratios post-deal, and interest coverage assessment.

The supervisor reviews the deal brief and writes a coordination plan that tells each workstream what to focus on. The specialists then execute with that context. The supervisor then synthesizes everything into a structured IC memo with a PROCEED / PROCEED WITH CONDITIONS / DO NOT PROCEED recommendation.

How it’s built

1. The supervisor plans — then decides who to engage

The supervisor isn’t just a pass-through. It reads the deal brief and writes a coordination plan. This plan is what each specialist will receive alongside the brief.

def supervisor_plan(state: DealState) -> dict:
    prompt = f"""
    You are the Managing Director. Review this deal and define
    specific priorities for Valuation, Legal, Synergies, and Financing.
    Deal Brief: {state['deal_brief']}
    """
    response = llm.invoke(prompt)
    return {"coordination_plan": response.content}

2. Delegation — the supervisor dispatches specialists with context

After planning, the supervisor dispatches all four specialists simultaneously. Each one receives the full state — which now includes the coordination plan — so they know what the MD wants them to focus on.

def dispatch_workstreams(state: DealState):
    return [
        Send("valuation", state),
        Send("legal", state),
        Send("synergy", state),
        Send("financing", state),
    ]

graph.add_conditional_edges(
    "supervisor_plan",
    dispatch_workstreams,
    ["valuation", "legal", "synergy", "financing"]
)

Each specialist uses the coordination plan in its prompt — it’s not just getting a raw brief, it’s getting the MD’s specific instructions for its workstream.

3. Each specialist owns one key and reports back

def valuation_agent(state: DealState) -> dict:
    prompt = f"""
    Per the coordination plan, perform DCF, comparable company
    analysis, and precedent transactions. Recommend an offer price.
    Coordination Plan: {state['coordination_plan']}
    Deal Brief: {state['deal_brief']}
    """
    response = llm.invoke(prompt)
    return {"valuation_analysis": response.content}  # owns only this key

# legal_agent returns {"legal_assessment": ...}
# synergy_agent returns {"synergy_analysis": ...}
# financing_agent returns {"financing_structure": ...}

4. The supervisor reconvenes and synthesizes

LangGraph waits until all four specialists have finished before triggering supervisor_synthesize. By then, all four analysis keys are in state. The supervisor reads all of them and writes the IC memo — which is a judgment call, not a concatenation.

def supervisor_synthesize(state: DealState) -> dict:
    prompt = f"""
    You are the Managing Director. Write the Investment Committee memo.
    Include: transaction overview, valuation, synergies, financing,
    key risks, regulatory considerations, and recommendation.

    Valuation: {state['valuation_analysis']}
    Legal: {state['legal_assessment']}
    Synergies: {state['synergy_analysis']}
    Financing: {state['financing_structure']}
    """
    response = llm.invoke(prompt)
    return {"ic_memo": response.content}

The supervisor appears twice — once to plan and delegate, once to synthesize. That is the defining characteristic of the hierarchical pattern. A plain fan-out has no such coordination layer.

When it fits

Complex, multi-domain tasks where a coordinator adds genuine value — not just aggregation, but strategic direction before and synthesis after. M&A transactions, regulatory response submissions, enterprise risk assessments, complex client case resolutions.

Pattern 6 — Human-in-the-Loop

The idea

AI handles all the analytical preparation. At a defined checkpoint, execution pauses. A human reviews the output and provides a decision. The workflow continues only based on that decision — approve, reject, or modify. The AI cannot proceed unilaterally.

This is the pattern for every workflow where the consequences of a wrong decision are too significant to automate entirely.

The flow

The use case: Wealth management trade approval

HDFC Bank has underperformed Nifty Bank by 12% over three months. Q3 results just beat estimates. Nine of twelve analysts covering the stock have a BUY. The stock is sitting at its 200-day moving average with RSI in oversold territory. The AI identifies a mean-reversion opportunity and recommends buying 1,000 shares at a limit price of Rs. 1,710 — a Rs. 17.1 lakh trade for the client.

Before any order is placed, two things happen automatically. First, the AI risk check validates the trade against the client’s guardrails: single trade value must not exceed 10% of AUM, sector concentration must stay below 35%, no derivatives for this client category. The trade passes — Rs. 17.1 lakh is 4.1% of the client’s Rs. 4.2 crore AUM, and banking exposure goes from 25% to 29%, within limits.

Then the graph pauses. The full recommendation, market signal, and risk check result are presented to the Relationship Manager. The RM has three options:

  • APPROVE — the trade executes as recommended
  • REJECT — the trade is cancelled, with notes recorded
  • MODIFY — the RM specifies changes (e.g. “reduce to 500 shares”), the AI revises the trade ticket, and execution proceeds

How it’s built

There are two separate mechanisms at work here — interruption and checkpointing. Both are required. Confusing them is the most common mistake.

1. The interrupt — this is what actually stops the graph

LangGraph’s interrupt() function halts execution mid-graph and surfaces control to the caller. This is the pause point. Without it, the graph would run straight through regardless of any checkpointer.

from langgraph.types import interrupt

def request_human_approval(state: TradeState) -> dict:
    # Pause here and send data out for the human to review
    decision = interrupt({
        "recommendation": state["trade_recommendation"],
        "risk_check": state["risk_check"],
        "client": state["client_profile"]
    })
    # Execution resumes here only after the human responds
    return {
        "human_decision": decision["action"],
        "human_notes": decision.get("notes", "")
    }

When interrupt() is called, LangGraph saves the current state, stops, and returns control. The human's response is passed back in when execution resumes.

2. The checkpointer — this is what makes resumption possible

MemorySaver saves a snapshot of the full graph state at every node. When the graph resumes after the human responds, it picks up from exactly where it left off — all prior context (recommendation, risk check, client profile) is still in state.

from langgraph.checkpoint.memory import MemorySaver

memory = MemorySaver()
app = graph.compile(checkpointer=memory, interrupt_before=["request_human_approval"])
config = {"configurable": {"thread_id": "trade-session-001"}}
# First run - graph executes until the interrupt, then pauses
app.invoke(initial_state, config=config)
# RM reviews the surfaced data and responds
# Second run - graph resumes from the checkpoint with RM's decision
app.invoke(Command(resume={"action": "APPROVE"}), config=config)

The thread_id is what links the two invocations. It's how LangGraph knows which checkpoint to reload when resuming. In production, swap MemorySaver for SqliteSaver or PostgresSaver so state survives process restarts between the two calls.

3. Routing after the decision

def route_after_human(state: TradeState) -> str:
    decision = state["human_decision"].upper()
    if decision == "APPROVE":
        return "execute"
    elif decision == "REJECT":
        return "rejected"
    elif decision.startswith("MODIFY"):
        return "modify"
    return "rejected"

graph.add_conditional_edges("request_human_approval", route_after_human, {
    "execute": "execute",
    "rejected": "rejected",
    "modify": "modify"
})

The interrupt surfaces data out, waits for the human, resumes with their input, and then this routing function determines which path execution takes next.

When it fits

Any decision that is irreversible, regulated, or carries significant financial, legal, or reputational consequences. Large trade execution, loan disbursements, credit limit increases above a threshold, material disclosure filings, KYC override approvals. Also valuable in the early phases of AI deployment — it lets you build confidence in the system’s recommendations before extending more autonomy.

Pattern 7 — Plan and Execute

The idea

A planner agent reads the problem and dynamically generates a task list specific to that input. An executor agent works through each task one by one, accumulating findings. A synthesizer compiles everything into a final output. The tasks are not defined at design time — they are created at runtime based on what the planner decides is relevant for this specific case.

This is the most powerful pattern, and the most expensive. Use it when the right approach genuinely cannot be predetermined.

The flow

The use case: Private equity due diligence

A PE fund is evaluating a Rs. 280 crore investment in PayNxt Technologies — a Series B fintech company providing payment gateway and reconciliation software to mid-market retailers and logistics companies. Monthly GMV of Rs. 4,200 crore. FY24 revenue Rs. 95 crore, growing 68% year-on-year.

The right due diligence questions for this company are not the same as for a lending NBFC or a wealth management platform. The key risks here are specific: the RBI payment aggregator license pending renewal, top-5 client concentration at 48% of revenue, the founding team’s depth, and unit economics on GMV.

A fixed sequential pipeline would ask generic questions regardless of the target. The planner reads PayNxt’s profile and generates a custom investigation task list — 5 to 7 tasks tailored to this company’s specific risk profile. The executor then works through each task, producing structured findings with a Red/Amber/Green rating. The synthesizer produces an Investment Committee DD report with a PROCEED / PROCEED WITH CONDITIONS / DO NOT INVEST recommendation.

How it’s built

1. The state — tasks and findings are lists, not fixed fields

This is what distinguishes Plan and Execute from every other pattern. The state doesn’t have pre-named fields for each step. It has a dynamically populated task list and a growing findings list.

class DDState(TypedDict):
    target_brief: str
    dd_plan: List[str]           # populated by planner at runtime
    current_task_index: int      # tracks executor position in the plan
    completed_findings: List[str]  # grows with each executor iteration
    final_report: str

In Sequential, you know the fields upfront. Here, dd_plan is empty until the planner runs. completed_findings grows with every executor loop. The structure of the work is discovered, not predetermined.

2. The planner — generates the task list from the brief

def create_dd_plan(state: DDState) -> dict:
    prompt = f"""
    You are a PE due diligence planner. Generate 5-7 specific
    investigation tasks tailored to this target's risk profile.
    Return ONLY a JSON array of task strings.
    Target: {state['target_brief']}
    """
    result = llm.invoke(prompt)
    dd_plan = json.loads(result.content.strip())
    return {"dd_plan": dd_plan, "current_task_index": 0, "completed_findings": []}

For PayNxt, the planner might generate: “Assess RBI PA license renewal risk and regulatory timeline”, “Analyse revenue concentration — top 5 clients, contract terms, churn risk”, “Review GMV quality and net revenue yield”. These are specific to PayNxt. Run the same planner on a lending NBFC and you get a completely different task list.

3. The executor loop — the graph re-enters itself until all tasks are done

def execute_task(state: DDState) -> dict:
    current_task = state["dd_plan"][state["current_task_index"]]
    prompt = f"""
    Complete this due diligence task. Provide findings and a
    Green / Amber / Red rating.
    Task: {current_task}
    Target: {state['target_brief']}
    """
    result = llm.invoke(prompt)
    return {
        "completed_findings": state["completed_findings"] + [result.content],
        "current_task_index": state["current_task_index"] + 1
    }

def should_continue(state: DDState) -> str:
    if state["current_task_index"] >= len(state["dd_plan"]):
        return "synthesize"
    return "execute"

graph.add_edge("plan", "execute")
graph.add_conditional_edges("execute", should_continue,
    {"execute": "execute", "synthesize": "synthesize"})

The conditional edge after execute is what creates the loop. If there are tasks remaining, it routes back to execute. If all tasks are done, it exits to synthesize. The number of iterations is determined at runtime by the planner's output — not by anything hardcoded.

This self-referential loop is the core mechanism. No other pattern does this.

4. The synthesizer — compiles all findings into a single report

def synthesize_dd_report(state: DDState) -> dict:
    all_findings = "\n\n---\n\n".join(state["completed_findings"])
    prompt = f"""
    Write an Investment Committee DD report based on these findings.
    Include: overall rating, findings summary, deal breakers if any,
    conditions to investment, and recommendation.
    Findings: {all_findings}
    """
    result = llm.invoke(prompt)
    return {"final_report": result.content}

By the time this runs, completed_findings contains one entry per task. The synthesizer reads all of them and produces a single coherent report.

When it fits

Open-ended investigation and research tasks where the right approach depends entirely on the specific input. Due diligence, forensic analysis, regulatory investigation responses, open-ended market research. Avoid it for well-defined workflows — Plan and Execute costs 3 to 5 times more in LLM calls than a simple sequential pipeline for the same task.

Choosing the right pattern

Steps fixed and sequential?                  →  Sequential Pipeline
Independent analyses on the same input?      →  Parallel Fan-out
Input type determines the handling?          →  Router
Output needs iterative quality improvement?  →  Reflection Loop
Multi-domain task requiring coordination?    →  Hierarchical Supervisor
Decision is irreversible or regulated?       →  Human-in-the-Loop
Right steps unknown until you see the input? →  Plan and Execute

A few principles worth internalizing:

Start simple. Plan and Execute is the most intellectually satisfying pattern to build. It is also the most expensive and the slowest. If your steps are fixed, use Sequential. Reach for complexity only when simpler patterns demonstrably fail.

Patterns compose. A loan disbursement system might use Sequential for the underwriting pipeline and Human-in-the-Loop before the disbursement trigger. An M&A advisory platform might chain Router (deal type classification) into Hierarchical (workstream coordination) into Reflection (IC memo quality gate). Real systems are almost never a single pure pattern.

The parallel patterns need discipline. In Parallel Fan-out and Hierarchical, every agent running concurrently must return only the keys it owns. Returning the full state from a parallel node causes an InvalidUpdateError in LangGraph. It is a one-line fix once you understand why, but it will catch you the first time.

Where to go from here

The full codebase for all seven patterns is available at https://github.com/AbhilashBahinipati/AI_Agents. Each pattern is a self-contained Python file with streaming output, detailed comments, and a realistic financial services scenario you can run against your own OpenAI key.

The repository also includes a PATTERNS_GUIDE.md with a comparison matrix, domain-specific recommendations for different parts of financial services, and notes on common implementation mistakes.

If you’re building agentic systems in finance and want to discuss what you’re running into, reach out.

Tested with LangGraph 0.2+, LangChain 0.3+, and GPT-4o-mini. All financial scenarios are illustrative.

A message from our Founder

Hey, Sunil here. I wanted to take a moment to thank you for reading until the end and for being a part of this community. Did you know that our team run these publications as a volunteer effort to over 3.5m monthly readers? We don’t receive any funding, we do this to support the community.

If you want to show some love, please take a moment to follow me on LinkedIn, TikTok, Instagram. You can also subscribe to our weekly newsletter. And before you go, don’t forget to clap and follow the writer️!

More in finance

Venture

Write for entrepreneurs, founders, and builders.

Share startup lessons, growth tactics, and founder stories with readers on the same journey.

One free account across In Plain English, Stackademic, Venture, and Cubed.

How it works
  • Startups & entrepreneurship
  • Marketing & growth
  • Productivity & leadership
  • Founder stories & lessons learned
1

Sign in

Google or GitHub

2

Complete profile

Takes a few minutes

3

Get approved & publish

Start sharing

Why write for Venture?

Entrepreneurship is rarely a straight path. The lessons worth sharing are learned while building.

Comments

Loading comments…

Posts Across the Network