Skip to main content
Glama
arashnicoomanesh

MCP Zero Shot Agentic Forecaster


エグゼクティブ概要とビジネス価値

このリポジトリとは?

MCP Zero Shot Agentic Forecasterは、Model Context Protocol (MCP)を介して公開される、本番環境対応のマイクロサービス型時系列予測エンジンです。最先端の基盤モデル(Google TimesFM 2.5 XRegおよびAmazon Chronos-2)を搭載し、自律型AIエージェント(LangGraphステートマシン、CrewAIスウォーム、OPA/Regoで管理されるNeSyスタック、標準的なReActループ)が、オフラインでのモデルトレーニング、ハイパーパラメータチューニング、SKUごとのデータセット準備を必要とせずに、確率的な需要予測をオンデマンドで照会できるようにします。

ビジネス価値とROI

  • コールドスタートレイテンシの排除:新製品の投入、プロモーション、履歴の短いSKUに対して、トレーニングパイプラインなしで即時のゼロショット確率予測を提供します。

  • 分位数制約付きリスク制御:較正された$p_{10}$、$p_{50}$、$p_{90}$の需要分位数を出力し、自律型購買エージェントが安全在庫バッファと資本保有コストのバランスを取れるようにします。

  • 総保有コスト(TCO)の低減:複雑なファインチューニングパイプラインを統一された3層フォールバックエンジンに置き換え、GPU計算要件とインフラストラクチャのドリフトを大幅に削減します。

  • エージェンティックな回復力:入力が無効な場合、修正のヒントを含む構造化されたAgentFriendlyErrorペイロードを返し、呼び出し元エージェントがサイレントに失敗したり未処理の例外を発生させたりすることなく、実行ループ内で自己修正できるようにします。

動作の仕組み

  1. エージェント呼び出し:呼び出し元エージェントは、MCPツールインターフェースを介してstdio/HTTP上でforecast_demandまたはforecast_batchを呼び出します。

  2. 契約の強制:Pydantic v2スキーマが、厳密な有限数値および時間的境界チェック([Type-Safe Input Contract])を実行します。

  3. ノンブロッキング推論:FastMCPはasyncio.to_threadを介して重いテンソル演算をスレッドプールにオフロードし、ゲートウェイの応答性を維持します。

  4. 統一3層パイプライン:常にTimesFM 2.5(Tier 1)を経由し、Chronos-2(Tier 2、共変量が存在する場合は保持)およびARIMA111(Tier 3、共変量を破棄)にフォールバックします。CUDA OOMまたは障害が発生した場合、エンジンは次の層に移行する前にメモリ回復(gc.collect() + torch.cuda.empty_cache())をトリガーします。

  5. 数学的サニタイゼーション:等張回帰ソートを適用して出力分位数の単調性($p_{10} \le p_{50} \le p_{90}$)を保証し、認識論的信頼スコアを正規化してから、構造化されたJSONペイロードを返します。


Related MCP server: forecast-mcp

システムアーキテクチャ

このマイクロサービスは関心の厳密な分離に準拠しています。MCPツール層はノンブロッキング転送、メモリ安全性、モデルレベルの出力サニタイゼーションを管理し、ドメイン固有のビジネスポリシーは下流のエージェントオーケストレーターに委ねます。

graph TD
    subgraph External Agent Orchestrator
        Agent[LLM Agent / Swarm / State Machine<br/>LangGraph / OPA Sidecar / ReAct Loop]
    end

    subgraph MCP Microservice Boundary
        Gateway[FastMCP Async Gateway Server<br/>mcp_server.py]
        Sanitizer[Pydantic v2 Input Contract<br/>TimeSeriesInputPayload]
        ErrorFormatter[AgentFriendlyError Formatter]
        
        subgraph Engine Memory & Concurrency Boundary
            ExecThread[Thread Executor<br/>asyncio.to_thread]
            Engine[ZeroShotForecastingEngine<br/>src/models/forecaster.py<br/>Lazy-Load Lock Protected]
            
            subgraph 3-Tier Fallback Model Chain
                T1[Tier 1: TimesFM 2.5<br/>XReg / Univariate]
                T2[Tier 2: Chronos-2<br/>Multivariate / Univariate]
                T3[Tier 3: ARIMA111<br/>CPU Baseline Fallback]
            end
            
            IsoSanitizer[Isotonic Quantile Sanitizer<br/>Enforces p10 ≤ p50 ≤ p90]
        end
    end

    Agent -->|FastMCP Tool Call<br/>forecast_demand / forecast_batch| Gateway
    Gateway -->|1. Validate Schema| Sanitizer
    Sanitizer -->|Validation Error| ErrorFormatter
    ErrorFormatter -.->|Structured Error + Remediation| Agent
    
    Sanitizer -->|2. Valid Payload| ExecThread
    ExecThread -->|3. Route Request| Engine
    Engine --> T1
    T1 -.->|CUDA OOM / Fail| T2
    T2 -.->|Fail| T3
    
    T1 -->|Raw Quantiles| IsoSanitizer
    T2 -->|Raw Quantiles| IsoSanitizer
    T3 -->|Raw Quantiles| IsoSanitizer
    
    IsoSanitizer -->|4. Validated ForecastResponse| Gateway
    Gateway -->|5. Return JSON Payload| Agent

