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:
Zero historical cash flow patterns in the LMS.
No prior recommendation feedback or override history.
ERP integration is partial; only static balance sheet data is available.
Regulatory constraints (VND capital controls) are strict and non-negotiable.
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.

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)}"]
}

Join the conversation! Your thoughts help the community grow.