383 lines
12 KiB
Plaintext
383 lines
12 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "g3-md-intro",
|
|
"metadata": {},
|
|
"source": [
|
|
"# 03 \u2014 Business Case\n",
|
|
"\n",
|
|
"Combine the benefits and costs into the consolidated TEI summary,\n",
|
|
"render the cash-flow exhibit, run scenario analysis, **and price the\n",
|
|
"Genesys AI Experience tokens line that the published study omits**.\n",
|
|
"This notebook should reproduce the headline numbers from the PDF\n",
|
|
"Financial Summary:\n",
|
|
"\n",
|
|
"* **NPV \\$10.78M \u2022 ROI 266% \u2022 Payback \u2248 4 months**\n",
|
|
"\n",
|
|
"It then exposes a sensitivity sweep for the AI-tokens annual cost so\n",
|
|
"you can see exactly what an honest deal looks like before sending it\n",
|
|
"to a client."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "03-bootstrap",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"import sys, pathlib # path shim: works on a fresh kernel\n",
|
|
"for _p in [pathlib.Path.cwd(), *pathlib.Path.cwd().parents]:\n",
|
|
" if (_p / \"pyproject.toml\").exists():\n",
|
|
" sys.path.insert(0, str(_p)); break\n",
|
|
"\n",
|
|
"from core.bootstrap import init\n",
|
|
"\n",
|
|
"pal = init(study=\"202512_GenesysCX\")\n",
|
|
"client, seed, config = pal.client, pal.seed_data, pal.config\n",
|
|
"\n",
|
|
"STUDY = pal.root / 'studies' / '202512_GenesysCX'\n",
|
|
"ROOT = pal.root\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "g3-code-imports",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from core.export.report_data import _compute_summary\n",
|
|
"from core.notebook_helpers import charts, display, tables"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "g3-md-summary",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Local summary (no Athena round-trip)\n",
|
|
"\n",
|
|
"Compute the moderate-case TEI summary directly from `seed_data` so the\n",
|
|
"notebook produces results even before the Athena tool is provisioned.\n",
|
|
"Headline numbers should match the published study."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "g3-code-summary",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"summary = _compute_summary(\n",
|
|
" seed.BENEFITS,\n",
|
|
" seed.COSTS,\n",
|
|
" config.DISCOUNT_RATE,\n",
|
|
" config.ANALYSIS_YEARS,\n",
|
|
")\n",
|
|
"# `_compute_summary` returns roi_pct; expose it as `roi` for kpi_cards.\n",
|
|
"summary['roi'] = summary.get('roi_pct')\n",
|
|
"display.kpi_cards(summary, title='Forrester composite \u2014 moderate case')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "g3-code-cashflow-table",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"df_cash = tables.cashflow_table(summary)\n",
|
|
"df_cash.style.format({c: '${:,.0f}' for c in df_cash.columns if c != 'Year'})"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "g3-md-cashflow",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Cash flow chart\n",
|
|
"\n",
|
|
"Mirrors the Forrester *Cash Flow Chart* exhibit: stacked benefits/costs\n",
|
|
"by year + cumulative-net line. Payback hits inside Year 1."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "g3-code-cashflow-chart",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"charts.cashflow_chart(\n",
|
|
" summary['yearly_breakdown'],\n",
|
|
" initial_cost=summary.get('initial_costs', 0),\n",
|
|
").show()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "g3-md-waterfall",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Waterfall: Benefits PV \u2192 Costs PV \u2192 NPV"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "g3-code-waterfall",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"charts.waterfall([\n",
|
|
" ('Benefits PV', summary['total_benefits_pv']),\n",
|
|
" ('Costs PV', -summary['total_costs_pv']),\n",
|
|
" ('NPV', summary['npv']),\n",
|
|
"]).show()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "g3-md-scenarios",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Scenario analysis\n",
|
|
"\n",
|
|
"Apply the default Palladium multipliers (see `core.calculations.SCENARIOS`):\n",
|
|
"\n",
|
|
"* **Conservative** \u2014 lower adoption, higher risk on benefits / lower on costs\n",
|
|
"* **Moderate** \u2014 base case (= the published Forrester study)\n",
|
|
"* **Aggressive** \u2014 full adoption, lower risk on benefits / higher on costs"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "g3-code-scenarios",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from core.calculations import apply_scenario\n",
|
|
"import pandas as pd\n",
|
|
"\n",
|
|
"scenario_summaries = {}\n",
|
|
"for name in ('conservative', 'moderate', 'aggressive'):\n",
|
|
" sb = apply_scenario(seed.BENEFITS, name, table='benefits')\n",
|
|
" sc = apply_scenario(seed.COSTS, name, table='costs')\n",
|
|
" scenario_summaries[name] = _compute_summary(sb, sc, config.DISCOUNT_RATE, config.ANALYSIS_YEARS)\n",
|
|
"\n",
|
|
"scen_df = pd.DataFrame([\n",
|
|
" {\n",
|
|
" 'Scenario': k,\n",
|
|
" 'Benefits PV': v['total_benefits_pv'],\n",
|
|
" 'Costs PV': v['total_costs_pv'],\n",
|
|
" 'NPV': v['npv'],\n",
|
|
" 'ROI %': v['roi_pct'],\n",
|
|
" 'Payback (mo)': round(v['payback_months'], 1) if v['payback_months'] is not None else None,\n",
|
|
" }\n",
|
|
" for k, v in scenario_summaries.items()\n",
|
|
"])\n",
|
|
"scen_df.style.format({\n",
|
|
" 'Benefits PV': '${:,.0f}', 'Costs PV': '${:,.0f}', 'NPV': '${:,.0f}', 'ROI %': '{:,.0f}%'\n",
|
|
"})"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "g3-code-scenario-chart",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"charts.scenario_comparison(scenario_summaries).show()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "g3-md-tokens-intro",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Genesys AI Experience tokens \u2014 annual cost\n",
|
|
"\n",
|
|
"Token pricing is tiered, capability-dependent, and deal-specific \u2014\n",
|
|
"Athena stores a single annual cost value per line, and so does the\n",
|
|
"seed. Enter the negotiated annual cost from the Genesys quote here.\n",
|
|
"Quote details (volume, unit price, tier) go into the field notes for\n",
|
|
"the audit trail.\n",
|
|
"\n",
|
|
"For sizing context, the study's own drivers imply roughly **1,040,000**\n",
|
|
"self-service interactions/yr and **3,120,000** agent-assisted\n",
|
|
"interactions/yr would draw tokens \u2014 bring the actual figure from the\n",
|
|
"quote, not a derivation."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "g3-code-token-input",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"# \u2500\u2500 Deal inputs \u2500\u2500\n",
|
|
"AI_TOKEN_ANNUAL_COST = 0.0 # $/yr from the Genesys quote \u2014 0 reproduces the published study\n",
|
|
"AI_TOKEN_QUOTE_NOTE = \"\" # e.g. \"Quote #1234: 4.2M tokens/yr @ $0.05, tier 2 commit\"\n",
|
|
"\n",
|
|
"print(f'AI token line: ${AI_TOKEN_ANNUAL_COST:,.0f}/yr')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "g3-md-sensitivity",
|
|
"metadata": {},
|
|
"source": [
|
|
"### Sensitivity \u2014 what the AI line does to NPV and ROI\n",
|
|
"\n",
|
|
"An annual cost `\u0394` raises Costs PV by `\u0394 \u00d7 2.4869` (the 3-year, 10%\n",
|
|
"annuity factor) and lowers NPV by the same amount. The sweep below\n",
|
|
"shows where the deal stops being attractive \u2014 and quantifies how much\n",
|
|
"of the published 266% ROI was *contingent on Forrester modelling \\$0\n",
|
|
"of token spend*."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "g3-code-sensitivity",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"ANNUITY = sum(1 / 1.10**n for n in (1, 2, 3)) # 2.4869\n",
|
|
"\n",
|
|
"base_benefits_pv = float(summary['total_benefits_pv'])\n",
|
|
"base_costs_pv = float(summary['total_costs_pv'])\n",
|
|
"\n",
|
|
"sweep = [0, 100_000, 250_000, 500_000, 750_000, 1_000_000, 1_500_000, 2_000_000]\n",
|
|
"if AI_TOKEN_ANNUAL_COST and AI_TOKEN_ANNUAL_COST not in sweep:\n",
|
|
" sweep = sorted(sweep + [AI_TOKEN_ANNUAL_COST])\n",
|
|
"\n",
|
|
"rows = []\n",
|
|
"for ai_annual in sweep:\n",
|
|
" costs_pv = base_costs_pv + ai_annual * ANNUITY\n",
|
|
" npv_v = base_benefits_pv - costs_pv\n",
|
|
" roi_pct = (npv_v / costs_pv * 100) if costs_pv else 0\n",
|
|
" rows.append({\n",
|
|
" 'AI cost/yr': f\"${ai_annual:,.0f}\" + (' \u2190 your input' if ai_annual == AI_TOKEN_ANNUAL_COST and ai_annual else ''),\n",
|
|
" 'Costs PV': f'${costs_pv:,.0f}',\n",
|
|
" 'NPV': f'${npv_v:,.0f}',\n",
|
|
" 'ROI': f'{roi_pct:,.0f}%',\n",
|
|
" })\n",
|
|
"\n",
|
|
"pd.DataFrame(rows)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "g3-md-tokens-push",
|
|
"metadata": {},
|
|
"source": [
|
|
"### Push the AI-tokens cost to Athena\n",
|
|
"\n",
|
|
"When `AI_TOKEN_ANNUAL_COST` is set and `TOOL_PUBLIC_ID` exists, write\n",
|
|
"the annual cost into the `genesys_ai_tokens` field, with the quote\n",
|
|
"details preserved in the field notes."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "g3-code-tokens-push",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"PUSH = False # \u2190 set True once AI_TOKEN_ANNUAL_COST is final\n",
|
|
"\n",
|
|
"if PUSH and config.TOOL_PUBLIC_ID:\n",
|
|
" from core.tei_client import TEIClient\n",
|
|
"\n",
|
|
" note = (\n",
|
|
" f'AI Experience tokens: ${AI_TOKEN_ANNUAL_COST:,.0f}/yr. '\n",
|
|
" + (f'{AI_TOKEN_QUOTE_NOTE} ' if AI_TOKEN_QUOTE_NOTE else '')\n",
|
|
" + 'Line absent from the published Forrester study.'\n",
|
|
" )\n",
|
|
" client = TEIClient()\n",
|
|
" client.update_values(config.TOOL_PUBLIC_ID, [{\n",
|
|
" 'field_key': 'genesys_ai_tokens',\n",
|
|
" 'year_values': {'1': round(AI_TOKEN_ANNUAL_COST, 2),\n",
|
|
" '2': round(AI_TOKEN_ANNUAL_COST, 2),\n",
|
|
" '3': round(AI_TOKEN_ANNUAL_COST, 2)},\n",
|
|
" 'notes': note,\n",
|
|
" }])\n",
|
|
" client.calculate(config.TOOL_PUBLIC_ID)\n",
|
|
" client.print_summary(config.TOOL_PUBLIC_ID)\n",
|
|
" client.save_version(config.TOOL_PUBLIC_ID, note=f'AI token cost set: {note}')\n",
|
|
" display.alert('Pushed, recalculated, and versioned.', 'success')\n",
|
|
"else:\n",
|
|
" display.alert('Dry run \u2014 set <code>PUSH = True</code> and ensure '\n",
|
|
" '<code>TOOL_PUBLIC_ID</code> is configured to write to Athena.', 'info')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "g3-md-crosscheck",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Cross-check vs Athena (optional)\n",
|
|
"\n",
|
|
"When `TOOL_PUBLIC_ID` is set, ask Athena to recalculate the summary on\n",
|
|
"the server side and confirm it matches our local computation (modulo\n",
|
|
"the documented Year-0 discounting delta \u2014 see `02_costs.ipynb`)."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "g3-code-crosscheck",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"if config.TOOL_PUBLIC_ID:\n",
|
|
" from core.tei_client import TEIClient\n",
|
|
"\n",
|
|
" client = TEIClient()\n",
|
|
" client.calculate(config.TOOL_PUBLIC_ID)\n",
|
|
" server_summary = client.get_summary(config.TOOL_PUBLIC_ID)\n",
|
|
" display.kpi_cards(server_summary, title='Athena server-side summary')\n",
|
|
"else:\n",
|
|
" display.alert('Set TOOL_PUBLIC_ID to compare Athena vs local.', 'info')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "g3-md-next",
|
|
"metadata": {},
|
|
"source": [
|
|
"Continue with [`04_export.ipynb`](04_export.ipynb) \u2192"
|
|
]
|
|
}
|
|
],
|
|
"metadata": {
|
|
"kernelspec": {
|
|
"display_name": "Python 3 (ipykernel)",
|
|
"language": "python",
|
|
"name": "python3"
|
|
},
|
|
"language_info": {
|
|
"codemirror_mode": {
|
|
"name": "ipython",
|
|
"version": 3
|
|
},
|
|
"file_extension": ".py",
|
|
"mimetype": "text/x-python",
|
|
"name": "python",
|
|
"nbconvert_exporter": "python",
|
|
"pygments_lexer": "ipython3",
|
|
"version": "3.12.7"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 5
|
|
}
|