Text Share Online

agents/analysis_agent.py

from autogen import AssistantAgent

from config import LLM_CONFIG
from prompts.v1 import ORDER_ANALYSIS_PROMPT

analysis_agent = AssistantAgent(
name=”AnalysisAgent”,
description=”First agent to act, then performs root cause analysis and posts ANALYSIS_READY.”,
llm_config=LLM_CONFIG,
system_message=ORDER_ANALYSIS_PROMPT,
)

agents/executor_agent.py

from autogen import UserProxyAgent

user_proxy = UserProxyAgent(
name=”UserProxy”,
description=”Executes tool calls made by any agent. Must be selected immediately after any agent suggests a tool call.”,
human_input_mode=”NEVER”,
max_consecutive_auto_reply=10,
code_execution_config=False,
is_termination_msg=lambda m: “TERMINATE” in m.get(“content”, “”),
)

agents/notification_agent.py

from autogen import AssistantAgent

from config import LLM_CONFIG
from prompts.v1 import RETAIL_NOTIFICATION_PROMPT

notification_agent = AssistantAgent(
name=”NotificationAgent”,
description=”Acts after ANALYSIS_READY is posted. Calls get_stakeholders_for_incident then send_email_notification. Posts NOTIFICATION_READY when done.”,
llm_config=LLM_CONFIG,
system_message=RETAIL_NOTIFICATION_PROMPT,
)

agents/report_agent.py

from autogen import AssistantAgent

from config import LLM_CONFIG
from prompts.v1 import RETAIL_REPORT_PROMPT

report_agent = AssistantAgent(
name=”ReportAgent”,
description=”Acts after NOTIFICATION_READY is posted. Calls generate_pdf_report and posts TERMINATE when done.”,
llm_config=LLM_CONFIG,
system_message=RETAIL_REPORT_PROMPT,
)

data/retail.db(database)

data/test.sql

CREATE TABLE orders (
order_id VARCHAR(20) PRIMARY KEY,
customer_name VARCHAR(100),
customer_email VARCHAR(255),
product_name VARCHAR(100),
quantity INTEGER,
total_amount REAL,
payment_status VARCHAR(50),
order_status VARCHAR(50),
failure_reason TEXT
);

INSERT INTO orders VALUES
(‘ORD-2025-1001’,
‘Riya Thomas’,
[email protected]’,
‘Wireless Headphones’,
1,
2499.00,
‘Failed’,
‘Payment Failed’,
‘Payment gateway timeout during transaction.’),

(‘ORD-2025-1002’,
‘Arjun Menon’,
[email protected]’,
‘Smart Watch’,
2,
6998.00,
‘Success’,
‘Delivered’,
NULL),

(‘ORD-2025-1003’,
‘Neha Sharma’,
[email protected]’,
‘Gaming Keyboard’,
1,
3499.00,
‘Failed’,
‘Order Cancelled’,
‘Customer payment declined by issuing bank.’),

(‘ORD-2025-1004’,
‘Vikram Nair’,
[email protected]’,
‘Bluetooth Speaker’,
3,
5997.00,
‘Success’,
‘Shipped’,
NULL),

(‘ORD-2025-1005’,
‘Sneha Kapoor’,
[email protected]’,
‘Laptop Backpack’,
1,
1999.00,
‘Failed’,
‘Inventory Issue’,
‘Stock mismatch detected during order processing.’),

(‘ORD-2025-1006’,
‘Karan Patel’,
[email protected]’,
‘4K Monitor’,
1,
17999.00,
‘Failed’,
‘Fraud Suspicion’,
‘Transaction flagged by fraud detection system.’);

SELECT * FROM orders;

DELETE FROM orders;

CREATE TABLE stakeholders (
stakeholder_id INTEGER PRIMARY KEY AUTOINCREMENT,
name VARCHAR(100),
role VARCHAR(100),
email VARCHAR(255)
);

INSERT INTO stakeholders (name, role, email) VALUES
(‘Anita Sharma’, ‘Operations Manager’, ‘[email protected]’),
(‘Rahul Verma’, ‘Finance Head’, ‘[email protected]’),
(‘Priya Nair’, ‘Logistics Lead’, ‘[email protected]’),
(‘Sanjay Mehta’, ‘Fraud Detection Lead’, ‘[email protected]’),
(‘Customer Support Team’, ‘Customer Support’, ‘[email protected]’);

