The Hard Truth About Enterprise Agent Prompts
After deploying multi-agent RAG systems across three fintech production environments, I learned that agent failures are rarely model failures—they are prompt architecture failures. The LLM is a compliant executor; when it hallucinates FD rates, misroutes compliance checks, or loses conversation state, the root cause is almost always how we structured cognitive instructions.
This article distills seven hard-won prompt design principles from production incidents in Fixed Deposit (FD) modules, implemented as a complete LangGraph multi-agent system with memory, state management, and audit-grade observability.
Seven Prompt Design Lessons from FD Production Incidents
Lesson 1: Role Prompts Must Include Negative Constraints
Failure: Advisor agent recommended a 5-year tax-saver FD to an NRI customer. NRIs are ineligible for Section 80C tax-saver FDs under FEMA regulations. The agent knew the rule but didn't apply it because the prompt said "recommend suitable FDs" without explicit exclusions.
Principle: Every role prompt must contain a NEVER section with domain-specific prohibitions, not just positive instructions.
Lesson 2: Retrieval Prompts Must Specify Document Schema, Not Just Intent
Failure: Retriever returned generic "FD interest rate" pages instead of the current quarter's rate card because the query was semantically similar but structurally wrong. Rate cards have a specific schema: {tenure_months, min_amount, senior_citizen_rate, effective_date}.
Principle: Retrieval prompts must reference the exact document structure expected downstream, not just natural language intent.
Lesson 3: Reasoning Prompts Must Force Structured Output Before Narrative
Failure: Compliance checker produced verbose reasoning that buried the actual pass/fail verdict in paragraph three. Downstream nodes couldn't reliably parse the decision.
Principle: Always require JSON/schema output first, then optional narrative explanation. Parse the structure; log the narrative.
Lesson 4: State Summarization Prompts Must Preserve Regulatory Facts Verbatim
Failure: Memory compressor paraphrased "RBI master direction RBI/2023-24/156" as "recent RBI guidelines." When the auditor agent later cited this, it was legally meaningless.
Principle: Compression prompts must distinguish between compressible context and immutable regulatory references. The latter are copied verbatim.
Lesson 5: Tool Selection Prompts Must Include Failure Mode Descriptions
Failure: Calculator agent called get_fd_rate with a tenure of 37 months. The API only accepts standard tenures (12, 15, 18, 24, 36, 48, 60). The error message confused the reasoner into retrying with 38 months.
Principle: Tool descriptions must include valid parameter ranges AND what happens when they're violated. Teach the agent the API's contract, not just its purpose.
Lesson 6: Multi-Agent Handoff Prompts Must Carry Explicit Context Contracts
Failure: Eligibility agent passed "customer_age": 62 to the advisor agent. Advisor treated this as exact age rather than "age at FD maturity," recommending a product with upper age limit 60. The handoff lacked semantic metadata.
Principle: Inter-agent messages must include field-level semantics via structured schemas, not bare key-value pairs.
Lesson 7: Guardrail Prompts Must Be Evaluated Separately from Task Prompts
Failure: We embedded RBI compliance checks inside the advisor prompt. When we updated the advisor's recommendation logic, we accidentally weakened the compliance check. Coupled prompts create regression risk.
Principle: Guardrails are independent evaluation nodes with frozen prompts. Task prompts evolve; guardrail prompts are version-controlled regulatory artifacts.
Real-Time Use Case: Fixed Deposit Advisory & Booking Pipeline
Scenario: A retail banking customer asks: "I'm 58, retiring next year. I have ₹15 lakhs maturing from an existing FD. What should I do for regular post-retirement income while staying tax-efficient?"
This triggers a regulated workflow:
Eligibility Check → KYC status, age, residency, existing FD portfolio
Rate & Product Retrieval → Current rate card, senior citizen premiums, tax-saver eligibility
Advisory Reasoning → Income projection, tax impact, laddering strategy
Compliance Validation → RBI suitability rules, disclosure requirements
Booking Preparation → Pre-fill application, generate term sheet
Each step applies the seven lessons above.

