The ERP Financial Agent is built as two nested LangGraph state machines. The outerDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/yohangr3/agentelanggrafh/llms.txt
Use this file to discover all available pages before exploring further.
ConversationGraph manages the full dialogue lifecycle — understanding intent, filling slots, clarifying ambiguity, and shaping the final response. When a data query is confirmed, it calls into the inner SQL pipeline Graph, which links schemas, generates and validates MySQL, executes on a read-only pool, and assembles charts or executive summaries before returning control to the outer layer.
Two-Level Architecture
Outer Graph: ConversationGraph
Defined insrc/agent/conversation_graph.py, the outer graph uses ConversationState (a plain dict for LangGraph flexibility) and contains 10 nodes.
understand
Extracts the user’s intent and fills a
SlotFrame (metric, period, dimensions, filters, output format). Classifies turn_type as one of: new_query, refine, viz_change, drill_down, forecast, file_analysis, discuss, or general. Sets missing_slots and confidence (0.0–1.0).clarify
Fires when
missing_slots is non-empty. Presents structured clarification options to the user (label/value/description chips). After max_clarification_rounds, applies smart defaults and routes onward to plan rather than continuing to prompt.plan
Resolves the
SlotFrame into a concrete inner_state (AgentState dict) by mapping metric names to tables, applying date range logic, and setting analysis_intent. This is the bridge between the outer conversation and the inner SQL graph.execute
Invokes
inner_graph.ainvoke(inner_state). Before calling, it copies RBAC fields (user_id, user_role, user_permissions, user_modules, allowed_tables, labor_cost_restricted, allowed_cebes) from ConversationState into inner_state so the SQL validator can enforce data-module access.present
Assembles the final user-facing response from
execution_result. Generates follow-up suggestion chips ([{label, query}]), sets display_mode (text_only, table_primary, chart_primary, chart_only), and lists any assumptions (smart defaults that were applied).viz_change
Skips the entire SQL pipeline. Changes the chart type for already-fetched data and re-renders the ECharts configuration without issuing a new query.
forecast
Routes to StatsForecast for time-series projection. Uses historical data from
previous_query_results to generate forward-looking projections, then hands off to present.analyze_file
Handles uploaded Excel/CSV data. Only reachable when
turn_type == "file_analysis" and file_data or file_metadata is present in state; otherwise falls back to general_respond with a helpful upload prompt.general_respond
Answers non-data questions (greetings, capability questions, ERP concepts) using the full conversation history and
session_digest from long-term memory. No SQL is generated.Outer Routing Conditions
Inner Graph: SQL Pipeline
Defined insrc/agent/graph.py, the inner graph uses AgentState and contains 13 nodes operating on structured, typed state.
router
Classifies the question’s
intent into one of: query, analysis, chart, follow_up_chart, follow_up_analysis, or general. The result drives all subsequent routing.schema_linker
Performs FAISS semantic search over table descriptions embedded with Amazon Titan V2 (1024 dims). Returns the top-k most relevant tables, their DDL-like column context, business rules, join hints, and few-shot query pattern examples. Populates
relevant_tables, schema_context, join_hints, and applicable_rules.sql_generator
Sends the enriched schema context to Claude (AWS Bedrock) to generate a MySQL query. Uses
metric_context from the MetricsRegistry as formula hints when business KPIs are detected in the question.sql_validator
Runs five validation layers in sequence using
sqlglot:- Syntax — parse-level SQL correctness
- Security — blocks DDL, DML, multi-statement, and forbidden functions
- Schema — verifies referenced tables/columns exist in
relevant_tables - Business rules — checks ERP-specific constraints (TRM JOIN,
version=0filter, etc.) - RBAC — enforces
allowed_tables,labor_cost_restricted(hidesCOSTOS MANO DE OBRAaccounts), andallowed_cebesWHERE injection
EXPLAIN call validates the query plan. Sets validation_passed, validation_errors, and validation_warnings.error_handler
Self-correction loop: receives
validation_errors, builds a corrective prompt, and re-generates SQL. Tracks correction_attempt (max 3 retries). After exhausting retries, routes to response_formatter with a graceful error.hitl_checker
Human-in-the-loop gate for expensive or ambiguous queries (e.g., full-table scans, missing date filters). Sets
confirmation_required = True and provides a confirmation_message if the query needs user approval before execution.sql_executor
Executes the validated query on a read-only MySQL connection pool (SQLAlchemy async). Returns
query_results (list of dicts), column_names, and row_count. Stores results in previous_query_results for future follow-up turns.data_analyst
Runs pandas-based analysis over
query_results. Extracts KPIs (totals, averages, top-N, period-over-period growth), builds a data_summary, and sets analysis_intent to guide downstream routing.visualizer
Generates an ECharts configuration dict (
chart_config) for bar, line, pie, treemap, or horizontal bar charts. Selects chart type from chart_type in state or infers it from the data shape.report_writer
Only reached when
analysis_intent == "analysis". Claude writes an executive summary (executive_summary) covering key findings, anomalies, and business recommendations in Spanish.response_formatter
Assembles the final
response dict from all state fields: SQL, results, KPIs, chart config, executive summary, and cost tracking metadata.follow_up_restore
Multi-turn shortcut: when
intent is follow_up_chart or follow_up_analysis, copies previous_query_results and previous_column_names into the current state so data_analyst and visualizer can reuse already-fetched data without re-querying.Inner Routing Conditions
State Shapes
- AgentState (inner)
- ConversationState (outer)
- SlotFrame
Key Design Decisions
Fail-closed Security
Any SQL containing DDL, DML, multi-statement constructs, or referencing tables outside the principal’s
allowed_tables is rejected immediately — no retries for security errors.Max 3 Self-Corrections
The
error_handler → sql_validator loop is capped at max_correction_attempts (default 3). Exceeding this routes to response_formatter with a transparent error rather than looping indefinitely.RBAC Propagation
RBAC context is resolved once per WebSocket connection and injected into both
ConversationState and AgentState by the execute node. SQL validation enforces module-level table access and row-level centro_beneficio filtering.Follow-up Without Re-query
follow_up_restore lets users request “show as pie chart” or “analyze this deeper” without burning another SQL round-trip — the previous results are reloaded from state and routed directly to data_analyst.The outer graph uses
StateGraph(dict) (plain dict) instead of a typed class because TypedDict is not serializable in LangGraph’s checkpoint store. The inner graph uses the strongly-typed AgentState(TypedDict) because it does not need cross-turn persistence.