Ir al contenido

Proveedores y Modelos del Asesor de IA

🔌 Proveedores, Cadena de Modelos y Presupuestos

Sección titulada «🔌 Proveedores, Cadena de Modelos y Presupuestos»

El asesor es agnóstico del LLM a nivel de dominio. Esta página cubre qué proveedores existen, cómo llegan las credenciales hasta ellos, cómo se comporta la cadena de fallback y cómo se derivan los límites por ejecución.


El conjunto cerrado de proveedores es openai, anthropic, google, opencode, ollama y ollama-cloud (AI_PROVIDER_IDS). Cada uno está además registrado en el registro de proveedores de la bóveda (GetAvailableProvidersUseCase) con category: { kind: 'ai-model' }, de modo que un proveedor no puede existir en una cadena de modelos y faltar en el registro.

flowchart TD
    A["Entrada de cadena de ai_advisor_model_chain"] --> B{"¿providerId es ollama?"}
    B -- sí --> C["Se conserva. Sin consulta a la bóveda.<br/>createOllama con OLLAMA_BASE_URL"]
    B -- no --> D{"¿Bóveda desbloqueada?"}
    D -- no --> E["Entrada descartada, nunca contactada"]
    D -- sí --> F{"¿Credencial almacenada y descifrable<br/>a un apiKey no vacío?"}
    F -- no --> E
    F -- sí --> G["Se conserva con apiKey.<br/>Id del router provider/model"]
    C --> H["Cadena resuelta"]
    G --> H
    E --> I{"¿Cadena resuelta vacía?"}
    H --> I
    I -- sí y bóveda bloqueada --> J["failed: VAULT_LOCKED"]
    I -- sí y bóveda desbloqueada --> K["failed: NO_MODEL_AVAILABLE"]
    I -- no --> L["La ejecución continúa"]
