In enterprise liquidity management, cold-start is not a user experience problem; it is a capital efficiency and risk problem. When a new subsidiary joins a cash pool, or when a treasury team onboards a new forecasting module, there is no historical transaction data to train behavioral models. A standard RAG system retrieving generic "liquidity best practices" is useless. The system must provide actionable, entity-specific guidance based on structural attributes (balance sheet composition, industry vertical, banking relationships) until behavioral signals accumulate. This article details an end-to-end implementation of an Adaptive Liquidity Advisor that dynamically switches retrieval strategies based on real-time data maturity assessment, using LangGraph for orchestration and ChromaDB for multi-modal knowledge representation.

The Real-Time Use Case: New Subsidiary Onboarding & Position Analysis

Scenario: A multinational corporation acquires a mid-sized manufacturing firm in Vietnam. The subsidiary’s treasury team logs into the central Liquidity Management System (LMS) for the first time. They ask: "How should we structure our VND cash pooling given our seasonal working capital cycle?"

The Cold-Start Reality:

Why Standard RAG Fails: Semantic search on "cash pooling" returns global policies irrelevant to VND regulations or manufacturing seasonality. Without behavioral history, the system cannot distinguish between a high-growth tech subsidiary and a capital-intensive manufacturer.

The Solution: An adaptive agent architecture that classifies the entity’s data maturity state and routes to specialized retrieval strategies: Structural Inference for true cold-start, Hybrid Analog Matching for warm-start, and Behavioral Personalization for mature entities.

418

Part 1: Data Representation for Liquidity Cold-Start

Cold-start resolution requires representing entities by their structure, not just their behavior. We maintain three distinct ChromaDB collections.

Collection 1: Entity Structural Profiles (entity_structures)

Used when behavioral data is absent. Embeddings capture balance sheet composition and business model semantics.

entity_document = {
    "id": "sub_vn_mfg_001",
    "page_content": "Manufacturing subsidiary, Ho Chi Minh City. Heavy machinery sector. 
                     Working capital cycle: 90-day inventory, 45-day receivables, 60-day payables. 
                     Capital intensive with quarterly debt service peaks. 
                     Subject to SBV VND transfer restrictions.",
    "metadata": {
        "type": "entity_structure",
        "industry_vertical": "heavy_manufacturing",
        "jurisdiction": "VN",
        "working_cap_cycle_days": 90,
        "debt_service_frequency": "quarterly",
        "currency_restrictions": ["VND_capital_control"],
        "banking_relationships": ["Vietcombank", "HSBC_VN"],
        "data_maturity_tags": ["static_only"]
    }
}

Collection 2: Regulatory & Policy Corpus (liquidity_policies)

Regulations are the ultimate cold-start anchor. They apply universally regardless of entity history.

policy_document = {
    "id": "reg_vnd_sbv_circular_22",
    "page_content": "SBV Circular 22/2023: VND cross-border transfers require underlying 
                     trade documentation. Cash pooling permitted only for entities with 
                     direct equity relationship. Max daily sweep limit: 20% of registered capital.",
    "metadata": {
        "type": "regulation",
        "jurisdiction": "VN",
        "regulator": "SBV",
        "applicable_currencies": ["VND"],
        "effective_date": "2023-11-15",
        "constraint_type": "hard_limit"
    }
}

Collection 3: Behavioral Patterns (entity_behaviors)

Populated only after sufficient transaction history accumulates. Empty for cold-start entities.

behavior_document = {
    "id": "behavior_sg_tech_042",
    "page_content": "Consistent Friday cash buildup averaging SGD 2.1M. 
                     Monthly tax payments cause predictable Day-25 drawdowns. 
                     Prefers MMF over term deposits for <30 day horizons. 
                     Override rate on automated sweeps: 8%.",
    "metadata": {
        "type": "entity_behavior",
        "entity_id": "sub_sg_tech_042",
        "pattern_confidence": 0.92,
        "observation_window_days": 180
    }
}

