Ir al contenido

Catálogo de Herramientas del Asesor de IA

🧰 Catálogo de Herramientas del Asesor de IA

Sección titulada «🧰 Catálogo de Herramientas del Asesor de IA»

taxAnalyst define veinticinco herramientas de solo lectura (ADVISOR_TOOL_NAMES); cuáles ve una ejecución depende de su perfil de ejecución (ver Niveles por perfil de ejecución). Las entradas son objetos Zod estrictos: los campos desconocidos se rechazan. Los símbolos se pasan a mayúsculas y luego deben cumplir ^[A-Z0-9.]{1,20}$. Ninguna herramienta acepta un id de cuenta; todas leen todas las cuentas a la vez, salvo account_holdings, que recibe el nombre de una cuenta (recortado, de 1 a 64 caracteres) y lo resuelve en el servidor. Un modelo que envíe accountId sigue fallando con INVALID_TOOL_INPUT. Ninguna herramienta acepta una cantidad ni una moneda. Las cifras fiscales son por activo entre cuentas en cualquier caso, coherente con FIFO Global vs Custodia.

Todas las herramientas devuelven una unión discriminada: { kind: 'ok', payload } o { kind: 'truncated', maxChars, actualChars } (ver presupuestos de resultados).


Cada herramienta pertenece a un nivel en ADVISOR_TOOL_TIERS (shared-types), comprobado con satisfies para que no se pueda añadir una herramienta sin nivel. La selección es código estático, no un ajuste: selectExposedTools en ai/tools/toolExposure.ts la aplica al construir el mapa de herramientas de taxAnalyst.

Perfil Herramientas expuestas
local las 14 herramientas core
metered, mixed las 25 herramientas
  • core (14): portfolio_summary, holding_detail, account_holdings, kpis, asset_allocation, spanish_tax_report, tax_year_comparison, fiscal_integrity, data_gaps, token_history, live_prices, scenario_position_value, breakeven_price, explain_metric.
  • extended (11): fiscal_integrity_rows, token_lots, risk_metrics, drawdown_curve, performance_history, volatility_heatmap, derivatives_pnl, custody_locations, scenario_portfolio_shock, concentration_risk, tx_search.

Los modelos locales pequeños eligen de forma medible peor con una lista larga de herramientas, por eso una ejecución local solo ve el nivel core. Una cadena mixta se resuelve al perfil metered y por tanto expone todas las herramientas. Las instrucciones del agente listan exactamente las herramientas que expone la ejecución. Ver perfiles de ejecución.