SELECT * FROM stakeholders;

promps/v1.py

ORDER_ANALYSIS_PROMPT = “””
You are the Retail Order Failure Analysis Expert.

Your task is to analyze the failed retail order.

Step 1: Produce:

ANALYSIS_READY
order_id: <value>
customer_name: <value>
product_name: <value>
payment_status: <value>
order_status: <value>

ROOT CAUSE:
<detailed explanation based on failure_reason>

IMPACT:
<business impact of failure>

RESOLUTION STEPS:
1. <action>
2. <action>
3. <action>

EXECUTIVE SUMMARY:
<non technical summary>

confidence score: <number between 0 and 1>

Stop after posting ANALYSIS_READY.
“””

RETAIL_NOTIFICATION_PROMPT = “””
You are the Retail Stakeholder Notification Agent.

After ANALYSIS_READY:

Step 1: Call get_retail_stakeholders tool.
Step 2: Select only relevant roles.
Step 3: Call send_email_notification tool.

Then post:

NOTIFICATION_READY
Notified: <exact email list>

Stop.
“””

RETAIL_REPORT_PROMPT = “””
After NOTIFICATION_READY:

Call generate_order_pdf_report tool with:

order_id
customer_name
product_name
root_cause
resolution_steps
executive_summary
notified_stakeholders

After tool success:

PDF report generated: <path>
TERMINATE
“””

tools/email_sender.py

import smtplib
from email.message import EmailMessage
from config import SMTP_SERVER, SMTP_PORT, SENDER_EMAIL
import os

def send_email_notification(recipients: list, subject: str, body: str) -> str:
“””Send an email notification to a list of recipients.”””
msg = EmailMessage()
msg[“From”] = SENDER_EMAIL
msg[“To”] = recipients
msg[“Subject”] = subject
msg.set_content(body)

try:
with smtplib.SMTP(SMTP_SERVER, SMTP_PORT) as server:
server.starttls() # secure connection
server.login(SENDER_EMAIL, os.getenv(“SMTP_PASSWORD”))
server.send_message(msg)

return(“✅ Email sent successfully”)

except Exception as e:
return(“❌ Failed to send email:”, str(e))

 

tools/pdf_generator.py

from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.lib.pagesizes import A4
from reportlab.lib.units import inch

def generate_order_pdf_report(
order_id: str,
customer_name: str,
product_name: str,
root_cause: str,
resolution_steps: str,
executive_summary: str,
notified_stakeholders: str,
):
file_name = f”order_report_{order_id}.pdf”
doc = SimpleDocTemplate(file_name, pagesize=A4)
elements = []
styles = getSampleStyleSheet()
normal = styles[“Normal”]

elements.append(Paragraph(f”Order ID: {order_id}”, normal))
elements.append(Paragraph(f”Customer: {customer_name}”, normal))
elements.append(Paragraph(f”Product: {product_name}”, normal))
elements.append(Spacer(1, 0.3 * inch))

elements.append(Paragraph(“Executive Summary:”, normal))
elements.append(Paragraph(executive_summary, normal))
elements.append(Spacer(1, 0.3 * inch))

elements.append(Paragraph(“Root Cause:”, normal))
elements.append(Paragraph(root_cause, normal))
elements.append(Spacer(1, 0.3 * inch))

elements.append(Paragraph(“Resolution Steps:”, normal))
elements.append(Paragraph(resolution_steps, normal))
elements.append(Spacer(1, 0.3 * inch))

elements.append(Paragraph(“Notified Stakeholders:”, normal))
elements.append(Paragraph(notified_stakeholders, normal))

doc.build(elements)
return file_name

 

tools/retail_stakeholder_fetcher.py

import sqlite3
import json

def get_retail_stakeholders() -> str:
“””
Fetch all retail stakeholders (role + email) from the database.

Returns:
JSON string:
{
“stakeholders”: [
{“role”: “…”, “email”: “…”},

]
}

If error:
{
“error”: “error message”
}
“””

