How to Build a underwriting Agent Using AutoGen in TypeScript for investment banking

By Cyprian AaronsUpdated 2026-04-21
underwritingautogentypescriptinvestment-banking

An underwriting agent in investment banking helps analysts and bankers turn raw deal inputs into a structured credit or equity underwriting package: it reads issuer data, extracts risk factors, checks policy constraints, drafts committee notes, and flags missing diligence. The value is simple: faster turnaround on deal memos without losing the controls that matter for compliance, auditability, and human approval.

Architecture

  • Ingress layer

    • Accepts deal documents, financial statements, management notes, and term sheets.
    • Normalizes input into a consistent schema before any model call.
  • Underwriting planner agent

    • Breaks the request into tasks like covenant review, issuer risk summary, and memo drafting.
    • Decides which specialist agent should handle each step.
  • Specialist analyst agents

    • One agent for financial analysis.
    • One agent for compliance/policy checks.
    • One agent for memo drafting and redlining.
  • Supervisor / orchestrator

    • Coordinates handoffs between agents.
    • Enforces stop conditions and human escalation.
  • Audit and trace store

    • Persists prompts, outputs, tool calls, and approval decisions.
    • Required for model governance and post-trade review.
  • Policy guardrail layer

    • Blocks unsupported claims, restricted data movement, and policy violations.
    • Ensures the workflow stays inside banking controls.

Implementation

1) Install AutoGen for TypeScript and define your deal context

Use the TypeScript AutoGen packages that expose the agent runtime. For an underwriting workflow, keep the input typed so you can validate required fields before any model interaction.

npm install @autogenai/agent-runtime zod
import { z } from "zod";

export const UnderwritingInputSchema = z.object({
  issuerName: z.string(),
  sector: z.string(),
  jurisdiction: z.string(),
  facilityType: z.enum(["revolver", "term_loan", "bond", "equity"]),
  amountUsd: z.number().positive(),
  financialHighlights: z.array(z.string()).min(1),
  risks: z.array(z.string()).default([]),
});

export type UnderwritingInput = z.infer<typeof UnderwritingInputSchema>;

This schema is not optional in banking. It gives you a clean boundary between upstream document extraction and downstream agent logic.

2) Create specialist agents with explicit roles

A practical underwriting setup uses multiple agents instead of one large prompt. The supervisor sends work to the right specialist based on task type.

import {
  AssistantAgent,
  UserProxyAgent,
} from "@autogenai/agent-runtime";

const financialAnalyst = new AssistantAgent({
  name: "financial_analyst",
  systemMessage:
    "You are a credit analyst. Summarize leverage, liquidity, coverage, and key downside risks. Do not invent numbers.",
});

const complianceAnalyst = new AssistantAgent({
  name: "compliance_analyst",
  systemMessage:
    "You are a banking compliance reviewer. Flag KYC/AML gaps, restricted language, missing disclosures, and jurisdiction issues.",
});

const memoWriter = new AssistantAgent({
  name: "memo_writer",
  systemMessage:
    "You draft investment banking underwriting memos. Write concise committee-ready language with clear assumptions and caveats.",
});

const supervisor = new UserProxyAgent({
  name: "supervisor",
});

The important part here is role separation. In underwriting, you want one agent optimizing for analysis quality and another optimizing for policy safety.

3) Orchestrate the workflow with group chat

AutoGen’s conversation pattern is useful here because underwriting is naturally iterative. The supervisor can collect outputs from each specialist and then synthesize them into a final memo draft.

import {
  GroupChat,
  GroupChatManager,
} from "@autogenai/agent-runtime";

async function runUnderwriting(input: UnderwritingInput) {
  const chat = new GroupChat({
    agents: [supervisor, financialAnalyst, complianceAnalyst, memoWriter],
    messages: [
      {
        role: "user",
        content: `Prepare an underwriting package for ${input.issuerName}. 
Sector: ${input.sector}
Jurisdiction: ${input.jurisdiction}
Facility: ${input.facilityType}
Amount USD: ${input.amountUsd}
Financial highlights:
${input.financialHighlights.map((x) => `- ${x}`).join("\n")}
Known risks:
${input.risks.map((x) => `- ${x}`).join("\n")}`,
      },
    ],
    maxRound: 6,
  });

  const manager = new GroupChatManager({
    groupChat: chat,
    llmConfig: {
      model: "gpt-4o-mini",
      apiKey: process.env.OPENAI_API_KEY!,
    },
  });

  const result = await manager.run();
  return result;
}

This pattern works because it keeps the conversation bounded. maxRound matters in production; otherwise you can burn tokens on circular debate between agents.

4) Add a guardrail pass before final output

For investment banking use cases, never let the memo go straight to users without a policy check. A simple post-processing pass can block unsupported claims or missing caveats before approval routing.

function validateMemo(text: string): string[] {
  const issues: string[] = [];

  if (!text.includes("for discussion purposes only")) {
    issues.push("Missing standard disclaimer.");
  }

   if (text.match(/guaranteed|certain|risk-free/i)) {
    issues.push("Contains prohibited absolute language.");
   }

   if (!text.includes("assumptions")) {
    issues.push("Missing assumptions section.");
   }

   return issues;
}

async function main() {
  const input = UnderwritingInputSchema.parse({
    issuerName: "Northwind Capital Partners",
    sector: "Healthcare Services",
    jurisdiction: "US",
    facilityType: "term_loan",
    amountUsd: 250000000,
    financialHighlights: [
      "EBITDA margin stable at ~24%",
      "Net leverage at 3.2x",
      "FCF conversion above peer median",
    ],
    risks: ["Customer concentration", "Regulatory reimbursement pressure"],
  });

  const result = await runUnderwriting(input);
}

In practice you would extract the final memo text from result, run validateMemo, then route any failures to a human banker or compliance reviewer.

Production Considerations

  • Deploy inside your bank’s approved region

    Keep model calls and vector storage in-region if your data residency policy requires it. Deal documents often contain MNPI or confidential borrower data; don’t ship that across uncontrolled endpoints.

  • Log every decision path

    Persist agent prompts, responses, tool calls, timestamps, model version, and approver identity. If a committee later asks why a covenant risk was downgraded, you need an audit trail that reconstructs the exact path.

  • Add hard guardrails for regulated language

    Block phrases that imply certainty around outcomes or returns. Underwriting output should be framed as analysis with assumptions, not promises about pricing performance or credit outcomes.

  • Route sensitive cases to humans

    Any deal involving sanctions exposure, cross-border restrictions, unusual structures, or incomplete diligence should trigger escalation. The agent should assist bankers, not replace judgment on edge cases.

Common Pitfalls

  • Using one generic agent for everything

    This creates mushy outputs and weak control boundaries. Split analysis, compliance review, and memo drafting into separate agents with narrow system instructions.

  • Skipping structured input validation

    If upstream extraction feeds garbage into the model, your memo will look confident but be wrong. Validate issuer name, facility type, amount fields, and required disclosures before any AutoGen run.

  • Ignoring governance after generation

    A good draft is not enough in investment banking. You still need disclaimer checks, restricted-language filters, reviewer sign-off, and immutable logs tied to the deal record.


Keep learning

By Cyprian Aarons, AI Consultant at Topiax.

Want the complete 8-step roadmap?

Grab the free AI Agent Starter Kit — architecture templates, compliance checklists, and a 7-email deep-dive course.

Get the Starter Kit

Related Guides