Crash0524/Forecast-Agent

一个轻量、可替换模型的事实驱动型上下文时间序列预测 Agent。**不训练模型**,也**不让 LLM 直接 生成预测值**。当前“前处理 → TSFM 推理 → 后处理”流程由 LLM 规划三个通用时间序列 操作,再由 TSFM 生成九条分位数轨迹(q0.1…q0.9),最后由 Resolver 在批准窗口内 逐时间点选择分位等级。

0

stars

7

commits

Python

primary language

Aug 15, 2026

updated

README

Forecast-Agent:项目介绍

一个轻量、可替换模型的事实驱动型上下文时间序列预测 Agent。不训练模型,也不让 LLM 直接 生成预测值。整体架构为 ReAct:历史阶段由一个 greedy 单步 Historical ReAct Controller 驱动——每轮观察当前 working history H_t、重建后的数值证据与已接受的 action trajectory,只选择一个最必要动作并立即在新 H_t 上生效;PREDICT 结束历史 阶段并声明预测后可控制的未来窗口。随后 TSFM 生成九条分位数轨迹(q0.1…q0.9), Resolver 只在这些窗口内选择分位等级。

设计原则:

  • LLM 负责语义推理,程序只校验和执行一个小型 DSL,不按事件、行业或数据集标签 增加规则;
  • 历史 ReAct 是 greedy 单步状态机MASKUSE_CURRENTUNDO 修改或恢复当前 H_t;
  • PREDICT 是阶段边界:提交 H_t,同时携带 deferred future_control_windows;未来控制不再是历史 action;
  • 每个最终值都从 TSFM 九条轨迹本地 gather,正常点默认 q0.5;
  • 程序只校验 schema、时间 provenance、最小有效历史量、控制窗口范围和分位 合法性,不覆盖 LLM 的有效语义决策。

当前已准备好可选的 DeepSeek API、本地 Qwen3.5 与 Toto 2.0 适配器,但都不会默认调用。 GiftCtxRulePlannerLastValueTSFMStub 只用于开发、测试接口和跑通数据, 不能作为正式模型或基准结果

快速运行

1. 数据集下载

评测使用 GIFT-CTX

将数据集放到本地目录:

git clone https://huggingface.co/datasets/Salesforce/GIFT-CTX

2. 项目 git 拉取

git clone https://github.com/Crash0524/Forecast-Agent.git
cd Forecast-Agent

3. 环境配置(含模型注册表运行环境说明)

项目依赖 Python 3.10+、NumPy、pandas;读取 GIFT-CTX Parquet 还需要 PyArrow。 在主环境安装(含数据 / DeepSeek / 开发依赖):

pip install -r requirements.txt

⚠️ 重点:Toto 2.0 运行在与主环境分离的 Conda 环境里。 模型通过通用 JSON 配置 + 模型注册表加载(src/forecast_agent/adapters/models/registry.py),不会往 CLI 加 模型专属参数。Toto2 有两种后端:

后端配置 mode说明
worker(默认)"worker"在独立 Conda 环境(默认名 agent_toto)中启动持久 worker 进程;主进程不需要安装 PyTorch/Toto,适合与外部 LLM API 组合
in_process"in_process"当前环境已安装 torch + toto2 时懒加载,进程内直接推理

config/models/toto2-2.5b.json 关键字段:

{
  "mode": "worker",          // worker 或 in_process
  "conda_env": "agent_toto", // worker 模式下,模型运行在哪个 Conda 环境
  "model_path": "/home/menchunting/work/model-cache/Datadog--Toto-2.0-2.5B",
  "device": "cuda:0",        // GPU 编号
  "local_files_only": true,  // 仅使用本地权重缓存
  "startup_timeout": 900.0,
  "forecast_timeout": 300.0
}

因此运行链路是:轻量主环境(LLM/编排) + agent_toto Conda 环境(Toto 推理) 两套环境。conda_env 必须指向已安装 torchtoto2 包、并能访问 model_path 权重的环境;worker 由主进程自动拉起,模型只加载一次,后续请求复用 同一进程。