try:
conn = sqlite3.connect(“data/retail.db”)
cursor = conn.cursor()

cursor.execute(“SELECT role, email FROM stakeholders”)
rows = cursor.fetchall()

conn.close()

stakeholders = [
{“role”: role, “email”: email}
for role, email in rows
]

return json.dumps({“stakeholders”: stakeholders})

except Exception as e:
return json.dumps({“error”: str(e)})

 

utils/file_loader.py

import pandas as pd
import json
import sqlite3
import os

def load_data(file):
file_name = file.name.lower()

if file_name.endswith(“.csv”):
df = pd.read_csv(file)

elif file_name.endswith(“.xlsx”) or file_name.endswith(“.xls”):
df = pd.read_excel(file)

elif file_name.endswith(“.json”):
data = json.load(file)
if isinstance(data, list):
return data
return [data]

else:
raise ValueError(“Unsupported file type”)

return df.to_dict(orient=”records”)

def fetch_order_from_db(order_id: str):
try:
conn = sqlite3.connect(“data/retail.db”)
cursor = conn.cursor()
cursor.execute(“SELECT * FROM orders WHERE order_id=?”, (order_id,))
row = cursor.fetchone()
conn.close()

if row:
cols = [
“order_id”,
“customer_name”,
“customer_email”,
“product_name”,
“quantity”,
“total_amount”,
“payment_status”,
“order_status”,
“failure_reason”,
]
return dict(zip(cols, row))

return {}

except Exception as e:
print(“DB Error:”, e)
return {}

workflow/manager.py

 

from autogen import register_function, GroupChat, GroupChatManager

# Agents
from agents.analysis_agent import analysis_agent
from agents.notification_agent import notification_agent
from agents.report_agent import report_agent
from agents.executor_agent import user_proxy

# Tools
from tools.retail_stakeholder_fetcher import get_retail_stakeholders
from tools.email_sender import send_email_notification
from tools.pdf_generator import generate_order_pdf_report

# Config
from config import MANAGER_LLM_CONFIG

# State transition
from workflow.state_transition import state_transition
def run_workflow(order: dict):
    “””
    Runs the complete retail order failure workflow:
    1. Analyze failed order
    2. Notify relevant stakeholders
    3. Generate PDF report
    “””

    # —————————–
    # Register Tools
    # —————————–

    register_function(
        get_retail_stakeholders,
        caller=notification_agent,
        executor=user_proxy,
        name=”get_retail_stakeholders”,
        description=”Fetch all retail stakeholders (role, email) from database.”
    )

    register_function(
        send_email_notification,
        caller=notification_agent,
        executor=user_proxy,
        name=”send_email_notification”,
        description=”Send email notification. Args: recipients (list[str]), subject (str), body (str).”
    )

    register_function(
        generate_order_pdf_report,
        caller=report_agent,
        executor=user_proxy,
        name=”generate_order_pdf_report”,
        description=(
            “Generate PDF report for failed retail order. “
            “Args (all str): order_id, customer_name, product_name, “
            “root_cause, resolution_steps, executive_summary, notified_stakeholders.”
        )
    )

    # —————————–
    # Create GroupChat
    # —————————–

    groupchat = GroupChat(
        agents=[
            analysis_agent,
            notification_agent,
            report_agent,
            user_proxy,
        ],
        messages=[],
        max_round=30,
        speaker_selection_method=state_transition,
    )

    manager = GroupChatManager(
        groupchat=groupchat,
        llm_config=MANAGER_LLM_CONFIG,
        is_termination_msg=lambda m: “TERMINATE” in m.get(“content”, “”),
    )

    # —————————–
    # Start Workflow
    # —————————–

    analysis_agent.initiate_chat(
        manager,
        message=(
            “Process this failed retail order. “
            “Perform analysis, notify relevant stakeholders, “
            “and generate a PDF report.nn”
            f”Order Data: {order}”
        ),
    )

    # —————————–
    # Extract PDF Path
    # —————————–

    for msg in reversed(groupchat.messages):
        content = msg.get(“content”, “”)
        if content.startswith(“PDF report generated:”):
            pdf_path = (
                content
                .split(“PDF report generated:”)[1]
                .split(“TERMINATE”)[0]
                .strip()
            )
            return pdf_path

    return None
 