コンポーネントアーキテクチャの内訳

FastMCPゲートウェイ(mcp_server.py):非同期JSON-RPCトランスポートを提供し、バッチ同時実行制限(asyncio.Semaphore(4))を適用します。

型安全な契約境界(src/schemas/payloads.py):時間的整合性、有限数値の保証、コンテキスト/ホライズン制限を強制します。

スレッドセーフなフォーキャスターコア(src/models/forecaster.py):遅延モデルロードにダブルチェックロッキング(threading.Lock())を採用し、CUDA OOMの自動回復(gc.collect() + torch.cuda.empty_cache())を処理します。

等張回帰出力サニタイザー:生の基盤モデル分位数を単調ソートで後処理し、予測をエージェントに返す前に統計的異常($p_{10} > p_{50}$)を排除します。

システム実行フロー(シーケンス図)

sequenceDiagram
    autonumber
    actor Agent as LLM Agent / Orchestrator
    participant Gateway as FastMCP Gateway (Async)
    participant Sanitizer as Pydantic Input Contract
    participant Executor as Thread Executor (asyncio.to_thread)
    participant Pipeline as 3-Tier Fallback Pipeline
    participant Std as Isotonic Quantile Sanitizer

    Agent->>Gateway: forecast_demand / forecast_batch (JSON)
    Gateway->>Sanitizer: Validate TimeSeriesInputPayload
    alt Validation Failure
        Sanitizer-->>Gateway: AgentFriendlyError {error_code, expected, received, remediation}
        Gateway-->>Agent: Structured Error Response
    else Validation Success
        Sanitizer->>Executor: Offload sync inference
        Executor->>Pipeline: Execute prediction
        Note right of Pipeline: Tier 1: TimesFM 2.5 → Tier 2: Chronos-2 → Tier 3: ARIMA111
        alt CUDA OOM / Transient Failure
            Pipeline->>Pipeline: gc.collect() + torch.cuda.empty_cache()
            Pipeline->>Pipeline: Degrade to next tier (retain covariates where possible)
        end
        Pipeline->>Std: Apply _enforce_quantile_monotonicity()
        Std-->>Executor: ForecastResponse {model_used, exogenous_dropped, warnings}
        Executor-->>Gateway: Return validated response
        Gateway-->>Agent: 200 OK with ForecastResponse
    end

中核となるアーキテクチャ原則

ノンブロッキング非同期トランスポート

すべてのテンソルフォワードパスはasyncio.to_threadを介してスレッドプールで実行され、負荷がかかった状態でもFastMCPイベントループが同時のヘルスチェックとツール呼び出しに応答し続けるようにします。

# mcp_server.py
result = await asyncio.to_thread(engine.predict, validated_payload)

VRAM遅延ロード

モデルウェイトはアクセサメソッドを介して最初の使用時のみ実体化され、起動時にVRAMを消費しません。スレッドセーフなダブルチェックロッキングにより、同時コールドスタート時の重複インスタンス化を防ぎます。

# src/models/forecaster.py
def _get_timesfm(self):
    if self._timesfm is None:
        with self._timesfm_lock:
            if self._timesfm is None:
                import timesfm
                logger.info(f"Lazily loading TimesFM-2.5 ({self.timesfm_repo_id}) onto {self._device}")
                self._timesfm = timesfm.TimesFM_2p5_200M_torch.from_pretrained(self.timesfm_repo_id)
    return self._timesfm

def _get_chronos(self):
    if self._chronos is None:
        with self._chronos_lock:
            if self._chronos is None:
                from chronos import BaseChronosPipeline
                logger.info(f"Lazily loading Chronos-2 ({self.chronos_repo_id}) onto {self._device}")
                self._chronos = BaseChronosPipeline.from_pretrained(
                    self.chronos_repo_id, device_map=self._device, dtype=torch.float32
                )
    return self._chronos

3層フォールバックエンジン

エンジンは、ペイロードの内容に関係なく、単一の統一された決定論的デグラデーションチェーンを維持します。

Tier

バックエンド

モード

model_used

exogenous_dropped

1

TimesFM 2.5

XReg / Univariate

TimesFM-2.5

false

2

Chronos-2

Multivariate / Univariate

Chronos-2-Fallback

false

3

ARIMA111

Baseline

ARIMA111-Baseline

true

TimesFMの障害時(torch.cuda.OutOfMemoryErrorを含む):

  1. gc.collect() + torch.cuda.empty_cache()

  2. 外生シグナルは_build_chronos_covariates()(過去/将来の共変量)を介してChronos-2用に保持されます

  3. exogenous_dropped = trueは、フォールバックがTier 3(ARIMA111)にデグラデーションした場合のみ

  4. 実行はChronos-2にルーティングされます

  5. Chronosが失敗した場合→ARIMA111ベースライン

時間的規則性の強制

Pydantic v2バリデータは、境界で無効なテレメトリを拒否します:

Validator

Rule

コンテキスト境界

16 <= len(target_series) <= 16000

ホライズン境界

1 <= forecast_horizon <= 1024

有限値

target_series、price_indexにNaN/Infを含まないこと

外生変数の整合性

len(price_index) == len(target_series) + forecast_horizon

バイナリフラグ

promo_flagの要素は0または1でなければならない

等張回帰分位数サニタイゼーション

すべてのバックエンド(TimesFM、Chronos-2、AutoARIMA)は、極端なOOD入力下で時折交差する($p_{10} > p_{50}$または$p_{50} > p_{90}$)生の分位数を出力します。エンジンは、タイムステップごとに等張回帰ソートを実行する軽量な後処理ステップ_enforce_quantile_monotonicity()を適用します。$(p_{10}, p_{50}, p_{90})$をスタックし、分位数軸に沿ってソートし、順序付けられたトリプレットを返します。これにより、分布形状を歪めることなく、すべての予測ホライズンステップで数学的に有効な$p_{10} \le p_{50} \le p_{90}$が保証されます。

制限付きバッチ同時実行

forecast_batch MCPツールは、asyncio.Semaphore(4)によって制限されたasyncio.gatherを使用して、マルチSKU推論を同時に実行します。これにより、無制限の同時テンソル割り当てからGPU/CPUメモリを保護しながら、並列スループットを提供します。各アイテムはセマフォを取得し、ペイロードを検証し、asyncio.to_threadを介してengine.predictをスレッドプールにオフロードし、アイテムごとのフォールバックメタデータを含む構造化されたForecastResponseを返します。サマリーブロックは、総アイテム数、エラー数、バックエンドごとの使用状況(model_usage)を報告します。

バックエンド別サーキットブレーカー

各基盤モデルバックエンドは、モデルハブに到達できない場合や一貫してエラーが発生する場合のカスケード障害を防ぐために、独立したサーキットブレーカー(CircuitBreakerState)を維持します。5回連続で失敗すると、ブレーカーが開き、テストコールを許可する前に60秒間トラフィックを直ちに次のフォールバック層にルーティングします。

バックエンド

失敗しきい値

クールダウン

オープン時の動作

TimesFM 2.5

5回の失敗

60s

MODEL_UNAVAILABLEを発生させ、Chronos-2にルーティング

Chronos-2

5回の失敗

60s

MODEL_UNAVAILABLEを発生させ、AutoARIMAにルーティング

これにより、一時的なHuggingFace Hubの停止や破損したウェイトのダウンロードがエージェントを無期限にブロックしないことが保証されます。

正規化された信頼度メトリック

信頼度スコアは、広い分散を0.0に圧縮する線形フロアの代わりに、有界な相対不確実性比を使用します:

$$\text{Confidence} = \frac{1}{1 + \frac{p_{90} - p_{10}}{\vert p_{50}\vert + \epsilon}}$$

ここで$\epsilon = 10^{-5}$です。特性:

  • 出力範囲$(0, 1]$ — 負になることはなく、0に圧縮されることもありません

  • スプレッド$(p_{90} - p_{10}) \to 0$のとき、信頼度は$\to 1$(タイトな境界)

  • スプレッドが$\to \infty$のとき、信頼度は漸近的に$\to 0$(極端な不確実性)

  • 中央値の大きさ$|p_{50}|$による除算でスケール不変

エージェンティックアーキテクチャ非依存

ステートレスでスキーマにバインドされたMCPツールマイクロサービスとして、このエンジンは、OPA/Regoポリシーで管理されるニューロシンボリックスタック、LangGraphステートマシン、CrewAIスウォーム、標準的なReActループなど、あらゆるエージェントオーケストレーターとシームレスに統合します。


MCPツール仕様とAPI契約

forecast_demand

単一の時系列予測。

リクエスト(TimeSeriesInputPayload)

{
  "target_series": [120.5, 115.0, 130.2, 125.8, 140.1],
  "forecast_horizon": 30,
  "price_index": [19.99, 19.99, 24.99, 24.99, 24.99, 24.99, ...],
  "promo_flag": [0, 0, 1, 0, 1, 0, ...]
}

レスポンス(ForecastResponse)

{
  "model_used": "Chronos-2-Fallback",
  "mean_prediction": [142.3, 145.1, 140.8, 148.2, 150.0],
  "p10_quantile": [120.1, 122.4, 118.7, 125.3, 127.9],
  "p50_quantile": [142.3, 145.1, 140.8, 148.2, 150.0],
  "p90_quantile": [165.2, 168.5, 162.1, 170.4, 172.8],
  "confidence_score": 0.87,
  "horizon_length": 30,
  "exogenous_dropped": false,
  "warnings": ["CUDA unavailable; running on CPU. Expect degraded inference performance."]
}

forecast_batch

アイテムごとのフォールバックサマリーを備えた、配列ベースのマルチSKU予測。

リクエスト

{
  "payloads": [
    {"target_series": [10.0]*20, "forecast_horizon": 5},
    {"target_series": [11.0]*30, "forecast_horizon": 3, "price_index": [20.0]*33}
  ]
}

レスポンス

{
  "results": [
    {"model_used": "Chronos-2-Fallback", "mean_prediction": [...], ...},
    {"model_used": "TimesFM-2.5", "mean_prediction": [...], ...}
  ],
  "summary": {
    "total": 2,
    "errors": 0,
    "model_usage": {"Chronos-2-Fallback": 1, "TimesFM-2.5": 1}
  }
}

AgentFriendlyError

検証または実行の失敗時に返される自己修正エラースキーマ。

{
  "error_code": "VALIDATION_ERROR",
  "message": "Input validation failed at 'price_index': Price array misalignment. Expected 25 elements (Context: 20 + Horizon: 5), got 2.",
  "expected": "Payload matching TimeSeriesInputPayload schema (context 16-16000 finite values, aligned exogenous signals).",
  "received": "{\"location\": \"price_index\", \"message\": \"Price array misalignment. Expected 25 elements (Context: 20 + Horizon: 5), got 2.\", \"context\": {\"expected\": \"25\", \"got\": \"2\"}}",
  "remediation_suggestion": "Correct field 'price_index' (Price array misalignment. Expected 25 elements (Context: 20 + Horizon: 5), got 2.) and resubmit. Ensure context length is between 16 and 16000, values are finite (no NaN/Inf), and exogenous arrays align to len(target_series) + forecast_horizon."
}

エラーコード:VALIDATION_ERROR、MODEL_UNAVAILABLE、TRANSIENT_FAILURE、MODEL_FAILURE、INTERNAL_ERROR


クイックスタートとMCP設定

インストール

# Requires Python 3.11+
uv sync --extra gpu   # or: pip install -r requirements.txt

環境

# Optional: force CPU if GPU memory constrained
export MODEL_CONFIG_PATH=configs/model_config.yaml
export DATA_STORAGE_ROOT=data/

エンジンはCUDAを自動検出します。利用できない場合はCPUにフォールバックし、warningsフィールドに警告を出力します。

MCPクライアント設定(Claude Desktop / OpenCode / LangGraph / CrewAI)

MCPクライアント設定(claude_desktop_config.json、opencode.json、または同等のもの)に追加します:

{
  "mcpServers": {
    "zero-shot-forecaster": {
      "command": "python",
      "args": ["mcp_server.py"],
      "cwd": "/absolute/path/to/zero-shot-demand-foundation",
      "env": {
        "MODEL_CONFIG_PATH": "configs/model_config.yaml"
      }
    }
  }
}

MCPクライアントを再起動します。ツールforecast_demandとforecast_batchが完全なJSONスキーマとともに自動登録されます。

呼び出し例(Claude / LLMエージェント)

{
  "tool": "forecast_demand",
  "arguments": {
    "target_series": [120, 115, 130, 125, 140, 135, 150, 145, 155, 160, 155, 165, 170, 168, 172, 175, 180, 178, 185, 190],
    "forecast_horizon": 7,
    "price_index": [19.99, 19.99, 19.99, 19.99, 19.99, 19.99, 24.99, 24.99, 24.99, 24.99, 24.99, 24.99, 24.99, 24.99, 24.99, 24.99, 24.99, 24.99, 24.99, 24.99, 24.99, 24.99, 24.99, 24.99, 24.99, 24.99, 24.99],
    "promo_flag": [0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
  }
}

検証とテスト

完全なテストスイートを実行(62テスト、AC-1からAC-5)

uv run pytest tests/ -q
# or
python -m pytest tests/ -q

期待される出力:

.............................................................. [100%]
62 passed in ~3s

テストカバレッジマトリックス

AC

基準

テスト関数

AC-1

ノンブロッキングイベントループ

test_forecast_demand_is_non_blocking

AC-2

遅延ロードとVRAM節約

test_lazy_loading_*, test_hardware_auto_detect_*

AC-3

グレースフルフォールバックとシグナル除去

test_timesfm_failure_falls_back_to_chronos_strips_exog, test_full_fallback_to_autoarima

AC-4

CUDA OOMリカバリ

test_cuda_oom_triggers_memory_recovery, test_recover_from_oom_calls_gc_and_empty_cache

AC-5

エージェント自己修正ペイロード

test_agent_friendly_error_on_short_sequence, test_forecast_batch_agent_friendly_error_per_item

レガシーテスト(後方互換性)

元の48件のテストはすべて引き続き成功しています:

pytest tests/test_forecasting_engine.py tests/test_forecaster_router.py tests/test_mcp_server.py tests/test_schemas.py tests/test_metrics.py -q

プロジェクト構造(リファクタリング後)

zero-shot-demand-foundation/
├── configs/
│   └── model_config.yaml            # Model IDs, device_map, num_samples
├── data/                            # Git-ignored (CSV, ZIP)
├── scripts/
│   ├── download_m5.py               # M5 dataset fetcher
│   └── download_favorita.py         # Favorita dataset fetcher
├── src/
│   ├── models/
│   │   └── forecaster.py            # ZeroShotForecastingEngine (refactored)
│   ├── schemas/
│   │   └── payloads.py              # TimeSeriesInputPayload, ForecastResponse, AgentFriendlyError
│   └── utils/
│       ├── data_loader.py           # DemandDataEngine, FavoritaDataLoader
│       └── metrics.py               # WAPE, RMSSE, Pinball Loss, CRPS
├── tests/
│   ├── test_forecasting_engine.py   # Updated for lazy loading
│   ├── test_forecaster_router.py    # Updated fixtures
│   ├── test_mcp_server.py           # Async + AgentFriendlyError
│   ├── test_metrics.py              # Unchanged
│   ├── test_schemas.py              # Unchanged
│   └── test_refactored_forecaster.py # NEW: AC-1..5 coverage
├── main.py                          # CLI evaluation entry point
├── mcp_server.py                    # FastMCP server (async, batch, errors)
├── requirements.txt
├── .gitignore                       # Ignores *.md, data/, __pycache__/
└── README.md                        # This file

ライセンス

MITライセンス。詳細はLICENSEを参照してください。

参考文献

  • Chronos-2: Ansari et al., Chronos: Learning the Language of Time Series, arXiv:2403.07815

  • TimesFM: Das et al., TimesFM: A Decoder-Only Foundation Model for Time-Series Forecasting, arXiv:2402.02592

  • M5 Competition: Makridakis et al., M5 Accuracy Competition, IJF 2022

  • Corporación Favorita: Kaggle Favorita Grocery Sales Forecasting

  • Model Context Protocol: Anthropic MCP Specification

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    An MCP server powered by Meta's Prophet that enables LLMs to perform time-series forecasting, trend analysis, and predictive modeling on historical data. It provides LLM-friendly statistical summaries, automated business-rule validation, and ready-to-render Chart.js visualizations.
    MIT
  • F
    license
    A
    quality
    C
    maintenance
    Enables demand forecasting and replenishment recommendations using statistical models (Syntetos-Boylan classification, AutoETS, TSB) and provides tools for forecasting, evaluation, and order quantity calculation.
    6
    -