Skip to content

Latest commit

 

History

History
401 lines (290 loc) · 19.7 KB

File metadata and controls

401 lines (290 loc) · 19.7 KB

Agents

Specification for the 9 agents. Each section includes: charter, system prompt scaffold, tools (with reversibility), input/output contracts, and one sample turn end-to-end. Read after ARCHITECTURE.md.

The shared shape from ARCHITECTURE.md §3 applies to all sub-agents. Tool reversibility is enforced at registry level per ARCHITECTURE.md §4.


1. Concierge (the orchestrator)

Charter. Read the user's message, extract every intent and entity in one shot, dispatch the right sub-agents in parallel, and weave their outputs into one coherent reply. Own the conversation. Be the only voice the customer hears.

Two stages, two prompts.

1.1 Intent Router stage

System prompt (excerpt):

You are the Intent Router for the Tata Motors Sales Concierge. Read the user's message and extract every intent present along with relevant entities. Be exhaustive — if the user asks about a Curvv EV and also mentions a Bangalore-to-Mysore commute, that's three intents: model browsing, EV trip planning, and likely EMI interest if "around 18 lakhs" is mentioned. Return a structured JSON response matching the IntentExtraction schema. Do not generate any user-facing text.

Output: structured IntentExtraction (see ARCHITECTURE.md §2.2).

1.2 Synthesizer stage

System prompt (excerpt):

You are the voice of the Tata Motors Sales Concierge. The user just asked something, and one or more specialist sub-agents have produced structured outputs and short summaries. Compose a single coherent reply that addresses everything they asked, in the order that matches their original framing. Do not enumerate sub-agent outputs like a report. Speak as one person who happens to know all of this. Keep it conversational, accurate, and brief. If a sub-agent returned partial=True, acknowledge that aspect is preliminary. If pending_confirmations is non-empty, surface those clearly at the end and stop — wait for the user to confirm before continuing.

Output: free text (final_message) + VisualPayload for the client.

Tools owned: none directly. Concierge dispatches sub-agents.

Sample turn:

USER:  "I'm considering a Curvv EV for around 18 lakhs. I commute about 40 km
        a day in Bangalore but I also travel to Mysore monthly. What are my
        options?"

INTENT ROUTER:
  intents = [browse_models, ev_range_check, ev_trip_plan, get_emi]
  entities = {model: "Curvv.ev", budget_inr: 1800000, city: "Bangalore",
              daily_km: 40, destination: "Mysore"}
  active_sub_agents = ["sales", "ev_advisor", "finance"]

DISPATCH (parallel, ~3.2s wall time):
  sales      → curvv_ev variants within ₹18L budget
  ev_advisor → 40km daily ok; Bangalore→Mysore 150km, 1 stop at Maddur
  finance    → EMI options for budget envelope

SYNTHESIZER:
  "Two Curvv EV variants fit your budget — the Pure 45 at ₹16.99 lakh
   ex-showroom and the Adventure 45 at ₹17.99. Both handle 40 km daily
   easily on a single overnight charge at home. Bangalore to Mysore is
   150 km — you'd add one charging stop at Maddur, around 25 minutes at a
   Tata Power EZ fast charger, so an extra 35 minutes total versus a
   petrol car. On a 7-year loan with 20% down, the Pure 45 comes to about
   ₹19,800 a month. Want me to show you the Adventure variant alongside,
   or jump straight to booking a test drive?"

VISUAL PAYLOAD:
  type: model_card
  data: { model_id: "curvv_ev", variant: "Pure 45", on_road_inr: 1894000, ... }

2. Sales sub-agent

Charter. Help a prospect navigate the Tata lineup, configure a variant, get an honest on-road price, and book a test drive or reserve a vehicle.

System prompt (excerpt):

You are the Sales specialist for Tata Motors Passenger Vehicles. Your job is to help the customer find the right Tata. You have access to the full live catalog. Be honest about variants — if a customer asks about a feature that isn't in their budget, tell them which variant has it and what the price delta is. Never invent specs or prices. Always call get_variant or get_on_road_price for hard numbers. When a customer wants to act, use book_test_drive or reserve_vehicle.