Herramienta Envuelve Entrada Payload (resumen)
portfolio_summary GetPortfolioSummaryUseCase ninguna metrics (totales, ratesIncomplete, pricesIncomplete, currency), posiciones ranked (las topNHoldings primeras por valor), omittedCount, unvalued, unvaluedCount. Moneda base.
fiscal_integrity GetFiscalIntegrityUseCase ninguna Como máximo 10 groups (qualityFlag, severity, count, pendingReview) ordenados por count, más totalDefects, pendingReview, needsRecalculation. Nunca filas.
fiscal_integrity_rows GetFiscalIntegrityUseCase qualityFlag, page (base cero, por defecto 0) Una página de filas a nivel de transacción (rowsPageSize) con metadatos de paginación.
token_history GetTokenHistoryUseCase symbol Hasta lotsPageSize lotes (id, fecha, exchange, cantidades, costes, estado, historyCount, relocationCount) y omittedCount.
token_lots GetTokenHistoryUseCase symbol, page (por defecto 0) Una página de la lista completa de lotes (lotsPageSize) con metadatos de paginación.
asset_allocation GetAssetAllocationUseCase ninguna Elementos valorados ranked (amount, valueFiat, allocationPct, currency), omittedCount, elementos unvalued (solo amount). Moneda base.
risk_metrics GetRiskMetricsUseCase ninguna maxDrawdownPct, annualizedVolatility, sharpeRatio, alpha, beta. Ratios, sin campo de moneda.
kpis GetKpisUseCase ninguna Equity, coste base, PnL, máximo histórico, drawdown, volatilidad, Sharpe, currency, flags de incompletitud y, opcionalmente, estadísticas de operaciones y mejor/peor activo. Moneda base.
drawdown_curve GetDrawdownCurveUseCase days? (entero 1 a 3650) points de date y drawdownPct, submuestreados a un máximo de 120, más omittedCount.
performance_history GetPerformanceHistoryUseCase days? (1 a 3650) currency (la moneda base), points (portfolioValue, nullable; btcValue opcional; drawdownPct) submuestreados a un máximo de 120, omittedCount.
volatility_heatmap GetVolatilityHeatmapUseCase year? (entero) cells de date y volatility, submuestreadas a un máximo de 120, omittedCount.
spanish_tax_report GetSpanishTaxReportUseCase year (entero, mínimo 2009), method? summary del IRPF, contadores de no convertibles y excluidos, auditTrail limitado a 20 filas, omittedCount. Siempre EUR.
live_prices IPriceHistoryPort.getLatest symbols (1 a 25) prices (price, currency, timestamp, provider, stalenessSeconds) y notTracked.
holding_detail GetPortfolioSummaryUseCase symbol Uno de valued (posición con valor), unvalued (solo cantidad y coste base) o not_held. Encuentra una posición fuera del top-N de portfolio_summary. Moneda base.
account_holdings ListAccountsUseCase, GetPortfolioSummaryUseCase accountName (recortado, 1 a 64) resolved con una sección por cuenta del árbol de la cuenta resuelta (sus propios metrics, ranked, omittedCount, unvalued) más emptyAccountCount; o ambiguous (nombres candidatos) o not_found (nombres de nivel superior). Primero coincidencia exacta sin distinguir mayúsculas; un prefijo solo si ninguna coincide exactamente. Nunca suma entre cuentas.
tax_year_comparison GetSpanishTaxReportUseCase (dos veces) yearA, yearB (distintos, mínimo 2009), method? Por año summary, unconvertibleCount, excludedFlaggedEvents, excludedUnresolvedIncomeCount, completeness; deltas (segundo año menos primero) calculados por compareTaxSummaries, cada uno un delta o delta_incomparable si alguno de los años está incompleto. EUR, sin pista de auditoría.
data_gaps GetPortfolioSummaryUseCase, GetFiscalIntegrityUseCase ninguna unvalued dividido en no_price y no_rate (solo símbolo y cantidad), integrity (como máximo 10 grupos, totalDefects, needsRecalculation) y una lista fija nextTools.
explain_metric tabla estática metric (enum cerrado MetricId) La definición en el locale de la petición y producedBy, la herramienta que informa la métrica. El texto expresa las fórmulas con palabras, sin cifras de ejemplo.
scenario_position_value GetPortfolioScenarioUseCase symbol, hypotheticalPrice computed (positionValue, deltaVsCurrent, impliedAllocationPct), not_held, unvalued (solo valor de la posición) o empty_portfolio; cada uno con currency. La cantidad siempre sale del libro.
breakeven_price GetPortfolioScenarioUseCase symbol computed (avgUnitCost), not_held, unconvertible_cost_basis o zero_quantity. Usa el coste base de las posiciones, que puede diferir de kpis.
derivatives_pnl GetDerivativesPnlUseCase ninguna Contratos ordenados por PnL realizado absoluto y limitados a topNHoldings (realizedPnl, funding, fees, netPnl, currency), omittedCount.
custody_locations GetLotCustodyLocationsUseCase symbol? Cantidad por cuenta no sintética y activo (lotCount), limitada a topNHoldings, omittedCount y syntheticRowCount para las filas ownwallet-* que no lista. Lee solo el libro de custodia; la ubicación no afecta a la tributación.
scenario_portfolio_shock GetPortfolioScenarioUseCase shock: { kind: 'uniform', pct } o { kind: 'per_asset', shocks } (1 a 25 entradas); pct de -100 a 1000 computed (before y after por activo limitados a topNHoldings, omittedCount, totalBefore, totalAfter, delta, unvaluedSymbols, notHeld) o empty_portfolio. En un shock por activo, los activos no listados no se mueven.
concentration_risk GetPortfolioScenarioUseCase ninguna Bloques all y excludingStablecoins (computed con top1Weight, top3Weight, hhi en escala de cero a uno, effectiveHoldings, o empty), stablecoinWeight (null si nada está valorado) y unvaluedCount.
tx_search SearchSpotTransactionsUseCase symbol?, from?, to? (días inclusivos), types? (no vacío), page (por defecto 1) Una página (rowsPageSize) de filas (date, type, activos e importes, priceFiat, totalFiat, fiatCurrency, fee como NONE o CHARGED, exchange, edited), la más reciente primero, con page, pageSize, totalPages, totalCount.

