How to Build a customer support Agent Using CrewAI in Python for investment banking

By Cyprian AaronsUpdated 2026-04-21
customer-supportcrewaipythoninvestment-banking

A customer support agent for investment banking handles client queries, routes requests to the right desk, and drafts compliant responses without exposing sensitive data. It matters because support in this domain is not just about speed; it has to respect compliance, preserve auditability, and avoid giving advice that crosses regulatory lines.

Architecture

  • Client intake layer

    • Accepts email, chat, or ticket payloads.
    • Normalizes the request into a structured case object.
  • Intent classification agent

    • Identifies whether the request is about account access, trade status, corporate actions, fees, statements, or escalation.
    • Flags anything that looks like regulated advice or a complaint.
  • Policy retrieval layer

    • Pulls from approved internal knowledge: support playbooks, product FAQs, escalation matrices, and compliance rules.
    • Keeps responses grounded in bank-approved content only.
  • Response drafting agent

    • Produces a concise reply with the right tone and required disclaimers.
    • Never invents facts; if data is missing, it asks for clarification or escalates.
  • Escalation and routing toolset

    • Creates tickets for operations, compliance, legal, or relationship management.
    • Preserves the full trace of what the agent saw and decided.
  • Audit logging

    • Stores prompt inputs, tool calls, outputs, and final disposition.
    • Required for review under internal controls and regulatory scrutiny.

Implementation

1. Install CrewAI and define your tools

For investment banking support, your tools should be narrow. The model should not have free-form access to systems; it should query approved sources and create controlled outputs.

pip install crewai
from crewai import Agent, Task, Crew
from crewai.tools import BaseTool
from pydantic import BaseModel, Field


class TicketInput(BaseModel):
    customer_id: str = Field(..., description="Internal client identifier")
    subject: str = Field(..., description="Support ticket subject")
    message: str = Field(..., description="Raw customer message")


class PolicyLookupTool(BaseTool):
    name: str = "policy_lookup"
    description: str = "Look up approved support policy text for investment banking support cases."

    def _run(self, query: str) -> str:
        policies = {
            "trade status": "Provide status only from OMS/EMS. Do not speculate on settlement timing.",
            "account access": "Reset only after identity verification. Escalate if MFA is locked.",
            "fees": "Use published fee schedule only. No negotiated pricing in chat.",
            "advice": "Do not provide investment advice. Escalate to licensed personnel."
        }
        return policies.get(query.lower(), "No approved policy found. Escalate to compliance.")


class CaseCreateTool(BaseTool):
    name: str = "case_create"
    description: str = "Create an internal escalation case with full audit details."

    def _run(self, payload: str) -> str:
        return f"CASE_CREATED::{payload}"

2. Create specialized agents

Use one agent to classify intent and another to draft the response. Keep their responsibilities separate so you can audit each decision path independently.

support_triage_agent = Agent(
    role="Investment Banking Support Triage",
    goal="Classify incoming client requests and identify compliance-sensitive issues.",
    backstory=(
        "You work in an investment banking client service team. "
        "You must never provide financial advice or disclose restricted information."
    ),
    tools=[PolicyLookupTool(), CaseCreateTool()],
    verbose=True,
)

response_agent = Agent(
    role="Investment Banking Support Response Writer",
    goal="Draft compliant client responses using approved policy language only.",
    backstory=(
        "You write concise client-facing messages for a regulated financial institution. "
        "Every response must be factual, neutral, and suitable for audit review."
    ),
    verbose=True,
)

3. Define tasks with explicit constraints

The first task classifies the issue and decides whether it can be answered directly. The second task drafts a response based on that classification.

triage_task = Task(
    description=(
        "Review this customer support request for an investment banking client:\n"
        "{message}\n\n"
        "Return:\n"
        "- intent\n"
        "- risk_level\n"
        "- whether escalation is required\n"
        "- policy reference if relevant"
    ),
    expected_output="A structured triage summary with compliance flags.",
    agent=support_triage_agent,
)

draft_task = Task(
    description=(
        "Using the triage output below, draft a client response.\n"
        "Rules:\n"
        "- Do not provide investment advice\n"
        "- Do not mention internal systems\n"
        "- If escalation is needed, tell the client their request is being reviewed\n\n"
        "Triage output:\n{triage_output}"
    ),
    expected_output="A compliant client-ready response.",
    agent=response_agent,
)

4. Run the crew and wire in your case payload

This pattern keeps the flow simple: triage first, then draft. In production you would insert validation between steps before sending anything to a client.

def handle_support_request(ticket: TicketInput):
    triage_crew = Crew(
        agents=[support_triage_agent],
        tasks=[triage_task],
        verbose=True,
    )

    triage_result = triage_crew.kickoff(inputs={
        "customer_id": ticket.customer_id,
        "subject": ticket.subject,
        "message": ticket.message,
    })

    response_crew = Crew(
        agents=[response_agent],
        tasks=[draft_task],
        verbose=True,
    )

    final_response = response_crew.kickoff(inputs={
        "triage_output": str(triage_result),
    })

    return {
      "customer_id": ticket.customer_id,
      "triage": str(triage_result),
      "response": str(final_response),
      "status": "ready_for_review",
    }


if __name__ == "__main__":
    ticket = TicketInput(
        customer_id="CUST-10492",
        subject="Trade settlement question",
        message="Can you confirm why my equity trade has not settled yet?"
    )

    result = handle_support_request(ticket)
    print(result["response"])

Production Considerations

  • Put human approval in front of external delivery

    • In investment banking, the agent should draft; a supervisor or rules engine should approve before sending.
    • This is especially important for complaints, trade disputes, margin issues, and anything that could be construed as advice.
  • Log everything for audit

    • Store input payloads, model output, tool calls, timestamps, user identity, and final disposition.
    • You need traceability for internal audit teams and regulators.
  • Enforce data residency

    • Keep prompts and retrieved documents inside approved regions.
    • If your bank has jurisdiction-specific storage requirements, do not route content through unapproved third-party endpoints.
  • Add guardrails around regulated topics

    • Block advice on securities selection, performance guarantees, tax guidance, or legal interpretation.
    • Route those cases to licensed staff or compliance review immediately.

Common Pitfalls

  • Letting the model answer from memory

    • Bad pattern: asking the agent to “just respond naturally.”
    • Fix: require policy lookup or retrieval-backed context before any client-facing output.
  • Skipping escalation logic

    • Bad pattern: treating every ticket as a normal FAQ.
    • Fix: classify high-risk intents like complaints, insider information requests, trading errors, AML concerns, or KYC issues as mandatory escalations.
  • Ignoring auditability

    • Bad pattern: only storing the final answer.
    • Fix: persist the full chain of custody — raw request, triage decision, retrieved policy text (or document IDs), draft response, reviewer action.

If you build this with narrow tools, explicit tasks in Crew, and strict review gates between draft and send-out stages from Agent, Task, and Crew.kickoff(), you get something usable in a regulated support environment instead of a chatbot that sounds confident and creates risk.


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