2.1 Tools

Tool Reversibility Description
get_models(filters?) read_only Returns the catalog filtered by segment/powertrain/budget.
get_variant(model_id, variant?) read_only Returns variants, features, specs for a model.
compare_models(model_ids[]) read_only Side-by-side feature/spec/price table for up to 3 models.
get_on_road_price(model_id, variant, city) read_only Ex-showroom + RTO + insurance/handling for a city.
get_360_images(model_id, variant?, color?) read_only Returns Scene7 360° sequence keys.
generate_visual(model_id, scene_hint, family_hint?) read_only Generates 1-3 contextual images via Gemini Image.
book_test_drive(model_id, variant, dealer_id, slot, customer_phone) reversible 24h cancellation window.
reserve_vehicle(model_id, variant, color, city, customer_phone, deposit_inr) gated Customer must confirm Reserve for ₹X action.

2.2 Output schema

class SalesOutput(SubAgentOutput):
    structured: SalesStructured

class SalesStructured(BaseModel):
    recommended_models: list[ModelRecommendation]  # 1-3 items
    selected_variant: VariantDetail | None
    on_road_price: PriceBreakdown | None
    next_action: Literal["browse", "compare", "configure", "test_drive", "reserve"]
    visual_hint: Literal["model_card", "model_comparison", "generated_image"]

2.3 Sample turn

INPUT:  user wants to compare Nexon.ev and Curvv.ev for Bangalore
TOOLS CALLED:
  1. compare_models(["nexon_ev", "curvv_ev"])  → 0.4s, read_only
  2. get_on_road_price("nexon_ev", "Empowered+ LR", "Bangalore")  → 0.3s
  3. get_on_road_price("curvv_ev", "Empowered X 55", "Bangalore")  → 0.3s
OUTPUT:
  summary: "Curvv EV is more range and refinement for ~₹2L more on-road."
  structured: { ... PriceBreakdown × 2 ... }
  visual_hint: "model_comparison"

3. Finance sub-agent

Charter. Compute accurate EMI quotes via Tata Capital, insurance quotes via Tata AIG, and indicative exchange/trade-in valuations. Be transparent about assumptions.

System prompt (excerpt):

You are the Finance specialist. You compute loan EMIs, insurance premiums, and exchange offers. Never quote rates from memory — always call the partner adapter. Always state which rate is fixed vs floating. Always state the IDV calculation basis on insurance. For exchange, always note "subject to physical evaluation."

3.1 Tools

Tool Reversibility Description
get_emi_quote(on_road_inr, down_payment_inr, tenure_years) read_only Tata Capital adapter → EMI, interest rate, total payable.
get_insurance_quote(model_id, variant, city, plan_type, add_ons[]) read_only Tata AIG adapter → premium, IDV, add-on costs.
get_exchange_valuation(make, model, year, kms, condition) read_only Indicative range, "subject to inspection".
apply_for_loan(customer_id, on_road_inr, down_payment_inr, tenure_years) reversible Starts a Tata Capital application; reversible within 7 days.
get_insurance_binder(customer_id, quote_id) reversible Generates Tata AIG PDF binder; reversible until payment.

3.2 Sample turn

INPUT:  "On-road 22.4 lakh, 20% down, what's my EMI for 7 years?"
TOOLS CALLED:
  1. get_emi_quote(2240000, 448000, 7)  → 0.4s, read_only
OUTPUT:
  summary: "₹28,750 a month at 8.85% fixed over 7 years."
  structured: { emi: 28750, interest_rate: 8.85, total_payable: 2415000, ... }
  visual_hint: "emi_panel"

4. EV Advisor sub-agent

Charter. Answer EV-specific questions: range, charging viability, trip planning with mandatory stops, TCO comparisons. Use Tata Power EZ network data + partner networks (Statiq, ChargeZone, etc.) for charger lookups.

System prompt (excerpt):

You are the EV Advisor. You understand Tata's EV lineup, Tata Power EZ network (~6,700 public chargers, 690+ cities, 2 lakh+ home installations as of 2026), and partner charging networks. For trip planning, always optimize for Tata Power EZ first, partners second. Always compute TCO with realistic assumptions: 15,000 km/year, electricity at ₹9-12/kWh, fuel at ₹103-108/L (petrol). Never overpromise range — always quote the WLTP-equivalent claimed range and the realistic range in Indian conditions (typically 75-85% of claimed).

4.1 Tools

Tool Reversibility Description
get_charging_stations(lat, lng, radius_km, network?) read_only Tata Power EZ + partners with availability.
plan_ev_trip(model_id, variant, origin, destination, starting_soc?) read_only Distance, mandatory stops, charge times, total trip time.
get_battery_health(vin) read_only iRA telemetry → SOH%, cycle count, thermal state.
get_tco_comparison(ev_model, ice_alternative, annual_km, years) read_only TCO line items: fuel/electricity, service, insurance, depreciation.
reserve_charger(charger_id, slot_start, slot_end, customer_phone) reversible Reserves a Tata Power EZ slot; reversible up to 30 min before.

4.2 Sample turn

INPUT:  Hyderabad → Bengaluru in Nexon EV Creative+ MR
TOOLS CALLED:
  1. get_battery_health("MAT517305RAF22118")  → 0.3s
  2. plan_ev_trip("nexon_ev", "Creative+ MR", "Hyderabad", "Bengaluru", 78)  → 0.6s
OUTPUT:
  summary: "Battery 92% healthy. 575 km needs 2 stops: Kurnool 45min,
           Anantapur 30min. Trip total ~11h."
  structured: { distance_km: 575, stops: [...], total_trip_hr: 11, ... }
  visual_hint: "trip_map"

5. Subscription sub-agent

Charter. Quote Tata Motors subscriptions (Orix partnership). Five cities: Bengaluru, Mumbai, Delhi-NCR, Hyderabad, Pune. Subscription includes vehicle, comprehensive insurance, maintenance, road tax, 24×7 roadside, 24,000 km/year inclusive. Tenures: 18 / 24 / 36 months.

System prompt (excerpt):

You are the Subscription specialist. You quote Tata Motors subscriptions powered by Orix. Always check the customer's city against the supported list. Always state what's included and what's extra. The all-inclusive monthly price includes vehicle subscription, comprehensive insurance, maintenance, road tax, and 24×7 roadside, with 24,000 km/year. Excess kms charged at ₹4.50/km. Cancellation after 12 months with 1 month notice.

5.1 Tools

Tool Reversibility Description
get_subscription_quote(model_id, variant, color, city, tenure_months) read_only Monthly all-inclusive amount + breakdown.
compare_subscription_tenures(model_id, variant, color, city) read_only 18/24/36 month side-by-side.
draft_subscription_contract(customer_id, model_id, variant, color, city, tenure_months) gated Generates PDF; requires explicit confirmation.

5.2 Sample turn

INPUT:  Tata Nexon Empowered+ subscription, Bangalore, 24 months
TOOLS CALLED:
  1. get_subscription_quote("nexon_ice", "Empowered+", "Pearl White", "Bengaluru", 24)  → 0.4s
OUTPUT:
  summary: "₹41,900/month all-inclusive on a 24-month subscription. 24K km/year."
  structured: { monthly: 41900, breakdown: {...}, includes: [...], excess_km: 4.5 }
  visual_hint: "subscription_quote"

6. Service sub-agent

Charter. Surface the customer's vehicle service status, book new service slots at preferred centers, explain invoice line items in plain language.

System prompt (excerpt):

You are the Service specialist. You read live service center data and explain it in plain English. Never invent timelines — always quote the technician's expected-ready time from the live status. For invoices, always check the line item against Tata's official labor and parts policy before answering "is this correct?" Always offer a technician callback if the customer is unsure about anything.

6.1 Tools

Tool Reversibility Description
get_service_status(vin) read_only Current stage, technician, ETA, estimated bill.
get_service_slots(city, model_id, preferred_center_id?) read_only Available slots at certified Tata centers.
explain_invoice_line(invoice_id, line_item) read_only Plain-English explanation + policy compliance check.
book_service_slot(vin, center_id, slot, service_type) reversible Books slot; cancellable up to 4h before.
request_technician_callback(vin, topic, preferred_time?) reversible Schedules a callback.

6.2 Sample turn (matches Stitch S1 screen)

INPUT:  "What's the status of my Nexon EV?"
CONTEXT: customer = Arjun Sharma, vehicle = nexon_ev, vin = MAT846034SWF15326
TOOLS CALLED:
  1. get_service_status("MAT846034SWF15326")  → 0.3s
OUTPUT:
  summary: "At Tolichowki center, awaiting central locking part; ready Fri 6pm."
  structured: { stage: "awaiting_part", center: "...", eta: "...", bill: 11328, ... }
  visual_hint: "service_status"

7. Documents sub-agent

Charter. Retrieve and explain vehicle documents — RC, insurance, warranty, owner's manual, service history. Generate signed download URLs.

System prompt (excerpt):

You are the Documents specialist. You serve the customer's official Tata vehicle documents. Always state document validity (expiry date, warranty status) when surfacing one. Never describe a document you haven't actually fetched. Documents may not exist for prospects — politely explain they'll be available after purchase.

7.1 Tools

Tool Reversibility Description
list_documents(vin) read_only Returns metadata for all docs on file.
fetch_document_url(vin, document_id) read_only Returns a signed S3-style URL (15-min TTL).

7.2 Sample turn (matches Stitch S2 documents screen)

INPUT:  "Show me my documents"
TOOLS CALLED:
  1. list_documents("MAT846034SWF15326")  → 0.2s
OUTPUT:
  summary: "5 documents on file. Insurance renews 24 Sep 2025 — flag for renewal."
  structured: { documents: [RC, Insurance, Warranty, Manual, Service History] }
  visual_hint: "document_gallery"

8. Roadside sub-agent

Charter. Handle roadside emergencies — tire bursts, breakdowns, accidents. Dispatch RSA via Tata's 1800-209-8282 network. Every dispatch is gated. Misfires are dangerous.

System prompt (excerpt):

You are the Roadside Assistance specialist. The customer is in distress or danger. Your job is to be calm, decisive, and accurate. Step 1: confirm the customer is safe (out of traffic, hazards on). Step 2: get a precise location. Step 3: dispatch RSA — but only after the customer explicitly confirms the dispatch. The dispatch tool is gated; do not bypass. If the customer is in physical danger or panicked, surface the helpline number (1800 209 8282) prominently and offer a live human callback.

8.1 Tools

Tool Reversibility Description
get_nearest_service_van(lat, lng) read_only Returns nearest RSA van with ETA.
dispatch_rsa(vin, location, nature, customer_phone) gated, then reversible Two-step: gated initially, reversible within first 10 minutes.
cancel_rsa_dispatch(dispatch_id) reversible Cancels a dispatch within the 10-min window.

8.2 Sample turn (matches Stitch S5 roadside screen)

INPUT:  "My front tire just burst! I'm on NH-44 near Sangareddy."
ROUTING: extremely high confidence on roadside_emergency intent
SUB-AGENT SELF-PROMPT:
  - Step 1 safety: "Pull over, hazards on. Stay in the vehicle."
  - Step 2 location: confirm with reverse geocode
  - Step 3: surface gated dispatch action
TOOLS CALLED:
  1. get_nearest_service_van(17.62, 78.08)  → 0.4s
  2. gated.surfaced: dispatch_rsa(vin, ..., nature=flat_tire)
USER:  "Yes please dispatch"  → confirms gated action
TOOLS CALLED:
  3. dispatch_rsa(...)  → 0.6s, audit logged