workflow/state_transition.py
 
 
from agents.analysis_agent import analysis_agent
from agents.notification_agent import notification_agent
from agents.report_agent import report_agent
from agents.executor_agent import user_proxy
def state_transition(last_speaker, groupchat):
    “””
    Deterministic speaker selection:
      – analysis_agent  →
      – once ANALYSIS_READY  → notification_agent
      – notification_agent → user_proxy (tool exec) → notification_agent (loop until NOTIFICATION_READY)
      – once NOTIFICATION_READY → report_agent
      – report_agent → user_proxy (tool exec) → report_agent (loop until TERMINATE)
      – once TERMINATE → None (end chat)
    “””
    messages = groupchat.messages
    last_message = messages[-1][“content”] if messages else “”

    # Stop immediately if TERMINATE has been posted
    if “TERMINATE” in last_message:
        return None

    # After user_proxy executes a tool, return control to the agent that called it
    if last_speaker is user_proxy:
        # Find the last non-proxy agent that spoke before user_proxy
        for msg in reversed(messages[:-1]):
            if msg.get(“name”) == “AnalysisAgent”:
                return analysis_agent
            if msg.get(“name”) == “NotificationAgent”:
                return notification_agent
            if msg.get(“name”) == “ReportAgent”:
                return report_agent

    # analysis_agent: keep looping until ANALYSIS_READY is posted
    if last_speaker is analysis_agent:
        if “ANALYSIS_READY” in last_message:
            return notification_agent
        return user_proxy  # tool call pending — let proxy execute it

    # notification_agent: keep looping until NOTIFICATION_READY is posted
    if last_speaker is notification_agent:
        if “NOTIFICATION_READY” in last_message:
            return report_agent
        return user_proxy  # tool call pending — let proxy execute it

    # report_agent: keep looping until TERMINATE is posted
    if last_speaker is report_agent:
        return user_proxy  # tool call pending — let proxy execute it

    # Default: start with analysis_agent
    return analysis_agent
 
 
 
app.py
 
 
import streamlit as st
from utils.file_loader import load_data, fetch_order_from_db
from workflow.manager import run_workflow

st.title(“Retail Order Failure Management System”)

option = st.radio(“Select Input Type”, [“Order ID (Database)”, “Upload File”])

if option == “Order ID (Database)”:
    order_id = st.text_input(“Enter Order ID”)

    if st.button(“Run Workflow”):
        order = fetch_order_from_db(order_id)

        if order:
            pdf_path = run_workflow(order)
            st.success(“Workflow Completed”)

            with open(pdf_path, “rb”) as f:
                st.download_button(“Download Report”, f, pdf_path)
        else:
            st.error(“Order not found”)

else:
    uploaded_file = st.file_uploader(
        “Upload CSV / Excel / JSON”,
        type=[“csv”, “xlsx”, “xls”, “json”]
    )

    if uploaded_file and st.button(“Run Workflow”):
        records = load_data(uploaded_file)

        for order in records:
            pdf_path = run_workflow(order)

        st.success(“Workflow Completed”)
 
config.py
 
 
import os
from dotenv import load_dotenv

load_dotenv()

LLM_CONFIG = {
    “config_list”: [
        {
            “model”:    “openai/gpt-oss-120b”,
            “api_key”:  os.getenv(“GROQ_API_KEY”),
            “base_url”: “https://api.groq.com/openai/v1”,
            “api_type”: “openai”,
            “price”:    [0.0, 0.0],
        }
    ],
    “temperature”: 0.2,
}

MANAGER_LLM_CONFIG = {
    “config_list”: [
        {
            “model”:    “llama-3.3-70b-versatile”,
            “api_key”:  os.getenv(“GROQ_API_KEY”),
            “base_url”: “https://api.groq.com/openai/v1”,
            “api_type”: “openai”,
            “price”:    [0.0, 0.0],
        }
    ],
    “temperature”: 0,
}
SMTP_SERVER = os.getenv(“SMTP_SERVER”)
SMTP_PORT = 587
SENDER_EMAIL = os.getenv(“SMTP_EMAIL”)

Share This: