{"slug": "neuro-symbolic-conversational-vehicle-advisor-deterministic-constraint-solving", "title": "Neuro-Symbolic Conversational Vehicle Advisor: Deterministic Constraint Solving, State Integrity, and Multi-Stage Recommendation", "summary": "A developer built Vehicle Advisor, a production conversational automotive discovery and financing platform on NestJS, TypeScript, and PostgreSQL that uses a hybrid neuro-symbolic architecture to keep LLM reasoning separate from business truth. The system confines non-deterministic language understanding to slot extraction, intent classification, and response synthesis, while inventory validation, constraint satisfaction, candidate scoring, and ranking run in deterministic, auditable stages, backed by an evaluation harness that checks invariant safety, slot extraction recall, and zero-drift persistence across multi-turn multilingual conversations.", "body_md": "Most conversational AI implementations in industry rely on naive Retrieval-Augmented Generation (RAG) or unconstrained agentic loops. While adequate for open-ended queries or low-stakes search, pure probabilistic Large Language Model (LLM) architectures fail catastrophically in high-consideration automotive e-commerce. In automotive discovery, hallucinating non-existent inventory, violating strict budget ceilings, misinterpreting physical vehicle capabilities, or dropping conversational constraints across multi-turn sessions directly destroys transactional trust and violates financial compliance boundaries.\n\nThis paper details the architecture, mathematical formulations, and engineering principles behind Vehicle Advisor—a production conversational discovery, recommendation, comparison, and financing decision-support platform engineered on NestJS, TypeScript, and PostgreSQL. By implementing a hybrid neuro-symbolic architecture, the system isolates non-deterministic natural language understanding (NLU) to slot extraction, intent classification, and conversational synthesis, while delegating inventory validation, constraint satisfaction, candidate scoring, and business ranking to deterministic, auditable software stages. Furthermore, this paper presents an evaluation harness (evals) that continuously verifies invariant safety, slot extraction recall, and zero-drift persistence across multi-turn multilingual conversational trajectories.\n\nAutomotive transactions are governed by an asymmetric balance of hard constraints (strict maximum purchase price, verified seating capacity, location proximity, financing eligibility) and soft preferences (fuel efficiency, cargo practicality, ground clearance, brand affinity).\n\nWhen deploying conversational agents in automotive marketplaces, four systemic failure modes arise in pure LLM architectures:\n\n**1. Inventory Hallucination & Phantom Listings:** Autoregressive models generate plausible-sounding vehicles (e.g., a \"2021 Toyota RAV4 for ₦8,500,000\") that do not exist in live inventory or have stale pricing.\n\n**2. Constraint Amnesia & Parameter Drift:** Over a 4- to 8-turn negotiation, stochastic context windows experience catastrophic forgetting, dropping prior hard filters (such as budget ceilings or 7-seater requirements).\n\n**3. Zero Automotive Knowledge Translation:** First-time buyers express needs in human lifestyle terms (\"I have 3 toddlers and need to navigate flooded streets during the rainy season in Lekki\"). Standard keyword search fails, while naive LLMs invent unverified vehicle specifications (e.g., claiming a sedan has high ground clearance).\n\n**4. Uncontrolled Business & Financing Policies:** Commercial prioritisation (dealer tiers, inspection grades) and regulatory financing boundaries must never secretly override customer hard constraints or imply credit approval without underwriting.\n\nTo resolve these failure modes, we established a strict architectural boundary: The LLM never defines business truth, inventory state, or ranking outcomes.\n\nThe system is architected as a Clean Architecture, Domain-Driven Design (DDD) backend built on NestJS and Fastify, decoupled from specific LLM providers through abstract orchestration adapters (supporting Google Gemini and OpenAI).\n\n**3.1 Pillar 1: Conversation State & Attribute Provenance**\n\nConversation state belongs exclusively to the application database, never to the ephemeral context window of an LLM. In this architecture, user requirements are modelled via a strongly typed BuyerProfile aggregate containing granular attributes with explicit provenance and preference types:\n\n```\n// conversation.model.ts (Excerpt)\nexport enum AttributeCategory {\n  BUDGET = 'BUDGET',\n  BODY_TYPE = 'BODY_TYPE',\n  SEATING = 'SEATING',\n  MAKE = 'MAKE',\n  USAGE = 'USAGE',\n  LOCATION = 'LOCATION',\n  FEATURE = 'FEATURE',\n  FINANCING = 'FINANCING',\n}\nexport enum PreferenceType {\n  HARD_CONSTRAINT = 'HARD_CONSTRAINT', // Absolute invariant; zero violation tolerance\n  SOFT_PREFERENCE = 'SOFT_PREFERENCE', // Used for scoring and affinity ranking\n}\nexport enum AttributeProvenance {\n  USER_EXPLICIT = 'USER_EXPLICIT',   // Stated directly by the user\n  MODEL_INFERRED = 'MODEL_INFERRED', // Inferred from natural language context\n  SYSTEM_DEFAULT = 'SYSTEM_DEFAULT', // Fallback policy\n}\nexport interface BuyerProfileAttribute {\n  category: AttributeCategory;\n  key: string;\n  value: BuyerAttributeValue;\n  preferenceType: PreferenceType;\n  provenance: AttributeProvenance;\n  confidenceScore: number;\n  isConfirmable: boolean;\n}\n```\n\n**The Provenance Rule**\n\nIf a customer explicitly specifies \"Budget max 15 million naira\", it is stored as USER_EXPLICIT + HARD_CONSTRAINT. If a customer mentions \"I have a family\", the system infers a SOFT_PREFERENCE for spacious body types (SUV, MINIVAN, MPV). Crucially, the recommendation pipeline enforces that model-inferred attributes cannot create artificial hard filters that would prematurely collapse viable inventory.\n\n**3.2 Pillar 2: Domain Intelligence & Goal Decomposition**\n\nRaw inventory records contain low-level attributes (make, model, year, price, mileage). However, users express lifestyle goals. To bridge this gap, we engineered SoftVehicleClassAffinityService to deterministically translate high-level customer intents into canonical body-class affinities without LLM hallucinations:\n\n```\n// soft-vehicle-class-affinity.service.ts (Excerpt)\n@Injectable()\nexport class SoftVehicleClassAffinityService {\n  private readonly goalClassAffinityCatalog: Record<CustomerGoalKey, string[] | null> = {\n    [CustomerGoalKey.SPACIOUSNESS]: ['SUV', 'MINIVAN', 'MPV', 'CROSSOVER', 'STATION_WAGON'],\n    [CustomerGoalKey.FAMILY_PRACTICALITY]: ['SUV', 'MINIVAN', 'MPV', 'CROSSOVER', 'STATION_WAGON'],\n    [CustomerGoalKey.CARGO_PRACTICALITY]: ['STATION_WAGON', 'SUV', 'MINIVAN', 'MPV'],\n    [CustomerGoalKey.LOW_MILEAGE_FOCUS]: null, // Directional metric, not body-class affinity\n    [CustomerGoalKey.PURCHASE_AFFORDABILITY]: null, // Handled via max_budget hard filter\n    [CustomerGoalKey.FINANCING_AFFORDABILITY]: null, // Handled via financing gate\n  };\n  public resolveCargoClasses(context: CargoUsageContext): string[] {\n    switch (context) {\n      case CargoUsageContext.EQUIPMENT_BULKY_GOODS:\n        return ['CARGO_VAN', 'PICKUP', 'STATION_WAGON', 'MINIVAN'];\n      case CargoUsageContext.COMMERCIAL_CARGO:\n        return ['CARGO_VAN', 'PICKUP'];\n      case CargoUsageContext.FAMILY_LUGGAGE:\n      case CargoUsageContext.GENERAL_LUGGAGE:\n      default:\n        return ['STATION_WAGON', 'SUV', 'MINIVAN', 'MPV'];\n    }\n  }\n}\n```\n\n**3.3 Pillar 3: Deterministic Multi-Stage Recommendation Pipeline**\n\nThe recommendation pipeline evaluates candidates through a sequential filter-and-rank flow:\n\nActive Inventory ➔ [Hard Filter] ➔ Viable Candidates ➔ [Finance Gate] ➔ Eligible Candidates ➔ [Fit Scorer] ➔ Scored Candidates ➔ [Business Ranker] ➔ Ranked Candidates ➔ [Diversity] ➔ Final Recommendations\n\n**Dynamic Signal Availability & Weight Renormalisation**\n\nIn emerging markets, marketplace listings often suffer from missing attributes (e.g., unconfirmed mileage or missing inspection sheets). Naive weighted sums penalise vehicles with missing data.\n\nTo solve this, we formulated and implemented a Dynamic Weight Renormaliser that redistributes weight among active, available dimensions:\n\n**Weight Renormalisation Formula:**\n\nFor each active dimension with an available signal: RenormalizedWeight = ConfiguredWeight / Sum(ConfiguredWeights of all available dimensions)\n\nFor unavailable or missing signals: RenormalizedWeight = 0 (and omitted reason is recorded for audit-ability)\n\n**Candidate Total Fit Score:**\n\nTotalScore(candidate) = Sum(RenormalizedWeight_i * RawScore_i(candidate)) across all available dimensions\n\n```\n// weight-renormalizer.util.ts\nexport function renormalizeWeights(\n  dimensions: ScoringDimensionResult[],\n): ScoringDimensionResult[] {\n  const activeDimensions = dimensions.filter(\n    (d) => d.capabilityState === 'AVAILABLE' && d.rawScore !== undefined,\n  );\n  const activeSum = activeDimensions.reduce(\n    (sum, d) => sum + (d.configuredWeight || 0),\n    0,\n  );\n  return dimensions.map((d) => {\n    if (d.capabilityState !== 'AVAILABLE' || d.rawScore === undefined) {\n      return {\n        ...d,\n        renormalizedWeight: 0,\n        weightedScore: 0,\n        omittedReason: d.omittedReason || `Capability state is ${d.capabilityState} (signal unavailable)`,\n      };\n    }\n    const renormalizedWeight = activeSum > 0 ? d.configuredWeight / activeSum : 0;\n    const weightedScore = d.rawScore * renormalizedWeight;\n    return {\n      ...d,\n      renormalizedWeight,\n      weightedScore,\n    };\n  });\n}\n```\n\n**3.4 Pillar 4: Grounded Explanation & Zero-Hallucination Delivery**\n\nWhen candidates reach the final turn generation, the LLM is provided with the structured scoring lineage (dimensions, explanationFactors, omittedReason).\n\n```\nStructured Score Payload:\n- Make Fit: 1.0 (Exact Match: Toyota)\n- Seating Fit: 1.0 (7 seats matches stated family requirement)\n- Inspection Grade: 4.5/5 (Authoritative Autochek Inspected)\n- Omitted Signal: Mileage Fit (Missing from listing, weight renormalized)\n```\n\nThe LLM is strictly constrained via system prompts and output validators:\n\nIt cannot introduce vehicles not returned by the deterministic pipeline.\n\nIt cannot quote financial terms (down payment, monthly payment) not produced by the authoritative financing calculator.\n\nIt explains why the vehicle was chosen based directly on the scoring dimensions.\n\nA critical challenge in emerging digital markets is conversational accessibility. In Nigeria, users alternate fluidly between standard English and Nigerian Pidgin (PCM).\n\nRather than relying on generic translation layers that distort technical constraints, the intent extractor natively handles Pidgin idioms while maintaining strict type boundaries:\n\n```\n// Pidgin Input Sample:\n// \"I get 8m naira, and I want clean SUV for my pikin to go school for Ikeja, road get pot-hole well well\"\n// Parsed Canonical State:\n{\n  budgetMax: 8000000,\n  currency: 'NGN',\n  bodyTypes: ['SUV'],\n  location: { state: 'Lagos', city: 'Ikeja' },\n  usageContext: 'FAMILY_COMMUTE',\n  roughRoadPracticality: true\n}\n```\n\nThe system generates responses natively in Nigerian Pidgin while maintaining complete mathematical grounding in the underlying inventory facts.\n\nTo guarantee that prompt edits or model upgrades never introduce behavioural regression, we designed an automated continuous evaluation suite (evals/) executed via npm run test:eval.\n\n``` js\n// evals/dataset/scenarios.data.ts (Excerpt)\nexport const SCENARIO_DATASET: ScenarioDefinition[] = [\n  {\n    scenarioId: 'SCEN-01',\n    description: 'Budget ₦15M, urban family commute, 5 seats',\n    language: 'EN',\n    customerInput: 'I need a family car in Lagos for my daily commute under 15 million naira.',\n    inventoryCondition: '10 matching candidates in Lagos under 15M',\n    primaryInvariants: ['Hard budget filter', 'Location filter'],\n    applicableCriteria: ['q1', 'q2', 'q3', 'q4'],\n    expectedResult: { status: 'QUALIFIED', shortlistCount: 3 },\n  },\n  {\n    scenarioId: 'SCEN-02',\n    description: 'PCM locale compliance: \"I get 8m naira, want car for my pikin\"',\n    language: 'PCM',\n    customerInput: 'I get 8m naira, want car for my pikin to go school for Ikeja',\n    inventoryCondition: '5 candidates matching Lagos under 8M',\n    primaryInvariants: ['PCM locale text generation'],\n    applicableCriteria: ['q1', 'q2', 'q4'],\n    expectedResult: { status: 'QUALIFIED' },\n  },\n  {\n    scenarioId: 'SCEN-03',\n    description: 'Budget ₦5M, financing requested, all inventory > ₦10M',\n    language: 'EN',\n    customerInput: 'I have a strict max budget of 5 million naira for a financed vehicle.',\n    inventoryCondition: 'All inventory prices > 10M',\n    primaryInvariants: ['Hard budget filter'],\n    applicableCriteria: ['q1', 'q3', 'q4'],\n    expectedResult: { status: 'ZERO_CANDIDATES', shortlistCount: 0 },\n  }\n];\n```\n\n**Measured Invariants & Verification Results**\n\nThe design of the Vehicle Advisor platform provides three key insights of high-stakes conversational systems:\n\n**Separation of Concerns via Neuro-Symbolic Boundaries:** Probabilistic LLMs should be treated as interpreters and synthesizers, never as databases or decision engines. Decoupling extraction from deterministic ranking eliminates hallucinations and ensures regulatory compliance.\n\n**Dynamic Signal Availability:** Recommendation scoring models in real-world marketplaces must gracefully degrade when data is sparse. Dynamic weight renormalisation prevents bias against vehicles with missing attributes.\n\n**Automated Regression Invariants:** Continuous evaluation frameworks (evals) that assert domain invariants (such as a strict 0.00% budget violation rate) are essential prerequisites for shipping conversational agents to production.\n\n**Runtime & Framework:** Node.js, NestJS, Fastify, TypeScript\n\n**State & Persistence:** PostgreSQL, TypeORM, CQRS (@nestjs/cqrs)\n\n**AI Orchestration:** Provider-Agnostic Adapter Pattern (Google Gemini @google/genai, OpenAI openai)\n\n**Observability & Telemetry:** OpenTelemetry SDK, Langfuse (AI Traces), PostHog (Analytics)\n\n**Testing & Evaluation:** Jest, Custom Multi-Turn Scenario Test Runner (evals/)", "url": "https://wpnews.pro/news/neuro-symbolic-conversational-vehicle-advisor-deterministic-constraint-solving", "canonical_source": "https://dev.to/christok/neuro-symbolic-conversational-vehicle-advisor-deterministic-constraint-solving-state-integrity-293a", "published_at": "2026-09-24 19:14:29+00:00", "updated_at": "2026-09-24 19:29:05.996463+00:00", "lang": "en", "topics": ["ai-agents", "large-language-models", "natural-language-processing", "ai-tools", "mlops"], "entities": ["Vehicle Advisor", "NestJS", "TypeScript", "PostgreSQL", "Fastify", "Google Gemini", "OpenAI"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/neuro-symbolic-conversational-vehicle-advisor-deterministic-constraint-solving", "markdown": "https://wpnews.pro/news/neuro-symbolic-conversational-vehicle-advisor-deterministic-constraint-solving.md", "text": "https://wpnews.pro/news/neuro-symbolic-conversational-vehicle-advisor-deterministic-constraint-solving.txt", "jsonld": "https://wpnews.pro/news/neuro-symbolic-conversational-vehicle-advisor-deterministic-constraint-solving.jsonld"}}