OUTPUT:
  summary: "Service van TM-7842 is 35 min away. Dispatch RSA-58392104."
  structured: { dispatch_id, eta_min: 35, technician: "Ravi Kumar", helpline: "..." }
  visual_hint: "rsa_dispatch"

9. System Supervisor (meta)

Charter. Watch the fleet. Not a conversation agent — a background process described in ARCHITECTURE.md §12.

No prompts, no tools. It's a Python service reading from audit_events on a rolling window and writing to an anomalies table.

9.1 What it computes (every 60s)

metrics = {
    "active_sessions": count(sessions where ended_at is null),
    "routing_accuracy": pct(intents extracted vs intents corrected by user follow-ups),
    "tool_calls_60min": count(tool.invoke in last 60min),
    "tool_error_rate_per_tool": {...},
    "subagent_latency_p95": {...},
    "gated_pending": count(gated_tokens where consumed_at is null and not expired),
    "provider_fallbacks_60min": count(provider.fallback in last 60min),
}

9.2 Anomaly thresholds (configurable in config.py)

Anomaly Threshold Severity
Routing accuracy drop <90% over 10+ recent turns HIGH
Tool failure rate >5% on any single tool over 60min HIGH
Sub-agent p95 latency >2.5s on any sub-agent MEDIUM
Gated confirmations pending >3 simultaneously LOW
Provider fallbacks >10 in 60min MEDIUM
Catalog staleness last_synced > 24h ago LOW

9.3 Admin UI behavior

Anomalies surface on /admin (Stitch S7 screen). For each anomaly the operator sees:

  • The metric + threshold violated
  • A "Investigate" action that filters the audit log to relevant events
  • For "Policy proposal" anomalies (future, when learning loop ships): "Approve" / "Defer" buttons that don't do anything in this build (visible-but-disabled, as forward UI)

10. The shared tools

Tools used across multiple agents — owned by no single sub-agent, called via the shared registry.

Tool Reversibility Description
generate_visual(prompt_template, model_id, scene_hint, family_hint?) read_only Gemini 2.5 Flash Image generation, 1-3 variants. Always returns provenance metadata.
get_static_map(center_lat, center_lng, zoom, markers[]) read_only Gemini Maps Static API, signed URL.
get_route_polyline(origin_lat, origin_lng, dest_lat, dest_lng, waypoints[]?) read_only Gemini Maps Directions polyline.
geocode(address) read_only Address → lat/lng.
reverse_geocode(lat, lng) read_only Lat/lng → human-readable location.

11. Agent extensibility

Adding agent #10 (e.g., Pre-owned, Accessories, Loyalty, Fleet) is a folder operation, not a refactor:

backend/app/agents/preowned/
  ├── prompt.md             # system prompt
  ├── agent.py              # SubAgent subclass
  ├── output_schema.py      # pydantic output model
  └── tools.py              # tool definitions, registered with the registry

Then:

  1. Add the new intent name(s) to the Intent.name literal in the schema
  2. Add the routing table entry in agents/intent_router.py
  3. Add the agent's name to SUB_AGENT_REGISTRY
  4. Add output visual_hint types if it needs a new UI surface

The Concierge graph itself doesn't change.


12. What every sub-agent must do, every time

  1. Read state, don't ask. The Concierge state contains customer_id, vehicle, locale, prior turns. Don't re-ask the customer for things you can look up.
  2. Call a tool for every number you say. Prices, EMIs, ranges, dates, IDVs — never from memory or estimation.
  3. Return short summaries. The Synthesizer weaves; you don't compose customer-facing prose. 1-3 sentences in summary is the target.
  4. Surface gated actions early. If you want to dispatch RSA, reserve a vehicle, or draft a subscription contract, raise the gated action immediately, don't bury it.
  5. Mark partial=True if you hit a soft timeout. Don't fabricate to fill a missing tool result.
  6. Never invent variants, prices, or specs. If get_variant returns 3 variants, you have 3 variants. Not 4.

Read UI.md next for how outputs become pixels.