Step 1: State Schema with Semantic Field Contracts (Lesson 6)
# state.py
from typing import Annotated, Literal, Optional
from typing_extensions import TypedDict
from langgraph.graph.message import add_messages
from langchain_core.documents import Document
from pydantic import BaseModel, Field
class CustomerProfile(BaseModel):
"""Semantic contract for customer data across agents."""
customer_id: str
age_at_evaluation: int = Field(description="Current age in years")
age_at_maturity: Optional[int] = Field(
default=None,
description="Projected age when proposed FD matures. Critical for senior citizen eligibility."
)
residency_status: Literal["resident", "nri", "oci"]
kyc_verified: bool
pan_number: str
existing_fd_portfolio: list[dict] = Field(default_factory=list)
annual_taxable_income: float = Field(description="INR, current FY estimate")
class FDRecommendation(BaseModel):
"""Structured output from advisor - parsed before narrative."""
product_code: str
tenure_months: int
principal_amount: float
applicable_rate: float = Field(description="% p.a., including senior citizen premium if applicable")
monthly_income_projected: float
tax_treatment: Literal["taxable", "80c_deduction", "exempt"]
laddering_strategy: Optional[list[dict]] = None
regulatory_references: list[str] = Field(
description="Verbatim RBI/master direction citations. NEVER paraphrase."
)
class ComplianceVerdict(BaseModel):
"""Guardrail output - separate from advisory reasoning."""
is_suitable: bool
violations: list[str] = Field(default_factory=list)
required_disclosures: list[str] = Field(default_factory=list)
regulation_version: str = Field(description="Exact RBI circular number and date")
class FDAdvisoryState(TypedDict):
messages: Annotated[list, add_messages]
# Customer context (immutable after eligibility check)
customer_profile: Optional[CustomerProfile]
# Retrieval artifacts
retrieved_rate_cards: list[Document]
retrieved_product_specs: list[Document]
retrieval_query_metadata: dict # Tracks what was searched and why
# Advisory artifacts
recommendations: list[FDRecommendation]
advisory_narrative: str
# Compliance artifacts (separate concern - Lesson 7)
compliance_verdict: Optional[ComplianceVerdict]
# Execution artifacts
booking_reference: Optional[str]
term_sheet_url: Optional[str]
# Memory & audit
thread_id: str
advisor_id: str
regulatory_snapshot_date: str # Date of rate/regulation captureStep 2: Version-Controlled Prompt Templates
prompts/eligibility.md — Negative Constraints (Lesson 1)
# FD Eligibility Checker
## ROLE
Determine customer eligibility for Fixed Deposit products per RBI Master Direction
RBI/2023-24/156 and bank internal policy FD-POL-2026-03.
## REQUIRED OUTPUT (JSON ONLY - Lesson 3)
{
"eligible_products": ["product_code_1", ...],
"ineligible_products": [{"code": "...", "reason": "..."}],
"senior_citizen_eligible": boolean,
"nri_restrictions": ["restriction_1", ...] | null,
"regulatory_references": ["verbatim citation 1", ...]
}
## NEVER (Negative Constraints)
- NEVER recommend Tax Saver FD (80C) to NRI or OCI customers
- NEVER assume senior citizen rate applies before age 60 AT MATURITY (not current age)
- NEVER proceed if KYC is not verified - return eligible_products: []
- NEVER use outdated rate cards - verify effective_date matches regulatory_snapshot_date
- NEVER infer residency status from name or address patternsprompts/retriever.md — Document Schema Awareness (Lesson 2)
# FD Document Retriever
## TASK
Retrieve documents matching the structured query below. Return ONLY documents
that match the EXPECTED SCHEMA. Discard all others.
## EXPECTED DOCUMENT SCHEMAS
Rate Card: {bank_code, effective_date, tenure_months, min_amount,
standard_rate, senior_citizen_premium_bps, nri_rate_override}
Product Spec: {product_code, product_name, min_tenure, max_tenure,
premature_penalty_formula, tax_treatment, eligibility_criteria}
Regulation: {circular_number, issue_date, section, verbatim_text}
## QUERY
{{retrieval_query}}
## FILTERING RULES
- Rate cards: ONLY where effective_date <= {{regulatory_snapshot_date}}
- Product specs: ONLY where product_code IN {{eligible_products}}
- Regulations: ONLY exact circular matches, never summariesprompts/advisor.md — Structured Output First (Lesson 3)
# FD Advisory Reasoner
## INPUT
Customer Profile: {{customer_profile_json}}
Retrieved Rate Cards: {{rate_cards_summary}}
Retrieved Product Specs: {{product_specs_summary}}
Eligible Products: {{eligible_products}}
## REQUIRED OUTPUT FORMAT
You MUST output valid JSON matching FDRecommendation schema FIRST.
After the JSON block, you may provide a customer-facing narrative.
### JSON BLOCK (parsed programmatically)
```json
{
"product_code": "...",
"tenure_months": ...,
...all FDRecommendation fields...
}
Join the conversation! Your thoughts help the community grow.