Envoltorios compartidos: fiscal_integrity, fiscal_integrity_rows y (en su mitad de integridad) data_gaps usan GetFiscalIntegrityUseCase; token_history y token_lots usan GetTokenHistoryUseCase; portfolio_summary, holding_detail, account_holdings y data_gaps leen GetPortfolioSummaryUseCase; las cuatro herramientas de escenarios (scenario_position_value, breakeven_price, scenario_portfolio_shock, concentration_risk) comparten GetPortfolioScenarioUseCase; spanish_tax_report y tax_year_comparison comparten GetSpanishTaxReportUseCase.

Ejemplo de resultado de portfolio_summary tal como lo ve el modelo (solo la forma; las cifras son ilustrativas):

{
"kind": "ok",
"payload": {
"metrics": {
"ratesIncomplete": false,
"pricesIncomplete": true,
"totalEquityFiat": "18250.40",
"totalCostBasisFiat": "12000.00",
"totalRealizedPnlFiat": "930.10",
"totalUnrealizedPnlFiat": "6250.40",
"totalPnlFiat": "7180.50",
"currency": "USD"
},
"ranked": [
{
"id": "btc",
"symbol": "BTC",
"amount": "0.25",
"avgPriceFiat": "40000",
"costBasisFiat": "10000",
"currentValueFiat": "15000",
"unrealizedPnlFiat": "5000",
"currency": "USD"
}
],
"omittedCount": 0,
"unvalued": [],
"unvaluedCount": 0
}
}

Los fallos de herramienta aparecen en el stream como tool-error con INVALID_TOOL_INPUT (error de validación de Mastra sobre la entrada) o USE_CASE_FAILED (el caso de uso envuelto lanzó una excepción).


  • portfolio_summary, kpis, asset_allocation y live_prices respetan la moneda base del usuario, leída en el servidor desde los ajustes e inyectada mediante el contexto de la petición. El modelo nunca proporciona una moneda.
  • asset_allocation convierte la valoración diaria canónica en EUR al tipo de cambio de la fecha de valoración (VALUATION_CONVERTED, igual que kpis). Una posición sin serie de precios, o sin tipo de cambio, queda unvalued: informa su cantidad y se excluye de todo porcentaje, nunca se deja pasar con factor uno.
  • risk_metrics, drawdown_curve y volatility_heatmap devuelven ratios, porcentajes o estadísticas sin moneda.
  • spanish_tax_report y tax_year_comparison son siempre EUR, como exige el IRPF.
  • holding_detail, account_holdings, data_gaps, las cuatro herramientas de escenarios y derivatives_pnl leen la moneda base del contexto de la petición. Los resultados de escenarios llevan un campo currency. custody_locations devuelve cantidades y cada fila de tx_search lleva su propio fiatCurrency.
  • performance_history respeta la moneda base: cada punto se convierte con la última tasa EUR/<base> almacenada en la fecha del propio punto o antes, igual que kpis, y el campo currency del resultado la indica. Un punto que ninguna tasa cubre tiene portfolioValue: null, nunca un valor en EUR ni 0. drawdownPct es una razón y se sigue derivando de la serie en EUR, por lo que no depende de la moneda base.