Id de proveedor Destino de la petición Clave en bóveda Clasificado como
openai, anthropic, google, opencode La API alojada del proveedor mediante el router de modelos de Mastra (<provider>/<model>). Sí (apiKey). medido (metered)
ollama (id de modelo sin sufijo cloud) El daemon local de Ollama (por defecto http://localhost:11434/api). Ninguna; la entrada del registro no tiene campos. local
ollama (id de modelo que termina en :cloud o -cloud) El daemon local, que hace de proxy hacia Ollama Cloud. Ninguna (el daemon guarda la autenticación de la cuenta). medido
ollama-cloud La API directa en la nube de Ollama mediante el router como ollama-cloud/<model>. Sí (apiKey). medido

Las credenciales se almacenan y acotan como cualquier otro secreto de la bóveda: AES-256-GCM, desbloqueado con la contraseña maestra de la bóveda (ver Bóveda Cifrada Local). Se introducen en Ajustes → Bóveda, agrupadas bajo el encabezado “AI Providers” (vault.category.ai-model), mediante la ruta de credenciales existente (POST /api/credentials/vault/:service, donde :service es el id del proveedor). Comportamiento que se deriva del código:

  • Un proveedor sin clave utilizable se omite en tiempo de petición y nunca se contacta. Nunca se depende de la autodetección de variables de entorno de Mastra.
  • Una bóveda bloqueada omite todos los proveedores con clave. Como la clave maestra vive solo en memoria del proceso, cada reinicio del backend la vuelve a bloquear. El cambio adyacente vault-remember-unlock-on-device (solo propuesta, no implementado) apunta a resolver esto.
  • GET /api/advisor/config informa el estado de la credencial por proveedor: present, absent o locked. ollama es siempre present.

Un Modelo con Sufijo Cloud a Través del Daemon Local También Sale de la Máquina

Sección titulada «Un Modelo con Sufijo Cloud a Través del Daemon Local También Sale de la Máquina»

Un daemon local de Ollama con sesión iniciada acepta ids de modelo que terminan en :cloud (por ejemplo qwen3:cloud) y los alias antiguos -cloud (por ejemplo gpt-oss:120b-cloud) y los reenvía a ollama.com. El daemon guarda esa autenticación, por lo que no existe ninguna clave en la bóveda.

[!CAUTION] Una petición así envía la conversación y los resultados de herramientas fuera de la máquina y por tanto se clasifica como medida, no local. isOllamaCloudModelId es la única regla; un modelo genuinamente local cuyo nombre termine en -cloud también se clasifica como nube —un falso positivo deliberado que se inclina hacia la divulgación—. La clasificación es derivada y el usuario no puede alterarla.

El nivel gratuito publicado por Ollama, en el momento de escribir esto, consiste en créditos mensuales de inicio, solo modelos de inicio y una petición concurrente. Esto procede de la página de precios de Ollama y este repositorio no lo aplica ni lo verifica; consulta los términos vigentes de Ollama.


El usuario ordena una cadena de modelos en Ajustes; se almacena como el ajuste de usuario ai_advisor_model_chain (JSON). Las formas de entrada se discriminan por contenido:

Entrada Forma Regla
Local { providerId: 'ollama', modelId, contextWindow } El id de modelo no debe tener sufijo cloud; contextWindow (tokens, entero positivo) es obligatorio.
Medida { providerId, modelId } contextWindow está prohibido. Una entrada ollama simple sin contextWindow se rechaza.
flowchart LR
    R["Petición"] --> E1["Entrada 1"]
    E1 -- "responde" --> Done["El stream continúa"]
    E1 -- "falla tras reintentos" --> E2["Entrada 2"]
    E2 -- "responde" --> Done
    E2 -- "falla tras reintentos" --> EN["Entrada N"]
    EN -- "falla" --> F["failed: ALL_PROVIDERS_FAILED<br/>causa del último fallo"]
  • La cadena se convierte en el array de fallback dinámico de Mastra (buildModelChainConfig). Reintentos por entrada: 1 para entradas del daemon local, 2 para todo lo demás (ollama con sufijo cloud y entradas del router).
  • Las entradas locales también pasan su contextWindow al daemon como options.num_ctx.
  • Una cadena almacenada que no puede parsearse o que ya no valida se trata como no configurada (NO_MODEL_AVAILABLE), por lo que un valor corrupto nunca produce un 500. Una entrada -cloud almacenada que aún conserva un contextWindow de antes de reconocer los sufijos cloud descarta esa ventana al leerse (dropMeaninglessCloudContextWindow).
  • Códigos de fallo: NO_MODEL_AVAILABLE (sin configurar, o todas las entradas filtradas con la bóveda desbloqueada), VAULT_LOCKED (todas las entradas filtradas y la bóveda estaba bloqueada), ALL_PROVIDERS_FAILED (se probaron entradas y el último error se clasifica como cause), INTERNAL_ERROR (error inesperado del servidor).
  • Mastra solo expone el fallo final una vez agotada la cadena, así que cause.providerId y cause.modelId nombran la última entrada que falló, no necesariamente la primera.

classifyProviderError inspecciona la cadena de causas del error (profundidad 5), primero el estado y después el código o clase de error:

cause.kind Se clasifica a partir de
auth-rejected HTTP 401 o 403
model-not-found HTTP 404, o código de error del proveedor model_not_found
rate-limited HTTP 429
provider-unavailable HTTP 5xx
network Sin respuesta HTTP y un error de conexión (ECONNREFUSED, ECONNRESET, ENOTFOUND, EAI_AGAIN, ETIMEDOUT, EHOSTUNREACH, ENETUNREACH, códigos de connect/socket de undici, un TimeoutError o el envoltorio de fetch fallido del AI SDK)
unknown Cualquier otra cosa

La red solo transporta esta clasificación, nunca el texto de error del proveedor, la URL, las cabeceras, la clave ni el cuerpo de la petición. El servidor registra un aviso por ejecución fallida con proveedor, modelo, tipo, estado HTTP y un mensaje saneado (se eliminan claves conocidas, URLs y tokens opacos largos, truncado a 200 caracteres).


Cada ejecución corre bajo un perfil de ejecución que fija límites de pasos, profundidad de historial, tamaños de página y presupuestos de resultados de herramientas. El perfil se deriva de la cadena resuelta; el usuario nunca lo activa.

Cadena resuelta Límites aplicados execution_profile registrado
Todas las entradas son ollama local local local
Todas las entradas son medidas metered metered
Ambos tipos presentes metered mixed

Una cadena mixta corre bajo los límites de las medidas porque cualquier entrada en la nube puede ser la que responda; una cadena totalmente local es requisito para obtener el margen local completo. El filtrado ocurre primero, así que una cadena que mezcla entradas locales y con clave pasa a ser totalmente local cuando la bóveda está bloqueada.

Los límites son editables por el usuario (Ajustes → Asesor de IA) y se almacenan como ai_advisor_execution_profiles. Cuando la clave no está definida, se aplica defaultExecutionProfiles().

Límite Por defecto medido Por defecto local Techo estricto
maxSteps 5 15 30
lastMessages 20 50 200
topNHoldings 15 50 ninguno
lotsPageSize 20 100 200
rowsPageSize 25 100 200

Los techos de maxSteps y lastMessages son protección frente a bucles descontrolados, no control de costes. Los techos de tamaño de página acotan una sola página. Tanto la validación del PUT como el formulario de Ajustes los aplican.

Un resultado de herramienta se mide como la longitud de su codificación JSON en caracteres, nunca en tokens, de modo que la comprobación es determinista y no necesita tokenizador por proveedor. Un resultado que excede el presupuesto no se entrega parcialmente: el modelo recibe { "kind": "truncated", "maxChars": ..., "actualChars": ... } y se le indica que lo comunique.

Medido: una tabla por herramienta (editable por el usuario, sin tope global por ejecución).

Herramienta Presupuesto por defecto (caracteres)
portfolio_summary 4000
fiscal_integrity 6000
token_history 6000
asset_allocation 3000
risk_metrics 1500
drawdown_curve 4000
performance_history 4000
kpis 3000
volatility_heatmap 4000
spanish_tax_report 5000
live_prices 2000
fiscal_integrity_rows 4000
token_lots 4000
holding_detail 3000
account_holdings 5000
tax_year_comparison 4000
data_gaps 4000
explain_metric 2000
scenario_position_value 1500
breakeven_price 1500
derivatives_pnl 4000
custody_locations 4000
scenario_portfolio_shock 4000
concentration_risk 2000
tx_search 5000

Una fila ai_advisor_execution_profiles almacenada antes de que existiera una herramienta sigue cargando: al leer, los toolBudgets almacenados se superponen a los valores por defecto clave por clave, de modo que una herramienta nueva toma su presupuesto por defecto. Solo se fusiona toolBudgets; el resto de campos almacenados siguen siendo obligatorios, y el PUT sigue siendo estricto y exige las 25 claves. Una herramienta cuyo presupuesto es demasiado pequeño para su payload devuelve truncated; sube su presupuesto en Ajustes.

Exposición de herramientas por perfil: el perfil local expone solo las 14 herramientas core, mientras que metered y mixed exponen las 25. Es código estático (ADVISOR_TOOL_TIERS), no un ajuste. Ver Niveles por perfil de ejecución.

Local: los presupuestos nunca se almacenan; se derivan del contextWindow declarado a 4 caracteres por token.

presupuesto por herramienta = floor(contextWindow x 4 x 0.15) caracteres, para cada herramienta
tope por ejecución = floor(contextWindow x 4 x 0.60) caracteres, sumados sobre los resultados de una ejecución

Para una ventana declarada de 8192: 4915 por herramienta y 19660 por ejecución. Ambas fórmulas viven en deriveLocalToolBudget y deriveLocalRunBudget (shared-types), de modo que Ajustes muestra exactamente lo que aplica el backend. El total por ejecución cuenta el tamaño de cada resultado de herramienta intentado, incluidos los truncados, y se crea un RunBudgetTracker nuevo por ejecución. Cuando una cadena totalmente local declara varias ventanas, se usa la menor.


Nombre Tipo Propósito
ai_advisor_model_chain Ajuste de usuario Cadena de modelos ordenada (JSON). Sin definir o inválida implica NO_MODEL_AVAILABLE.
ai_advisor_execution_profiles Ajuste de usuario Límites de los perfiles de ejecución (JSON). Se aplican los valores por defecto del código si no está definido.
language Ajuste de usuario Idioma de respuesta (en o es; por defecto en).
base_currency Ajuste de usuario Moneda base para herramientas sensibles a la moneda (por defecto USD).
OLLAMA_BASE_URL Entorno del backend URL del daemon local de Ollama (por defecto http://localhost:11434/api).
ADVISOR_DB_PATH Entorno del backend Ruta de la base de datos de conversaciones; los valores relativos se anclan a la raíz de datos; se permite :memory:.
CRIPTOFOLIO_DATA_DIR Entorno del backend Raíz de datos que contiene ai-advisor.db junto a las demás bases de datos.
VITE_API_URL Entorno del frontend URL base del backend usada por bffClient y el adaptador de stream (por defecto http://localhost:3001).
Claves API de proveedores Bóveda Almacenadas por id de proveedor en la bóveda cifrada; nunca en variables de entorno.

Los ajustes de usuario los almacena IUserSettingsPort en la base de datos SQLite de la bóveda. Consulta también Configuración y Entorno.