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.
🗝️ Proveedores, Bóveda y Credenciales
Sección titulada «🗝️ Proveedores, Bóveda y Credenciales»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/configinforma el estado de la credencial por proveedor:present,absentolocked.ollamaes siemprepresent.
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.
isOllamaCloudModelIdes la única regla; un modelo genuinamente local cuyo nombre termine en-cloudtambié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.
Límites del Nivel Gratuito de Ollama Cloud
Sección titulada «Límites del Nivel Gratuito de Ollama Cloud»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.
🔗 Cadena de Modelos y Fallback
Sección titulada «🔗 Cadena de Modelos y Fallback»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 (ollamacon sufijo cloud y entradas del router). - Las entradas locales también pasan su
contextWindowal daemon comooptions.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-cloudalmacenada que aún conserva uncontextWindowde 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 comocause),INTERNAL_ERROR(error inesperado del servidor). - Mastra solo expone el fallo final una vez agotada la cadena, así que
cause.providerIdycause.modelIdnombran la última entrada que falló, no necesariamente la primera.
Clasificación de la Causa del Fallo
Sección titulada «Clasificación de la Causa del Fallo»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).
📏 Perfiles de Ejecución y Presupuestos
Sección titulada «📏 Perfiles de Ejecución y Presupuestos»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.
Ajustes y Techos Estrictos
Sección titulada «Ajustes y Techos Estrictos»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.
Presupuestos de Resultados de Herramientas
Sección titulada «Presupuestos de Resultados de Herramientas»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 herramientatope por ejecución = floor(contextWindow x 4 x 0.60) caracteres, sumados sobre los resultados de una ejecuciónPara 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.
⚙️ Referencia de Configuración
Sección titulada «⚙️ Referencia de Configuración»| 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.