Part 2: Adaptive State Schema

The LangGraph state carries both the maturity assessment and strategy-specific outputs. This prevents downstream agents from assuming uniform data quality.

from typing import Annotated, List, Dict, Optional, Literalfrom typing_extensions import TypedDict
from langgraph.graph.message import add_messages
from langchain_core.documents import Document

MaturityLevel = Literal["true_cold", "warm_start", "mature"]

class LiquidityRecState(TypedDict):
    messages: Annotated[List, add_messages]
    entity_id: str
    
    # Maturity Assessment
    maturity_level: MaturityLevel
    available_signals: Dict[str, bool]
    inferred_analog_entity: Optional[str]  # For warm-start analog matching
    
    # Strategy-Specific Retrieval
    retrieved_context: List[Document]
    retrieval_strategy: str
    
    # Compliance Gate
    regulatory_compliant: bool
    compliance_reasoning: str
    
    # Output
    recommendation: str
    confidence_score: float
    data_gap_disclosure: str  # Explicit transparency about missing data
    agent_trace: List[str]

Part 3: Multi-Agent Implementation

Agent A: Data Maturity Classifier

This node runs first and deterministically assesses what data is actually available. No LLM inference—pure signal inventory.

def maturity_classifier_node(state: LiquidityRecState):
    """Deterministic assessment of available data signals."""
    # Fetch from metadata store / ERP integration status API
    signals = get_entity_signal_inventory(state["entity_id"])
    
    has_static_profile = signals.get("erp_balance_sheet_connected", False)
    has_transaction_history = signals.get("transaction_count_90d", 0) > 30
    has_behavioral_patterns = signals.get("pattern_model_trained", False)
    has_manual_preferences = signals.get("treasury_preferences_set", False)
    
    if has_behavioral_patterns and has_transaction_history:
        level = "mature"
    elif has_static_profile or has_manual_preferences:
        level = "warm_start"
    else:
        level = "true_cold"
    
    return {
        "maturity_level": level,
        "available_signals": signals,
        "agent_trace": [f"classifier: maturity={level}, signals={signals}"]
    }

Agent B: Adaptive Retrieval Router

Routes to fundamentally different retrieval strategies based on maturity. This is the core cold-start handling mechanism.

from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings

# Initialize stores (assume embeddings configured elsewhere)
structure_store = Chroma(collection_name="entity_structures", embedding_function=OpenAIEmbeddings())
policy_store = Chroma(collection_name="liquidity_policies", embedding_function=OpenAIEmbeddings())
behavior_store = Chroma(collection_name="entity_behaviors", embedding_function=OpenAIEmbeddings())

def adaptive_retriever_node(state: LiquidityRecState):
    query = state["messages"][-1].content
    level = state["maturity_level"]
    
    if level == "true_cold":
        # STRATEGY: Regulatory-first + jurisdiction defaults
        # No entity-specific retrieval possible; anchor entirely on hard constraints
        docs = policy_store.similarity_search(
            query, k=5,
            filter={"jurisdiction": {"$in": get_entity_jurisdictions(state["entity_id"])}}
        )
        strategy = "regulatory_defaults_only"
        gap_disclosure = "No transaction history or profile data available. Recommendations based solely on regulatory framework."
        
    elif level == "warm_start":
        # STRATEGY: Structural analog matching + policy overlay
        # Find structurally similar entities WITH behavioral history
        analog_docs = structure_store.similarity_search(query, k=3)
        analog_entity_id = analog_docs[0].metadata.get("entity_id") if analog_docs else None
        
        # Retrieve policies for this entity's jurisdiction
        policy_docs = policy_store.similarity_search(
            query, k=3,
            filter={"jurisdiction": {"$in": get_entity_jurisdictions(state["entity_id"])}}
        )
        
        docs = analog_docs + policy_docs
        strategy = "structural_analog_matching"
        gap_disclosure = f"Limited history. Insights derived from structurally similar entities in {analog_docs[0].metadata.get('industry_vertical', 'similar')} sector."
        
    else:  # mature
        # STRATEGY: Full behavioral personalization + policy validation
        behavior_docs = behavior_store.similarity_search(
            query, k=5,
            filter={"entity_id": state["entity_id"]}
        )
        policy_docs = policy_store.similarity_search(query, k=2)
        docs = behavior_docs + policy_docs
        strategy = "full_behavioral_personalization"
        gap_disclosure = ""
    
    return {
        "retrieved_context": docs,
        "retrieval_strategy": strategy,
        "inferred_analog_entity": state.get("inferred_analog_entity"),
        "data_gap_disclosure": gap_disclosure,
        "agent_trace": state["agent_trace"] + [f"retriever: strategy={strategy}, docs={len(docs)}"]
    }

Agent C: Regulatory Compliance Validator

Hard gate that applies regardless of maturity level. Cold-start recommendations are more vulnerable to compliance failures because they lack behavioral guardrails.

from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate

llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)

compliance_prompt = ChatPromptTemplate.from_messages([
    ("system", """You are a Treasury Regulatory Compliance Auditor.
    Validate this liquidity recommendation against retrieved regulations.
    
    ENTITY JURISDICTION: {jurisdiction}
    MATURITY LEVEL: {maturity} (cold-start requires EXTRA scrutiny)
    
    Check for:
    1. Currency transfer restrictions
    2. Cash pooling eligibility requirements  
    3. Sweep limit violations
    4. Documentation requirements
    
    Return JSON: {{"compliant": bool, "reasoning": string}}"""),
    ("human", "Recommendation Context: {context}\nQuery: {query}")
])

def compliance_validator_node(state: LiquidityRecState):
    chain = compliance_prompt | llm.with_structured_output(dict)
    result = chain.invoke({
        "jurisdiction": get_entity_jurisdictions(state["entity_id"]),
        "maturity": state["maturity_level"],
        "context": "\n---\n".join(d.page_content for d in state["retrieved_context"]),
        "query": state["messages"][-1].content
    })
    
    return {
        "regulatory_compliant": result["compliant"],
        "compliance_reasoning": result["reasoning"],
        "agent_trace": state["agent_trace"] + [f"compliance: passed={result['compliant']}"]
    }

Agent D: Confidence-Aware Synthesizer

Explicitly communicates data limitations. Cold-start responses must never present inferred insights as established facts.

synth_prompt = ChatPromptTemplate.from_messages([
    ("system", """You are an Enterprise Liquidity Management Advisor.
    
    MATURITY: {maturity}
    STRATEGY: {strategy}
    DATA GAP DISCLOSURE: {gap_disclosure}
    COMPLIANCE STATUS: {compliant} ({compliance_reasoning})
    
    RESPONSE RULES BY MATURITY:
    - true_cold: Provide ONLY regulatory-compliant baseline guidance. 
      Never suggest specific amounts or timings. Always end with data onboarding next steps.
    - warm_start: Frame insights as "based on similar entities." 
      Clearly distinguish analog-derived vs. regulation-derived guidance.
    - mature: Standard personalized recommendations with normal confidence.
    
    ALWAYS include the data gap disclosure verbatim if non-empty.
    
    Retrieved Context: {context}"""),
    ("human", "{query}")
])

def synthesizer_node(state: LiquidityRecState):
    if not state["regulatory_compliant"]:
        return {
            "recommendation": "Unable to provide recommendation: regulatory compliance check failed. "
                            f"Reason: {state['compliance_reasoning']}. "
                            "Please consult your regional treasury compliance officer.",
            "confidence_score": 0.0
        }
    
    chain = synth_prompt | llm
    response = chain.invoke({
        "maturity": state["maturity_level"],
        "strategy": state["retrieval_strategy"],
        "gap_disclosure": state["data_gap_disclosure"],
        "compliant": state["regulatory_compliant"],
        "compliance_reasoning": state["compliance_reasoning"],
        "context": "\n---\n".join(d.page_content for d in state["retrieved_context"]),
        "query": state["messages"][-1].content
    })
    
    # Confidence reflects data maturity, not just retrieval quality
    base_confidence = {"true_cold": 0.35, "warm_start": 0.60, "mature": 0.90}
    
    return {
        "recommendation": response.content,
        "confidence_score": base_confidence[state["maturity_level"]],
        "messages": [("assistant", response.content)],
        "agent_trace": state["agent_trace"] + ["synthesizer: response generated"]
    }

Part 4: Compiling the Adaptive Graph

from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.postgres import PostgresSaver

workflow = StateGraph(LiquidityRecState)

workflow.add_node("classify_maturity", maturity_classifier_node)
workflow.add_node("adaptive_retrieve", adaptive_retriever_node)
workflow.add_node("validate_compliance", compliance_validator_node)
workflow.add_node("synthesize", synthesizer_node)

workflow.add_edge(START, "classify_maturity")
workflow.add_edge("classify_maturity", "adaptive_retrieve")
workflow.add_edge("adaptive_retrieve", "validate_compliance")

def compliance_router(state: LiquidityRecState):
    # Always synthesize—even failures need compliant messaging
    return "synthesize"

workflow.add_conditional_edges("validate_compliance", compliance_router)
workflow.add_edge("synthesize", END)

checkpointer = PostgresSaver.from_conn_string("postgresql://...")
app = workflow.compile(checkpointer=checkpointer)

Part 5: Cold-Start Quality Measurement

Standard engagement metrics are meaningless for cold-start liquidity recommendations. Use these instead:

Maturity StagePrimary MetricTargetRationale
True ColdData Onboarding Completion Rate>70% within 14 daysSystem success = accelerating transition out of cold-start
Warm StartAnalog Recommendation Acceptance Rate>50%Validates structural similarity matching accuracy
MatureForecast Accuracy Improvement vs. Baseline>15% MAPE reductionBehavioral personalization must demonstrably improve outcomes
All StagesRegulatory Violation Rate0%Non-negotiable safety floor

Implementation: Tag every recommendation event with maturity_level and retrieval_strategy. Run separate funnel analyses per stage. Track the velocity of maturity transitions—your cold-start system succeeds when entities stop being cold-start.

Production Hardening

  1. Analog Validation Guardrails: For warm-start, never use analogs from different regulatory jurisdictions. Add hard filters: filter={"jurisdiction": entity_jurisdiction} on analog searches. Cross-jurisdiction analogs in liquidity management create compliance risk.

  2. Maturity Transition Automation: When transaction count crosses threshold or behavioral model training completes, trigger async job to populate entity_behaviors collection. Don’t wait for next query. Log transition events for audit.

  3. Confidence Score Surfacing: Expose confidence_score and data_gap_disclosure in the UI. Treasury users need to calibrate trust. Low-confidence responses should render with distinct visual treatment and explicit next-step actions ("Connect ERP to improve accuracy").

  4. Regulatory Corpus Versioning: Policies change. Include effective_date and superseded_by metadata. Auto-expire stale regulations. Cold-start entities are especially vulnerable to outdated policy retrieval since they lack behavioral correction signals.

  5. Feedback Capture for Cold-Start: Even when behavioral data is absent, capture explicit feedback (thumbs up/down, "not applicable to my entity"). This accelerates warm-start calibration and flags bad analog matches before they compound.

Conclusion

Cold-start in fintech liquidity management is solved not by better embeddings but by honest data representation and adaptive orchestration. By maintaining separate structural, regulatory, and behavioral collections, classifying maturity deterministically before retrieval, routing to strategy-appropriate search methods, and explicitly disclosing data gaps in synthesized responses, you transform cold-start from a failure mode into a guided onboarding experience that respects both capital risk and regulatory reality. The architectural principle: your system must know what it doesn’t know, communicate that limitation transparently, and have a deterministic path toward knowing more. In liquidity management, that honesty is worth more than any confident-sounding hallucination.