4. 配置 API

LLM 通过 config/LLM/*.json 配置。可直接用 DeepSeek API (deepseek-v4-flash.json / deepseek-v4-pro.json),API Key 从环境变量读取 (不提交真实 Key):

export DEEPSEEK_API_KEY="sk-..."            # 方式一:环境变量
# 或方式二:通过 --env-file 读取 .env.example

也可用本地部署的 Qwen3.5(qwen3.5-4b-local.json / qwen3.5-9b-local.json):先启动 vLLM 服务(scripts/serve_qwen35_vllm.sh),再把 base_url 指向 http://127.0.0.1:8000/v1model 指向本地权重路径即可。

5. 运行

批量 A/B/C 评估(245 条全量,前处理 + Toto + 后处理):

PYTHONPATH=src python scripts/evaluate_gift_ctx_preprocess_postprocess.py \
  --data {data_dir} \
  --model-config config/models/toto2-2.5b.json \
  --llm-config config/LLM/qwen3.5-9b-local.json \
  --llm-thinking disabled \
  --env-file .env.example \
  --output output/gift_ctx_v8.5_qwen9b_thinking.jsonl \
  --raw-baseline

常用参数:--idx(单条)、--limit(条数上限)、--shard-count/--shard-index (GPU 分片)、--resume(续跑跳过已成功样本)、--raw-baseline(额外跑一次无 LLM 的 raw,输出 A/B/C 三阶段归因,共两次 Toto 调用)。

项目细节

完整流程

raw facts + 权威时间轴 + working history H0
          │
          ▼
┌──────────────────────────────────────────────────┐
│        Historical ReAct Controller(LLM)          │
│                                                    │
│  观察:H_t + 重建的数值证据 + action trajectory      │
│  思考并选择唯一 action                              │
│                                                    │
│    MASK / USE_CURRENT / UNDO                        │
│      → 修改/恢复 H_t → 重建证据 → 再观察             │
│                                                    │
│    PREDICT + future_control_windows                 │
│      → 退出历史循环                                 │
└──────────────────────────────────────────────────┘
          │
          ▼
TSFM:一次生成 q0.1 … q0.9
          │
          ▼
future_control_windows 是否为空?
  ├── 是:本地全 horizon 选 q0.5(跳过 Resolver)
  └── 否:Quantile Route Resolver(LLM)→ 本地校验并 gather Toto 数值

Historical ReAct action space

操作LLM 决策wire 字段程序执行边界
MASK哪些有界历史窗口不应作为预测证据(reason_type 只标记区间性质)start / end / reason_typegrounded 范围、保留足够有效点;写为 NaN,不插值
USE_CURRENT当前 regime 从哪个边界开始boundary检查边界与段长度;确定性截取 H_t
UNDO最近一次历史编辑是否应撤销pop working-state stack;trajectory 仍保留完整审计
PREDICTH_t 是否可提交;未来哪些窗口允许 resolver 处理future_control_windowsexactly-once TSFM;仅此 action 可携带 future_control_windows

运行时 wire 使用拆分字段:MASK 和 future windows 使用 start/endUSE_CURRENT 只使用包含式 boundary,Resolver overrides 也使用 start/end。 程序内部统一按左闭右开 [start, end) 执行;当 end 恰好是当前历史或预测期的最后 timestamp 时,是否包含最后点继续遵循对应的 final-point policy。旧静态 plan 的 start/end 双闭输入仍由 compatibility parser 转换为等价执行范围;旧静态 normalized history context 也只在 compatibility executor 中保留,不属于 Historical Controller action space。

历史 Controller 默认最多执行 8 轮,可在 LLM 配置中通过 max_history_steps 设置,或在评估命令中用 --max-history-steps N 覆盖(1–32)。 步数上限只由外层程序执行并写入结果元数据,不发送给 LLM;如果在上限内没有得到 PREDICT,程序会在调用 TSFM 前安全终止。

Historical Controller 的每轮输入包含 text_context、完整当前 current_historycurrent_conditioninghistory_summaryreal_timeline、压缩后的 action_historylast_observationhistory_summary 保留文本时间/区间与处理后 数值证据的紧凑绑定,便于 LLM 联合判断事实与 working history。Action history 只向 LLM 暴露 actionargumentsreason,完整执行结果仍保留在最终 trajectory 中。 Prompt 版本常量仅用于评估元数据与 resume 兼容校验,不写入任何实际发送给 LLM 的 system/user message。

评估 JSONL 的 llm_responses.historical_controller_rounds 按 Controller 轮次 输出原始 LLM response。每轮包含对应的 working-history 起点和长度,以及该轮所有 retry response、校验结果与原始 JSON 内容。

Historical Controller 的每个 step 与 Quantile Resolver 阶段都最多发送三次请求 (初次请求加两次 retry)。retry prompt 只携带当前阶段最近一次失败的 response、 错误类型、具体原因及当前请求轮次;当前 step 成功后,下一 step 不继承此前的 retry 对话,只保留已经接受的 action trajectory 和重新构建的 working state。

目录

src/forecast_agent/
  agent.py                      # 编排:ForecastAgent 与 PreprocessPostprocessForecastAgent
  interfaces.py                 # LLM / TSFM / Preprocessor / Postprocessor / 边界检测接口
  models.py                     # action、plan、window、result 等数据模型
  prompting.py                  # 单阶段 ContextPlan prompt、JSON Schema、语义校验
  prompting_preprocess_postprocess.py  # ReAct controller 与 quantile resolver prompt
  validators_preprocess_postprocess.py # schema/parser 与 quantile routing 校验
  evidence.py                   # 时间归属、历史局部证据和数值候选
  history.py                    # missing mask、历史编辑执行与归一化
  future.py                     # 未来确定性编辑
  boundary.py                   # 确定性阶段边界检测
  intervals.py                  # 时间区间解析与执行范围
  timeline.py                   # history / future 时间轴构建
  metrics.py                    # MAE 等指标
  cli.py                        # 单样本 direct / llm 命令行入口
  adapters/
    deepseek.py                 # DeepSeek/OpenAI 兼容 LLM 适配器
    deepseek_preprocess_postprocess.py  # ReAct preprocessor 与 quantile postprocessor
    gift_ctx_rules.py           # 开发用规则 planner(不可作为基准)
    stubs.py                    # 开发桩
    models/                     # TSFM registry、Toto2 与 worker 后端
  data/                         # GIFT-CTX 读取
config/
  models/                       # 模型运行配置(Toto2 等)
  LLM/                          # LLM 配置(deepseek-v4 / qwen3.5 本地)
scripts/                        # 批量评估、分片合并、vLLM 启动
tests/                          # 单元与端到端测试
doc/                            # 架构、数据映射、模型接入与验收报告

实验结果

  • A = 无 LLM 的 Toto raw 预测;B = 前处理后的 q0.5 预测;C = 分位路由后的最终预测;
  • 提升/不变/降低按 MAE 严格大小判断(等值计为“不变”)。

总览(Qwen3.5-9B + Toto2-2.5B,244 条成功样本)

指标A (raw)B (前处理 q0.5)C (最终)
mean MAE553.90270.48269.12
median MAE8.088.228.08

三阶段 win / tie / bad(244)

阶段提升不变降低提升和损失和净改善
前处理 B vs A581305676,603.17,449.9+69,153.3
路由 C vs B551881333.60.0+333.6
最终 C vs A111785576,936.77,449.9+69,486.8

按 skill / source

分组pre (w/t/b)pre 净route (w/t/b)route 净final (w/t/b)final 净
anomaly (60)22/12/26−4,526.50/60/0+0.022/12/26−4,526.5
phase_change (60)23/19/18+75,810.01/59/0+0.324/19/17+75,810.3
future (124)13/99/12−2,130.154/69/1+333.365/47/12−1,796.9
CIK (120)18/82/20−2,311.539/80/1+158.055/46/19−2,153.5
GIFT synthesize (124)40/48/36+71,464.816/108/0+175.656/32/36+71,640.4

Contributors

Crash0524

7 commits

Crash0524/Forecast-Agent

一个轻量、可替换模型的事实驱动型上下文时间序列预测 Agent。**不训练模型**,也**不让 LLM 直接 生成预测值**。当前“前处理 → TSFM 推理 → 后处理”流程由 LLM 规划三个通用时间序列 操作,再由 TSFM 生成九条分位数轨迹(q0.1…q0.9),最后由 Resolver 在批准窗口内 逐时间点选择分位等级。

0

stars

7

commits

Python

primary language

Aug 15, 2026

updated

README

Forecast-Agent:项目介绍

一个轻量、可替换模型的事实驱动型上下文时间序列预测 Agent。不训练模型,也不让 LLM 直接 生成预测值。整体架构为 ReAct:历史阶段由一个 greedy 单步 Historical ReAct Controller 驱动——每轮观察当前 working history H_t、重建后的数值证据与已接受的 action trajectory,只选择一个最必要动作并立即在新 H_t 上生效;PREDICT 结束历史 阶段并声明预测后可控制的未来窗口。随后 TSFM 生成九条分位数轨迹(q0.1…q0.9), Resolver 只在这些窗口内选择分位等级。

设计原则:

  • LLM 负责语义推理,程序只校验和执行一个小型 DSL,不按事件、行业或数据集标签 增加规则;
  • 历史 ReAct 是 greedy 单步状态机MASKUSE_CURRENTUNDO 修改或恢复当前 H_t;
  • PREDICT 是阶段边界:提交 H_t,同时携带 deferred future_control_windows;未来控制不再是历史 action;
  • 每个最终值都从 TSFM 九条轨迹本地 gather,正常点默认 q0.5;
  • 程序只校验 schema、时间 provenance、最小有效历史量、控制窗口范围和分位 合法性,不覆盖 LLM 的有效语义决策。

当前已准备好可选的 DeepSeek API、本地 Qwen3.5 与 Toto 2.0 适配器,但都不会默认调用。 GiftCtxRulePlannerLastValueTSFMStub 只用于开发、测试接口和跑通数据, 不能作为正式模型或基准结果

快速运行

1. 数据集下载

评测使用 GIFT-CTX

将数据集放到本地目录:

git clone https://huggingface.co/datasets/Salesforce/GIFT-CTX

2. 项目 git 拉取

git clone https://github.com/Crash0524/Forecast-Agent.git
cd Forecast-Agent

3. 环境配置(含模型注册表运行环境说明)

项目依赖 Python 3.10+、NumPy、pandas;读取 GIFT-CTX Parquet 还需要 PyArrow。 在主环境安装(含数据 / DeepSeek / 开发依赖):

pip install -r requirements.txt

⚠️ 重点:Toto 2.0 运行在与主环境分离的 Conda 环境里。 模型通过通用 JSON 配置 + 模型注册表加载(src/forecast_agent/adapters/models/registry.py),不会往 CLI 加 模型专属参数。Toto2 有两种后端:

后端配置 mode说明
worker(默认)"worker"在独立 Conda 环境(默认名 agent_toto)中启动持久 worker 进程;主进程不需要安装 PyTorch/Toto,适合与外部 LLM API 组合
in_process"in_process"当前环境已安装 torch + toto2 时懒加载,进程内直接推理

config/models/toto2-2.5b.json 关键字段:

{
  "mode": "worker",          // worker 或 in_process
  "conda_env": "agent_toto", // worker 模式下,模型运行在哪个 Conda 环境
  "model_path": "/home/menchunting/work/model-cache/Datadog--Toto-2.0-2.5B",
  "device": "cuda:0",        // GPU 编号
  "local_files_only": true,  // 仅使用本地权重缓存
  "startup_timeout": 900.0,
  "forecast_timeout": 300.0
}

因此运行链路是:轻量主环境(LLM/编排) + agent_toto Conda 环境(Toto 推理) 两套环境。conda_env 必须指向已安装 torchtoto2 包、并能访问 model_path 权重的环境;worker 由主进程自动拉起,模型只加载一次,后续请求复用 同一进程。

4. 配置 API

LLM 通过 config/LLM/*.json 配置。可直接用 DeepSeek API (deepseek-v4-flash.json / deepseek-v4-pro.json),API Key 从环境变量读取 (不提交真实 Key):

export DEEPSEEK_API_KEY="sk-..."            # 方式一:环境变量
# 或方式二:通过 --env-file 读取 .env.example

也可用本地部署的 Qwen3.5(qwen3.5-4b-local.json / qwen3.5-9b-local.json):先启动 vLLM 服务(scripts/serve_qwen35_vllm.sh),再把 base_url 指向 http://127.0.0.1:8000/v1model 指向本地权重路径即可。

5. 运行

批量 A/B/C 评估(245 条全量,前处理 + Toto + 后处理):

PYTHONPATH=src python scripts/evaluate_gift_ctx_preprocess_postprocess.py \
  --data {data_dir} \
  --model-config config/models/toto2-2.5b.json \
  --llm-config config/LLM/qwen3.5-9b-local.json \
  --llm-thinking disabled \
  --env-file .env.example \
  --output output/gift_ctx_v8.5_qwen9b_thinking.jsonl \
  --raw-baseline

常用参数:--idx(单条)、--limit(条数上限)、--shard-count/--shard-index (GPU 分片)、--resume(续跑跳过已成功样本)、--raw-baseline(额外跑一次无 LLM 的 raw,输出 A/B/C 三阶段归因,共两次 Toto 调用)。

项目细节

完整流程

raw facts + 权威时间轴 + working history H0
          │
          ▼
┌──────────────────────────────────────────────────┐
│        Historical ReAct Controller(LLM)          │
│                                                    │
│  观察:H_t + 重建的数值证据 + action trajectory      │
│  思考并选择唯一 action                              │
│                                                    │
│    MASK / USE_CURRENT / UNDO                        │
│      → 修改/恢复 H_t → 重建证据 → 再观察             │
│                                                    │
│    PREDICT + future_control_windows                 │
│      → 退出历史循环                                 │
└──────────────────────────────────────────────────┘
          │
          ▼
TSFM:一次生成 q0.1 … q0.9
          │
          ▼
future_control_windows 是否为空?
  ├── 是:本地全 horizon 选 q0.5(跳过 Resolver)
  └── 否:Quantile Route Resolver(LLM)→ 本地校验并 gather Toto 数值

Historical ReAct action space

操作LLM 决策wire 字段程序执行边界
MASK哪些有界历史窗口不应作为预测证据(reason_type 只标记区间性质)start / end / reason_typegrounded 范围、保留足够有效点;写为 NaN,不插值
USE_CURRENT当前 regime 从哪个边界开始boundary检查边界与段长度;确定性截取 H_t
UNDO最近一次历史编辑是否应撤销pop working-state stack;trajectory 仍保留完整审计
PREDICTH_t 是否可提交;未来哪些窗口允许 resolver 处理future_control_windowsexactly-once TSFM;仅此 action 可携带 future_control_windows

运行时 wire 使用拆分字段:MASK 和 future windows 使用 start/endUSE_CURRENT 只使用包含式 boundary,Resolver overrides 也使用 start/end。 程序内部统一按左闭右开 [start, end) 执行;当 end 恰好是当前历史或预测期的最后 timestamp 时,是否包含最后点继续遵循对应的 final-point policy。旧静态 plan 的 start/end 双闭输入仍由 compatibility parser 转换为等价执行范围;旧静态 normalized history context 也只在 compatibility executor 中保留,不属于 Historical Controller action space。

历史 Controller 默认最多执行 8 轮,可在 LLM 配置中通过 max_history_steps 设置,或在评估命令中用 --max-history-steps N 覆盖(1–32)。 步数上限只由外层程序执行并写入结果元数据,不发送给 LLM;如果在上限内没有得到 PREDICT,程序会在调用 TSFM 前安全终止。

Historical Controller 的每轮输入包含 text_context、完整当前 current_historycurrent_conditioninghistory_summaryreal_timeline、压缩后的 action_historylast_observationhistory_summary 保留文本时间/区间与处理后 数值证据的紧凑绑定,便于 LLM 联合判断事实与 working history。Action history 只向 LLM 暴露 actionargumentsreason,完整执行结果仍保留在最终 trajectory 中。 Prompt 版本常量仅用于评估元数据与 resume 兼容校验,不写入任何实际发送给 LLM 的 system/user message。

评估 JSONL 的 llm_responses.historical_controller_rounds 按 Controller 轮次 输出原始 LLM response。每轮包含对应的 working-history 起点和长度,以及该轮所有 retry response、校验结果与原始 JSON 内容。

Historical Controller 的每个 step 与 Quantile Resolver 阶段都最多发送三次请求 (初次请求加两次 retry)。retry prompt 只携带当前阶段最近一次失败的 response、 错误类型、具体原因及当前请求轮次;当前 step 成功后,下一 step 不继承此前的 retry 对话,只保留已经接受的 action trajectory 和重新构建的 working state。

目录

src/forecast_agent/
  agent.py                      # 编排:ForecastAgent 与 PreprocessPostprocessForecastAgent
  interfaces.py                 # LLM / TSFM / Preprocessor / Postprocessor / 边界检测接口
  models.py                     # action、plan、window、result 等数据模型
  prompting.py                  # 单阶段 ContextPlan prompt、JSON Schema、语义校验
  prompting_preprocess_postprocess.py  # ReAct controller 与 quantile resolver prompt
  validators_preprocess_postprocess.py # schema/parser 与 quantile routing 校验
  evidence.py                   # 时间归属、历史局部证据和数值候选
  history.py                    # missing mask、历史编辑执行与归一化
  future.py                     # 未来确定性编辑
  boundary.py                   # 确定性阶段边界检测
  intervals.py                  # 时间区间解析与执行范围
  timeline.py                   # history / future 时间轴构建
  metrics.py                    # MAE 等指标
  cli.py                        # 单样本 direct / llm 命令行入口
  adapters/
    deepseek.py                 # DeepSeek/OpenAI 兼容 LLM 适配器
    deepseek_preprocess_postprocess.py  # ReAct preprocessor 与 quantile postprocessor
    gift_ctx_rules.py           # 开发用规则 planner(不可作为基准)
    stubs.py                    # 开发桩
    models/                     # TSFM registry、Toto2 与 worker 后端
  data/                         # GIFT-CTX 读取
config/
  models/                       # 模型运行配置(Toto2 等)
  LLM/                          # LLM 配置(deepseek-v4 / qwen3.5 本地)
scripts/                        # 批量评估、分片合并、vLLM 启动
tests/                          # 单元与端到端测试
doc/                            # 架构、数据映射、模型接入与验收报告

实验结果

  • A = 无 LLM 的 Toto raw 预测;B = 前处理后的 q0.5 预测;C = 分位路由后的最终预测;
  • 提升/不变/降低按 MAE 严格大小判断(等值计为“不变”)。

总览(Qwen3.5-9B + Toto2-2.5B,244 条成功样本)

指标A (raw)B (前处理 q0.5)C (最终)
mean MAE553.90270.48269.12
median MAE8.088.228.08

三阶段 win / tie / bad(244)

阶段提升不变降低提升和损失和净改善
前处理 B vs A581305676,603.17,449.9+69,153.3
路由 C vs B551881333.60.0+333.6
最终 C vs A111785576,936.77,449.9+69,486.8

按 skill / source

分组pre (w/t/b)pre 净route (w/t/b)route 净final (w/t/b)final 净
anomaly (60)22/12/26−4,526.50/60/0+0.022/12/26−4,526.5
phase_change (60)23/19/18+75,810.01/59/0+0.324/19/17+75,810.3
future (124)13/99/12−2,130.154/69/1+333.365/47/12−1,796.9
CIK (120)18/82/20−2,311.539/80/1+158.055/46/19−2,153.5
GIFT synthesize (124)40/48/36+71,464.816/108/0+175.656/32/36+71,640.4

Contributors

Crash0524

7 commits

Languages

Python

99.7%