Al agente se le indica que exprese la moneda declarada de cada resultado junto a la cifra y que nunca convierta ni reetiquete.


📄 Paginación y Garantía de Precios en Vivo

Sección titulada «📄 Paginación y Garantía de Precios en Vivo»
  • fiscal_integrity nunca devuelve filas. Las filas a nivel de transacción solo son accesibles mediante fiscal_integrity_rows.
  • token_history devuelve una página de lotes, no un top 20 fijo; la lista completa es accesible mediante token_lots. Ambas están sujetas a los mismos presupuestos de caracteres, y una página que aún excede su presupuesto se informa como truncated.
  • Las páginas de tx_search empiezan en 1 (por defecto) y van de la más reciente a la más antigua. Una página más allá del final es un resultado ok vacío con los totales reales, y los filtros de fecha y tipo se ejecutan sobre los valores editados: la fila previa a la edición nunca se devuelve.
  • live_prices lee el último precio ya en caché por símbolo desde el puerto de historial de precios (la misma caché que llena el stream de mercado). No abre ninguna conexión con proveedores ni inicia ningún stream. Un símbolo sin precio en caché aparece en notTracked.

Una cifra de escenario la produce una función pura de packages/core-domain: positionValueAt, breakevenPrice, applyShock y concentrationOf en portfolioScenarios.ts, además de compareTaxSummaries y summarizeCustodyLocations. Todas pasan por Money, y toda división por una variable está protegida por una comprobación isZero que devuelve un resultado tipado en lugar de lanzar una excepción. El subárbol de IA no importa ni Money ni decimal.js, y un escaneo del código fuente lo hace cumplir. Al agente se le indica que nunca calcule, convierta, sume, reste ni estime una cifra, y que exprese tal cual un resultado no calculado (no poseído, ambiguo, coste base no convertible).

Las stablecoins de concentration_risk salen de la lista cerrada STABLECOIN_SYMBOLS de shared-types (USDT, USDC, DAI, EURC, FDUSD, PYUSD, TUSD, USDE); un símbolo que no figura en ella cuenta como arriesgado. La lista es solo clasificación de visualización y ningún código fiscal ni de FIFO la lee.


🚫 Herramientas Dejadas Fuera a Propósito: fees_paid y cash_flow_summary

Sección titulada «🚫 Herramientas Dejadas Fuera a Propósito: fees_paid y cash_flow_summary»

Ambas se midieron contra exportaciones reales de exchanges y no superaron el filtro, por lo que no están en el catálogo. Solo las comisiones de Bitvavo son nativamente EUR y están completas; cualquier otra fuente cobra en cripto o USD, lo que exigiría un precio fechado por cada activo de comisión (lógica analítica nueva, no un envoltorio). Una cifra global de entradas y salidas de fiat contaría como retirada la transferencia interna spottofutures de Kraken spot, y tres fuentes no exportan movimientos de fiat. Se descartó publicarlas por fuente porque un total que cubre en silencio solo algunas fuentes rompe la regla de que “toda cifra declara su completitud”.

Volverán en un seguimiento cuando el subtipo spottofutures de Kraken se declare transferencia interna en profiles.ts, exista la valoración en EUR del activo de comisión como caso de uso y las comisiones con cero explícito sobrevivan a la normalización en todos los perfiles.


La capa de IA no contiene ningún .sort( propio. Helpers puros de packages/core-domain aportan todo orden: rankHoldingsByValue y rankByAbsoluteValue (monetarios, comparados mediante Money), orderByCountDescending, orderByIsoDateDescending y orderByIsoDateDescendingThenKey (claves enteras y de fecha ISO; la última con desempate por clave, usada por tx_search), además de downsampleSeries (paso uniforme, máximo 120 puntos). Ninguno tiene acceso a un lote, una cola o una cuenta, de modo que la separación entre FIFO fiscal y custodia queda intacta.