Mesa de Ayuda IA is built around a multi-agent architecture where each domain of sales knowledge — product catalog, commercial policies, sales process, image analysis, and CRM registration — is handled by a dedicated, independently deployable LangChain agent. Rather than forcing a single generalist model to answer every question, the system routes each query to the agent (or agents) that own the relevant knowledge. LangGraph’sDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/geremyjampiersalasgarcia-eng/Caso_Practico_Semillero_IA/llms.txt
Use this file to discover all available pages before exploring further.
StateGraph was chosen as the backbone because it models the entire routing and execution flow as an explicit directed graph, providing deterministic, auditable paths with no risk of infinite loops or hallucinated tool invocations that are common in classic AgentExecutor setups. Parallel fan-out for mixed-intent queries — where three agents run simultaneously — is a first-class feature of the graph model that would require complex custom logic in a chain-based approach.
Agent Registry Pattern
All agents are managed through anAgentRegistry singleton that decouples agent discovery from the orchestrator. This means new agents can be added to the system by simply calling register() — the LangGraph graph never needs to be modified.
register(agent)— stores anyBaseAgentsubclass instance underagent.name. If an agent with the same name already exists, it logs a warning and overwrites it (useful for hot-reloading during development).get_agent(name)— retrieves an agent by its string identifier. RaisesAgentNotFoundErrorif no match is found, preventing silent failures in the graph nodes.list_agents()— returns a list ofAgentInfoobjects (name + description) for introspection, useful during development to confirm which agents are loaded and available.
agent_registry singleton at the bottom of registry.py is imported by every graph node in orchestrator.py. Agents are registered at startup inside backend/app/agents/__init__.py, so by the time the first HTTP request arrives, all five agents are already in the registry and ready to serve.
BaseAgent Abstract Class
Every agent in the system inherits fromBaseAgent, an abstract base class that enforces a consistent interface and encapsulates the entire RAG + LLM execution pipeline. Subclasses only need to declare four properties; all the heavy lifting (retrieval, prompt construction, LLM invocation, token counting) lives in the base class.
The four abstract properties that every subclass must implement are:
| Property | Type | Purpose |
|---|---|---|
name | str | Unique identifier used as the registry key and in AgentResult.agent_name |
description | str | Human-readable description exposed by list_agents() |
collection_name | str | ChromaDB collection that this agent queries (e.g. col_catalogo) |
system_prompt_path | str | Absolute path to the Markdown file containing the agent’s system prompt |
process_query() method signature and its four-step flow:
AgentResult with a user-friendly error message instead of raising an uncaught exception that would crash the graph. Similarly, Gemini rate-limit errors (HTTP 429) produce a specific advisory message rather than a generic traceback.
The Five Agents
agente_catalogo
Answers questions about products, specifications, list prices, and stock availability. Queries the
col_catalogo collection backed by 01_Catalogo_Productos_Precios.txt.agente_politicas
Answers questions about discount tiers, authorization levels, credit conditions, warranties, and returns. Queries the
col_politicas collection backed by 02_Politicas_Comerciales_Descuentos_Credito.txt.agente_proceso_ventas
Covers the sales funnel stages, CRM registration requirements, and closing criteria. Queries the
col_proceso_ventas collection backed by 03_Proceso_Ventas_CRM.txt.agente_multimodal
Analyzes product images using Gemini Vision and cross-references findings against
col_catalogo. Accepts a image_data parameter (base64) in addition to the text question.agente_accion
Registers sales opportunities in
data/registro_oportunidades.txt and the PostgreSQL oportunidades table using LangChain @tool Function Calling. Validates all required CRM fields, warns on discounts above 10%, and requests user confirmation before writing.| Agent Name | ChromaDB Collection | Primary Responsibility |
|---|---|---|
agente_catalogo | col_catalogo | Products, specs, list prices, availability |
agente_politicas | col_politicas | Discounts, credit, guarantees, returns |
agente_proceso_ventas | col_proceso_ventas | Sales funnel, CRM steps, closing requirements |
agente_multimodal | col_catalogo | Image analysis via Gemini Vision + catalog cross-reference |
agente_accion | (none — action agent) | CRM opportunity registration via Function Calling |
Intent Categories
The classifier (powered by Gemini attemperature=0.0 for maximum determinism) maps each incoming question to one of six categories defined in classifier.py:
| Category | Meaning | Routed To |
|---|---|---|
catalogo_precios | Questions about products, specs, prices, or stock | agente_catalogo |
politicas_comerciales | Questions about discounts, authorization levels, credit, warranties, or returns | agente_politicas |
proceso_ventas | Questions about the sales funnel, CRM steps, or closing criteria | agente_proceso_ventas |
accion_registro | Explicit requests to register or save a sales opportunity | agente_accion |
multimodal | Any query that includes an attached image (detected before LLM classification) | agente_multimodal |
mixta | Questions that span two or more domains simultaneously | agente_catalogo + agente_politicas + agente_proceso_ventas (parallel) |
Image detection is short-circuited before the LLM classifier runs. If
has_image=True is passed, classify_intent() immediately returns category="multimodal" with confidence=1.0 — no Gemini API call is made, saving tokens and latency."catalogo_precios: alta confianza", it will strip and match "catalogo_precios"). If no match is found, it defaults to "mixta" to ensure the query always gets an answer.
Parallel Execution for Mixed Queries
When the classifier returnsmixta, the LangGraph conditional edge fires three agent nodes simultaneously — catalogo, politicas, and proceso_ventas — rather than executing them sequentially. This is made possible by returning a list from route_agents() inside add_conditional_edges:
AgentResult into state["agent_results"], which is typed as Annotated[List[AgentResult], operator.add] — meaning results from parallel branches are accumulated (concatenated) rather than overwriting each other. Once all three branches complete, execution converges at the consolidate node, which calls Gemini one final time to synthesize a single coherent answer from all partial responses.
AgentResult Schema
Every agent node — whether a RAG agent, the multimodal agent, or the action agent — returns a standardizedAgentResult Pydantic model that the consolidate node consumes:
| Field | Type | Description |
|---|---|---|
agent_name | str | The agent’s registry identifier (e.g. "agente_catalogo") |
answer | str | The full LLM-generated answer text for this agent’s domain |
sources | List[SourceInfo] | List of ChromaDB chunks used as context, each with document name, content snippet (truncated at 200 chars), and relevance score |
tokens_input | int | Input tokens consumed by this agent’s Gemini call (from usage_metadata) |
tokens_output | int | Output tokens produced by this agent’s Gemini call |
tokens_input and tokens_output fields flow back into GraphState via operator.add accumulation, giving the final API response an aggregate token count across all agents involved in answering a single question.