<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Storm Spirit</title>
    <description>这是我的中文博客，记录自己在技术、生活等方面的一些感想。博客叫 Storm Spirit， 因为我特别喜欢 Dota 里蓝猫这个英雄，可惜玩得不好…… 希望自己能够通过博客，得到成长。
</description>
    <link>https://wulfric.me/</link>
    <atom:link href="https://wulfric.me/feed.xml" rel="self" type="application/rss+xml" />
    <pubDate>Sun, 19 Apr 2026 12:11:48 +0000</pubDate>
    <lastBuildDate>Sun, 19 Apr 2026 12:11:48 +0000</lastBuildDate>
    <generator>Jekyll v4.4.1</generator>
    <rights>wulfric © 2013~2026. All rights reserved.</rights>
    
      <item>
        <title>理解 Python 进程注入：从 ptrace 到 PEP 768</title>
        <description>&lt;p&gt;你有一个正在运行的 Python 进程——也许是一个生产环境中的 Web 服务，也许是一个跑了三小时的数据管道。它变慢了，或者出了某种诡异的 bug。你不能停掉它、不能重启它、不能加 print 语句。&lt;/p&gt;

&lt;p&gt;怎么办？&lt;/p&gt;

&lt;p&gt;答案是&lt;strong&gt;进程注入&lt;/strong&gt;（process injection）：在不重启目标进程的前提下，将诊断代码注入到一个正在运行的 Python 解释器中。这正是 &lt;a href=&quot;https://github.com/wwulfric/peeka&quot;&gt;peeka&lt;/a&gt;、&lt;a href=&quot;https://github.com/bloomberg/memray&quot;&gt;memray&lt;/a&gt;、&lt;a href=&quot;https://github.com/lmacken/pyrasite&quot;&gt;pyrasite&lt;/a&gt; 等工具的核心能力。&lt;/p&gt;

&lt;p&gt;本文将从底层原理到工程实践，系统地拆解 Python 进程注入的三代技术方案。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;1-进程注入的本质问题&quot;&gt;1. 进程注入的本质问题&lt;/h2&gt;

&lt;p&gt;要理解进程注入，先要理解我们面临的约束：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;地址空间隔离&lt;/strong&gt;：每个进程有独立的虚拟地址空间，A 进程无法直接读写 B 进程的内存&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;GIL（全局解释器锁）&lt;/strong&gt;：Python 的 C API 调用几乎都需要持有 GIL&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;执行时机&lt;/strong&gt;：目标进程可能正处于任何状态——malloc 中途、GC 扫描中、持有 import 锁……&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;所以，进程注入需要回答三个问题：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;如何进入目标进程的地址空间？&lt;/strong&gt;（跨进程控制）&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;如何安全地执行 Python 代码？&lt;/strong&gt;（GIL 获取）&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;何时执行才不会崩溃？&lt;/strong&gt;（时机选择）&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;不同的工具对这三个问题给出了不同的答案。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;2-第一代pyrasite-与-gdb-直接调用-c-api&quot;&gt;2. 第一代：pyrasite 与 GDB 直接调用 C API&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/lmacken/pyrasite&quot;&gt;pyrasite&lt;/a&gt;（2011 年）是最早的 Python 进程注入工具之一。它的方案简单直接：&lt;/p&gt;

&lt;h3 id=&quot;原理&quot;&gt;原理&lt;/h3&gt;

&lt;p&gt;利用 &lt;span class=&quot;codespan&quot;&gt;ptrace&lt;/span&gt; 系统调用暂停目标进程，然后通过 GDB 直接调用 Python 的 C API 执行任意 Python 代码。&lt;/p&gt;

&lt;h3 id=&quot;核心代码&quot;&gt;核心代码&lt;/h3&gt;

&lt;p&gt;pyrasite 的注入逻辑只有几行：&lt;/p&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# pyrasite/injector.py（简化）
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gdb_cmds&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;call (int) PyGILState_Ensure()&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;call (int) PyRun_SimpleString(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;exec(open(&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;\\&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;).read())&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;call (void) PyGILState_Release($1)&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;subprocess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Popen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;gdb -p %d -batch %s&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;-eval-command=&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;call %s&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cmd&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cmd&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gdb_cmds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;即：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;GDB 通过 &lt;span class=&quot;codespan&quot;&gt;ptrace(PTRACE_ATTACH)&lt;/span&gt; 暂停目标进程&lt;/li&gt;
  &lt;li&gt;在&lt;strong&gt;暂停点&lt;/strong&gt;直接调用 &lt;span class=&quot;codespan&quot;&gt;PyGILState_Ensure()&lt;/span&gt; 获取 GIL&lt;/li&gt;
  &lt;li&gt;调用 &lt;span class=&quot;codespan&quot;&gt;PyRun_SimpleString()&lt;/span&gt; 执行一段 Python 代码&lt;/li&gt;
  &lt;li&gt;调用 &lt;span class=&quot;codespan&quot;&gt;PyGILState_Release()&lt;/span&gt; 释放 GIL&lt;/li&gt;
  &lt;li&gt;GDB detach，目标进程恢复执行&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;ptrace-是什么&quot;&gt;ptrace 是什么？&lt;/h3&gt;

&lt;p&gt;&lt;span class=&quot;codespan&quot;&gt;ptrace&lt;/span&gt; 是 Linux 内核提供的进程调试接口。GDB、strace、lldb 的底层都依赖它。&lt;/p&gt;

&lt;div  class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;调试器进程                    目标进程
    │                           │
    │── ptrace(ATTACH, pid) ───&amp;gt;│  暂停目标
    │                           │  (SIGSTOP)
    │── ptrace(PEEKTEXT) ──────&amp;gt;│  读内存
    │── ptrace(POKETEXT) ──────&amp;gt;│  写内存
    │── ptrace(GETREGS) ───────&amp;gt;│  读寄存器
    │── ptrace(SETREGS) ───────&amp;gt;│  改寄存器 -&amp;gt; 修改执行流
    │── ptrace(CONT) ──────────&amp;gt;│  恢复执行
    │── ptrace(DETACH) ────────&amp;gt;│  脱离
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;GDB 正是利用 ptrace 的能力，在目标进程的上下文中&quot;调用&quot;C 函数。具体来说，GDB 每执行一条 &lt;span class=&quot;codespan&quot;&gt;call&lt;/span&gt; 命令，都会经历以下 6 步：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;保存目标进程当前的寄存器状态&lt;/li&gt;
  &lt;li&gt;将函数参数写入寄存器/栈（遵循 ABI 调用约定）&lt;/li&gt;
  &lt;li&gt;将指令指针（&lt;span class=&quot;codespan&quot;&gt;rip&lt;/span&gt;）设为目标函数地址&lt;/li&gt;
  &lt;li&gt;设置断点用于在函数返回后接管控制&lt;/li&gt;
  &lt;li&gt;恢复执行&lt;/li&gt;
  &lt;li&gt;函数执行完毕后，GDB 命中断点，恢复原始寄存器&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;也就是说，pyrasite 的三条 GDB 命令（&lt;span class=&quot;codespan&quot;&gt;call PyGILState_Ensure()&lt;/span&gt;、&lt;span class=&quot;codespan&quot;&gt;call PyRun_SimpleString(...)&lt;/span&gt;、&lt;span class=&quot;codespan&quot;&gt;call PyGILState_Release($1)&lt;/span&gt;）各自独立地经历这 6 步，而非 6 步整体对应 3 个函数。每次 &lt;span class=&quot;codespan&quot;&gt;call&lt;/span&gt; 都是一次完整的&quot;保存-&amp;gt;篡改-&amp;gt;执行-&amp;gt;恢复&quot;循环。&lt;/p&gt;

&lt;h3 id=&quot;为什么这个方案危险&quot;&gt;为什么这个方案危险？&lt;/h3&gt;

&lt;p&gt;问题出在&lt;strong&gt;时机&lt;/strong&gt;上。当 GDB attach 并暂停目标进程时，目标可能正处于任何状态：&lt;/p&gt;

&lt;div  class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;目标进程的执行时间线：
... -&amp;gt; malloc() -&amp;gt; [GDB 在这里暂停] -&amp;gt; PyGILState_Ensure() -&amp;gt; 💥

问题：malloc 内部持有 heap lock
      PyRun_SimpleString 可能也要调 malloc
      -&amp;gt; 死锁
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;更具体地说：&lt;/p&gt;

&lt;div class=&quot;table-container&quot;&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;暂停时的状态&lt;/th&gt;
      &lt;th&gt;调用 C API 的后果&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;malloc/free 中途&lt;/td&gt;
      &lt;td&gt;堆锁重入 -&amp;gt; 死锁或内存损坏&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;GC 扫描中&lt;/td&gt;
      &lt;td&gt;对象引用计数不一致 -&amp;gt; 段错误&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;持有 GIL&lt;/td&gt;
      &lt;td&gt;&lt;span class=&quot;codespan&quot;&gt;PyGILState_Ensure()&lt;/span&gt; 死锁&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;持有 import 锁&lt;/td&gt;
      &lt;td&gt;注入代码中的 import 死锁&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;

&lt;p&gt;pyrasite 本质上是在&quot;碰运气&quot;——大多数时候目标进程不在这些危险状态，所以注入成功。但在生产环境中，这种不确定性是不可接受的。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;3-第二代memraypeeka-的-dlopen--pthread-方案&quot;&gt;3. 第二代：memray/peeka 的 dlopen + pthread 方案&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/bloomberg/memray&quot;&gt;memray&lt;/a&gt;（Bloomberg 开源的内存分析器）和 &lt;a href=&quot;https://github.com/wwulfric/peeka&quot;&gt;peeka&lt;/a&gt; 在 Python 3.8-3.13 上采用了相似的改进方案。核心思路是：&lt;strong&gt;不在 ptrace 暂停点直接执行 Python 代码，而是注入一个 C 扩展，由 C 扩展在安全的时机执行代码&lt;/strong&gt;。&lt;/p&gt;

&lt;h3 id=&quot;原理概览&quot;&gt;原理概览&lt;/h3&gt;

&lt;div  class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;调试器(GDB/LLDB)                目标进程
    │                              │
    │── ptrace ATTACH ────────────&amp;gt;│  暂停
    │── 等待安全断点命中 ────────────&amp;gt;│  malloc/PyMem_* 返回后
    │                              │
    │── dlopen(&quot;_inject.so&quot;) ─────&amp;gt;│  加载 C 扩展到目标地址空间
    │── call peeka_spawn_agent() ─&amp;gt;│  创建新 pthread
    │── ptrace DETACH ────────────&amp;gt;│  恢复执行
    │                              │
    │                              │  [新 pthread]
    │                              │  ├─ connect() 回连调试器
    │                              │  ├─ recv() 接收 agent 脚本
    │                              │  ├─ PyGILState_Ensure() ← 等待安全时机
    │                              │  ├─ PyEval_EvalCode() 执行 agent
    │                              │  └─ PyGILState_Release()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;关键改进有三点：&lt;strong&gt;安全断点&lt;/strong&gt;、&lt;strong&gt;dlopen 注入&lt;/strong&gt;、&lt;strong&gt;独立线程&lt;/strong&gt;。&lt;/p&gt;

&lt;h3 id=&quot;31-安全断点选择正确的时机&quot;&gt;3.1 安全断点——选择正确的时机&lt;/h3&gt;

&lt;p&gt;pyrasite 在&lt;strong&gt;任意位置&lt;/strong&gt;暂停后就执行 C API，而 memray/peeka 等到&lt;strong&gt;安全位置&lt;/strong&gt;才注入。&lt;/p&gt;

&lt;p&gt;以 peeka 的 GDB 脚本为例：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-gdb&quot;&gt;# peeka/core/_attach.gdb
b malloc
b calloc
b realloc
b free
b PyMem_Malloc
b PyMem_Calloc
b PyMem_Realloc
b PyMem_Free
b PyErr_CheckSignals
b PyCallable_Check

commands 1-10
    disable breakpoints
    delete breakpoints
    call (void*)dlopen($peeka_injector, $peeka_rtld_now)
    call (int)peeka_spawn_agent($peeka_port)
end

continue
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;解读：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;在 &lt;span class=&quot;codespan&quot;&gt;malloc&lt;/span&gt;、&lt;span class=&quot;codespan&quot;&gt;PyMem_Malloc&lt;/span&gt; 等函数的&lt;strong&gt;入口&lt;/strong&gt;设置断点&lt;/li&gt;
  &lt;li&gt;调用 &lt;span class=&quot;codespan&quot;&gt;Py_AddPendingCall(&amp;amp;PyCallable_Check, 0)&lt;/span&gt; 安排一个 pending call（确保 CPython 的 eval loop 会调用 &lt;span class=&quot;codespan&quot;&gt;PyCallable_Check&lt;/span&gt;，从而命中我们的断点）&lt;/li&gt;
  &lt;li&gt;恢复执行（&lt;span class=&quot;codespan&quot;&gt;continue&lt;/span&gt;），等待目标进程&lt;strong&gt;自然地&lt;/strong&gt;调用这些函数&lt;/li&gt;
  &lt;li&gt;断点命中时，我们知道当前位置是&lt;strong&gt;函数入口&lt;/strong&gt;——没有持有 heap lock、没有处于 GC 中途&lt;/li&gt;
  &lt;li&gt;此时再执行 dlopen 和 spawn&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这比 pyrasite 安全得多：我们不是在任意位置注入，而是在一个已知的安全点。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Py_AddPendingCall 的作用&lt;/strong&gt;：如果目标进程处于纯 Python 代码的 tight loop 中（不调用任何 C 函数），我们的 malloc/PyMem 断点可能永远不会命中。&lt;span class=&quot;codespan&quot;&gt;Py_AddPendingCall&lt;/span&gt; 注册的回调会在 Python eval loop 的下一个&quot;检查点&quot;被调用，确保断点一定会触发。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;32-dlopen将-c-扩展加载到目标进程&quot;&gt;3.2 dlopen——将 C 扩展加载到目标进程&lt;/h3&gt;

&lt;p&gt;&lt;span class=&quot;codespan&quot;&gt;dlopen&lt;/span&gt; 是 POSIX 系统的动态链接器接口。通过 GDB 在目标进程中调用 &lt;span class=&quot;codespan&quot;&gt;dlopen(&quot;_inject.abi3.so&quot;, RTLD_NOW)&lt;/span&gt;，我们将一个编译好的 C 扩展加载到目标进程的地址空间中。&lt;/p&gt;

&lt;p&gt;这比 &lt;span class=&quot;codespan&quot;&gt;PyRun_SimpleString&lt;/span&gt; 强大的关键在于：&lt;strong&gt;C 代码可以创建线程，可以操作底层系统资源，不受 GIL 约束&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;peeka 在 macOS 上使用 LLDB 完成相同的工作：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-lldb&quot;&gt;# peeka/core/_attach.lldb
expr auto $dlsym = (void* (*)(void*, const char*))&amp;amp;::dlsym
expr auto $dlopen = $dlsym($rtld_default, &quot;dlopen&quot;)
expr auto $dll = ((void*(*)(const char*, int))$dlopen)($libpath, $rtld_now)
expr auto $spawn = $dlsym($dll, &quot;peeka_spawn_agent&quot;)
p ((int(*)(int))$spawn)($port) ? &quot;FAILURE&quot; : &quot;SUCCESS&quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;33-独立线程解耦注入与执行&quot;&gt;3.3 独立线程——解耦注入与执行&lt;/h3&gt;

&lt;p&gt;dlopen 之后，GDB 调用 C 扩展的 &lt;span class=&quot;codespan&quot;&gt;peeka_spawn_agent()&lt;/span&gt; 函数。这个函数做了什么？&lt;/p&gt;

&lt;div  class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// peeka/core/_inject.c（核心逻辑）&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;__attribute__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;visibility&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;default&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;peeka_spawn_agent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;pthread_t&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pthread_create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;thread_body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;uintptr_t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;它&lt;strong&gt;不执行任何 Python 代码&lt;/strong&gt;。它只是创建一个 pthread，然后立即返回。这样 GDB 可以快速 detach，目标进程恢复正常执行。&lt;/p&gt;

&lt;p&gt;真正的工作在新线程中完成：&lt;/p&gt;

&lt;div  class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 新线程的执行流程&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;run_client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;uint16_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 1. 通过 TCP 回连调试器，接收 agent 脚本&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;connect_client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;recvall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;script_len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 2. 安全地执行 Python 代码&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;run_script&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;errmsg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;run_script&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;**&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;errmsg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 检查 Python 是否已初始化&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Py_IsInitialized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;errmsg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;copy_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Python is not initialized&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 获取 GIL —— 这里会等待，直到安全&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;PyGILState_STATE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gstate&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PyGILState_Ensure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 编译并执行 agent 脚本&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;run_script_impl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;errmsg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;PyGILState_Release&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gstate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;run_script_impl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;**&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;errmsg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 构建干净的 globals 字典&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;PyObject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builtins&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PyImport_ImportModule&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;builtins&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;PyObject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;globals&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PyDict_New&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;PyDict_SetItemString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;globals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;__builtins__&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builtins&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// 编译 + 执行&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;PyObject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;code&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Py_CompileString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                       &lt;span class=&quot;s&quot;&gt;&quot;_peeka_attach_hook.py&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                       &lt;span class=&quot;n&quot;&gt;Py_file_input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;PyObject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mod&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PyEval_EvalCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;globals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;globals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;为什么独立线程更安全？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;在 pyrasite 方案中，&lt;span class=&quot;codespan&quot;&gt;PyGILState_Ensure()&lt;/span&gt; 在 GDB 暂停目标进程时被调用——如果暂停的线程恰好持有 GIL，这里会死锁。&lt;/p&gt;

&lt;p&gt;在 dlopen + pthread 方案中：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;GDB 执行 dlopen -&amp;gt; spawn -&amp;gt; &lt;strong&gt;立刻返回&lt;/strong&gt; -&amp;gt; GDB detach -&amp;gt; 目标进程恢复&lt;/li&gt;
  &lt;li&gt;新线程调用 &lt;span class=&quot;codespan&quot;&gt;PyGILState_Ensure()&lt;/span&gt; 时，目标进程已经在正常运行&lt;/li&gt;
  &lt;li&gt;&lt;span class=&quot;codespan&quot;&gt;PyGILState_Ensure()&lt;/span&gt; 会&lt;strong&gt;等待&lt;/strong&gt; GIL 可用，而不是在暂停状态下强行获取&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这消除了 GIL 死锁的主要原因。&lt;/p&gt;

&lt;h3 id=&quot;34-反向连接agent-代码的传输&quot;&gt;3.4 反向连接——agent 代码的传输&lt;/h3&gt;

&lt;p&gt;一个有趣的设计细节是 agent 代码的传输。peeka 不是把代码内容写入 GDB 命令（字符串转义会很痛苦），而是采用&lt;strong&gt;反向连接&lt;/strong&gt;：&lt;/p&gt;

&lt;div  class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[1] 调试器启动一个 TCP 服务，等待连接
[2] GDB 将端口号传给 peeka_spawn_agent(port)
[3] C 扩展中的新线程连接这个端口
[4] 调试器将 agent.py 的完整内容通过 TCP 发送过去
[5] C 扩展接收、编译、执行
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这样做的好处是：agent 代码可以任意长、包含任意字符，不受 GDB 命令行的转义限制。&lt;/p&gt;

&lt;h3 id=&quot;35-legacy-回退没有-c-扩展时怎么办&quot;&gt;3.5 Legacy 回退——没有 C 扩展时怎么办？&lt;/h3&gt;

&lt;p&gt;如果 C 扩展不可用（比如没有编译、或者 GLIBC 版本不兼容），peeka 会回退到类似 pyrasite 的方式，但做了改进：&lt;/p&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# peeka/core/attach.py — _inject_via_gdb_legacy()
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bootstrap&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;_c = open(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;agent_script&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;).read(); exec(_c);&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;gdb_commands&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;call (int) PyGILState_Ensure()&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;call (int) PyRun_SimpleString(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bootstrap&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;call (void) PyGILState_Release($1)&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;通过 &lt;span class=&quot;codespan&quot;&gt;exec()&lt;/span&gt; 执行文件内容而非直接内联代码，减少了转义问题。但本质上仍然有 pyrasite 的时机安全隐患，只是作为最后的回退方案存在。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;4-第三代pep-768-与-sysremote_exec&quot;&gt;4. 第三代：PEP 768 与 sys.remote_exec&lt;/h2&gt;

&lt;p&gt;Python 3.14 引入了 &lt;a href=&quot;https://peps.python.org/pep-0768/&quot;&gt;PEP 768&lt;/a&gt;，从&lt;strong&gt;解释器层面&lt;/strong&gt;提供了原生的进程注入支持。这是一个根本性的范式转变。&lt;/p&gt;

&lt;h3 id=&quot;核心思想&quot;&gt;核心思想&lt;/h3&gt;

&lt;p&gt;前两代方案都是从&lt;strong&gt;外部&lt;/strong&gt;强行进入目标进程（通过 ptrace/GDB/LLDB），然后在一个&quot;可能安全&quot;的位置执行代码。PEP 768 的思路则是：&lt;strong&gt;告诉解释器你要执行什么，让解释器自己在安全的时候执行&lt;/strong&gt;。&lt;/p&gt;

&lt;h3 id=&quot;运作机制&quot;&gt;运作机制&lt;/h3&gt;

&lt;div  class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;外部进程                       目标 Python 解释器
    │                              │
    │  (1) 读 /proc/pid/maps       │
    │      定位 PyRuntime 地址       │
    │                              │
    │  (2) 读 _Py_DebugOffsets      │
    │      获取内部结构偏移量         │
    │                              │
    │  (3) 写入脚本路径到            │
    │      tstate-&amp;gt;debugger_        │
    │        script_path            │
    │                              │
    │  (4) 设置 pending call 标志   │
    │      tstate-&amp;gt;debugger_        │
    │        pending_call = 1       │
    │                              │
    │  (5) 设置 eval breaker        │
    │      eval_breaker |=          │
    │        PLEASE_STOP_BIT        │
    │                              │
    │  完成，无需等待                │
    │                              │
    │                              │  ... 正常执行字节码 ...
    │                              │
    │                              │  eval loop 检查 eval_breaker
    │                              │  ↓
    │                              │  发现 pending_call 标志
    │                              │  ↓
    │                              │  _PyRunRemoteDebugger()
    │                              │  ↓
    │                              │  读取 script_path
    │                              │  ↓
    │                              │  fopen + PyRun_AnyFile
    │                              │  ↓
    │                              │  执行脚本 ✅
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;CPython 内部的关键代码：&lt;/p&gt;

&lt;div  class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Python/ceval_gil.c&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_PyRunRemoteDebugger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PyThreadState&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tstate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;remote_debug&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tstate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;remote_debugger_support&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;debugger_pending_call&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;tstate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;remote_debugger_support&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;debugger_pending_call&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 复制脚本路径（避免 race condition）&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;script_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pathsz&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;memcpy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;script_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
               &lt;span class=&quot;n&quot;&gt;tstate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;remote_debugger_support&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;debugger_script_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
               &lt;span class=&quot;n&quot;&gt;pathsz&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 审计事件（可被安全策略拦截）&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;PySys_Audit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;cpython.remote_debugger_script&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;s&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;script_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 执行脚本&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;FILE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fopen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;script_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;r&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;PyRun_AnyFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;script_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;fclose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;每个-pythreadstate-中的数据结构&quot;&gt;每个 PyThreadState 中的数据结构&lt;/h3&gt;

&lt;div  class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Include/cpython/pystate.h&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;#define _Py_MAX_SCRIPT_PATH_SIZE 512
&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int32_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;debugger_pending_call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;debugger_script_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_Py_MAX_SCRIPT_PATH_SIZE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_PyRemoteDebuggerSupport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;每个线程状态增加约 516 字节，但运行时开销几乎为零——只是 eval loop 中一次分支预测极大概率命中的 &lt;span class=&quot;codespan&quot;&gt;if&lt;/span&gt; 检查。&lt;/p&gt;

&lt;h3 id=&quot;使用方式&quot;&gt;使用方式&lt;/h3&gt;

&lt;p&gt;peeka 的实现非常简洁：&lt;/p&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# peeka/core/attach.py
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_attach_pep768&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;agent_code&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_read_agent_code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;agent_script&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;_create_agent_script&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;agent_code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# 一行搞定
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;remote_exec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;agent_script&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# 等待 agent 就绪
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;_wait_for_agent_ready&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;READY_TIMEOUT_PEP768&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;不需要 GDB、不需要 ptrace、不需要 C 扩展、不需要 dlopen。只需要调用者和目标进程是同一用户（或拥有 &lt;span class=&quot;codespan&quot;&gt;CAP_SYS_PTRACE&lt;/span&gt;），并且目标进程没有禁用远程调试。&lt;/p&gt;

&lt;h3 id=&quot;安全保障&quot;&gt;安全保障&lt;/h3&gt;

&lt;p&gt;PEP 768 在安全性上的设计非常审慎：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;仅接受文件路径&lt;/strong&gt;：&lt;span class=&quot;codespan&quot;&gt;sys.remote_exec&lt;/span&gt; 写入的是一个&lt;strong&gt;文件路径&lt;/strong&gt;，不是代码内容。这意味着攻击者不仅需要跨进程写内存的权限，还需要在目标进程可读的文件系统路径上放置恶意脚本&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;审计钩子&lt;/strong&gt;：执行前触发 &lt;span class=&quot;codespan&quot;&gt;cpython.remote_debugger_script&lt;/span&gt; 审计事件，安全策略可以拦截&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;可禁用&lt;/strong&gt;：&lt;span class=&quot;codespan&quot;&gt;PYTHON_DISABLE_REMOTE_DEBUG&lt;/span&gt; 环境变量或 &lt;span class=&quot;codespan&quot;&gt;-X disable-remote-debug&lt;/span&gt; 启动参数&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;编译时可移除&lt;/strong&gt;：&lt;span class=&quot;codespan&quot;&gt;./configure --without-remote-debug&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;为什么-pep-768-是本质性的进步&quot;&gt;为什么 PEP 768 是本质性的进步？&lt;/h3&gt;

&lt;div class=&quot;table-container&quot;&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;ptrace/GDB 注入&lt;/th&gt;
      &lt;th&gt;PEP 768&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;执行时机&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;取决于暂停点/断点位置&lt;/td&gt;
      &lt;td&gt;解释器自己选择安全检查点&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;GIL 处理&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;外部强制获取&lt;/td&gt;
      &lt;td&gt;自然持有（在 eval loop 中）&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;运行时开销&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;无（仅 attach 时）&lt;/td&gt;
      &lt;td&gt;接近零（一次分支检查）&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;所需权限&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;ptrace + GDB/LLDB&lt;/td&gt;
      &lt;td&gt;同 UID 或 process_vm_writev&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;多线程安全&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;需要 scheduler-locking&lt;/td&gt;
      &lt;td&gt;天然安全（per-thread 标志）&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;崩溃风险&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;存在（不安全时机）&lt;/td&gt;
      &lt;td&gt;极低（安全检查点执行）&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;

&lt;h3 id=&quot;pep-768-实现中的已知问题与-cpython-修复&quot;&gt;PEP 768 实现中的已知问题与 CPython 修复&lt;/h3&gt;

&lt;p&gt;PEP 768 在 Python 3.14.0a5 合入后（&lt;a href=&quot;https://github.com/python/cpython/pull/131937&quot;&gt;gh-131937&lt;/a&gt;），社区在实际使用中发现了一系列实现缺陷。以下按严重程度梳理关键问题及其修复方案。&lt;/p&gt;

&lt;h4 id=&quot;命名空间污染gh-132859&quot;&gt;命名空间污染（gh-132859）&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;问题&lt;/strong&gt;：注入脚本在 &lt;span class=&quot;codespan&quot;&gt;__main__&lt;/span&gt; 模块的命名空间中执行，会意外覆盖目标进程的变量：&lt;/p&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# 目标进程
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;pass&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# 注入脚本设置了 x = 42 -&amp;gt; 目标进程意外退出
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;修复&lt;/strong&gt;：&lt;a href=&quot;https://github.com/python/cpython/pull/132860&quot;&gt;PR #132860&lt;/a&gt;（3.14.0a5）——脚本现在在隔离的命名空间中执行。如需访问主模块，需显式 &lt;span class=&quot;codespan&quot;&gt;import __main__&lt;/span&gt;。&lt;/p&gt;

&lt;h4 id=&quot;非-utf-8-路径编码失败gh-133886&quot;&gt;非 UTF-8 路径编码失败（gh-133886）&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;问题&lt;/strong&gt;：&lt;span class=&quot;codespan&quot;&gt;sys.remote_exec()&lt;/span&gt; 硬编码使用 UTF-8 编码脚本路径，导致在非 UTF-8 locale 环境下无法处理非 ASCII 路径，&lt;span class=&quot;codespan&quot;&gt;os.access()&lt;/span&gt; 找不到文件。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;修复&lt;/strong&gt;：&lt;a href=&quot;https://github.com/python/cpython/pull/133887&quot;&gt;PR #133887&lt;/a&gt;（3.14.0b1）——改用文件系统编码（&lt;span class=&quot;codespan&quot;&gt;sys.getfilesystemencoding()&lt;/span&gt;）代替硬编码 UTF-8。&lt;/p&gt;

&lt;h4 id=&quot;无效参数导致段错误gh-134064&quot;&gt;无效参数导致段错误（gh-134064）&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;问题&lt;/strong&gt;：&lt;span class=&quot;codespan&quot;&gt;sys.remote_exec(0, None)&lt;/span&gt; 在 ASAN/debug 构建中触发段错误——缺少 NULL 检查就直接解引用 &lt;span class=&quot;codespan&quot;&gt;script_path&lt;/span&gt;。由 fusil fuzzer 发现。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;修复&lt;/strong&gt;：&lt;a href=&quot;https://github.com/python/cpython/pull/134067&quot;&gt;PR #134067&lt;/a&gt;（3.14.0b1）——添加参数校验。&lt;/p&gt;

&lt;h4 id=&quot;elf-搜索中的异常状态污染gh-137293&quot;&gt;ELF 搜索中的异常状态污染（gh-137293）&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;问题&lt;/strong&gt;：在 Linux 上搜索 &lt;span class=&quot;codespan&quot;&gt;/proc/pid/maps&lt;/span&gt; 定位 &lt;span class=&quot;codespan&quot;&gt;PyRuntime&lt;/span&gt; 时，&lt;span class=&quot;codespan&quot;&gt;search_linux_map_for_section()&lt;/span&gt; 对每个无法打开的文件（如已删除的 &lt;span class=&quot;codespan&quot;&gt;.so&lt;/span&gt;）都会设置异常。当最终在其他文件中找到 &lt;span class=&quot;codespan&quot;&gt;PyRuntime&lt;/span&gt; 时，函数返回成功但异常仍被设置：&lt;/p&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# sys.remote_exec() 返回成功，但附带一个未清除的异常：
# OSError: Cannot open ELF file &apos;/path/to/lib.so (deleted)&apos;
# -&amp;gt; SystemError: returned a result with an exception set
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;修复&lt;/strong&gt;：&lt;a href=&quot;https://github.com/python/cpython/pull/137309&quot;&gt;PR #137309&lt;/a&gt;——在搜索循环中继续搜索前清除异常。&lt;/p&gt;

&lt;h4 id=&quot;ctypes-导致的重复-libpython-映射gh-144563&quot;&gt;ctypes 导致的重复 libpython 映射（gh-144563）&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;问题&lt;/strong&gt;：当目标进程导入了 &lt;span class=&quot;codespan&quot;&gt;_ctypes&lt;/span&gt; 或 &lt;span class=&quot;codespan&quot;&gt;polars&lt;/span&gt;（底层使用 ctypes）时，&lt;span class=&quot;codespan&quot;&gt;/proc/pid/maps&lt;/span&gt; 中会出现多个 &lt;span class=&quot;codespan&quot;&gt;libpython&lt;/span&gt; 映射。远程调试代码无法处理重复项，导致 &quot;Can&apos;t determine Python version&quot; 或 &quot;Failed to read debug offsets&quot; 错误。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;修复&lt;/strong&gt;：&lt;a href=&quot;https://github.com/python/cpython/pull/144595&quot;&gt;PR #144595&lt;/a&gt;（3.15.0a6）——优雅处理重复映射，使用第一个有效的 PyRuntime。&lt;/p&gt;

&lt;h4 id=&quot;远程调试偏移表缺乏校验gh-148178&quot;&gt;远程调试偏移表缺乏校验（gh-148178）&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;问题&lt;/strong&gt;：&lt;span class=&quot;codespan&quot;&gt;_remote_debugging&lt;/span&gt; 模块从目标进程内存读取 &lt;span class=&quot;codespan&quot;&gt;async_debug_offsets&lt;/span&gt; 后直接使用，未校验结构有效性。&lt;span class=&quot;codespan&quot;&gt;asyncio_task_object.size&lt;/span&gt; 字段被用作读取长度写入固定 4096 字节的栈缓冲区（&lt;span class=&quot;codespan&quot;&gt;SIZEOF_TASK_OBJ&lt;/span&gt;），&lt;strong&gt;恶意/被攻陷的目标进程可以构造更大的 size 导致栈缓冲区溢出&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;修复&lt;/strong&gt;：&lt;a href=&quot;https://github.com/python/cpython/pull/148187&quot;&gt;PR #148187&lt;/a&gt;——新增 &lt;span class=&quot;codespan&quot;&gt;validate_async_debug_offsets()&lt;/span&gt; 和 &lt;span class=&quot;codespan&quot;&gt;validate_debug_offsets()&lt;/span&gt; 对所有偏移表做边界校验（+511 行校验基础设施）。已回移至 3.14 分支（&lt;a href=&quot;https://github.com/python/cpython/pull/148577&quot;&gt;PR #148577&lt;/a&gt;）。&lt;/p&gt;

&lt;h4 id=&quot;远程内存数据损坏导致崩溃gh-140739&quot;&gt;远程内存数据损坏导致崩溃（gh-140739）&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;问题&lt;/strong&gt;：在 free-threading 构建中使用 &lt;span class=&quot;codespan&quot;&gt;--mode=gil&lt;/span&gt; 做性能采样时，远程调试 unwinder 读取到目标进程的损坏内存会导致 SIGSEGV——缺乏对远程读取数据的边界检查。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;修复&lt;/strong&gt;：&lt;a href=&quot;https://github.com/python/cpython/pull/143190&quot;&gt;PR #143190&lt;/a&gt;（3.15.0a4）——对所有远程读取的数据结构添加健壮性校验。&lt;/p&gt;

&lt;h4 id=&quot;错误处理路径缺失gh-144316-gh-146308&quot;&gt;错误处理路径缺失（gh-144316, gh-146308）&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;问题&lt;/strong&gt;：&lt;span class=&quot;codespan&quot;&gt;_remote_debugging&lt;/span&gt; 模块中多处错误处理缺陷：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;span class=&quot;codespan&quot;&gt;RemoteUnwinder.get_stack_trace()&lt;/span&gt; 在 &lt;span class=&quot;codespan&quot;&gt;debug=False&lt;/span&gt; 时可能返回 NULL 但不设置异常&lt;/li&gt;
  &lt;li&gt;varint 解码路径缺少错误检查&lt;/li&gt;
  &lt;li&gt;&lt;span class=&quot;codespan&quot;&gt;PyMem_RawMalloc()&lt;/span&gt; 返回 NULL 时缺少 &lt;span class=&quot;codespan&quot;&gt;PyErr_NoMemory()&lt;/span&gt; 调用&lt;/li&gt;
  &lt;li&gt;跨平台错误传播不一致&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;修复&lt;/strong&gt;：&lt;a href=&quot;https://github.com/python/cpython/pull/144442&quot;&gt;PR #144442&lt;/a&gt; 和 &lt;a href=&quot;https://github.com/python/cpython/pull/146309&quot;&gt;PR #146309&lt;/a&gt;——全面审计并修复错误处理路径，&lt;span class=&quot;codespan&quot;&gt;set_exception_cause&lt;/span&gt; 宏改为无条件设置异常。&lt;/p&gt;

&lt;h4 id=&quot;权限问题sudo-创建的临时文件不可读gh-143511&quot;&gt;权限问题：sudo 创建的临时文件不可读（gh-143511）&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;问题&lt;/strong&gt;：使用 &lt;span class=&quot;codespan&quot;&gt;sudo&lt;/span&gt; 运行注入脚本时，&lt;span class=&quot;codespan&quot;&gt;NamedTemporaryFile&lt;/span&gt; 以 root 用户创建（权限 &lt;span class=&quot;codespan&quot;&gt;0600&lt;/span&gt;），非 root 的目标进程无法读取该脚本文件。这是 PEP 768 &quot;仅接受文件路径&quot;设计的副作用。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;处理&lt;/strong&gt;：文档化（&lt;a href=&quot;https://github.com/python/cpython/pull/143575&quot;&gt;PR #143575&lt;/a&gt;）——用户需确保脚本文件对目标进程可读，或以相同用户运行。&lt;/p&gt;

&lt;h4 id=&quot;remote-pdb-无法中断死循环gh-132975&quot;&gt;Remote PDB 无法中断死循环（gh-132975）&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;问题&lt;/strong&gt;：在远程 PDB 提示符下输入 &lt;span class=&quot;codespan&quot;&gt;while True: pass&lt;/span&gt; 后无法中断——能中断脚本本身的执行，但无法中断 PDB 命令求值中的死循环。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;修复&lt;/strong&gt;：&lt;a href=&quot;https://github.com/python/cpython/pull/133223&quot;&gt;PR #133223&lt;/a&gt;（3.14.0b1）——实现基于 socket 的中断机制：Unix 上发送 SIGINT 到远程进程，Windows 上通过 &lt;span class=&quot;codespan&quot;&gt;socketpair&lt;/span&gt; 传递信号。&lt;/p&gt;

&lt;h4 id=&quot;审计事件缺失gh-135543&quot;&gt;审计事件缺失（gh-135543）&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;问题&lt;/strong&gt;：&lt;span class=&quot;codespan&quot;&gt;sys.remote_exec()&lt;/span&gt; 最初未触发审计事件，安全工具无法监控进程注入行为。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;修复&lt;/strong&gt;：&lt;a href=&quot;https://github.com/python/cpython/pull/135544&quot;&gt;PR #135544&lt;/a&gt;（3.14.0b1）——添加 &lt;span class=&quot;codespan&quot;&gt;sys.remote_exec&lt;/span&gt; 审计事件，参数为 &lt;span class=&quot;codespan&quot;&gt;(pid, script)&lt;/span&gt;。&lt;/p&gt;

&lt;h4 id=&quot;外部工具跟进&quot;&gt;外部工具跟进&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;debugpy（VS Code）&lt;/strong&gt;：&lt;a href=&quot;https://github.com/microsoft/debugpy/commit/c7e86a1954381ceadb2ea398fc60079deef91358&quot;&gt;已支持 sys.remote_exec()&lt;/a&gt;（2026.1），Python 3.14+ 优先使用原生 API，可通过 &lt;span class=&quot;codespan&quot;&gt;--disable-sys-remote-exec&lt;/span&gt; 回退&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;helicopter-parent&lt;/strong&gt;：&lt;a href=&quot;https://github.com/a-reich/helicopter-parent/&quot;&gt;社区工具&lt;/a&gt;，通过管理子进程绕过 &lt;span class=&quot;codespan&quot;&gt;ptrace_scope&lt;/span&gt; 限制&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;peeka/memray&lt;/strong&gt;：已集成 PEP 768 作为首选注入路径&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;5-三代方案对比&quot;&gt;5. 三代方案对比&lt;/h2&gt;

&lt;div class=&quot;table-container&quot;&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;方案&lt;/th&gt;
      &lt;th&gt;代表工具&lt;/th&gt;
      &lt;th&gt;Python 版本&lt;/th&gt;
      &lt;th&gt;安全性&lt;/th&gt;
      &lt;th&gt;依赖&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;GDB + PyRun_SimpleString&lt;/td&gt;
      &lt;td&gt;pyrasite&lt;/td&gt;
      &lt;td&gt;2.4 - 3.13&lt;/td&gt;
      &lt;td&gt;⚠️ 可能崩溃/死锁&lt;/td&gt;
      &lt;td&gt;GDB, ptrace&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;GDB/LLDB + dlopen + pthread&lt;/td&gt;
      &lt;td&gt;memray, peeka&lt;/td&gt;
      &lt;td&gt;3.8 - 3.13&lt;/td&gt;
      &lt;td&gt;✅ 较安全&lt;/td&gt;
      &lt;td&gt;GDB/LLDB, ptrace, C 编译器&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;sys.remote_exec (PEP 768)&lt;/td&gt;
      &lt;td&gt;peeka, memray&lt;/td&gt;
      &lt;td&gt;3.14+&lt;/td&gt;
      &lt;td&gt;✅ 安全&lt;/td&gt;
      &lt;td&gt;无外部依赖&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;

&lt;h3 id=&quot;memray-vs-peeka-的容错策略&quot;&gt;memray vs peeka 的容错策略&lt;/h3&gt;

&lt;p&gt;两个项目虽然共享 dlopen + pthread 的核心方案，但在&lt;strong&gt;失败处理&lt;/strong&gt;上有本质区别：&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;memray：一次选择，不回退。&lt;/strong&gt; memray 在启动时通过 &lt;span class=&quot;codespan&quot;&gt;resolve_debugger()&lt;/span&gt; 按优先级（&lt;span class=&quot;codespan&quot;&gt;sys.remote_exec&lt;/span&gt; &amp;gt; &lt;span class=&quot;codespan&quot;&gt;gdb&lt;/span&gt; &amp;gt; &lt;span class=&quot;codespan&quot;&gt;lldb&lt;/span&gt;）选定&lt;strong&gt;一种&lt;/strong&gt;注入方法，此后不再切换。GDB 路径硬依赖 C 扩展（&lt;span class=&quot;codespan&quot;&gt;assert injecter.exists()&lt;/span&gt;），dlopen 失败直接报错，没有 legacy 回退。这是刻意的设计选择——memray 作为专业的内存分析器，对运行环境有明确的前置要求。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;peeka：逐级降级，尽力而为。&lt;/strong&gt; peeka 的决策树包含多层 fallback：&lt;/p&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# peeka 的 attach 决策树
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;hasattr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;remote_exec&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;     &lt;span class=&quot;c1&quot;&gt;# Python 3.14+
&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;remote_exec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;              &lt;span class=&quot;c1&quot;&gt;# 最优路径
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_has_injector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;                &lt;span class=&quot;c1&quot;&gt;# C 扩展可用
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Linux&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GDB&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dlopen&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pthread&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 安全回退
&lt;/span&gt;        &lt;span class=&quot;nf&quot;&gt;except &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;TimeoutError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...):&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GDB&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PyRun_SimpleString&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# 降级到 legacy
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;macOS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LLDB&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dlopen&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pthread&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Linux&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GDB&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PyRun_SimpleString&lt;/span&gt;       &lt;span class=&quot;c1&quot;&gt;# 最后手段
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;报错&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这种设计源于 peeka 的定位——作为通用诊断工具，它需要在各种环境下都能工作，包括 GLIBC 版本不匹配（C 扩展无法加载）或 GIL 死锁（dlopen 路径超时）等边缘情况。代价是多了一层复杂度和潜在的时机安全风险（legacy 路径），但换来了更广的兼容性。&lt;/p&gt;

&lt;div class=&quot;table-container&quot;&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;memray&lt;/th&gt;
      &lt;th&gt;peeka&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;C 扩展缺失&lt;/td&gt;
      &lt;td&gt;硬报错 (&lt;span class=&quot;codespan&quot;&gt;assert&lt;/span&gt;)&lt;/td&gt;
      &lt;td&gt;回退到 legacy GDB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;dlopen 运行时失败&lt;/td&gt;
      &lt;td&gt;报错退出&lt;/td&gt;
      &lt;td&gt;回退到 legacy GDB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;dlopen 后 GIL 死锁&lt;/td&gt;
      &lt;td&gt;超时报错&lt;/td&gt;
      &lt;td&gt;超时后回退到 legacy GDB&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;设计哲学&lt;/td&gt;
      &lt;td&gt;明确前置要求，快速失败&lt;/td&gt;
      &lt;td&gt;尽力而为，逐级降级&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;6-实践中的坑&quot;&gt;6. 实践中的坑&lt;/h2&gt;

&lt;p&gt;在实际开发 peeka 的过程中，我们踩过一些有趣的坑：&lt;/p&gt;

&lt;h3 id=&quot;61-glibc-版本不匹配&quot;&gt;6.1 GLIBC 版本不匹配&lt;/h3&gt;

&lt;p&gt;C 扩展 &lt;span class=&quot;codespan&quot;&gt;_inject.abi3.so&lt;/span&gt; 在高版本 Linux 上编译后，放到低版本容器（如 Python 3.8 的 Debian 旧镜像）中会因为 &lt;span class=&quot;codespan&quot;&gt;GLIBC_2.34 not found&lt;/span&gt; 而 dlopen 失败。&lt;/p&gt;

&lt;p&gt;但 &lt;span class=&quot;codespan&quot;&gt;importlib.util.find_spec()&lt;/span&gt; 只检查文件是否存在，不检查能否加载：&lt;/p&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# ❌ 错误：文件存在但无法加载
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_has_injector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;importlib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;util&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;find_spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;peeka.core._inject&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# ✅ 正确：实际尝试 import
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_has_injector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;peeka.core._inject&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;except &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;ImportError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;OSError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# OSError 捕获 dlopen 失败
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;62-gdb-dlopen-后的-gil-死锁&quot;&gt;6.2 GDB dlopen 后的 GIL 死锁&lt;/h3&gt;

&lt;p&gt;在 Python 3.12 上，GDB dlopen 注入的 C 扩展创建的 pthread 调用 &lt;span class=&quot;codespan&quot;&gt;PyGILState_Ensure()&lt;/span&gt; 时，可能因为 GDB 操作后 GIL 状态不一致而永远无法获取 GIL。&lt;/p&gt;

&lt;p&gt;解决方案是将 dlopen 路径视为&lt;strong&gt;乐观尝试&lt;/strong&gt;，失败后回退到 legacy GDB：&lt;/p&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_has_injector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;_inject_via_gdb_dlopen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;except &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;TimeoutError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;RuntimeError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;OSError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;warning&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;GDB dlopen failed, falling back to legacy GDB&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# 控制流落入 legacy 路径
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;63-ptrace-权限&quot;&gt;6.3 ptrace 权限&lt;/h3&gt;

&lt;p&gt;不同 Linux 发行版的默认 &lt;span class=&quot;codespan&quot;&gt;ptrace_scope&lt;/span&gt; 不同：&lt;/p&gt;

&lt;div class=&quot;table-container&quot;&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;ptrace_scope&lt;/th&gt;
      &lt;th&gt;含义&lt;/th&gt;
      &lt;th&gt;默认发行版&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;0&lt;/td&gt;
      &lt;td&gt;任意同 UID 进程可 attach&lt;/td&gt;
      &lt;td&gt;Arch, RHEL&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;仅父进程可 attach&lt;/td&gt;
      &lt;td&gt;Ubuntu, Debian&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;仅 &lt;span class=&quot;codespan&quot;&gt;CAP_SYS_PTRACE&lt;/span&gt;&lt;/td&gt;
      &lt;td&gt;—&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;3&lt;/td&gt;
      &lt;td&gt;完全禁止&lt;/td&gt;
      &lt;td&gt;—&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;

&lt;p&gt;Docker 容器默认&lt;strong&gt;不授予&lt;/strong&gt; &lt;span class=&quot;codespan&quot;&gt;CAP_SYS_PTRACE&lt;/span&gt;，且 seccomp 配置也会阻止 ptrace 系统调用。需要：&lt;/p&gt;

&lt;div  class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run &lt;span class=&quot;nt&quot;&gt;--cap-add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;SYS_PTRACE &lt;span class=&quot;nt&quot;&gt;--security-opt&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;seccomp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;unconfined your-image
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;64-sysmonitoring-tool_id-冲突&quot;&gt;6.4 sys.monitoring tool_id 冲突&lt;/h3&gt;

&lt;p&gt;Python 3.12+ 的 &lt;span class=&quot;codespan&quot;&gt;sys.monitoring&lt;/span&gt; 只有 6 个 tool slot（0-5）。如果 coverage 工具或 debugger 已经占用了 slot 0，硬编码 &lt;span class=&quot;codespan&quot;&gt;use_tool_id(0, ...)&lt;/span&gt; 会抛 &lt;span class=&quot;codespan&quot;&gt;ValueError&lt;/span&gt;。应该遍历可用 slot：&lt;/p&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;candidate_id&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;monitoring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;use_tool_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;candidate_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;peeka-trace&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;ValueError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;7-总结&quot;&gt;7. 总结&lt;/h2&gt;

&lt;p&gt;Python 进程注入经历了三代演进：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;直接调用 C API&lt;/strong&gt;（pyrasite）：简单粗暴，但有崩溃和死锁风险&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;dlopen + pthread&lt;/strong&gt;（memray/peeka）：通过安全断点和独立线程显著降低风险&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;解释器原生支持&lt;/strong&gt;（PEP 768）：从根本上解决问题，让解释器自己在安全时机执行代码&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;如果你的目标环境是 Python 3.14+，&lt;span class=&quot;codespan&quot;&gt;sys.remote_exec&lt;/span&gt; 是毫无争议的最佳选择。如果需要支持更老的版本，dlopen + pthread 方案提供了合理的安全性和兼容性平衡。&lt;/p&gt;

&lt;p&gt;进程注入不是魔法，它是系统编程、编译器原理和 CPython 内部机制的交汇点。理解这些底层原理，能帮助你在遇到&quot;进程卡死&quot;或&quot;注入失败&quot;时，快速定位问题所在。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;本文基于 &lt;a href=&quot;https://github.com/wwulfric/peeka&quot;&gt;peeka&lt;/a&gt; 的实际开发经验撰写。peeka 是一个受 &lt;a href=&quot;https://github.com/alibaba/arthas&quot;&gt;Alibaba Arthas&lt;/a&gt; 启发的 Python 运行时诊断工具，支持 Python 3.8-3.14+。&lt;/em&gt;&lt;/p&gt;
</description>
        <pubDate>Sun, 19 Apr 2026 00:00:00 +0000</pubDate>
        <link>https://wulfric.me/2026/04/python-process-injection/</link>
        <guid isPermaLink="true">https://wulfric.me/2026/04/python-process-injection/</guid>
        
        <category>python</category>
        
        <category>安全</category>
        
        <category>cpython</category>
        
        
        <category>技术</category>
        
      </item>
    
      <item>
        <title>翻译：AGENTS.md 在我们的智能体评估中优于 Skills</title>
        <description>&lt;p&gt;翻译自 Vercel 博客文章：&lt;a href=&quot;https://vercel.com/blog/agents-md-outperforms-skills-in-our-agent-evals&quot;&gt;AGENTS.md outperforms skills in our agent evals&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;我们原本期望技能是教授编码智能体框架特定知识的解决方案。在构建专注于 Next.js 16 API 的评估后，我们发现了意想不到的结果。&lt;/p&gt;

&lt;p&gt;直接嵌入在 &lt;span class=&quot;codespan&quot;&gt;AGENTS.md&lt;/span&gt; 中的压缩版 8KB 文档索引实现了 100% 的通过率，而技能即使在明确指示智能体使用它们的情况下，最高也只能达到 79%。如果没有这些指示，技能的表现与完全没有文档时没有区别。&lt;/p&gt;

&lt;p&gt;以下是我们尝试的方法、我们学到的经验，以及如何在自己的 Next.js 项目中设置这一功能。&lt;/p&gt;

&lt;h2 id=&quot;我们试图解决的问题&quot;&gt;我们试图解决的问题&lt;/h2&gt;

&lt;p&gt;AI 编码智能体依赖于会过时的训练数据。Next.js 16 引入了 &lt;span class=&quot;codespan&quot;&gt;&apos;use cache&apos;&lt;/span&gt;、&lt;span class=&quot;codespan&quot;&gt;connection()&lt;/span&gt; 和 &lt;span class=&quot;codespan&quot;&gt;forbidden()&lt;/span&gt; 等 API，这些 API 并不在当前模型的训练数据中。当智能体不了解这些 API 时，它们会生成不正确的代码或回退到旧的模式。&lt;/p&gt;

&lt;p&gt;反之也可能发生——你运行的是较旧版本的 Next.js，而模型建议了你的项目中尚不存在的新 API。我们希望通过向智能体提供版本匹配的文档来解决这个问题。&lt;/p&gt;

&lt;h2 id=&quot;教授智能体框架知识的两种方法&quot;&gt;教授智能体框架知识的两种方法&lt;/h2&gt;

&lt;p&gt;在深入探讨结果之前，先简要介绍一下我们测试的两种方法：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;技能&lt;/strong&gt;是一种开放标准，用于封装编码智能体可以使用的领域知识。一个技能将智能体可以按需调用的提示、工具和文档捆绑在一起。其理念是智能体在意识到需要特定框架的帮助时调用该技能，并获取相关文档。&lt;/li&gt;
  &lt;li&gt;&lt;span class=&quot;codespan&quot;&gt;AGENTS.md&lt;/span&gt; 是项目根目录中的一个 Markdown 文件，为编码智能体提供持久上下文。无论你在 &lt;span class=&quot;codespan&quot;&gt;AGENTS.md&lt;/span&gt; 中放入什么内容，智能体在每个回合都可以使用，而无需智能体决定加载它。Claude Code 使用 &lt;span class=&quot;codespan&quot;&gt;CLAUDE.md&lt;/span&gt; 来达到相同的目的。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;我们构建了一个 Next.js 文档技能和一个 &lt;span class=&quot;codespan&quot;&gt;AGENTS.md&lt;/span&gt; 文档索引，然后将它们通过我们的评估套件，看看哪种表现更好。&lt;/p&gt;

&lt;h2 id=&quot;我们最初押注于技能&quot;&gt;我们最初押注于技能&lt;/h2&gt;

&lt;p&gt;技能似乎是正确的抽象。你将框架文档打包成一个技能，智能体在处理 Next.js 任务时调用它，然后你就能得到正确的代码。职责分离清晰，上下文开销最小，智能体只加载它需要的内容。甚至在 skills.sh 上还有一个不断增长的现成技能目录。&lt;/p&gt;

&lt;p&gt;我们期望智能体遇到 Next.js 任务，调用技能，阅读版本匹配的文档，然后生成正确的代码。&lt;/p&gt;

&lt;p&gt;然后我们运行了评估。&lt;/p&gt;

&lt;h2 id=&quot;技能没有被可靠地触发&quot;&gt;技能没有被可靠地触发&lt;/h2&gt;

&lt;p&gt;在 56% 的评估案例中，技能从未被调用。智能体可以访问文档但没有使用它。添加技能相比基线没有产生任何改进：&lt;/p&gt;

&lt;div class=&quot;table-container&quot;&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;配置&lt;/th&gt;
      &lt;th&gt;通过率&lt;/th&gt;
      &lt;th&gt;相比基线&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;基线（无文档）&lt;/td&gt;
      &lt;td&gt;53%&lt;/td&gt;
      &lt;td&gt;—&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;技能（默认行为）&lt;/td&gt;
      &lt;td&gt;53%&lt;/td&gt;
      &lt;td&gt;+0pp&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;

&lt;p&gt;零改进。技能存在，智能体可以使用它，但智能体选择不使用。在详细的构建/Lint/测试细分中，技能在某些指标上实际上表现比基线更差（测试方面为 58% vs 63%），这表明环境中未使用的技能可能会引入噪声或干扰。&lt;/p&gt;

&lt;p&gt;这并非我们设置所独有。智能体不能可靠地使用可用工具是当前模型的一个已知局限性。&lt;/p&gt;

&lt;h2 id=&quot;明确指令有帮助但措辞很脆弱&quot;&gt;明确指令有帮助，但措辞很脆弱&lt;/h2&gt;

&lt;p&gt;我们尝试在 &lt;span class=&quot;codespan&quot;&gt;AGENTS.md&lt;/span&gt; 中添加明确指示，告诉智能体使用该技能。&lt;/p&gt;

&lt;div  class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Before writing code, first explore the project structure,
then invoke the nextjs-doc skill for documentation.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这提高了触发率到 95% 以上，并将通过率提升到 79%。&lt;/p&gt;

&lt;div class=&quot;table-container&quot;&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;配置&lt;/th&gt;
      &lt;th&gt;通过率&lt;/th&gt;
      &lt;th&gt;相比基线&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;基线（无文档）&lt;/td&gt;
      &lt;td&gt;53%&lt;/td&gt;
      &lt;td&gt;—&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;技能（默认行为）&lt;/td&gt;
      &lt;td&gt;53%&lt;/td&gt;
      &lt;td&gt;+0pp&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;带明确指令的技能&lt;/td&gt;
      &lt;td&gt;79%&lt;/td&gt;
      &lt;td&gt;+26pp&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;

&lt;p&gt;一个实质性的改进。但我们发现指令措辞影响智能体行为的方式有些出乎意料。&lt;/p&gt;

&lt;p&gt;不同的措辞产生了截然不同的结果：&lt;/p&gt;

&lt;div class=&quot;table-container&quot;&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;指令&lt;/th&gt;
      &lt;th&gt;行为&lt;/th&gt;
      &lt;th&gt;结果&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&quot;你必须调用技能&quot;&lt;/td&gt;
      &lt;td&gt;先阅读文档，以文档模式为锚点&lt;/td&gt;
      &lt;td&gt;错过项目上下文&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&quot;先探索项目，再调用技能&quot;&lt;/td&gt;
      &lt;td&gt;先构建心理模型，使用文档作为参考&lt;/td&gt;
      &lt;td&gt;更好的结果&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;

&lt;p&gt;相同的技能。相同的文档。基于微小的措辞变化产生不同的结果。&lt;/p&gt;

&lt;p&gt;在一个评估（&lt;span class=&quot;codespan&quot;&gt;&apos;use cache&apos;&lt;/span&gt; 指令测试）中，&quot;先调用&quot;的方法编写了正确的 &lt;span class=&quot;codespan&quot;&gt;page.tsx&lt;/span&gt;，但完全错过了所需的 &lt;span class=&quot;codespan&quot;&gt;next.config.ts&lt;/span&gt; 更改。&quot;先探索&quot;的方法两者都得到了。&lt;/p&gt;

&lt;p&gt;这种脆弱性让我们担心。如果微小的措辞调整会导致巨大的行为变化，这种方法在生产环境中感觉会很脆弱。&lt;/p&gt;

&lt;h2 id=&quot;构建我们可以信任的评估&quot;&gt;构建我们可以信任的评估&lt;/h2&gt;

&lt;p&gt;在得出结论之前，我们需要能够信任的评估。我们的初始测试套件存在模棱两可的提示、验证实现细节而非可观察行为的测试，以及关注模型训练数据中已有的 API。我们没有测量我们真正关心的东西。&lt;/p&gt;

&lt;p&gt;我们通过移除测试泄露、解决矛盾以及转向基于行为的断言来强化评估套件。最重要的是，我们添加了针对不在模型训练数据中的 Next.js 16 API 的测试。&lt;/p&gt;

&lt;p&gt;我们重点评估套件中的 API：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;span class=&quot;codespan&quot;&gt;connection()&lt;/span&gt; 用于动态渲染&lt;/li&gt;
  &lt;li&gt;&lt;span class=&quot;codespan&quot;&gt;&apos;use cache&apos;&lt;/span&gt; 指令&lt;/li&gt;
  &lt;li&gt;&lt;span class=&quot;codespan&quot;&gt;cacheLife()&lt;/span&gt; 和 &lt;span class=&quot;codespan&quot;&gt;cacheTag()&lt;/span&gt;&lt;/li&gt;
  &lt;li&gt;&lt;span class=&quot;codespan&quot;&gt;forbidden()&lt;/span&gt; 和 &lt;span class=&quot;codespan&quot;&gt;unauthorized()&lt;/span&gt;&lt;/li&gt;
  &lt;li&gt;&lt;span class=&quot;codespan&quot;&gt;proxy.ts&lt;/span&gt; 用于 API 代理&lt;/li&gt;
  &lt;li&gt;异步 &lt;span class=&quot;codespan&quot;&gt;cookies()&lt;/span&gt; 和 &lt;span class=&quot;codespan&quot;&gt;headers()&lt;/span&gt;&lt;/li&gt;
  &lt;li&gt;&lt;span class=&quot;codespan&quot;&gt;after()&lt;/span&gt;、&lt;span class=&quot;codespan&quot;&gt;updateTag()&lt;/span&gt;、&lt;span class=&quot;codespan&quot;&gt;refresh()&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;以下所有结果都来自这个强化的评估套件。每种配置都根据相同的测试进行判断，并通过重试排除模型差异。&lt;/p&gt;

&lt;h2 id=&quot;产生回报的直觉&quot;&gt;产生回报的直觉&lt;/h2&gt;

&lt;p&gt;如果我们完全移除决策会怎样？与其希望智能体调用技能，我们可以直接在 &lt;span class=&quot;codespan&quot;&gt;AGENTS.md&lt;/span&gt; 中嵌入文档索引。不是完整的文档，只是一个告诉智能体在哪里找到与你的项目 Next.js 版本匹配的特定文档文件的索引。然后智能体可以根据需要读取这些文件，无论你是使用最新版本还是维护旧项目，都能获得版本准确的信息。&lt;/p&gt;

&lt;p&gt;我们在注入的内容中添加了一个关键指令。&lt;/p&gt;

&lt;div  class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;IMPORTANT: Prefer retrieval-led reasoning over pre-training-led reasoning
for any Next.js tasks.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这告诉智能体查阅文档，而不是依赖可能过时的训练数据。&lt;/p&gt;

&lt;h2 id=&quot;结果让我们惊讶&quot;&gt;结果让我们惊讶&lt;/h2&gt;

&lt;p&gt;我们在所有四种配置上运行了强化评估套件：&lt;/p&gt;

&lt;p&gt;最终通过率：&lt;/p&gt;

&lt;div class=&quot;table-container&quot;&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;配置&lt;/th&gt;
      &lt;th&gt;通过率&lt;/th&gt;
      &lt;th&gt;相比基线&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;基线（无文档）&lt;/td&gt;
      &lt;td&gt;53%&lt;/td&gt;
      &lt;td&gt;—&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;技能（默认行为）&lt;/td&gt;
      &lt;td&gt;53%&lt;/td&gt;
      &lt;td&gt;+0pp&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;带明确指令的技能&lt;/td&gt;
      &lt;td&gt;79%&lt;/td&gt;
      &lt;td&gt;+26pp&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;span class=&quot;codespan&quot;&gt;AGENTS.md&lt;/span&gt; 文档索引&lt;/td&gt;
      &lt;td&gt;100%&lt;/td&gt;
      &lt;td&gt;+47pp&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;

&lt;p&gt;在详细细分中，&lt;span class=&quot;codespan&quot;&gt;AGENTS.md&lt;/span&gt; 在构建、Lint 和测试方面都获得了满分。&lt;/p&gt;

&lt;div class=&quot;table-container&quot;&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;配置&lt;/th&gt;
      &lt;th&gt;构建&lt;/th&gt;
      &lt;th&gt;Lint&lt;/th&gt;
      &lt;th&gt;测试&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;基线&lt;/td&gt;
      &lt;td&gt;84%&lt;/td&gt;
      &lt;td&gt;95%&lt;/td&gt;
      &lt;td&gt;63%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;技能（默认行为）&lt;/td&gt;
      &lt;td&gt;84%&lt;/td&gt;
      &lt;td&gt;89%&lt;/td&gt;
      &lt;td&gt;58%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;带明确指令的技能&lt;/td&gt;
      &lt;td&gt;95%&lt;/td&gt;
      &lt;td&gt;100%&lt;/td&gt;
      &lt;td&gt;84%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;span class=&quot;codespan&quot;&gt;AGENTS.md&lt;/span&gt;&lt;/td&gt;
      &lt;td&gt;100%&lt;/td&gt;
      &lt;td&gt;100%&lt;/td&gt;
      &lt;td&gt;100%&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;

&lt;p&gt;这并非我们所预期的。&quot;简单&quot;的方法（静态 Markdown 文件）表现优于更复杂的基于技能的检索，即使我们微调了技能触发器。&lt;/p&gt;

&lt;p&gt;为什么被动上下文能胜过主动检索？&lt;/p&gt;

&lt;p&gt;我们的工作理论归结为三个因素。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;没有决策点。 使用 &lt;span class=&quot;codespan&quot;&gt;AGENTS.md&lt;/span&gt;，智能体不需要决定&quot;我应该查找这个吗？&quot;的时刻。信息已经存在。&lt;/li&gt;
  &lt;li&gt;一致的可用性。 技能异步加载且仅在调用时加载。&lt;span class=&quot;codespan&quot;&gt;AGENTS.md&lt;/span&gt; 内容在每个回合的系统提示中都可用。&lt;/li&gt;
  &lt;li&gt;没有顺序问题。 技能创建排序决策（先读文档 vs 先探索项目）。被动上下文完全避免了这个问题。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;解决上下文膨胀的担忧&quot;&gt;解决上下文膨胀的担忧&lt;/h2&gt;

&lt;p&gt;在 &lt;span class=&quot;codespan&quot;&gt;AGENTS.md&lt;/span&gt; 中嵌入文档有膨胀上下文窗口的风险。我们通过压缩来解决这个问题。&lt;/p&gt;

&lt;p&gt;最初的文档注入大约 40KB。我们将其压缩到 8KB（减少了 80%），同时保持了 100% 的通过率。压缩格式使用管道分隔的结构，将文档索引打包到最小空间中：&lt;/p&gt;

&lt;div  class=&quot;language-markdown highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[Next.js Docs Index]|root: ./.next-docs
|IMPORTANT: Prefer retrieval-led reasoning over pre-training-led reasoning
|01-app/01-getting-started:{01-installation.mdx,02-project-structure.mdx,...}
|01-app/02-building-your-application/01-routing:{01-defining-routes.mdx,...}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;完整索引涵盖 Next.js 文档的每个部分。&lt;/p&gt;

&lt;p&gt;智能体知道在哪里可以找到文档，而无需在上下文中包含完整内容。当它需要特定信息时，它会从 &lt;span class=&quot;codespan&quot;&gt;.next-docs/&lt;/span&gt; 目录读取相关文件。&lt;/p&gt;

&lt;h2 id=&quot;亲自尝试&quot;&gt;亲自尝试&lt;/h2&gt;

&lt;p&gt;一条命令即可为你的 Next.js 项目设置：&lt;/p&gt;

&lt;p&gt;&lt;span class=&quot;codespan&quot;&gt;npx @next/codemod@canary agents-md&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;此功能是官方 &lt;span class=&quot;codespan&quot;&gt;@next/codemod&lt;/span&gt; 包的一部分。&lt;/p&gt;

&lt;p&gt;此命令执行三件事：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;检测你的 Next.js 版本&lt;/li&gt;
  &lt;li&gt;将匹配的文档下载到 &lt;span class=&quot;codespan&quot;&gt;.next-docs/&lt;/span&gt;&lt;/li&gt;
  &lt;li&gt;将压缩索引注入到你的 &lt;span class=&quot;codespan&quot;&gt;AGENTS.md&lt;/span&gt; 中&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;如果你使用支持 &lt;span class=&quot;codespan&quot;&gt;AGENTS.md&lt;/span&gt; 的智能体（如 Cursor 或其他工具），同样的方法也适用。&lt;/p&gt;

&lt;h2 id=&quot;这对框架作者意味着什么&quot;&gt;这对框架作者意味着什么&lt;/h2&gt;

&lt;p&gt;技能并非无用。&lt;span class=&quot;codespan&quot;&gt;AGENTS.md&lt;/span&gt; 方法在所有任务中提供智能体与 Next.js 协作的广泛、横向改进。技能更适合用户明确触发的垂直、特定于操作的工作流，如&quot;升级我的 Next.js 版本&quot;、&quot;迁移到 App Router&quot;或应用框架最佳实践。这两种方法相辅相成。&lt;/p&gt;

&lt;p&gt;也就是说，对于一般框架知识，被动上下文目前优于按需检索。如果你维护一个框架并希望编码智能体生成正确的代码，请考虑提供一个 &lt;span class=&quot;codespan&quot;&gt;AGENTS.md&lt;/span&gt; 片段供用户添加到他们的项目中。&lt;/p&gt;

&lt;p&gt;实用建议：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;不要等待技能改进。 随着模型在工具使用方面变得更好，差距可能会缩小，但结果现在就很重要。&lt;/li&gt;
  &lt;li&gt;积极压缩。 你不需要上下文中的完整文档。指向可检索文件的索引同样有效。&lt;/li&gt;
  &lt;li&gt;使用评估进行测试。 构建针对训练数据中不存在的 API 的评估。这是文档访问最重要的地方。&lt;/li&gt;
  &lt;li&gt;为检索而设计。 构建你的文档结构，以便智能体可以找到并读取特定文件，而不需要预先获取所有内容。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;目标是将智能体从预训练主导的推理转变为检索主导的推理。&lt;span class=&quot;codespan&quot;&gt;AGENTS.md&lt;/span&gt; 结果证明是实现这一目标的最可靠方法。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;研究和评估由 Jude Gao 完成。CLI 可通过 &lt;span class=&quot;codespan&quot;&gt;npx @next/codemod@canary agents-md&lt;/span&gt; 使用&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;原文链接：https://vercel.com/blog/agents-md-outperforms-skills-in-our-agent-evals&lt;/p&gt;
</description>
        <pubDate>Mon, 09 Feb 2026 06:43:00 +0000</pubDate>
        <link>https://wulfric.me/2026/02/agents-md-outperforms-skills-ch/</link>
        <guid isPermaLink="true">https://wulfric.me/2026/02/agents-md-outperforms-skills-ch/</guid>
        
        <category>ai</category>
        
        <category>agent</category>
        
        
        <category>技术</category>
        
      </item>
    
      <item>
        <title>翻译：Pi：OpenClaw 中的最小智能体</title>
        <description>&lt;p&gt;翻译自 Armin Ronacher 博客文章：&lt;a href=&quot;https://lucumr.pocoo.org/2026/1/31/pi/&quot;&gt;Pi: The Minimal Agent Within OpenClaw&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;如果你最近没有与世隔绝的话，你会注意到本周我的朋友 Peter 的一个项目在互联网上爆火了。它有很多名字，最近的名字是 &lt;a href=&quot;https://openclaw.ai/&quot;&gt;OpenClaw&lt;/a&gt;，但在新闻中你可能会看到它被称为 ClawdBot 或 MoltBot，具体取决于你什么时候读到它。它是一个连接到你选择的通信渠道的智能体，&lt;a href=&quot;https://lucumr.pocoo.org/2025/7/3/tools/&quot;&gt;只是运行代码&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;你可能不太熟悉的是，OpenClaw 底层其实是一个叫做 &lt;a href=&quot;https://github.com/badlogic/pi-mono/&quot;&gt;Pi&lt;/a&gt; 的小型编码智能体。而 Pi 恰好是我目前几乎专门使用的编码智能体。在过去几周里，我越来越成为这个小智能体的推销员。在我最近做了一次关于它的演讲后，我意识到我实际上还没有在这个博客上写过关于 Pi 的文章，所以我觉得应该提供一些背景，解释为什么我对它如此着迷，以及它与 OpenClaw 的关系。&lt;/p&gt;

&lt;p&gt;Pi 是由 &lt;a href=&quot;https://mariozechner.at/&quot;&gt;Mario Zechner&lt;/a&gt; 编写的，与 Peter 追求&quot;带有一丝疯狂的科幻感&quot;不同，Mario 非常务实。尽管方法不同，但 OpenClaw 和 Pi 都遵循同样的理念：LLM 非常擅长编写和运行代码，所以要充分利用这一点。在某种程度上，我认为这不是偶然的，因为是 Peter 让我和 Mario 在去年迷上了这个想法和智能体。&lt;/p&gt;

&lt;h2 id=&quot;什么是-pi&quot;&gt;什么是 Pi？&lt;/h2&gt;

&lt;p&gt;Pi 是一个编码智能体。市面上有很多编码智能体。实际上，我认为你现在基本上可以随便挑一个现成的，就能体验智能体编程是什么样的。在这个博客的评测中，我积极谈到过 AMP，我与 AMP 产生共鸣的原因之一是，它确实感觉像是一个由既沉迷于智能体编程、又尝试过几种不同方法以了解哪些有效的人构建的产品，而不仅仅是在它周围构建一个花哨的 UI。&lt;/p&gt;

&lt;p&gt;Pi 对我来说有趣主要有两个原因：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;首先，它有一个极小的核心。它拥有我所知道的最短的系统提示词，并且只有四个工具：Read（读取）、Write（写入）、Edit（编辑）、Bash（命令行）。&lt;/li&gt;
  &lt;li&gt;其次，它通过提供一个扩展系统来弥补其极小的核心，该系统还允许扩展将状态持久化到会话中，这非常强大。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;还有一个小福利：Pi 本身就像优秀的软件一样编写。它不会闪烁，不会消耗大量内存，不会随机崩溃，非常可靠，并且由一个非常关心软件质量的人编写。&lt;/p&gt;

&lt;p&gt;Pi 也是一系列小组件的集合，你可以在其上构建自己的智能体。OpenClaw 就是这样构建的，我自己的小 Telegram 机器人也是这样构建的，Mario 构建了他的 &lt;a href=&quot;https://github.com/badlogic/pi-mono/tree/main/packages/mom&quot;&gt;mom&lt;/a&gt;。如果你想构建自己的智能体，连接到某个地方，将 Pi 指向它自己和 mom，它就会为你创造一个。&lt;/p&gt;

&lt;h2 id=&quot;pi-中没有什么&quot;&gt;Pi 中没有什么&lt;/h2&gt;

&lt;p&gt;要理解 Pi 中有什么，更重要的是要理解 Pi 中没有什么、为什么没有，以及更重要的是：为什么不会有。最明显的遗漏是对 MCP 的支持。它没有 MCP 支持。虽然你可以为它构建一个扩展，但你也可以像 OpenClaw 那样做来支持 MCP，即使用 &lt;a href=&quot;https://github.com/steipete/mcporter&quot;&gt;mcporter&lt;/a&gt;。mcporter 通过 CLI 接口或 TypeScript 绑定暴露 MCP 调用，也许你的智能体可以用它做些什么。或者不能，我不知道 :)&lt;/p&gt;

&lt;p&gt;这不是一个懒惰的遗漏。这源于 Pi 工作原理的哲学。Pi 的整个理念是，如果你想让智能体做它还不能做的事情，你不需要去下载扩展或技能之类的东西。你让智能体扩展自己。它推崇编写和运行代码的理念。&lt;/p&gt;

&lt;p&gt;这并不是说你不能下载扩展。这是完全支持的。但与其一定要鼓励你下载别人的扩展，你也可以将你的智能体指向一个已经存在的扩展，比如说，按照你看到的那个东西构建它，但做出你喜欢的这些改变。&lt;/p&gt;

&lt;h2 id=&quot;为构建智能体的智能体而构建的智能体&quot;&gt;为构建智能体的智能体而构建的智能体&lt;/h2&gt;

&lt;p&gt;当你看 Pi 以及延伸的 OpenClaw 在做什么时，会发现这是一个像粘土一样可塑的软件示例。这对其底层架构设定了某些要求，这些要求在许多方面对系统设定了某些约束，这些约束确实需要融入核心设计中。&lt;/p&gt;

&lt;p&gt;例如，Pi 的底层 AI SDK 被设计成一个会话可以真正包含来自许多不同模型提供商的许多不同消息。它认识到会话在模型提供商之间的可移植性有些有限，因此它不会过度依赖任何无法转移到另一个提供商的特定模型提供商的功能集。&lt;/p&gt;

&lt;p&gt;其次，除了模型消息外，它还在会话文件中维护自定义消息，这些消息可以被扩展用来存储状态，或者被系统本身用来维护根本不发送给 AI 或只发送部分内容的信息。&lt;/p&gt;

&lt;p&gt;因为这个系统存在，扩展状态也可以持久化到磁盘，所以它内置了热重载功能，这样智能体就可以编写代码、重新加载、测试，然后循环，直到你的扩展实际上可以工作。它还附带了智能体本身可以用来扩展自己的文档和示例。更妙的是：Pi 中的会话是树状结构。你可以在会话内分支和导航，这开启了各种有趣的机会，比如启用工作流来进行旁支任务以修复损坏的智能体工具，而不会浪费主会话中的上下文。工具修复后，我可以将会话倒回到之前的状态，Pi 会总结在另一个分支上发生了什么。&lt;/p&gt;

&lt;p&gt;所有这些都很重要，因为例如，如果你考虑 MCP 的工作原理，在大多数模型提供商上，MCP 的工具，就像 LLM 的任何工具一样，需要在会话开始时加载到系统上下文或其工具部分中。这使得在不清除完整缓存或让 AI 对之前的调用如何以不同方式工作感到困惑的情况下，完全重新加载工具的功能变得非常困难或不可能。&lt;/p&gt;

&lt;h2 id=&quot;上下文之外的工具&quot;&gt;上下文之外的工具&lt;/h2&gt;

&lt;p&gt;Pi 中的扩展可以注册一个工具供 LLM 调用，我偶尔会发现这很有用。例如，尽管我对 Beads 的实现方式有批评，但我确实认为给智能体提供一个待办事项列表是非常有用的。我确实使用了一个在本地工作的智能体特定的问题跟踪器，这是我让智能体自己构建的。因为我希望智能体也能管理待办事项，在这种特殊情况下，我决定给它一个工具而不是 CLI。对于问题的范围来说，这感觉很合适，它目前是我加载到上下文中的唯一额外工具。&lt;/p&gt;

&lt;p&gt;但在大多数情况下，我添加到智能体的所有内容要么是技能，要么是 TUI 扩展，以使与智能体的工作对我来说更愉快。除了斜杠命令外，Pi 扩展还可以直接在终端中渲染自定义 TUI 组件：加载器、进度条、交互式文件选择器、数据表、预览窗格。TUI 足够灵活，Mario 证明了你可以&lt;a href=&quot;https://x.com/badlogicgames/status/2008702661093454039&quot;&gt;在其中运行 Doom&lt;/a&gt;。虽然不实用，但如果你可以运行 Doom，你当然可以构建一个有用的仪表板或调试界面。&lt;/p&gt;

&lt;p&gt;我想重点介绍一些我的扩展，让你了解可能性。虽然你可以不加修改地使用它们，但整个理念实际上是你将智能体指向其中一个，然后随心所欲地重新混合它。&lt;/p&gt;

&lt;h3 id=&quot;answer&quot;&gt;&lt;a href=&quot;https://github.com/mitsuhiko/agent-stuff/blob/main/pi-extensions/answer.ts&quot;&gt;&lt;span class=&quot;codespan&quot;&gt;/answer&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;我&lt;a href=&quot;https://lucumr.pocoo.org/2025/12/17/what-is-plan-mode/&quot;&gt;不使用计划模式&lt;/a&gt;。我鼓励智能体提问，并进行富有成效的来回交流。但我不喜欢如果你给智能体一个问题工具就会出现的结构化问题对话框。我更喜欢智能体的自然散文，其中穿插着解释和图表。&lt;/p&gt;

&lt;p&gt;问题是：内联回答问题会变得混乱。所以 &lt;span class=&quot;codespan&quot;&gt;/answer&lt;/span&gt; 读取智能体的最后一个响应，提取所有问题，并将它们重新格式化为一个漂亮的输入框。&lt;/p&gt;

&lt;p&gt;&lt;figure&gt;
          &lt;picture&gt;
            
            
            &lt;img src=&quot;/images/pi/pi-answer.png&quot; alt=&quot;/answer 扩展显示问题对话框&quot; width= height= /&gt;
          &lt;/picture&gt;
          &lt;figcaption&gt;&lt;i class=&apos;icon-pencil&apos;&gt;&lt;/i&gt;/answer 扩展显示问题对话框&lt;/figcaption&gt;
        &lt;/figure&gt;&lt;/p&gt;

&lt;h3 id=&quot;todos&quot;&gt;&lt;a href=&quot;https://github.com/mitsuhiko/agent-stuff/blob/main/pi-extensions/todos.ts&quot;&gt;&lt;span class=&quot;codespan&quot;&gt;/todos&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;尽管我批评 &lt;a href=&quot;https://github.com/steveyegge/beads&quot;&gt;Beads&lt;/a&gt; 的实现，但给智能体一个待办事项列表确实很有用。&lt;span class=&quot;codespan&quot;&gt;/todos&lt;/span&gt; 命令会显示存储在 &lt;span class=&quot;codespan&quot;&gt;.pi/todos&lt;/span&gt; 中的所有项目（作为 markdown 文件）。智能体和我都可以操作它们，会话可以认领任务以将其标记为进行中。&lt;/p&gt;

&lt;iframe width=&quot;100%&quot; style=&quot;aspect-ratio: 16/9&quot; src=&quot;https://www.youtube.com/embed/ZcKbzxziA5k&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;h3 id=&quot;review&quot;&gt;&lt;a href=&quot;https://github.com/mitsuhiko/agent-stuff/blob/main/pi-extensions/review.ts&quot;&gt;&lt;span class=&quot;codespan&quot;&gt;/review&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;随着越来越多的代码由智能体编写，在智能体先审查之前就将未完成的工作抛给人类几乎没有意义。因为 Pi 会话是树状结构，我可以分支到一个新的审查上下文，获得发现，然后将修复带回主会话。&lt;/p&gt;

&lt;p&gt;UI 是根据 Codex 建模的，它提供了易于审查的提交、差异、未提交的更改或远程 PR。提示词关注我关心的事情，所以我得到了我想要的提示（例如：我让它指出新添加的依赖项）。&lt;/p&gt;

&lt;p&gt;&lt;figure&gt;
          &lt;picture&gt;
            
            
            &lt;img src=&quot;/images/pi/pi-review.png&quot; alt=&quot;/review 扩展显示审查预设选项&quot; width= height= /&gt;
          &lt;/picture&gt;
          &lt;figcaption&gt;&lt;i class=&apos;icon-pencil&apos;&gt;&lt;/i&gt;/review 扩展显示审查预设选项&lt;/figcaption&gt;
        &lt;/figure&gt;&lt;/p&gt;

&lt;h3 id=&quot;control&quot;&gt;&lt;a href=&quot;https://github.com/mitsuhiko/agent-stuff/blob/main/pi-extensions/control.ts&quot;&gt;&lt;span class=&quot;codespan&quot;&gt;/control&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;一个我正在实验但不积极使用的扩展。它让一个 Pi 智能体向另一个发送提示。这是一个没有复杂编排的简单多智能体系统，对实验很有用。&lt;/p&gt;

&lt;h3 id=&quot;files&quot;&gt;&lt;a href=&quot;https://github.com/mitsuhiko/agent-stuff/blob/main/pi-extensions/files.ts&quot;&gt;&lt;span class=&quot;codespan&quot;&gt;/files&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;列出会话中更改或引用的所有文件。你可以在 Finder 中显示它们，在 VS Code 中对比差异，快速查看它们，或在提示中引用它们。&lt;span class=&quot;codespan&quot;&gt;shift+ctrl+r&lt;/span&gt; 快速查看最近提到的文件，当智能体生成 PDF 时这很方便。&lt;/p&gt;

&lt;p&gt;其他人也构建了扩展：&lt;a href=&quot;https://github.com/nicobailon/pi-subagents&quot;&gt;Nico 的子智能体扩展&lt;/a&gt;和 &lt;a href=&quot;https://www.npmjs.com/package/pi-interactive-shell&quot;&gt;interactive-shell&lt;/a&gt;，它让 Pi 可以在可观察的 TUI 覆盖层中自主运行交互式 CLI。&lt;/p&gt;

&lt;h2 id=&quot;软件构建软件&quot;&gt;软件构建软件&lt;/h2&gt;

&lt;p&gt;这些都只是你可以用智能体做什么的想法。重点主要是这些都不是我写的，它们是智能体根据我的规格创建的。我告诉 Pi 制作一个扩展，它就做了。没有 MCP，没有社区技能，什么都没有。不要误会我的意思，我使用了大量的技能。但它们是由我的 clanker 手工制作的，而不是从任何地方下载的。例如，我用一个&lt;a href=&quot;https://github.com/mitsuhiko/agent-stuff/blob/main/skills/web-browser/SKILL.md&quot;&gt;只使用 CDP 的技能&lt;/a&gt;完全取代了我所有用于浏览器自动化的 CLI 或 MCP。不是因为替代方案不起作用或不好，而是因为这样做简单自然。智能体维护自己的功能。&lt;/p&gt;

&lt;p&gt;我的智能体有&lt;a href=&quot;https://github.com/mitsuhiko/agent-stuff/tree/main/skills&quot;&gt;相当多的技能&lt;/a&gt;，关键是如果我不需要它们，我会扔掉技能。例如，我给了它一个技能来阅读其他工程师分享的 Pi 会话，这有助于代码审查。或者我有一个技能来帮助智能体制作我想要的提交消息和提交行为，以及如何更新变更日志。这些最初是斜杠命令，但我目前正在将它们迁移到技能，看看这是否同样有效。我还有一个技能，希望能帮助 Pi 使用 &lt;span class=&quot;codespan&quot;&gt;uv&lt;/span&gt; 而不是 &lt;span class=&quot;codespan&quot;&gt;pip&lt;/span&gt;，但我也添加了一个自定义扩展来拦截对 &lt;span class=&quot;codespan&quot;&gt;pip&lt;/span&gt; 和 &lt;span class=&quot;codespan&quot;&gt;python&lt;/span&gt; 的调用，并将它们重定向到 &lt;span class=&quot;codespan&quot;&gt;uv&lt;/span&gt;。&lt;/p&gt;

&lt;p&gt;使用像 Pi 这样的最小智能体给我的部分吸引力在于，它让你体验使用构建更多软件的软件的想法。极端情况是当你移除 UI 和输出，并将其连接到你的聊天时。这就是 OpenClaw 所做的，鉴于其巨大的增长，我真的越来越觉得这将以某种方式成为我们的未来。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;原文链接：https://lucumr.pocoo.org/2026/1/31/pi/&lt;/p&gt;

&lt;p&gt;相关资源：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Syntax 播客第 976 集：与 Armin Ronacher 和 Mario Zechner 讨论 Pi&lt;/li&gt;
  &lt;li&gt;OpenClaw 项目：https://openclaw.ai/&lt;/li&gt;
  &lt;li&gt;Pi GitHub 仓库：https://github.com/badlogic/pi-mono/&lt;/li&gt;
  &lt;li&gt;作者的扩展示例：https://github.com/mitsuhiko/agent-stuff/&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;译者注：这篇文章展示了在 AI 智能体领域中一种反潮流的设计哲学——通过极简主义和自我扩展能力，而非功能堆砌，来实现真正的灵活性和可靠性。Pi 的核心理念是&quot;让智能体编写代码来扩展自己&quot;，这种方法将软件开发推向了一个新的范式：软件构建软件。&lt;/p&gt;
</description>
        <pubDate>Sat, 31 Jan 2026 00:00:00 +0000</pubDate>
        <link>https://wulfric.me/2026/01/pi-ch/</link>
        <guid isPermaLink="true">https://wulfric.me/2026/01/pi-ch/</guid>
        
        <category>ai</category>
        
        <category>agent</category>
        
        <category>coding</category>
        
        
        <category>技术</category>
        
      </item>
    
      <item>
        <title>一个简单的通过 sql 查询 prometheus 的转换器实现</title>
        <description>&lt;h2 id=&quot;背景&quot;&gt;背景&lt;/h2&gt;

&lt;p&gt;可观测的一个重要的趋势是链路、日志、指标的统一存储和使用。大模型时代，在业务层实现链路、日志、指标的统一查询语言将是一个重要的过渡方案。本文尝试实现一个简单的通过 sql 查询 prometheus 指标的工具。&lt;/p&gt;

&lt;h2 id=&quot;实现步骤&quot;&gt;实现步骤&lt;/h2&gt;

&lt;p&gt;经调研，我决定基于 &lt;a href=&quot;https://calcite.apache.org/&quot;&gt;Apache Calcite&lt;/a&gt;，实现 sql 解析并对接真实的 promql 查询终端。&lt;/p&gt;

&lt;p&gt;Apache Calcite 是一个优秀的动态数据管理框架，提供了如：SQL 解析、SQL 校验、SQL 查询优化、SQL 生成以及数据连接查询等典型数据库管理功能&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;。&lt;/p&gt;

&lt;p&gt;在Calcite中，查询处理分为逻辑计划和物理计划两个层次，这种分离使得优化更加灵活和模块化。逻辑计划描述了查询要做什么，而不关心具体如何执行。它是对SQL语句的抽象表示，专注于查询的语义而非执行细节。物理计划描述了查询如何执行，包含了具体的算法选择和执行策略。它是逻辑计划的具体实现，考虑了底层存储系统的特性和约束。&lt;/p&gt;

&lt;p&gt;我的思路是，利用calcite自带的 sql 解析和逻辑计划分析能力，手动解析最终的物理计划逻辑，并翻译为 prom ql 和实际的查询动作。&lt;/p&gt;

&lt;h3 id=&quot;效果预期&quot;&gt;效果预期&lt;/h3&gt;

&lt;div  class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;`app_name`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;`timestamp`&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;`value`&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prometheus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;system_metrics_cpu_util&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;my_app&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hostname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;host1&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;`timestamp`&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1696118400000&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;`timestamp`&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1696204800000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;应翻译为 prometheus 查询&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-promql&quot;&gt;system_metrics_cpu_util{app_name = &apos;my_app&apos;, hostname = &apos;host1&apos;}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;并查询出 JSON/CSV 数据，示例如下&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-csv&quot;&gt;app_name, timestamp, value
my_app, 1696118460000, 0.01
my_app, 1696118520000, 0.02
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;查询的方式是标准的 jdbc 连接和查询语法。如下图所示&lt;/p&gt;

&lt;div  class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;Properties&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;info&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setProperty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;CalciteConnectionProperty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;LEX&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;camelName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Lex&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;MYSQL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 获取连接&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;Connection&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;connection&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DriverManager&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getConnection&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;jdbc:calcite:&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 获取Calcite连接&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;CalciteConnection&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;calciteConnection&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;CalciteConnection&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 获取根模式&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;SchemaPlus&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rootSchema&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;calciteConnection&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getRootSchema&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 注册Prometheus Schema&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dstStr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&quot;&quot;
                      [
          {
            &quot;tableName&quot;: &quot;system_metrics_cpu_util&quot;,
            &quot;fields&quot;: [
              {&quot;fieldName&quot;: &quot;timestamp&quot;, &quot;fieldType&quot;: &quot;BIGINT&quot;},
              {&quot;fieldName&quot;: &quot;app_name&quot;, &quot;fieldType&quot;: &quot;VARCHAR&quot;},
              {&quot;fieldName&quot;: &quot;hostname&quot;, &quot;fieldType&quot;: &quot;VARCHAR&quot;},
              {&quot;fieldName&quot;: &quot;value&quot;, &quot;fieldType&quot;: &quot;DOUBLE&quot;}
            ]
          }
        ]
        &quot;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;DataSourceTable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dataSourceTables&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;parseArray&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dstStr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DataSourceTable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;Sql2PrometheusSchema&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;promSchema&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Sql2PrometheusSchema&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dataSourceTables&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;rootSchema&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;prometheus&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;promSchema&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 创建语句对象&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;Statement&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;statement&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;createStatement&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 执行查询语句&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sql&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;select `timestamp` , `value` from prometheus.system_metrics_cpu_util where app_name = &apos;my_app&apos; and hostname = &apos;host1&apos; and `timestamp` &amp;gt;= 1696118400000 and `timestamp` &amp;lt;= 1696204800000&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;ResultSet&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;statement&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;executeQuery&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sql&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;();&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 转换 rs 为最终结果&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;ResultSetMetaData&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;metaData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getMetaData&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;columnCount&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;metaData&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getColumnCount&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;设计并实现一个-schema&quot;&gt;设计并实现一个 schema&lt;/h3&gt;

&lt;p&gt;从上面代码的使用可以看到，我们需要先实现一个 Sql2PrometheusSchema。calcite 里的 schema 和 Postgres 的schema的概念比较像。在PostgreSQL中，schema是一个逻辑容器，用于组织和管理数据库中的对象（如表、视图、函数、索引等）。它类似于一个命名空间，可以将相关的数据库对象分组在一起。在 MySQL中，类似的概念是 database。&lt;/p&gt;

&lt;div  class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Sql2PrometheusSchema&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AbstractSchema&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Table&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tableMap&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Sql2PrometheusSchema&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;DataSourceTable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dataSourceTables&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;enableQuery&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;isNull&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dataSourceTables&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IllegalArgumentException&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;dataSourceTables can not be null&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;DataSourceTable&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dataSourceTable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dataSourceTables&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tableName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dataSourceTable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getTableName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;tableMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tableName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Sql2PrometheusTable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tableName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dataSourceTable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getFields&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;enableQuery&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Table&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getTableMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tableMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;注意到我们在初始化 Sql2PrometheusSchema 的时候，传入了表的定义 &lt;span class=&quot;codespan&quot;&gt;List&amp;lt;DataSourceTable&amp;gt; dataSourceTables&lt;/span&gt;，表的定义对应不同的 promtheus 的指标和 label，一个 promtheus 指标对应一张表，每个指标的所有 label 即为表的字段。每个表还有额外的2个字段：&lt;span class=&quot;codespan&quot;&gt;timestamp&lt;/span&gt; 和 &lt;span class=&quot;codespan&quot;&gt;value&lt;/span&gt;。&lt;/p&gt;

&lt;div  class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DataSourceTable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tableName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;java&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;util&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;DataSourceTableField&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fields&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DataSourceTableField&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fieldName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fieldType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// e.g., &quot;STRING&quot;, &quot;DOUBLE&quot;, &quot;LONG&quot;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;metricField&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 是否为指标字段&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TimeRange&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;设计并实现一个-table&quot;&gt;设计并实现一个 table&lt;/h3&gt;

&lt;p&gt;&lt;span class=&quot;codespan&quot;&gt;Sql2PrometheusTable&lt;/span&gt; 是该方案的核心。table 的scan方法就是执行解析后的物理计划的地方。只需要解析物理计划，并翻译成 prometheus 的查询语句，然后将查询动作包装成一个Enumerator即可。&lt;/p&gt;

&lt;div  class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Sql2PrometheusTable&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AbstractTable&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FilterableTable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;TIMESTAMP_FIELD&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;timestamp&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@Getter&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;metricTable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;DataSourceTableField&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fields&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;enableQuery&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fieldIndexMap&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;indexFieldMap&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@Getter&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;active&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@Getter&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@Getter&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;activeQuery&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Sql2PrometheusTable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;metricTable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;DataSourceTableField&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fields&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;enableQuery&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;metricTable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;metricTable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;fields&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fields&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;enableQuery&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;enableQuery&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fields&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;fieldIndexMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fields&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getFieldName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;indexFieldMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fields&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getFieldName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;


    &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Enumerable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;scan&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;DataContext&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;RexNode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filters&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;active&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;JavaTypeFactory&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;typeFactory&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getTypeFactory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AbstractEnumerable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Enumerator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;enumerator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// 在这里处理过滤逻辑&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;// 具体实现取决于filters中的条件&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Sql2PrometheusEnumerator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;buildMetricsQueryRequest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;typeFactory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filters&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fieldIndexMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;


    &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RelDataType&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getRowType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;RelDataTypeFactory&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;relDataTypeFactory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 这里应该返回 table 的 schema&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;RelDataTypeFactory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Builder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;relDataTypeFactory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;DataSourceTableField&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;field&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fields&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;SqlTypeName&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fieldType&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SqlTypeName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getFieldType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;Objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;requireNonNull&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fieldType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;fieldType is null&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getFieldName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fieldType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MetricsQueryRequest&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;buildMetricsQueryRequest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;JavaTypeFactory&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;typeFactory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;RexNode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filters&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;StringBuilder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;labelSelectors&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;StringBuilder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;metricTable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;RexNode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filtersRemoveTs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;TimeRange&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timeRanges&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;RexNode&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filters&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;TimestampVisitor&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timestampVisitor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TimestampVisitor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fieldIndexMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;TIMESTAMP_FIELD&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;typeFactory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;filtersRemoveTs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;accept&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timestampVisitor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;timeRanges&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timestampVisitor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getTimeRange&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timeRanges&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;isEmpty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IllegalArgumentException&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Time range is required&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;TimeRange&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timeRange&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timeRanges&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getFirst&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;RexNode&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filtersRemoveTs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;PromQLConverterVisitor&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visitor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PromQLConverterVisitor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;typeFactory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;indexFieldMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;labelSelector&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;accept&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;visitor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;labelSelector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;isEmpty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;labelSelectors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;labelSelector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;labelSelectors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;lastIndexOf&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;,&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;labelSelectors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;labelSelectors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;labelSelectors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;labelSelectors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;labelSelectors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;}&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;query&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;labelSelectors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MetricsQueryRequest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timeRange&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timeRange&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;enableQuery&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;包装成enumerator&quot;&gt;包装成Enumerator&lt;/h3&gt;

&lt;p&gt;上面的scan方法返回的是一个Enumerator：&lt;span class=&quot;codespan&quot;&gt;Sql2PrometheusEnumerator&lt;/span&gt;。其入参即 prometheus 的 &lt;a href=&quot;https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries&quot;&gt;query range&lt;/a&gt; 参数。&lt;/p&gt;

&lt;div  class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MetricsQueryRequest&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在Enumerator里把 prom 查询包装起来，并提供Enumerator接口。&lt;/p&gt;

&lt;div  class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Sql2PrometheusEnumerator&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Enumerator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Iterator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;iterator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fieldIndexMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;currentElement&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// 用于存储当前元素的成员变量&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Sql2PrometheusEnumerator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;MetricsQueryRequest&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;metricsQueryRequest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fieldIndexMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;fieldIndexMap&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fieldIndexMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;metricsQueryRequest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getStart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;metricsQueryRequest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getEnd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;metricsQueryRequest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getQuery&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MetricsQueryRequest: &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, start: &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, end: &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// todo 执行 prom 查询，并将查询结果转换为 Iterator&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;iterator&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fetchData&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;metricsQueryRequest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;iterator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;currentElement&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NoSuchElementException&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;No current element, call moveNext() first.&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;currentElement&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;moveNext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iterator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;hasNext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;currentElement&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;iterator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;currentElement&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// 当没有更多元素时，清除当前元素&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;reset&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;iterator&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;convertData&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;iterator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;currentElement&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// 重置时也应清除当前元素&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;解析物理计划&quot;&gt;解析物理计划&lt;/h2&gt;

&lt;p&gt;介绍完实现步骤，我们再来重点看下物理计划的解析。物理计划解析的入口是&lt;span class=&quot;codespan&quot;&gt;Sql2PrometheusTable&lt;/span&gt; 的buildMetricsQueryRequest方法。&lt;/p&gt;

&lt;h3 id=&quot;时间戳提取&quot;&gt;时间戳提取&lt;/h3&gt;

&lt;p&gt;sql 里是包含时间的过滤条件的，但是 prometheus 的查询语法里，时间戳范围和 query 是分开的。因此，我们的第一步是从物理计划里把时间戳相关的查询提取出来。即&lt;span class=&quot;codespan&quot;&gt;TimestampVisitor&lt;/span&gt;。&lt;/p&gt;

&lt;div  class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TimestampVisitor&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RexShuttle&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;cm&quot;&gt;/**
     * 时间戳列的索引
     */&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tsIndex&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RexBuilder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rexBuilder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Long&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Long&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TimestampVisitor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tsIndex&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;JavaTypeFactory&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;typeFactory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Pass true to visit each node only once&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;tsIndex&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tsIndex&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;rexBuilder&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RexBuilder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;typeFactory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RexNode&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;visitCall&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;RexCall&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isTimestampFilter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;RexNode&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;right&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getOperands&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;setTimeRange&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getOperator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;RexLiteral&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rexBuilder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;makeLiteral&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;visitCall&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;isTimestampFilter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;RexCall&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getOperands&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 检查是否包含时间戳列&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;RexNode&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;left&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getOperands&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getFirst&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;left&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;instanceof&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RexInputRef&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ref&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tsIndex&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ref&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getIndex&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;setTimeRange&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SqlOperator&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sqlOperator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RexLiteral&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;literal&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;SqlKind&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kind&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sqlOperator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getKind&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SqlKind&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;GREATER_THAN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SqlKind&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;GREATER_THAN_OR_EQUAL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// ts &amp;gt;, ts &amp;gt;=&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;greater than&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Optional&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;ofNullable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;BigDecimal&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;literal&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getValue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;BigDecimal:&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;longValue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;orElse&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SqlKind&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;LESS_THAN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SqlKind&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;LESS_THAN_OR_EQUAL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// ts &amp;lt;, ts &amp;lt;=&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;less than&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Optional&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;ofNullable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;BigDecimal&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;literal&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getValue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;BigDecimal:&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;longValue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;orElse&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SqlKind&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;SEARCH&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// ts between&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;literal&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getValue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;instanceof&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Sarg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;?&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sarg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;sarg: {}&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sarg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;nc&quot;&gt;Range&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;BigDecimal&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;range&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Range&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;BigDecimal&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sarg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;rangeSet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;span&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;lowerEndpoint&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;longValue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;upperEndpoint&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;longValue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TimeRange&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getTimeRange&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// start 不能是 null，但是 end 可以是 null&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;Objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;requireNonNull&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;start is null&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;isNull&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getTime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TimeRange&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;label查询条件拼接&quot;&gt;label查询条件拼接&lt;/h3&gt;

&lt;p&gt;第二步，是将除了时间戳之外的 where 查询条件拼接为 prometheus 查询的 label 条件。支持的 where 条件包含 =、!=、like、not like，分别对应 prometheus 查询的 =、!=、=~、!~。&lt;/p&gt;

&lt;div  class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PromQLConverterVisitor&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RexVisitorImpl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;indexFieldMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;PromQLConverterVisitor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;JavaTypeFactory&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;typeFactory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;indexFieldMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;indexFieldMap&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;indexFieldMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;visitCall&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;RexCall&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;SqlOperator&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getOperator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SqlStdOperatorTable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;AND&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;handleAndCondition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SqlStdOperatorTable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;OR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;handleOrCondition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;handleComparisonOperator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;visitInputRef&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;RexInputRef&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inputRef&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;indexFieldMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inputRef&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getIndex&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;visitLiteral&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;RexLiteral&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;literal&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;literal&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;isAlwaysTrue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;literal&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getValue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;instanceof&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Sarg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;handleSargLiteral&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;literal&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;literal&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getValue2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;null&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;visitNode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;RexNode&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;instanceof&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RexCall&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;visitCall&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;RexCall&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;instanceof&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RexInputRef&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;visitInputRef&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;RexInputRef&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;instanceof&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RexLiteral&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;visitLiteral&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;RexLiteral&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;handleAndCondition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;RexCall&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;conditions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;RexNode&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;operand&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getOperands&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;condition&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visitNode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;operand&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;condition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;isEmpty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;conditions&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;condition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;,&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;conditions&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;handleOrCondition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;RexCall&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;orGroups&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;RexNode&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;operand&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getOperands&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;branchConditions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extractBranchConditions&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;operand&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;branchConditions&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;isEmpty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;orGroups&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;branchConditions&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;combineOrConditions&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;orGroups&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;extractBranchConditions&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;RexNode&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;conditions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;instanceof&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RexCall&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getOperator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SqlStdOperatorTable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;AND&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;RexNode&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;operand&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getOperands&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;condition&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visitNode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;operand&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;condition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;isEmpty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;conditions&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;condition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;condition&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visitNode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;condition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;isEmpty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;conditions&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;condition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;conditions&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;handleComparisonOperator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;RexCall&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getOperands&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;columnName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visitNode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getOperands&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;visitNode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getOperands&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;convertOperator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getOperator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Strings&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;isNullOrEmpty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;columnName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Strings&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;isNullOrEmpty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Strings&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;isNullOrEmpty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;operator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;%s%s\&quot;%s\&quot;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;columnName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;operator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;escapeValue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;combineOrConditions&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;orGroups&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;labelValueMap&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;();&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;group&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;orGroups&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;condition&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;condition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;=&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;label&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;];&lt;/span&gt;
                    &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;replaceAll&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;\&quot;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;labelValueMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;computeIfAbsent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HashSet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;()).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;combined&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Entry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;labelValueMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;entrySet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;label&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getKey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;values&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getValue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;combined&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;%s=\&quot;%s\&quot;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;iterator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()));&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;combined&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;%s=~\&quot;%s\&quot;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;|&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)));&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;,&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;combined&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;convertOperator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;SqlOperator&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;operator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SqlStdOperatorTable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;EQUALS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;=&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SqlStdOperatorTable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;NOT_EQUALS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;!=&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SqlStdOperatorTable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;LIKE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SqlStdOperatorTable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;SEARCH&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;=~&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SqlStdOperatorTable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;NOT_LIKE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;!~&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;escapeValue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;\&quot;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;\\\&quot;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;handleSargLiteral&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;RexLiteral&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;literal&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;Sarg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;NlsString&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sarg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Sarg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;NlsString&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;literal&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getValue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;Objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;requireNonNull&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sarg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Sarg value is null&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 处理范围类型&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;handleSargPoints&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sarg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;handleSargPoints&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Sarg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;NlsString&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sarg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;points&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ArrayList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;();&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Range&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;NlsString&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;range&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sarg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;rangeSet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;asRanges&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;RangeSets&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;isPoint&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;points&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;lowerEndpoint&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getValue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Invalid range: {}&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;points&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;%s&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;points&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getFirst&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 多个点使用正则表达式匹配&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;%s&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;|&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;points&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;formatValue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;null&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;instanceof&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;\&quot;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;escapeValue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;\&quot;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;至此，我们实现了一个简易的通过 sql 查询 prometheus 数据的工具。执行的打印日志为：&lt;/p&gt;

&lt;div  class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;MetricsQueryRequest: system_metrics_cpu_util{app_name=&quot;my_app&quot;,hostname=&quot;host1&quot;}, start: 1696118400000, end: 1696204800000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;https://strongduanmu.com/blog/apache-calcite-learning-materials.html 挺不错的系列文章 &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Tue, 14 Oct 2025 14:00:00 +0000</pubDate>
        <link>https://wulfric.me/2025/10/sql-to-prom/</link>
        <guid isPermaLink="true">https://wulfric.me/2025/10/sql-to-prom/</guid>
        
        <category>prometheus</category>
        
        <category>sql</category>
        
        <category>calcite</category>
        
        
        <category>技术</category>
        
      </item>
    
      <item>
        <title>翻译：大语言模型是 zero-shot 的时间序列预测器</title>
        <description>&lt;p&gt;论文原文：Large Language Models Are Zero-Shot Time Series Forecasters&lt;/p&gt;

&lt;h2 id=&quot;摘要&quot;&gt;摘要&lt;/h2&gt;

&lt;p&gt;通过将时间序列编码为一串数字，我们可以将时间序列预测视为文本中的下一个标记预测。在开发这种方法时，我们发现大型语言模型（如GPT-3和LLaMA-2）可以令人惊讶地在零样本外推时间序列，其性能可与或超过在下游任务上训练的专用时间序列模型的性能相媲美。为了提高性能，我们提出了一种有效地对时间序列数据进行标记化和将离散分布转换为高度灵活的连续值密度的方法。我们认为，语言模型在处理时间序列方面的成功源于其能够自然地表示多模态分布，结合了简单性和重复性的偏好，这与许多时间序列的显著特征（如重复的季节性趋势）相吻合。我们还展示了语言模型如何通过非数值文本自然地处理缺失数据，适应文本侧信息，并回答问题以帮助解释预测结果。 尽管我们发现增加模型大小通常会提高时间序列的性能，但我们发现由于数字的分词方式以及不良的不确定性校准，GPT-4的表现可能会比GPT-3差，这很可能是由于诸如RLHF等对齐干预的结果。&lt;/p&gt;

&lt;h2 id=&quot;1-引言&quot;&gt;1. 引言&lt;/h2&gt;

&lt;p&gt;尽管与文本、音频或视频等其他序列建模问题相似，时间序列具有两个特别具有挑战性的属性。与通常具有一致的输入比例和采样率的视频或音频不同，聚合的时间序列数据集通常包含来自完全不同来源的序列，有时还会存在缺失值。此外，时间序列预测的常见应用，如天气或金融数据，需要从包含极少可能信息的观测中进行外推，使得准确的点预测几乎不可能，因此不确定性估计尤为重要。虽然大规模预训练已成为训练视觉和文本中的大型神经网络的关键要素，使性能能够直接与数据可用性相匹配，但在时间序列建模中通常不使用预训练，因为缺乏共识的无监督目标，并且大规模、连贯的预训练数据集不容易获取。因此，在流行的基准测试中，简单的时间序列方法（例如ARIMA（Box和Jenkins，1968）和线性模型（Zeng等，2022））通常优于深度学习方法（Hewamalage等）。[20], 2023).&lt;/p&gt;

&lt;p&gt;在本文中，我们展示了大语言模型（LLM）如何自然地弥合传统方法的简单偏见与现代深度学习的复杂表示学习和生成能力之间的差距。特别地，我们引入了一种非常简单的方法，LLMTime。&lt;/p&gt;

&lt;p&gt;为了解决连续时间序列预测问题，我们提出了一种使用预训练LLMs的方法，如图1所示。在核心部分，该方法将时间序列表示为一串数字，并将时间序列预测视为文本中的下一个标记的预测，从而利用了强大的预训练模型和概率能力，例如似然度评估和采样。为了实现强大的性能，我们提出了两种技术：（1）将时间序列有效地编码为一串数字，（2）将LLMs的离散分布调整为能够建模复杂多模态分布的连续密度。使用这些技术，我们发现LLMTime可以在多种不同问题上超越或与专门设计的时间序列方法相匹配，而且无需对其他模型使用的下游数据进行任何微调即可实现。&lt;/p&gt;

&lt;p&gt;LLMTime的zero-shot特性具有几个自然优势：(1) 它便于直接应用LLMs，消除了需要专门了解微调程序和大量计算资源的必要性，同时避免了访问专有源代码或LLM训练或微调的API的问题；(2) 它自然适用于数据有限的场景，其中训练或微调的信息很少；(3) 通过利用广泛预训练的LLMs的广泛模式外推能力，它避免了通常需要耗费大量时间、精力和领域专业知识来构建专用时间序列模型的问题。&lt;/p&gt;

&lt;p&gt;为了理解LLMTime出色的性能起源，我们研究了LLMs对简单或重复序列的偏好（Goldblum等人，2023），并表明这些偏好实际上与时间序列的显著结构（如季节性）是相容的。除了这些偏好之外，LLMs还可以自然地适应缺失数据，并表达多模态分布，这对于时间序列尤其有用。我们还展示了LLMs如何实现吸引人的功能，例如通过提示提供额外的辅助信息，并查询LLM以解释其预测。&lt;/p&gt;

&lt;p&gt;最后，除了具有广泛的预测性能之外，我们发现性能往往随着规模的增大而提高，而点预测的质量也随着不确定性表示的质量而提高。然而，我们还发现GPT-4的不确定性校准比GPT-3差，这可能是由于人类反馈强化学习等干预措施所致。&lt;/p&gt;

&lt;p&gt;&lt;figure&gt;
          &lt;picture&gt;
            
            
            &lt;img src=&quot;https://ar5iv.labs.arxiv.org/html/2310.07820/assets/x1.png&quot; alt=&quot;图1：我们提出了LLMTime，一种使用大型语言模型（LLMs）进行时间序列预测的方法，通过将数字编码为文本，并通过文本补全来采样可能的外推结果。LLMTime可以在没有对目标数据集进行任何训练的情况下（即零-shot）胜过许多流行的时间序列方法。LLMTime的性能也随着基础模型的能力而扩展。值得注意的是，经过对齐的模型（例如RLHF）不遵循这种扩展趋势。例如，GPT-4的性能不如GPT-3（第6节）。&quot; width= height= /&gt;
          &lt;/picture&gt;
          &lt;figcaption&gt;&lt;i class=&apos;icon-pencil&apos;&gt;&lt;/i&gt;图1：我们提出了LLMTime，一种使用大型语言模型（LLMs）进行时间序列预测的方法，通过将数字编码为文本，并通过文本补全来采样可能的外推结果。LLMTime可以在没有对目标数据集进行任何训练的情况下（即零-shot）胜过许多流行的时间序列方法。LLMTime的性能也随着基础模型的能力而扩展。值得注意的是，经过对齐的模型（例如RLHF）不遵循这种扩展趋势。例如，GPT-4的性能不如GPT-3（第6节）。&lt;/figcaption&gt;
        &lt;/figure&gt;&lt;/p&gt;

&lt;h2 id=&quot;2-背景&quot;&gt;2. 背景&lt;/h2&gt;

&lt;p&gt;语言模型：语言模型是在一系列序列  $\mathcal{U} = {U_1, U_2, \dots, U_i, \dots, U_N}$ 上进行训练的，其中 $U_i = (u_1,u_2,\dots,u_j,\dots,u_{n_i})$ ，每个token $u_i$ 属于一个词汇表 $\mathcal{V}$。大型语言模型通常编码为自回归分布，其中每个token的概率仅依赖于序列中的前面的tokens $p_\theta\left( U_i\right) = \prod_{j=1}^{n_i} p_\theta\left(u_j \mid u_{0:j-1}\right)$。参数 $\theta$ 通过最大化整个数据集 ${p_\theta(\mathcal{U}) = \prod_{i=1}^N p_\theta(U_i)}$ 的概率来学习。每个语言模型都有一个关联的分词器，它将输入字符串分解为一系列 tokens，每个token属于 𝒱 。正确的分词非常重要，细节的微小变化可能会产生意想不到的显著影响。自回归语言模型最常见的分词方法是字节对编码（BPE），它将输入视为位串，并根据在训练语料库中出现的频率分配token，以便平均生成较短的token序列。从语言模型中进行采样通常从一个提示 $u_{0:k}$，并使用 $p_\theta\left(u_j \mid u_{0:j-1}\right)$进行预处理，例如通过温度缩放或核心采样（Holtzman等，2019）。&lt;/p&gt;

&lt;p&gt;大语言模型：Brown 等人（2020）表明，增加语言模型的参数数量和训练数据大小会带来新的能力，例如零样本泛化，即模型可以在没有对任何特定任务的数据进行训练的情况下执行文本格式的任务。大型语言模型，例如 GPT-3（Brown 等人，2020）或 LLaMA-2（Touvron 等人，2023b），通过上下文学习实现这种泛化，它通过识别语言模型的提示中的模式，并通过下一个token的预测进行推断。许多作者推测，上下文学习是由语言模型对输入数据进行广泛压缩而产生的（Goldblum 等人，2023；Sutskever，2023；Delétang 等人，2023）。压缩有利于使用编程抽象在输入数据上操作的学习算法，例如无上下文文法（Allen-Zhu 和 Li，2023）或归纳头（Olsson 等人，2022），它们可以实现复制粘贴类型的操作，以生成具有高度结构化语法的样本。 在这项工作中，我们展示了LLMs的zero-shot泛化能力以及它们对可压缩模式的偏好不仅限于语言理解，还可以用于时间序列预测。&lt;/p&gt;

&lt;p&gt;零样本泛化使得LLMs作为助手变得更加有用，人们借此创建了一些方法，用于将LLMs与人类的偏好和指令进行对齐，例如从人类反馈中进行强化学习（RLHF）（Ouyang等，2022）和指令调整（Wei等，2021）。虽然对于现代LLMs产品至关重要，但对齐方法也会显著影响底层模型的能力和校准（OpenAI，2023；Bubeck等，2023）。在这里，我们展示了这些方法也会影响预测能力。&lt;/p&gt;

&lt;p&gt;时间序列数据：时间序列数据通常与语言建模数据具有相同的形式，都是一系列的序列 $U_i = (u_1,u_2,\dots,u_j,\dots,u_{n_i})$ ，但在时间序列中 $u_j$ 是数值型的。由于语言模型的构建是为了表示复杂的序列概率分布，因此从理论上讲，它们非常适合用于时间序列建模。然而，在实践中，语言模型受到了对数字进行分词的细节的限制。BPE（字节对编码）根据在训练数据中出现的频率压缩数字，因此数字可能被分解成令人尴尬的片段，这使得学习基本数值运算变得具有挑战性。因此，Touvron等人（2023a）设计了LLaMA分词器，将数字映射为单个digits，这可以显著提高数学能力，小型LLaMA模型的性能超过了GPT-4（Liu和Low，2023）。&lt;/p&gt;

&lt;p&gt;将语言模型应用于时间序列数据的另一个挑战是适当的评估。通常使用的是均方绝对误差（MAE），但它忽略了预测中的不确定性，这对于随机数据限制很大（Hewamalage等，2023; Benton等，2022）。连续排名概率分数（CRPS）捕捉了分布特性，并且可以比较生成样本而不是似然的模型。对于单个预测，CRPS分数定义为针对估计的累积分布函数（CDF）的$\hat{F}$ ，其中$
    \text{CRPS}(\hat{F},y) = \int_{\mathbb{R}} \left(\hat{F}(z) - \mathbb{I}_{(z - y) &amp;gt; 0}\right)^2 dz,
$ 是通过采样预测产生的经验CDF， $\hat{F}(z)$ 是指示函数。虽然CRPS相对于MAE是一种改进，但它也忽略了数据中的关键结构，例如时间步之间的相关性。幸运的是，语言模型可以为完整的时间序列数据分配似然，我们展示了如何对LLM的离散似然进行小的修改，从而得到一个对模型比较有用的连续密度。&lt;/p&gt;

&lt;p&gt;时间序列的语言模型：一些作者已经探索使用预训练的语言模型编码器作为时间序列模型的初始化工具。例如，Zhou等人（2023年）提出了FPT，它对BERT编码器进行微调以进行时间序列预测。同样，Zhang等人（2023年）引入了Meta-Transformer，这是一个用于对非文本模态（包括时间序列）进行语言模型微调的框架。少数几篇论文探索了在没有微调的情况下使用LLMs作为预测器的情况。我们所知道的唯一方法是PromptCast（Xue和Salim，2023年），它将预测视为带提示的问答过程。&lt;/p&gt;

&lt;p&gt;我们的工作：与利用LLM骨干的方法不同，我们的方法完全是zero-shot的，不需要微调。与PromptCast不同，我们展示了如果我们仔细预处理数值本身，LLMs可以直接用作预测器，而不需要任何额外的文本或提示工程。我们的方法仅仅依赖于LLM在一般序列中外推模式的能力，而不依赖于英语或任何其他语言的特定内容。超越以往的工作，我们还培养了大型语言模型的概率性质以及它们捕捉高度随机时间序列的不确定性的能力。&lt;/p&gt;

&lt;h2 id=&quot;3-llmtime使用语言模型进行预测&quot;&gt;3. LLMTime：使用语言模型进行预测&lt;/h2&gt;

&lt;p&gt;使用大语言模型进行预测相对来说步骤较少。一旦将数值处理成字符串，使用语言模型进行预测就遵循标准的采样过程。然而，正如我们接下来所展示的，正确的预处理并不总是直观的，但却非常重要，而处理不当可能导致无法使用的预测结果。&lt;/p&gt;

&lt;p&gt;Tokenization：Tokenization特别重要，因为它直接影响到 tokenized 序列中模式的形成方式以及语言模型可以学习的操作类型。不幸的是，常见的tokenization方法如BPE往往会将一个数字(number)分成不与digits对齐的token，这可能会使算术运算变得更加困难（Liu和Low，2023）。例如，数字 42235630 在GPT-3的标记器中被标记为 [422,35,630] ，即使只改变一个数字，也可能导致完全不同的tokenization结果。相比之下，在许多新的开源LLMs（例如LLaMA（Touvron等，2023a））中，默认情况下将数字标记为单个数字。为了解决GPT模型的标记化问题，我们使用空格将数字的每个数字分开，以强制对每个数字进行单独的tokenization，并使用逗号（&quot;，&quot;）将时间序列中的每个时间步骤分开。由于在给定固定精度的情况下，小数点是多余的，我们在编码中去掉它们以节省上下文长度。因此，例如，在具有 2 位精度的情况下，我们在将时间序列输入标记器之前进行以下预处理：&lt;/p&gt;

\[0.123, 1.23, 12.3, 123.0 \rightarrow \text{&quot; 1 2 , 1 2 3 , 1 2 3 0 , 1 2 3 0 0&quot;}.\]

&lt;p&gt;在图2中，我们展示了这种编码方式的额外空格对于GPT模型是有帮助的，可以防止模型在采样过程中输出异常的标记而偏离正常情况。对于LLaMA模型来说，由于其对数字的独特分词方式，额外的空格则产生了相反的效果。每个数字和空格已经被分配了自己的标记，空格标记成为了干扰输入，增加了序列长度但没有简化序列的结构，可能使得序列对于模型来说是超出分布范围的。&lt;/p&gt;

&lt;p&gt;&lt;figure&gt;
          &lt;picture&gt;
            
            
            &lt;img src=&quot;https://ar5iv.labs.arxiv.org/html/2310.07820/assets/x2.png&quot; alt=&quot;&quot; width= height= /&gt;
          &lt;/picture&gt;
          
        &lt;/figure&gt;&lt;/p&gt;

&lt;p&gt;重新缩放：为了避免在输入非常大的情况下浪费标记，我们将值缩小，使得重新缩放的时间序列值的 $\alpha$ 百分位为 1 。我们避免使用最大值进行缩放，以便LLM可以看到一些数字位数发生变化的示例（ $1-\alpha$），并在其输出中重现此行为以产生比其所见过的更大的值。我们还尝试使用基于输入数据的百分位数计算的偏移量 $\beta$ ，并通过验证对数似然度来调整这两个参数（详见附录A）。&lt;/p&gt;

&lt;p&gt;采样/预测：为了进行预测，从LLM中抽取多个样本（例如20个），并使用每个时间步骤的样本统计数据构建一个点估计（例如中位数）或概率预测（例如分位数）。为了控制采样，我们使用温度缩放、logit偏差和核心采样（附录C）。&lt;/p&gt;

&lt;p&gt;连续似然性：对于建模数字序列来说，除了生成良好的样本之外，还有其他好处。在基数为 $B$ 的情况下，小数点后的 $n$ 位数字序列对应于 $B^n$ 个可能的区间（图3），每个区间的宽度为 $B^{-n}$ 。由于每个分布 \(p(u_j \mid u_{0:j-1}; \theta)\) 都是可能数字的softmax函数，我们可以将每个单独数字的分布视为分层softmax（Mnih和Hinton，2008），其中 \(p(u_1, ..., u_n)= p(u_n \mid u_{n-1}, ..., u_0) \, p(u_1 \mid u_0) \, p(u_0)\) 。尽管语言模型的概率分布是离散的，但我们可以通过在每个区间中放置均匀分布来轻松地将其调整为连续密度。用索引 $k \in \mathbb{N}$ 枚举模型可以产生的所有可数无穷数字(因为模型可以在小数点前输出任意数量的数字),每个概率为 $p_k$，我们可以将分布写成不相交均匀分布在区间$p(x) = \sum_{k \in \mathbb{N}} p_k U_k(x)$上的混合，其中 $U_k(x) = B^n\mathbb{I}_{x\in [B^{-n}k,B^{-n}(k+1))}$。因此,如果给定的数据点在区间 $k$ 中，其连续对数似然度为 $log p(x)=\log p_k + n\log B$。最后,为了获得原始输入空间中的似然度$log p(z)$，我们添加一个变量变换因子 \(log \vert\tfrac{dx}{dz}\vert,\) 其中 $z \mapsto x=s(z)$ 是预处理中的缩放操作。因此，我们的构造方式使得可以使用离散的数字标记来表示灵活且高分辨率的连续分布，尽管存在指数级数量的区间和指数级小的区间宽度，但其效率令人惊讶地高。&lt;/p&gt;

&lt;p&gt;语言模型作为灵活的分布：语言模型可以表达数值的灵活分布，这个事实对于时间序列数据来说至关重要。不确定性量化对于预测至关重要，而表示时间序列中的不确定性的典型方法可能会受到错误规定的限制。例如，创建概率预测的常见方法之一是拟合高斯或拉普拉斯观测模型。当底层数据分布是多峰的时候，这两个模型都会表现不佳。高斯混合模型（GMMs）等方法可以解决多峰性问题，但会引入优化和模型选择方面的额外挑战。我们通过在各种一维分布上训练一个小型自回归模型来展示语言模型是一个被低估的解决方案，如图3（右侧）所示。这些分布来自指数随机变量、均匀分布和学生t分布的混合，以及基于MonthlyMilk数据集上ARIMA模型的时间序列预测残差的重尾分布（Herzen等人，2022）。 [20] 我们通过计算Wasserstein距离来定量评估这些拟合结果，并与拉普拉斯观测模型、使用期望最大化训练的高斯混合模型以及对数据进行平坦分箱的逻辑回归进行比较（使用调整后的区间大小）。每个模型仅使用分布中的 200 个样本进行训练。结果表明，十进制自回归语言模型（&quot;Decimal AR&quot;）表现非常出色，能够处理非对称、多峰和重尾分布，这些是时间序列数据的典型多样性类型之一。&lt;/p&gt;

&lt;p&gt;&lt;figure&gt;
          &lt;picture&gt;
            
            
            &lt;img src=&quot;https://arxiv.org/html/2310.07820v3/x3.png&quot; alt=&quot;图3：（左）自回归模型在数字序列上的作用类似于对应数字的分层softmax分布。当与每个离散桶中的均匀分布相结合时，字符串的分布可以成为连续域上的表达性分布&quot; width= height= /&gt;
          &lt;/picture&gt;
          &lt;figcaption&gt;&lt;i class=&apos;icon-pencil&apos;&gt;&lt;/i&gt;图3：（左）自回归模型在数字序列上的作用类似于对应数字的分层softmax分布。当与每个离散桶中的均匀分布相结合时，字符串的分布可以成为连续域上的表达性分布&lt;/figcaption&gt;
        &lt;/figure&gt;&lt;/p&gt;

&lt;p&gt;&lt;figure&gt;
          &lt;picture&gt;
            
            
            &lt;img src=&quot;https://arxiv.org/html/2310.07820v3/x4.png&quot; alt=&quot;图3（右）：使用简单的自回归模型（例如RNN）在数字的字符串表示上进行训练，我们可以拟合复杂的分布，这对于其他方法来说可能具有挑战性，例如重尾或多模态分布。简单的自回归模型可以与密度估计的知名方法（如高斯混合模型（GMM）或具有固定分辨率的分箱）相匹配或胜过，衡量标准是样本之间的Wasserstein距离。&quot; width= height= /&gt;
          &lt;/picture&gt;
          &lt;figcaption&gt;&lt;i class=&apos;icon-pencil&apos;&gt;&lt;/i&gt;图3（右）：使用简单的自回归模型（例如RNN）在数字的字符串表示上进行训练，我们可以拟合复杂的分布，这对于其他方法来说可能具有挑战性，例如重尾或多模态分布。简单的自回归模型可以与密度估计的知名方法（如高斯混合模型（GMM）或具有固定分辨率的分箱）相匹配或胜过，衡量标准是样本之间的Wasserstein距离。&lt;/figcaption&gt;
        &lt;/figure&gt;&lt;/p&gt;

&lt;h2 id=&quot;7-讨论&quot;&gt;7. 讨论&lt;/h2&gt;

&lt;p&gt;我们已经证明，大语言模型可以通过将数值编码为文本的方式用作预训练的时间序列预测器。与其他&quot;基础&quot;模型一样，预训练赋予了模型对可泛化模式的有用偏置，这些偏置原本需要通过架构设计来工程化地嵌入模型中[21]，并且能够随着基础预训练模型的改进自然地扩展性能。由于LLM预测器是在语言上训练的，它们还具备非常规的能力，比如问答功能。更广泛地说，将时间序列预测框架化为自然语言生成可以被视为在单一大型强大模型中统一更多能力的又一步，在这种模型中，理解可以在多个任务和模态之间共享。&lt;/p&gt;

&lt;p&gt;此外，零样本预测能够在不需要大量计算资源、领域专业知识或大量下游训练数据点的情况下实现广泛令人信服的性能。&lt;/p&gt;

&lt;p&gt;虽然LLM预测器受益于预训练transformer的优势，但它们也继承了其弱点，包括有限的上下文窗口。虽然许多单变量时间序列问题可以舒适地适应日益增大的上下文窗口，但多变量问题构成了更大的挑战。最近在将LLM上下文窗口扩展到10-100K个token方面有了几项进展[36, 4, 5, 1]。将这些进展与时间序列预测相结合是未来研究的一个特别令人兴奋的方向。&lt;/p&gt;

&lt;p&gt;使用当前LLM架构的另一个潜在挑战可能是它们在算术以及执行递归和组合操作方面的弱点，这可能对特别具有挑战性的时间序列构成限制。另一方面，许多时间序列并不需要精确的算术。理解这种情况的程度，以及放宽这一限制，也是未来研究的一个有前景的方向。&lt;/p&gt;

&lt;p&gt;除了任何限制之外，研究在时间序列上微调LLM的有效程序也将是很有前景的。我们希望将LLM研究与时间序列预测相结合能为两个社区带来益处。&lt;/p&gt;

</description>
        <pubDate>Sat, 11 Oct 2025 15:27:00 +0000</pubDate>
        <link>https://wulfric.me/2025/10/llm-zeroshot-ts-forecaster/</link>
        <guid isPermaLink="true">https://wulfric.me/2025/10/llm-zeroshot-ts-forecaster/</guid>
        
        <category>llm</category>
        
        <category>forecast</category>
        
        <category>time series</category>
        
        
        <category>技术</category>
        
      </item>
    
      <item>
        <title>翻译：统计、机器学习和神经预测方法</title>
        <description>&lt;p&gt;&lt;a href=&quot;https://nixtlaverse.nixtla.io/statsforecast/docs/tutorials/statisticalneuralmethods.html&quot;&gt;原文地址&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;简介&quot;&gt;简介&lt;/h2&gt;

&lt;p&gt;在本教程中，我们将通过利用最适合每个时间序列的模型来探索在M5数据集上进行预测的过程。我们将通过一种称为交叉验证的基本技术来实现这一目标。这种方法有助于我们估计模型的预测性能，并选择为每个时间序列提供最佳性能的模型。&lt;/p&gt;

&lt;p&gt;M5数据集包含了沃尔玛五年的分层销售数据。目标是预测未来28天的日销售额。该数据集按照美国的50个州进行了细分，每个州有10家商店。&lt;/p&gt;

&lt;p&gt;在时间序列预测和分析领域中，较为复杂的任务之一是确定最适合特定系列的模型。很多时候，这个选择过程很大程度上依赖直觉，但这可能并不一定与我们数据集的实证现实相一致。&lt;/p&gt;

&lt;p&gt;在本教程中，我们旨在为M5基准数据集中不同系列的模型选择提供更加结构化、数据驱动的方法。这个数据集在预测领域中非常著名，它能够展示我们方法的多样性和强大性。&lt;/p&gt;

&lt;p&gt;我们将从各种预测范式中训练一系列模型：&lt;/p&gt;

&lt;p&gt;(1) StatsForecast&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;基准模型：这些模型简单但通常非常有效，可以为预测问题提供初始视角。我们将在此类别中使用 SeasonalNaive 和 HistoricAverage 模型。&lt;/li&gt;
  &lt;li&gt;间歇模型：对于具有零散、不连续需求的序列，我们将使用 CrostonOptimized 、 IMAPA 和 ADIDA 等模型。这些模型特别适用于处理零膨胀序列。&lt;/li&gt;
  &lt;li&gt;状态空间模型：这些是使用系统的数学描述进行预测的统计模型。statsforecast库中的 AutoETS 模型属于这个类别。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(2) MLForecast&lt;/p&gt;

&lt;p&gt;机器学习：利用 LightGBM 、 XGBoost 和 LinearRegression 等ML模型可以有优势，因为它们能够揭示数据中的复杂模式。我们将使用MLForecast库来实现这个目的。&lt;/p&gt;

&lt;p&gt;(3) NeuralForecast&lt;/p&gt;

&lt;p&gt;深度学习：DL模型，如Transformers（ AutoTFT ）和神经网络（ AutoNHITS ），使我们能够处理时间序列数据中的复杂非线性依赖关系。我们将利用NeuralForecast库来使用这些模型。&lt;/p&gt;

&lt;p&gt;使用Nixtla套件库，我们将能够通过数据驱动我们的模型选择过程，确保我们在数据集中针对特定系列使用最合适的模型。&lt;/p&gt;

&lt;p&gt;大纲：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;读取数据：在这个初始步骤中，我们将数据集加载到内存中，以便后续的分析和预测。在这个阶段，了解数据集的结构和细微差别非常重要。&lt;/li&gt;
  &lt;li&gt;使用统计和深度学习方法进行预测：我们应用了从基本统计技术到先进的深度学习模型的广泛预测方法。目标是基于我们的数据集生成未来28天的预测。&lt;/li&gt;
  &lt;li&gt;不同窗口上的模型性能评估：我们评估我们的模型在不同窗口上的性能。&lt;/li&gt;
  &lt;li&gt;选择一组序列的最佳模型：使用性能评估，我们为每组序列确定最优模型。这一步骤确保所选择的模型适应每个组的独特特征。&lt;/li&gt;
  &lt;li&gt;筛选最佳预测结果：最后，我们通过筛选我们选择的模型生成的预测结果，得到最有希望的预测。这是我们的最终输出，代表了根据我们的模型得出的每个系列的最佳可能预测。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;此教程最初是在一个 c5d.24xlarge EC2 实例上执行的。&lt;/p&gt;

&lt;h2 id=&quot;准备工作&quot;&gt;准备工作&lt;/h2&gt;

&lt;h3 id=&quot;安装库&quot;&gt;安装库&lt;/h3&gt;

&lt;pre&gt;&lt;code class=&quot;language-jupyter&quot;&gt;!pip install statsforecast mlforecast neuralforecast datasetforecast s3fs pyarrow
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;下载并准备数据&quot;&gt;下载并准备数据&lt;/h3&gt;

&lt;p&gt;该示例使用M5数据集。它由 30,490 个底部时间序列组成。&lt;/p&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pandas&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pd&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Load the training target dataset from the provided URL
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y_df&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read_parquet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;https://m5-benchmarks.s3.amazonaws.com/data/train/target.parquet&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Rename columns to match the Nixtlaverse&apos;s expectations
# The &apos;item_id&apos; becomes &apos;unique_id&apos; representing the unique identifier of the time series
# The &apos;timestamp&apos; becomes &apos;ds&apos; representing the time stamp of the data points
# The &apos;demand&apos; becomes &apos;y&apos; representing the target variable we want to forecast
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y_df&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Y_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;rename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;columns&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;item_id&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;unique_id&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ds&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;demand&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Convert the &apos;ds&apos; column to datetime format to ensure proper handling of date-related operations in subsequent steps
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ds&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_datetime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ds&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;为简单起见，我们将只保留一个类别&lt;/p&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Y_df&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Y_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;unique_id.str.startswith(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;FOODS_3&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;reset_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;drop&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;Y_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;unique_id&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Y_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;unique_id&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;astype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;基本绘图&quot;&gt;基本绘图&lt;/h3&gt;

&lt;p&gt;使用 StatsForecast 类的plot方法绘制一些系列。该方法从数据集中打印出8个随机序列，对基本的探索性数据分析很有用。&lt;/p&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;statsforecast&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StatsForecast&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Feature: plot random series for EDA
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StatsForecast&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;plot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Feature: plot groups of series for EDA
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StatsForecast&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;plot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unique_ids&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;FOODS_3_432_TX_2&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Feature: plot groups of series for EDA
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StatsForecast&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;plot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unique_ids&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;FOODS_3_432_TX_2&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;engine&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;matplotlib&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;figure&gt;
          &lt;picture&gt;
            
            
            &lt;img src=&quot;https://mintcdn.com/nixtla/Oro4FJl3t6oNJW6V/statsforecast/docs/tutorials/StatisticalNeuralMethods_files/figure-markdown_strict/cell-9-output-1.png?fit=max&amp;amp;auto=format&amp;amp;n=Oro4FJl3t6oNJW6V&amp;amp;q=85&amp;amp;s=bfafc0b13bcf8793218f2637b53d2836&quot; alt=&quot;&quot; width= height= /&gt;
          &lt;/picture&gt;
          
        &lt;/figure&gt;&lt;/p&gt;

&lt;h2 id=&quot;使用统计机器学习和神经方法创建预测&quot;&gt;使用统计、机器学习和神经方法创建预测&lt;/h2&gt;

&lt;h3 id=&quot;statsforecast&quot;&gt;StatsForecast&lt;/h3&gt;
&lt;p&gt;StatsForecast 是一个综合性的库，提供了一套流行的单变量时间序列预测模型，所有模型都专注于高性能和可扩展性。&lt;/p&gt;

&lt;p&gt;以下是使StatsForecast成为时间序列预测强大工具的原因：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;本地模型集合：StatsForecast提供了多样化的本地模型，可以分别应用于每个时间序列，使我们能够捕捉到每个序列中的独特模式。&lt;/li&gt;
  &lt;li&gt;简洁性：使用StatsForecast，训练、预测和回测多个模型变得简单直接，只需要几行代码。这种简洁性使其成为初学者和经验丰富的从业者的便利工具。&lt;/li&gt;
  &lt;li&gt;针对速度进行优化：StatsForecast中的模型实现经过速度优化，确保大规模计算高效执行，从而减少模型训练和预测的总时间。&lt;/li&gt;
  &lt;li&gt;水平可扩展性：StatsForecast的一个显著特点是其水平扩展能力。它与Spark、Dask和Ray等分布式计算框架兼容。该特性使其能够通过在集群中的多个节点上分布计算来处理大型数据集，使其成为大规模时间序列预测任务的首选解决方案。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;StatsForecast 每次接收到要拟合的模型列表。由于我们处理的是每日数据，使用7作为季节性将是有益的。&lt;/p&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Import necessary models from the statsforecast library
&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;statsforecast.models&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# SeasonalNaive: A model that uses the previous season&apos;s data as the forecast
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;SeasonalNaive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Naive: A simple model that uses the last observed value as the forecast
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;Naive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# HistoricAverage: This model uses the average of all historical data as the forecast
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;HistoricAverage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# CrostonOptimized: A model specifically designed for intermittent demand forecasting
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;CrostonOptimized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# ADIDA: Adaptive combination of Intermittent Demand Approaches, a model designed for intermittent demand
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;ADIDA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# IMAPA: Intermittent Multiplicative AutoRegressive Average, a model for intermittent series that incorporates autocorrelation
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;IMAPA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# AutoETS: Automated Exponential Smoothing model that automatically selects the best Exponential Smoothing model based on AIC
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;AutoETS&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我们通过使用以下参数实例化一个新的StatsForecast对象来拟合模型：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;models ：模型列表。从模型中选择您想要的模型并导入。&lt;/li&gt;
  &lt;li&gt;freq ：一个表示数据频率的字符串。（请参阅pandas提供的&lt;a href=&quot;https://pandas.pydata.org/docs/user_guide/timeseries.html#offset-aliases&quot;&gt;可用频率&lt;/a&gt;）&lt;/li&gt;
  &lt;li&gt;n_jobs ：int，用于并行处理的作业数量，使用-1表示使用所有核心。&lt;/li&gt;
  &lt;li&gt;fallback_model ：如果模型失败，将使用该模型。任何设置都传递给构造函数。然后调用其fit方法并传入历史数据框。&lt;/li&gt;
&lt;/ul&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;horizon&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;28&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;SeasonalNaive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;season_length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;Naive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;HistoricAverage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;CrostonOptimized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;ADIDA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;IMAPA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;AutoETS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;season_length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Instantiate the StatsForecast class
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;StatsForecast&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# A list of models to be used for forecasting
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;freq&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;D&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# The frequency of the time series data (in this case, &apos;D&apos; stands for daily frequency)
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;n_jobs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# The number of CPU cores to use for parallel execution (-1 means use all available cores)
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;预测方法接受两个参数：预测下一个h（时间段）和水平。&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;h （int）：表示未来预测的h步。在这种情况下，是提前12个月。&lt;/li&gt;
  &lt;li&gt;level （浮点数列表）：此可选参数用于概率预测。设置预测区间的水平（或置信度百分位）。例如，level=[90] 表示模型预计真实值在该区间内的概率为90%。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这里的预测对象是一个新的 data frame，其中包括一个带有模型名称和y hat值的列，以及用于表示不确定性区间的列。&lt;/p&gt;

&lt;p&gt;这段代码计算运行StatsForecast类的预测函数所需的时间，该函数预测未来的28天（h=28）。级别设置为[90]，表示将计算90%的预测区间。时间以分钟计算，并在最后打印出来。&lt;/p&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Get the current time before forecasting starts, this will be used to measure the execution time
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;init&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Call the forecast method of the StatsForecast instance to predict the next 28 days (h=28) 
# Level is set to [90], which means that it will compute the 90% prediction interval
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fcst_df&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;forecast&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;28&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;90&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Get the current time after the forecasting ends
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Calculate and print the total time taken for the forecasting in minutes
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Forecast Minutes: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;fcst_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;head&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;table-container&quot;&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;unique_id&lt;/th&gt;
      &lt;th&gt;ds&lt;/th&gt;
      &lt;th&gt;SeasonalNaive&lt;/th&gt;
      &lt;th&gt;SeasonalNaive-lo-90&lt;/th&gt;
      &lt;th&gt;SeasonalNaive-hi-90&lt;/th&gt;
      &lt;th&gt;Naive&lt;/th&gt;
      &lt;th&gt;Naive-lo-90&lt;/th&gt;
      &lt;th&gt;Naive-hi-90&lt;/th&gt;
      &lt;th&gt;HistoricAverage&lt;/th&gt;
      &lt;th&gt;HistoricAverage-lo-90&lt;/th&gt;
      &lt;th&gt;HistoricAverage-hi-90&lt;/th&gt;
      &lt;th&gt;CrostonOptimized&lt;/th&gt;
      &lt;th&gt;ADIDA&lt;/th&gt;
      &lt;th&gt;IMAPA&lt;/th&gt;
      &lt;th&gt;AutoETS&lt;/th&gt;
      &lt;th&gt;AutoETS-lo-90&lt;/th&gt;
      &lt;th&gt;AutoETS-hi-90&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;FOODS_3_001_CA_1&lt;/td&gt;
      &lt;td&gt;2016-05-23&lt;/td&gt;
      &lt;td&gt;1.0&lt;/td&gt;
      &lt;td&gt;-2.847174&lt;/td&gt;
      &lt;td&gt;4.847174&lt;/td&gt;
      &lt;td&gt;2.0&lt;/td&gt;
      &lt;td&gt;0.098363&lt;/td&gt;
      &lt;td&gt;3.901637&lt;/td&gt;
      &lt;td&gt;0.448738&lt;/td&gt;
      &lt;td&gt;-1.009579&lt;/td&gt;
      &lt;td&gt;1.907055&lt;/td&gt;
      &lt;td&gt;0.345192&lt;/td&gt;
      &lt;td&gt;0.345477&lt;/td&gt;
      &lt;td&gt;0.347249&lt;/td&gt;
      &lt;td&gt;0.381414&lt;/td&gt;
      &lt;td&gt;-1.028122&lt;/td&gt;
      &lt;td&gt;1.790950&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;FOODS_3_001_CA_1&lt;/td&gt;
      &lt;td&gt;2016-05-24&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;-3.847174&lt;/td&gt;
      &lt;td&gt;3.847174&lt;/td&gt;
      &lt;td&gt;2.0&lt;/td&gt;
      &lt;td&gt;-0.689321&lt;/td&gt;
      &lt;td&gt;4.689321&lt;/td&gt;
      &lt;td&gt;0.448738&lt;/td&gt;
      &lt;td&gt;-1.009579&lt;/td&gt;
      &lt;td&gt;1.907055&lt;/td&gt;
      &lt;td&gt;0.345192&lt;/td&gt;
      &lt;td&gt;0.345477&lt;/td&gt;
      &lt;td&gt;0.347249&lt;/td&gt;
      &lt;td&gt;0.286933&lt;/td&gt;
      &lt;td&gt;-1.124136&lt;/td&gt;
      &lt;td&gt;1.698003&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;FOODS_3_001_CA_1&lt;/td&gt;
      &lt;td&gt;2016-05-25&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;-3.847174&lt;/td&gt;
      &lt;td&gt;3.847174&lt;/td&gt;
      &lt;td&gt;2.0&lt;/td&gt;
      &lt;td&gt;-1.293732&lt;/td&gt;
      &lt;td&gt;5.293732&lt;/td&gt;
      &lt;td&gt;0.448738&lt;/td&gt;
      &lt;td&gt;-1.009579&lt;/td&gt;
      &lt;td&gt;1.907055&lt;/td&gt;
      &lt;td&gt;0.345192&lt;/td&gt;
      &lt;td&gt;0.345477&lt;/td&gt;
      &lt;td&gt;0.347249&lt;/td&gt;
      &lt;td&gt;0.334987&lt;/td&gt;
      &lt;td&gt;-1.077614&lt;/td&gt;
      &lt;td&gt;1.747588&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;FOODS_3_001_CA_1&lt;/td&gt;
      &lt;td&gt;2016-05-26&lt;/td&gt;
      &lt;td&gt;1.0&lt;/td&gt;
      &lt;td&gt;-2.847174&lt;/td&gt;
      &lt;td&gt;4.847174&lt;/td&gt;
      &lt;td&gt;2.0&lt;/td&gt;
      &lt;td&gt;-1.803274&lt;/td&gt;
      &lt;td&gt;5.803274&lt;/td&gt;
      &lt;td&gt;0.448738&lt;/td&gt;
      &lt;td&gt;-1.009579&lt;/td&gt;
      &lt;td&gt;1.907055&lt;/td&gt;
      &lt;td&gt;0.345192&lt;/td&gt;
      &lt;td&gt;0.345477&lt;/td&gt;
      &lt;td&gt;0.347249&lt;/td&gt;
      &lt;td&gt;0.186851&lt;/td&gt;
      &lt;td&gt;-1.227280&lt;/td&gt;
      &lt;td&gt;1.600982&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;FOODS_3_001_CA_1&lt;/td&gt;
      &lt;td&gt;2016-05-27&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;-3.847174&lt;/td&gt;
      &lt;td&gt;3.847174&lt;/td&gt;
      &lt;td&gt;2.0&lt;/td&gt;
      &lt;td&gt;-2.252190&lt;/td&gt;
      &lt;td&gt;6.252190&lt;/td&gt;
      &lt;td&gt;0.448738&lt;/td&gt;
      &lt;td&gt;-1.009579&lt;/td&gt;
      &lt;td&gt;1.907055&lt;/td&gt;
      &lt;td&gt;0.345192&lt;/td&gt;
      &lt;td&gt;0.345477&lt;/td&gt;
      &lt;td&gt;0.347249&lt;/td&gt;
      &lt;td&gt;0.308112&lt;/td&gt;
      &lt;td&gt;-1.107548&lt;/td&gt;
      &lt;td&gt;1.723771&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;

&lt;h3 id=&quot;mlforecast&quot;&gt;MLForecast&lt;/h3&gt;

&lt;p&gt;MLForecast 是一个强大的库，为时间序列预测提供自动化特征创建功能，便于使用全局机器学习模型。它专为高性能和可扩展性而设计。&lt;/p&gt;

&lt;p&gt;MLForecast的主要特点包括：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;支持sklearn模型：MLForecast与遵循scikit-learn API的模型兼容。这使其具有高度的灵活性，并能与各种机器学习算法无缝集成。&lt;/li&gt;
  &lt;li&gt;简洁性：使用MLForecast，训练、预测和回测模型的任务可以仅需几行代码完成。这种简化的简洁性使其对各级专业人士都非常友好。&lt;/li&gt;
  &lt;li&gt;针对速度进行优化：MLForecast被设计为能够快速执行任务，这在处理大型数据集和复杂模型时至关重要。&lt;/li&gt;
  &lt;li&gt;水平可扩展性：MLForecast能够使用Spark、Dask和Ray等分布式计算框架进行水平扩展。该功能使其能够通过在集群中的多个节点上分布计算来高效处理大规模数据集，非常适用于大规模时间序列预测任务。&lt;/li&gt;
&lt;/ul&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mlforecast&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MLForecast&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mlforecast.target_transforms&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Differences&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mlforecast.utils&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PredictionIntervals&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window_ops.expanding&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expanding_mean&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pip&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lightgbm&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;xgboost&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Import the necessary models from various libraries
&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# LGBMRegressor: A gradient boosting framework that uses tree-based learning algorithms from the LightGBM library
&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lightgbm&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LGBMRegressor&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# XGBRegressor: A gradient boosting regressor model from the XGBoost library
&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;xgboost&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;XGBRegressor&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# LinearRegression: A simple linear regression model from the scikit-learn library
&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sklearn.linear_model&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LinearRegression&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;使用 MLForecast 进行时间序列预测，我们实例化一个新的 MLForecast 对象，并提供各种参数来根据我们的特定需求定制建模过程：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;models ：此参数接受一个机器学习模型列表，您可以从scikit-learn、lightgbm和xgboost中导入您喜欢的模型进行预测。&lt;/li&gt;
  &lt;li&gt;freq ：这是一个字符串，表示您的数据的频率（每小时、每天、每周等）。该字符串的具体格式应与pandas识别的频率字符串相匹配。&lt;/li&gt;
  &lt;li&gt;target_transforms ：这些是在模型训练之前和模型预测之后应用于目标变量的转换。当处理可能受益于转换的数据时，例如对高度偏斜的数据进行对数转换，这可能会很有用。&lt;/li&gt;
  &lt;li&gt;lags ：此参数接受特定的滞后值作为回归因子使用。滞后表示在创建模型特征时要向后查看多少时间步。例如，如果您想使用前一天的数据作为预测今天值的特征，您将指定滞后值为1。&lt;/li&gt;
  &lt;li&gt;lags_transforms ：这些是每个滞后的特定转换。这使您能够对滞后特征应用转换。&lt;/li&gt;
  &lt;li&gt;date_features ：此参数指定要用作回归器的日期相关特征。例如，您可能希望在模型中包含星期几或月份作为特征。&lt;/li&gt;
  &lt;li&gt;num_threads ：该参数控制用于并行化特征创建的线程数，有助于在处理大型数据集时加快此过程的速度。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;所有这些设置都传递给 MLForecast 构造函数。一旦使用这些设置初始化 MLForecast 对象，我们调用它的 fit 方法并将历史的 dataframe 作为参数传递。该 fit 方法在提供的历史数据上训练模型，为未来的预测任务做好准备。&lt;/p&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Instantiate the MLForecast object
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mlf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MLForecast&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;LGBMRegressor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;XGBRegressor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;LinearRegression&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()],&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# List of models for forecasting: LightGBM, XGBoost and Linear Regression
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;freq&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;D&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Frequency of the data - &apos;D&apos; for daily frequency
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;lags&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Specific lags to use as regressors: 1 to 6 days
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;lag_transforms&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expanding_mean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Apply expanding mean transformation to the lag of 1 day
&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;date_features&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;year&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;month&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;day&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;dayofweek&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;quarter&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;week&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Date features to use as regressors
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;只需调用 fit 模型来训练选择的模型。在这种情况下，我们正在生成符合性预测区间。&lt;/p&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Start the timer to calculate the time taken for fitting the models
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;init&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Fit the MLForecast models to the data, with prediction intervals set using a window size of 28 days
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mlf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prediction_intervals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;PredictionIntervals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;window_size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;28&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Calculate the end time after fitting the models
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Print the time taken to fit the MLForecast models, in minutes
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;MLForecast Minutes: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;之后，只需调用 predict 来生成预测。&lt;/p&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;fcst_mlf_df&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mlf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;predict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;28&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;90&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;fcst_mlf_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;head&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;table-container&quot;&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;unique_id&lt;/th&gt;
      &lt;th&gt;ds&lt;/th&gt;
      &lt;th&gt;LGBMRegressor&lt;/th&gt;
      &lt;th&gt;XGBRegressor&lt;/th&gt;
      &lt;th&gt;LinearRegression&lt;/th&gt;
      &lt;th&gt;LGBMRegressor-lo-90&lt;/th&gt;
      &lt;th&gt;LGBMRegressor-hi-90&lt;/th&gt;
      &lt;th&gt;XGBRegressor-lo-90&lt;/th&gt;
      &lt;th&gt;XGBRegressor-hi-90&lt;/th&gt;
      &lt;th&gt;LinearRegression-lo-90&lt;/th&gt;
      &lt;th&gt;LinearRegression-hi-90&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;0&lt;/td&gt;
      &lt;td&gt;FOODS_3_001_CA_1&lt;/td&gt;
      &lt;td&gt;2016-05-23&lt;/td&gt;
      &lt;td&gt;0.549520&lt;/td&gt;
      &lt;td&gt;0.598431&lt;/td&gt;
      &lt;td&gt;0.359638&lt;/td&gt;
      &lt;td&gt;-0.213915&lt;/td&gt;
      &lt;td&gt;1.312955&lt;/td&gt;
      &lt;td&gt;-0.020050&lt;/td&gt;
      &lt;td&gt;1.216912&lt;/td&gt;
      &lt;td&gt;0.030000&lt;/td&gt;
      &lt;td&gt;0.689277&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;FOODS_3_001_CA_1&lt;/td&gt;
      &lt;td&gt;2016-05-24&lt;/td&gt;
      &lt;td&gt;0.553196&lt;/td&gt;
      &lt;td&gt;0.337268&lt;/td&gt;
      &lt;td&gt;0.100361&lt;/td&gt;
      &lt;td&gt;-0.251383&lt;/td&gt;
      &lt;td&gt;1.357775&lt;/td&gt;
      &lt;td&gt;-0.201449&lt;/td&gt;
      &lt;td&gt;0.875985&lt;/td&gt;
      &lt;td&gt;-0.216195&lt;/td&gt;
      &lt;td&gt;0.416917&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;FOODS_3_001_CA_1&lt;/td&gt;
      &lt;td&gt;2016-05-25&lt;/td&gt;
      &lt;td&gt;0.599668&lt;/td&gt;
      &lt;td&gt;0.349604&lt;/td&gt;
      &lt;td&gt;0.175840&lt;/td&gt;
      &lt;td&gt;-0.203974&lt;/td&gt;
      &lt;td&gt;1.403309&lt;/td&gt;
      &lt;td&gt;-0.284416&lt;/td&gt;
      &lt;td&gt;0.983624&lt;/td&gt;
      &lt;td&gt;-0.150593&lt;/td&gt;
      &lt;td&gt;0.502273&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;3&lt;/td&gt;
      &lt;td&gt;FOODS_3_001_CA_1&lt;/td&gt;
      &lt;td&gt;2016-05-26&lt;/td&gt;
      &lt;td&gt;0.638097&lt;/td&gt;
      &lt;td&gt;0.322144&lt;/td&gt;
      &lt;td&gt;0.156460&lt;/td&gt;
      &lt;td&gt;0.118688&lt;/td&gt;
      &lt;td&gt;1.157506&lt;/td&gt;
      &lt;td&gt;-0.085872&lt;/td&gt;
      &lt;td&gt;0.730160&lt;/td&gt;
      &lt;td&gt;-0.273851&lt;/td&gt;
      &lt;td&gt;0.586771&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4&lt;/td&gt;
      &lt;td&gt;FOODS_3_001_CA_1&lt;/td&gt;
      &lt;td&gt;2016-05-27&lt;/td&gt;
      &lt;td&gt;0.763305&lt;/td&gt;
      &lt;td&gt;0.300362&lt;/td&gt;
      &lt;td&gt;0.328194&lt;/td&gt;
      &lt;td&gt;-0.313091&lt;/td&gt;
      &lt;td&gt;1.839701&lt;/td&gt;
      &lt;td&gt;-0.296636&lt;/td&gt;
      &lt;td&gt;0.897360&lt;/td&gt;
      &lt;td&gt;-0.657089&lt;/td&gt;
      &lt;td&gt;1.313476&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;

&lt;h3 id=&quot;neuralforecast&quot;&gt;NeuralForecast&lt;/h3&gt;

&lt;p&gt;NeuralForecast 是一个强大的神经预测模型集合，专注于易用性和性能。它包括多种模型架构，从经典的网络如多层感知器（MLP）和循环神经网络（RNN），到像 N-BEATS、N-HITS、时间融合 Transformer（TFT）等创新贡献。&lt;/p&gt;

&lt;p&gt;NeuralForecast 的主要特点包括：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;全球模型的广泛集合。开箱即用的 MLP、LSTM、RNN、TCN、DilatedRNN、NBEATS、NHITS、ESRNN、TFT、Informer、PatchTST和HINT 的实现。&lt;/li&gt;
  &lt;li&gt;一个简单直观的界面，可以通过几行代码进行模型的训练、预测和回测。&lt;/li&gt;
  &lt;li&gt;支持GPU加速以提高计算速度。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;可以使用Colab的GPU来训练NeuralForecast。&lt;/p&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Read the results from Colab
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fcst_nf_df&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read_parquet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;https://m5-benchmarks.s3.amazonaws.com/data/forecast-nf.parquet&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;fcst_nf_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;head&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;table-container&quot;&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;unique_id&lt;/th&gt;
      &lt;th&gt;ds&lt;/th&gt;
      &lt;th&gt;AutoNHITS&lt;/th&gt;
      &lt;th&gt;AutoNHITS-lo-90&lt;/th&gt;
      &lt;th&gt;AutoNHITS-hi-90&lt;/th&gt;
      &lt;th&gt;AutoTFT&lt;/th&gt;
      &lt;th&gt;AutoTFT-lo-90&lt;/th&gt;
      &lt;th&gt;AutoTFT-hi-90&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;0&lt;/td&gt;
      &lt;td&gt;FOODS_3_001_CA_1&lt;/td&gt;
      &lt;td&gt;2016-05-23&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;2.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;2.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;FOODS_3_001_CA_1&lt;/td&gt;
      &lt;td&gt;2016-05-24&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;2.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;2.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;FOODS_3_001_CA_1&lt;/td&gt;
      &lt;td&gt;2016-05-25&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;2.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;1.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;3&lt;/td&gt;
      &lt;td&gt;FOODS_3_001_CA_1&lt;/td&gt;
      &lt;td&gt;2016-05-26&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;2.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;2.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4&lt;/td&gt;
      &lt;td&gt;FOODS_3_001_CA_1&lt;/td&gt;
      &lt;td&gt;2016-05-27&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;2.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;2.0&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Merge the forecasts from StatsForecast and NeuralForecast
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fcst_df&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fcst_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;merge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fcst_nf_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;how&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;unique_id&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ds&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Merge the forecasts from MLForecast into the combined forecast dataframe
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fcst_df&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fcst_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;merge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fcst_mlf_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;how&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;unique_id&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ds&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;fcst_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;head&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;预测图的绘制&quot;&gt;预测图的绘制&lt;/h3&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;sf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;plot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fcst_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;max_insample_length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;28&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;使用plot函数来探索模型和ID。&lt;/p&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;sf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;plot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fcst_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;max_insample_length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;28&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
        &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;CrostonOptimized&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;AutoNHITS&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;SeasonalNaive&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;LGBMRegressor&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;验证模型的性能&quot;&gt;验证模型的性能&lt;/h2&gt;

&lt;p&gt;这三个库 - StatsForecast ， MLForecast 和 NeuralForecast - 提供了针对时间序列专门设计的开箱即用的交叉验证功能。这使我们能够使用历史数据评估模型的性能，以获得对每个模型在未见数据上表现如何的无偏评估。&lt;/p&gt;

&lt;h3 id=&quot;在statsforecast中的交叉验证&quot;&gt;在StatsForecast中的交叉验证&lt;/h3&gt;

&lt;p&gt;StatsForecast 类中的 cross_validation 方法接受以下参数：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;df ：表示训练数据的 dataframe。&lt;/li&gt;
  &lt;li&gt;h(int)：预测的时间范围，表示我们希望预测的未来步数。例如，如果我们正在预测每小时的数据， h=24 将表示一个24小时的预测。&lt;/li&gt;
  &lt;li&gt;step_size(int)：每个交叉验证窗口之间的步长。该参数确定我们希望多久运行一次预测过程。&lt;/li&gt;
  &lt;li&gt;n_windows(int)：用于交叉验证的窗口数量。该参数定义了我们要评估多少个过去的预测过程。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这些参数允许我们控制交叉验证过程的范围和粒度。通过调整这些设置，我们可以在计算成本和交叉验证的彻底性之间取得平衡。&lt;/p&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;init&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;cv_df&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cross_validation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;horizon&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n_windows&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;step_size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;horizon&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;90&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;CV Minutes: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;crossvalidation_df 对象是一个包含以下列的新 data frame：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;unique_id索引：（如果您不喜欢使用索引，请运行forecasts_cv_df.reset_index()）&lt;/li&gt;
  &lt;li&gt;ds ：日期戳或时间索引&lt;/li&gt;
  &lt;li&gt;cutoff：n_windows的最后日期戳或时间索引。如果n_windows=1，则一个唯一的截止值；如果n_windows=2，则两个唯一的截止值。&lt;/li&gt;
  &lt;li&gt;y：真实值&lt;/li&gt;
  &lt;li&gt;model：具有模型名称和拟合值的列。&lt;/li&gt;
&lt;/ul&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;cv_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;head&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;table-container&quot;&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;unique_id&lt;/th&gt;
      &lt;th&gt;ds&lt;/th&gt;
      &lt;th&gt;cutoff&lt;/th&gt;
      &lt;th&gt;y&lt;/th&gt;
      &lt;th&gt;SeasonalNaive&lt;/th&gt;
      &lt;th&gt;SeasonalNaive-lo-90&lt;/th&gt;
      &lt;th&gt;SeasonalNaive-hi-90&lt;/th&gt;
      &lt;th&gt;Naive&lt;/th&gt;
      &lt;th&gt;Naive-lo-90&lt;/th&gt;
      &lt;th&gt;Naive-hi-90&lt;/th&gt;
      &lt;th&gt;HistoricAverage 历史平均值&lt;/th&gt;
      &lt;th&gt;HistoricAverage-lo-90&lt;/th&gt;
      &lt;th&gt;HistoricAverage-hi-90&lt;/th&gt;
      &lt;th&gt;CrostonOptimized&lt;/th&gt;
      &lt;th&gt;ADIDA&lt;/th&gt;
      &lt;th&gt;IMAPA&lt;/th&gt;
      &lt;th&gt;AutoETS&lt;/th&gt;
      &lt;th&gt;AutoETS-lo-90&lt;/th&gt;
      &lt;th&gt;AutoETS-hi-90&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;FOODS_3_001_CA_1&lt;/td&gt;
      &lt;td&gt;2016-02-29&lt;/td&gt;
      &lt;td&gt;2016-02-28&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;2.0&lt;/td&gt;
      &lt;td&gt;-1.878885&lt;/td&gt;
      &lt;td&gt;5.878885&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;-1.917011&lt;/td&gt;
      &lt;td&gt;1.917011&lt;/td&gt;
      &lt;td&gt;0.449111&lt;/td&gt;
      &lt;td&gt;-1.021813&lt;/td&gt;
      &lt;td&gt;1.920036&lt;/td&gt;
      &lt;td&gt;0.618472&lt;/td&gt;
      &lt;td&gt;0.618375&lt;/td&gt;
      &lt;td&gt;0.617998&lt;/td&gt;
      &lt;td&gt;0.655286&lt;/td&gt;
      &lt;td&gt;-0.765731&lt;/td&gt;
      &lt;td&gt;2.076302&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;FOODS_3_001_CA_1&lt;/td&gt;
      &lt;td&gt;2016-03-01&lt;/td&gt;
      &lt;td&gt;2016-02-28&lt;/td&gt;
      &lt;td&gt;1.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;-3.878885&lt;/td&gt;
      &lt;td&gt;3.878885&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;-2.711064&lt;/td&gt;
      &lt;td&gt;2.711064&lt;/td&gt;
      &lt;td&gt;0.449111&lt;/td&gt;
      &lt;td&gt;-1.021813&lt;/td&gt;
      &lt;td&gt;1.920036&lt;/td&gt;
      &lt;td&gt;0.618472&lt;/td&gt;
      &lt;td&gt;0.618375&lt;/td&gt;
      &lt;td&gt;0.617998&lt;/td&gt;
      &lt;td&gt;0.568595&lt;/td&gt;
      &lt;td&gt;-0.853966&lt;/td&gt;
      &lt;td&gt;1.991155&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;FOODS_3_001_CA_1&lt;/td&gt;
      &lt;td&gt;2016-03-02&lt;/td&gt;
      &lt;td&gt;2016-02-28&lt;/td&gt;
      &lt;td&gt;1.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;-3.878885&lt;/td&gt;
      &lt;td&gt;3.878885&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;-3.320361&lt;/td&gt;
      &lt;td&gt;3.320361&lt;/td&gt;
      &lt;td&gt;0.449111&lt;/td&gt;
      &lt;td&gt;-1.021813&lt;/td&gt;
      &lt;td&gt;1.920036&lt;/td&gt;
      &lt;td&gt;0.618472&lt;/td&gt;
      &lt;td&gt;0.618375&lt;/td&gt;
      &lt;td&gt;0.617998&lt;/td&gt;
      &lt;td&gt;0.618805&lt;/td&gt;
      &lt;td&gt;-0.805298&lt;/td&gt;
      &lt;td&gt;2.042908&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;FOODS_3_001_CA_1&lt;/td&gt;
      &lt;td&gt;2016-03-03&lt;/td&gt;
      &lt;td&gt;2016-02-28&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;1.0&lt;/td&gt;
      &lt;td&gt;-2.878885&lt;/td&gt;
      &lt;td&gt;4.878885&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;-3.834023&lt;/td&gt;
      &lt;td&gt;3.834023&lt;/td&gt;
      &lt;td&gt;0.449111&lt;/td&gt;
      &lt;td&gt;-1.021813&lt;/td&gt;
      &lt;td&gt;1.920036&lt;/td&gt;
      &lt;td&gt;0.618472&lt;/td&gt;
      &lt;td&gt;0.618375&lt;/td&gt;
      &lt;td&gt;0.617998&lt;/td&gt;
      &lt;td&gt;0.455891&lt;/td&gt;
      &lt;td&gt;-0.969753&lt;/td&gt;
      &lt;td&gt;1.881534&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;FOODS_3_001_CA_1&lt;/td&gt;
      &lt;td&gt;2016-03-04&lt;/td&gt;
      &lt;td&gt;2016-02-28&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;1.0&lt;/td&gt;
      &lt;td&gt;-2.878885&lt;/td&gt;
      &lt;td&gt;4.878885&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;-4.286568&lt;/td&gt;
      &lt;td&gt;4.286568&lt;/td&gt;
      &lt;td&gt;0.449111&lt;/td&gt;
      &lt;td&gt;-1.021813&lt;/td&gt;
      &lt;td&gt;1.920036&lt;/td&gt;
      &lt;td&gt;0.618472&lt;/td&gt;
      &lt;td&gt;0.618375&lt;/td&gt;
      &lt;td&gt;0.617998&lt;/td&gt;
      &lt;td&gt;0.591197&lt;/td&gt;
      &lt;td&gt;-0.835987&lt;/td&gt;
      &lt;td&gt;2.018380&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;

&lt;h3 id=&quot;mlforecast-1&quot;&gt;MLForecast&lt;/h3&gt;
&lt;p&gt;MLForecast 类中的 cross_validation 方法接受以下参数。&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;data ：训练数据 dataframe&lt;/li&gt;
  &lt;li&gt;window_size(int)：表示正在预测的未来h步。在这种情况下，是指24小时。&lt;/li&gt;
  &lt;li&gt;step_size(int)：每个窗口之间的步长。换句话说：您希望多久运行一次预测过程。&lt;/li&gt;
  &lt;li&gt;n_windows(int)：用于交叉验证的窗口数量。换句话说：您想要评估过去的预测过程的数量。&lt;/li&gt;
  &lt;li&gt;prediction_intervals：用于计算一致性区间的类。&lt;/li&gt;
&lt;/ul&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;init&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;cv_mlf_df&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mlf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cross_validation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;n&quot;&gt;window_size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;horizon&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;n&quot;&gt;n_windows&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;n&quot;&gt;step_size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;horizon&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;n&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;90&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;CV Minutes: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;crossvalidation_df 对象是一个包含以下列的新 data frame：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;unique_id索引：（如果您不喜欢使用索引，请运行forecasts_cv_df.reset_index()）&lt;/li&gt;
  &lt;li&gt;ds：日期戳或时间索引&lt;/li&gt;
  &lt;li&gt;cutoff ：n_windows的最后日期戳或时间索引。如果n_windows=1，则一个唯一的截止值；如果n_windows=2，则两个唯一的截止值。&lt;/li&gt;
  &lt;li&gt;y：真实值&lt;/li&gt;
  &lt;li&gt;model：具有模型名称和拟合值的列。&lt;/li&gt;
&lt;/ul&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;cv_mlf_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;head&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;table-container&quot;&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;unique_id&lt;/th&gt;
      &lt;th&gt;ds&lt;/th&gt;
      &lt;th&gt;cutoff&lt;/th&gt;
      &lt;th&gt;y&lt;/th&gt;
      &lt;th&gt;LGBMRegressor&lt;/th&gt;
      &lt;th&gt;XGBRegressor&lt;/th&gt;
      &lt;th&gt;LinearRegression 线性回归&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;0&lt;/td&gt;
      &lt;td&gt;FOODS_3_001_CA_1&lt;/td&gt;
      &lt;td&gt;2016-02-29&lt;/td&gt;
      &lt;td&gt;2016-02-28&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;0.435674&lt;/td&gt;
      &lt;td&gt;0.556261&lt;/td&gt;
      &lt;td&gt;-0.312492&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;FOODS_3_001_CA_1&lt;/td&gt;
      &lt;td&gt;2016-03-01&lt;/td&gt;
      &lt;td&gt;2016-02-28&lt;/td&gt;
      &lt;td&gt;1.0&lt;/td&gt;
      &lt;td&gt;0.639676&lt;/td&gt;
      &lt;td&gt;0.625806&lt;/td&gt;
      &lt;td&gt;-0.041924&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;FOODS_3_001_CA_1&lt;/td&gt;
      &lt;td&gt;2016-03-02&lt;/td&gt;
      &lt;td&gt;2016-02-28&lt;/td&gt;
      &lt;td&gt;1.0&lt;/td&gt;
      &lt;td&gt;0.792989&lt;/td&gt;
      &lt;td&gt;0.659650&lt;/td&gt;
      &lt;td&gt;0.263699&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;3&lt;/td&gt;
      &lt;td&gt;FOODS_3_001_CA_1&lt;/td&gt;
      &lt;td&gt;2016-03-03&lt;/td&gt;
      &lt;td&gt;2016-02-28&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;0.806868&lt;/td&gt;
      &lt;td&gt;0.535121&lt;/td&gt;
      &lt;td&gt;0.482491&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4&lt;/td&gt;
      &lt;td&gt;FOODS_3_001_CA_1&lt;/td&gt;
      &lt;td&gt;2016-03-04&lt;/td&gt;
      &lt;td&gt;2016-02-28&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;0.829106&lt;/td&gt;
      &lt;td&gt;0.313353&lt;/td&gt;
      &lt;td&gt;0.677326&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;

&lt;h3 id=&quot;neuralforecast-1&quot;&gt;NeuralForecast&lt;/h3&gt;
&lt;p&gt;使用Colab的GPU来训练NeuralForecast。&lt;/p&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;cv_nf_df&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read_parquet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;https://m5-benchmarks.s3.amazonaws.com/data/cross-validation-nf.parquet&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;cv_nf_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;head&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;table-container&quot;&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;unique_id&lt;/th&gt;
      &lt;th&gt;ds&lt;/th&gt;
      &lt;th&gt;cutoff&lt;/th&gt;
      &lt;th&gt;AutoNHITS&lt;/th&gt;
      &lt;th&gt;AutoNHITS-lo-90&lt;/th&gt;
      &lt;th&gt;AutoNHITS-hi-90&lt;/th&gt;
      &lt;th&gt;AutoTFT&lt;/th&gt;
      &lt;th&gt;AutoTFT-lo-90&lt;/th&gt;
      &lt;th&gt;AutoTFT-hi-90&lt;/th&gt;
      &lt;th&gt;y&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;0&lt;/td&gt;
      &lt;td&gt;FOODS_3_001_CA_1&lt;/td&gt;
      &lt;td&gt;2016-02-29&lt;/td&gt;
      &lt;td&gt;2016-02-28&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;2.0&lt;/td&gt;
      &lt;td&gt;1.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;2.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;FOODS_3_001_CA_1&lt;/td&gt;
      &lt;td&gt;2016-03-01&lt;/td&gt;
      &lt;td&gt;2016-02-28&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;2.0&lt;/td&gt;
      &lt;td&gt;1.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;2.0&lt;/td&gt;
      &lt;td&gt;1.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;FOODS_3_001_CA_1&lt;/td&gt;
      &lt;td&gt;2016-03-02&lt;/td&gt;
      &lt;td&gt;2016-02-28&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;2.0&lt;/td&gt;
      &lt;td&gt;1.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;2.0&lt;/td&gt;
      &lt;td&gt;1.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;3&lt;/td&gt;
      &lt;td&gt;FOODS_3_001_CA_1&lt;/td&gt;
      &lt;td&gt;2016-03-03&lt;/td&gt;
      &lt;td&gt;2016-02-28&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;2.0&lt;/td&gt;
      &lt;td&gt;1.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;2.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4&lt;/td&gt;
      &lt;td&gt;FOODS_3_001_CA_1&lt;/td&gt;
      &lt;td&gt;2016-03-04&lt;/td&gt;
      &lt;td&gt;2016-02-28&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;2.0&lt;/td&gt;
      &lt;td&gt;1.0&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;

&lt;h3 id=&quot;合并交叉验证预测&quot;&gt;合并交叉验证预测&lt;/h3&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;cv_df&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cv_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;merge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cv_nf_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;drop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;columns&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;how&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;unique_id&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ds&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;cutoff&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;cv_df&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cv_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;merge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cv_mlf_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;drop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;columns&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;how&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;unique_id&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ds&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;cutoff&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;交叉验证绘图&quot;&gt;交叉验证绘图&lt;/h3&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;cutoffs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cv_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;cutoff&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;unique&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cutoff&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cutoffs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;img&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;plot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Y_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
        &lt;span class=&quot;n&quot;&gt;cv_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;cutoff == @cutoff&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;drop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;columns&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;cutoff&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]),&lt;/span&gt; 
        &lt;span class=&quot;n&quot;&gt;max_insample_length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;28&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
        &lt;span class=&quot;n&quot;&gt;unique_ids&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;FOODS_3_001_CA_1&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;img&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;汇总需求&quot;&gt;汇总需求&lt;/h3&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;agg_cv_df&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cv_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cv_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;columns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;hi|lo&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;groupby&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ds&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;cutoff&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;numeric_only&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;reset_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;agg_cv_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;unique_id&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;agg_demand&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;agg_Y_df&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Y_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;groupby&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ds&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;numeric_only&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;reset_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;agg_Y_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;unique_id&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;agg_demand&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cutoff&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cutoffs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;img&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;plot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;agg_Y_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
        &lt;span class=&quot;n&quot;&gt;agg_cv_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;cutoff == @cutoff&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;drop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;columns&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;cutoff&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;max_insample_length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;28&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;img&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;按序列和交叉验证窗口进行评估&quot;&gt;按序列和交叉验证窗口进行评估&lt;/h2&gt;

&lt;p&gt;在本节中，我们将评估每个模型在每个时间序列和每个交叉验证窗口中的性能。由于有许多组合，我们将使用 dask 来并行评估。并行化将使用 fugue 完成。&lt;/p&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;typing&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Callable&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;distributed&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Client&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fugue&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transform&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fugue_dask&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DaskExecutionEngine&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datasetsforecast.losses&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mae&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;smape&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;evaluate 函数接收一个时间序列和一个窗口的唯一组合，并计算 df 中每个模型的不同 metrics 。&lt;/p&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;evaluate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DataFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;metrics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Callable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DataFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;eval_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;columns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;unique_id|y|ds|cutoff|lo|hi&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;columns&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;eval_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;metric&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;metrics&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;eval_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;metric&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__name__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;metric&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;eval_df&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;DataFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;eval_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;rename_axis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;metric&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;reset_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;eval_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;cutoff&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;cutoff&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;eval_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;unique_id&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;unique_id&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eval_df&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;str_models&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cv_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cv_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;columns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;unique_id|y|ds|cutoff|lo|hi&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;columns&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;str_models&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;:float&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str_models&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;cv_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;cutoff&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cv_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;cutoff&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;astype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;cv_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;unique_id&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cv_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;unique_id&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;astype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;让我们创建一个 dask 客户端。&lt;/p&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# without this, dask is not in distributed mode
# fugue.dask.dataframe.default.partitions determines the default partitions for a new DaskDataFrame
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;engine&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DaskExecutionEngine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;fugue.dask.dataframe.default.partitions&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;96&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;transform 函数接受 evaluate 函数，并将其应用于每个时间序列（ unique_id ）和交叉验证窗口（ cutoff ）的组合，使用之前我们创建的 dask 客户端。&lt;/p&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;evaluation_df&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cv_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cv_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;columns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;lo|hi&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)],&lt;/span&gt; 
    &lt;span class=&quot;n&quot;&gt;evaluate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;n&quot;&gt;engine&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;dask&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;metrics&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mae&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;smape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]},&lt;/span&gt; 
    &lt;span class=&quot;n&quot;&gt;schema&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;unique_id:str,cutoff:str,metric:str, &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str_models&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;n&quot;&gt;as_local&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;partition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;by&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;unique_id&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;cutoff&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;evaluation_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;head&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;table-container&quot;&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;unique_id&lt;/th&gt;
      &lt;th&gt;cutoff&lt;/th&gt;
      &lt;th&gt;metric&lt;/th&gt;
      &lt;th&gt;SeasonalNaive&lt;/th&gt;
      &lt;th&gt;Naive&lt;/th&gt;
      &lt;th&gt;HistoricAverage&lt;/th&gt;
      &lt;th&gt;CrostonOptimized&lt;/th&gt;
      &lt;th&gt;ADIDA&lt;/th&gt;
      &lt;th&gt;IMAPA&lt;/th&gt;
      &lt;th&gt;AutoETS&lt;/th&gt;
      &lt;th&gt;AutoNHITS&lt;/th&gt;
      &lt;th&gt;AutoTFT&lt;/th&gt;
      &lt;th&gt;LGBMRegressor&lt;/th&gt;
      &lt;th&gt;XGBRegressor&lt;/th&gt;
      &lt;th&gt;LinearRegression&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;0&lt;/td&gt;
      &lt;td&gt;FOODS_3_003_WI_3&lt;/td&gt;
      &lt;td&gt;2016-02-28&lt;/td&gt;
      &lt;td&gt;mse&lt;/td&gt;
      &lt;td&gt;1.142857&lt;/td&gt;
      &lt;td&gt;1.142857&lt;/td&gt;
      &lt;td&gt;0.816646&lt;/td&gt;
      &lt;td&gt;0.816471&lt;/td&gt;
      &lt;td&gt;1.142857&lt;/td&gt;
      &lt;td&gt;1.142857&lt;/td&gt;
      &lt;td&gt;1.142857&lt;/td&gt;
      &lt;td&gt;1.142857&lt;/td&gt;
      &lt;td&gt;1.142857&lt;/td&gt;
      &lt;td&gt;0.832010&lt;/td&gt;
      &lt;td&gt;1.020361&lt;/td&gt;
      &lt;td&gt;0.887121&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;FOODS_3_003_WI_3&lt;/td&gt;
      &lt;td&gt;2016-02-28&lt;/td&gt;
      &lt;td&gt;mae&lt;/td&gt;
      &lt;td&gt;0.571429&lt;/td&gt;
      &lt;td&gt;0.571429&lt;/td&gt;
      &lt;td&gt;0.729592&lt;/td&gt;
      &lt;td&gt;0.731261&lt;/td&gt;
      &lt;td&gt;0.571429&lt;/td&gt;
      &lt;td&gt;0.571429&lt;/td&gt;
      &lt;td&gt;0.571429&lt;/td&gt;
      &lt;td&gt;0.571429&lt;/td&gt;
      &lt;td&gt;0.571429&lt;/td&gt;
      &lt;td&gt;0.772788&lt;/td&gt;
      &lt;td&gt;0.619949&lt;/td&gt;
      &lt;td&gt;0.685413&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;FOODS_3_003_WI_3&lt;/td&gt;
      &lt;td&gt;2016-02-28&lt;/td&gt;
      &lt;td&gt;smape&lt;/td&gt;
      &lt;td&gt;71.428574&lt;/td&gt;
      &lt;td&gt;71.428574&lt;/td&gt;
      &lt;td&gt;158.813507&lt;/td&gt;
      &lt;td&gt;158.516235&lt;/td&gt;
      &lt;td&gt;200.000000&lt;/td&gt;
      &lt;td&gt;200.000000&lt;/td&gt;
      &lt;td&gt;200.000000&lt;/td&gt;
      &lt;td&gt;71.428574&lt;/td&gt;
      &lt;td&gt;71.428574&lt;/td&gt;
      &lt;td&gt;145.901947&lt;/td&gt;
      &lt;td&gt;188.159164&lt;/td&gt;
      &lt;td&gt;178.883743&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;3&lt;/td&gt;
      &lt;td&gt;FOODS_3_013_CA_3&lt;/td&gt;
      &lt;td&gt;2016-04-24&lt;/td&gt;
      &lt;td&gt;mse&lt;/td&gt;
      &lt;td&gt;4.000000&lt;/td&gt;
      &lt;td&gt;6.214286&lt;/td&gt;
      &lt;td&gt;2.406764&lt;/td&gt;
      &lt;td&gt;3.561202&lt;/td&gt;
      &lt;td&gt;2.267853&lt;/td&gt;
      &lt;td&gt;2.267600&lt;/td&gt;
      &lt;td&gt;2.268677&lt;/td&gt;
      &lt;td&gt;2.750000&lt;/td&gt;
      &lt;td&gt;2.125000&lt;/td&gt;
      &lt;td&gt;2.160508&lt;/td&gt;
      &lt;td&gt;2.370228&lt;/td&gt;
      &lt;td&gt;2.289606&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4&lt;/td&gt;
      &lt;td&gt;FOODS_3_013_CA_3&lt;/td&gt;
      &lt;td&gt;2016-04-24&lt;/td&gt;
      &lt;td&gt;mae&lt;/td&gt;
      &lt;td&gt;1.500000&lt;/td&gt;
      &lt;td&gt;2.142857&lt;/td&gt;
      &lt;td&gt;1.214286&lt;/td&gt;
      &lt;td&gt;1.340446&lt;/td&gt;
      &lt;td&gt;1.214286&lt;/td&gt;
      &lt;td&gt;1.214286&lt;/td&gt;
      &lt;td&gt;1.214286&lt;/td&gt;
      &lt;td&gt;1.107143&lt;/td&gt;
      &lt;td&gt;1.142857&lt;/td&gt;
      &lt;td&gt;1.140084&lt;/td&gt;
      &lt;td&gt;1.157548&lt;/td&gt;
      &lt;td&gt;1.148813&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Calculate the mean metric for each cross validation window
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;evaluation_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;groupby&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;cutoff&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;metric&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;mean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;numeric_only&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;table-container&quot;&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;SeasonalNaive&lt;/th&gt;
      &lt;th&gt;Naive&lt;/th&gt;
      &lt;th&gt;HistoricAverage&lt;/th&gt;
      &lt;th&gt;CrostonOptimized&lt;/th&gt;
      &lt;th&gt;ADIDA&lt;/th&gt;
      &lt;th&gt;IMAPA&lt;/th&gt;
      &lt;th&gt;AutoETS&lt;/th&gt;
      &lt;th&gt;AutoNHITS&lt;/th&gt;
      &lt;th&gt;AutoTFT&lt;/th&gt;
      &lt;th&gt;LGBMRegressor&lt;/th&gt;
      &lt;th&gt;XGBRegressor&lt;/th&gt;
      &lt;th&gt;LinearRegression&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;cutoff&lt;/td&gt;
      &lt;td&gt;metric&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2016-02-28&lt;/td&gt;
      &lt;td&gt;mae&lt;/td&gt;
      &lt;td&gt;1.744289&lt;/td&gt;
      &lt;td&gt;2.040496&lt;/td&gt;
      &lt;td&gt;1.730704&lt;/td&gt;
      &lt;td&gt;1.633017&lt;/td&gt;
      &lt;td&gt;1.527965&lt;/td&gt;
      &lt;td&gt;1.528772&lt;/td&gt;
      &lt;td&gt;1.497553&lt;/td&gt;
      &lt;td&gt;1.434938&lt;/td&gt;
      &lt;td&gt;1.485419&lt;/td&gt;
      &lt;td&gt;1.688403&lt;/td&gt;
      &lt;td&gt;1.514102&lt;/td&gt;
      &lt;td&gt;1.576320&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;mse&lt;/td&gt;
      &lt;td&gt;14.510710&lt;/td&gt;
      &lt;td&gt;19.080585&lt;/td&gt;
      &lt;td&gt;12.858994&lt;/td&gt;
      &lt;td&gt;11.785032&lt;/td&gt;
      &lt;td&gt;11.114497&lt;/td&gt;
      &lt;td&gt;11.100909&lt;/td&gt;
      &lt;td&gt;10.347847&lt;/td&gt;
      &lt;td&gt;10.010982&lt;/td&gt;
      &lt;td&gt;10.964664&lt;/td&gt;
      &lt;td&gt;10.436206&lt;/td&gt;
      &lt;td&gt;10.968788&lt;/td&gt;
      &lt;td&gt;10.792831&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;smape&lt;/td&gt;
      &lt;td&gt;85.202042&lt;/td&gt;
      &lt;td&gt;87.719086&lt;/td&gt;
      &lt;td&gt;125.418488&lt;/td&gt;
      &lt;td&gt;124.749908&lt;/td&gt;
      &lt;td&gt;127.591858&lt;/td&gt;
      &lt;td&gt;127.704102&lt;/td&gt;
      &lt;td&gt;127.790672&lt;/td&gt;
      &lt;td&gt;79.132614&lt;/td&gt;
      &lt;td&gt;80.983368&lt;/td&gt;
      &lt;td&gt;118.489983&lt;/td&gt;
      &lt;td&gt;140.420578&lt;/td&gt;
      &lt;td&gt;127.043137&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2016-03-27&lt;/td&gt;
      &lt;td&gt;mae&lt;/td&gt;
      &lt;td&gt;1.795973&lt;/td&gt;
      &lt;td&gt;2.106449&lt;/td&gt;
      &lt;td&gt;1.754029&lt;/td&gt;
      &lt;td&gt;1.662087&lt;/td&gt;
      &lt;td&gt;1.570701&lt;/td&gt;
      &lt;td&gt;1.572741&lt;/td&gt;
      &lt;td&gt;1.535301&lt;/td&gt;
      &lt;td&gt;1.432412&lt;/td&gt;
      &lt;td&gt;1.502393&lt;/td&gt;
      &lt;td&gt;1.712493&lt;/td&gt;
      &lt;td&gt;1.600193&lt;/td&gt;
      &lt;td&gt;1.601612&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;mse&lt;/td&gt;
      &lt;td&gt;14.810259&lt;/td&gt;
      &lt;td&gt;26.044472&lt;/td&gt;
      &lt;td&gt;12.804104&lt;/td&gt;
      &lt;td&gt;12.020620&lt;/td&gt;
      &lt;td&gt;12.083861&lt;/td&gt;
      &lt;td&gt;12.120033&lt;/td&gt;
      &lt;td&gt;11.315013&lt;/td&gt;
      &lt;td&gt;9.445867&lt;/td&gt;
      &lt;td&gt;10.762877&lt;/td&gt;
      &lt;td&gt;10.723589&lt;/td&gt;
      &lt;td&gt;12.924312&lt;/td&gt;
      &lt;td&gt;10.943772&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;smape&lt;/td&gt;
      &lt;td&gt;87.407471&lt;/td&gt;
      &lt;td&gt;89.453247&lt;/td&gt;
      &lt;td&gt;123.587196&lt;/td&gt;
      &lt;td&gt;123.460030&lt;/td&gt;
      &lt;td&gt;123.428459&lt;/td&gt;
      &lt;td&gt;123.538521&lt;/td&gt;
      &lt;td&gt;123.612991&lt;/td&gt;
      &lt;td&gt;79.926781&lt;/td&gt;
      &lt;td&gt;82.013168&lt;/td&gt;
      &lt;td&gt;116.089699&lt;/td&gt;
      &lt;td&gt;138.885941&lt;/td&gt;
      &lt;td&gt;127.304871&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2016-04-24&lt;/td&gt;
      &lt;td&gt;mae&lt;/td&gt;
      &lt;td&gt;1.785983&lt;/td&gt;
      &lt;td&gt;1.990774&lt;/td&gt;
      &lt;td&gt;1.762506&lt;/td&gt;
      &lt;td&gt;1.609268&lt;/td&gt;
      &lt;td&gt;1.527627&lt;/td&gt;
      &lt;td&gt;1.529721&lt;/td&gt;
      &lt;td&gt;1.501820&lt;/td&gt;
      &lt;td&gt;1.447401&lt;/td&gt;
      &lt;td&gt;1.505127&lt;/td&gt;
      &lt;td&gt;1.692946&lt;/td&gt;
      &lt;td&gt;1.541845&lt;/td&gt;
      &lt;td&gt;1.590985&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;mse&lt;/td&gt;
      &lt;td&gt;13.476350&lt;/td&gt;
      &lt;td&gt;16.234917&lt;/td&gt;
      &lt;td&gt;13.151311&lt;/td&gt;
      &lt;td&gt;10.647048&lt;/td&gt;
      &lt;td&gt;10.072225&lt;/td&gt;
      &lt;td&gt;10.062395&lt;/td&gt;
      &lt;td&gt;9.393439&lt;/td&gt;
      &lt;td&gt;9.363891&lt;/td&gt;
      &lt;td&gt;10.436214&lt;/td&gt;
      &lt;td&gt;10.347073&lt;/td&gt;
      &lt;td&gt;10.774202&lt;/td&gt;
      &lt;td&gt;10.608137&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;smape&lt;/td&gt;
      &lt;td&gt;89.238815&lt;/td&gt;
      &lt;td&gt;90.685867&lt;/td&gt;
      &lt;td&gt;121.124947&lt;/td&gt;
      &lt;td&gt;119.721245&lt;/td&gt;
      &lt;td&gt;120.325401&lt;/td&gt;
      &lt;td&gt;120.345284&lt;/td&gt;
      &lt;td&gt;120.649582&lt;/td&gt;
      &lt;td&gt;81.402748&lt;/td&gt;
      &lt;td&gt;83.614029&lt;/td&gt;
      &lt;td&gt;113.334198&lt;/td&gt;
      &lt;td&gt;136.755234&lt;/td&gt;
      &lt;td&gt;124.618622&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;

&lt;p&gt;前面试验的结果。&lt;/p&gt;

&lt;div class=&quot;table-container&quot;&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;model&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;MSE&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;MQCNN&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;10.09&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;DeepAR-student_t&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;10.11&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;DeepAR-lognormal DeepAR-对数正态分布&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;30.20&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;DeepAR&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;9.13&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;NPTS&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;11.53&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;

&lt;p&gt;Top 3 模型：DeepAR，AutoNHITS，AutoETS。&lt;/p&gt;

&lt;h3 id=&quot;误差分布&quot;&gt;误差分布&lt;/h3&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pip&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;seaborn&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matplotlib.pyplot&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;seaborn&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sns&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;evaluation_df_melted&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;melt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;evaluation_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;id_vars&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;unique_id&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;cutoff&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;metric&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;smape&quot;&gt;SMAPE&lt;/h3&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;sns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;violinplot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;evaluation_df_melted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;metric==&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;smape&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;figure&gt;
          &lt;picture&gt;
            
            
            &lt;img src=&quot;https://mintcdn.com/nixtla/Oro4FJl3t6oNJW6V/statsforecast/docs/tutorials/StatisticalNeuralMethods_files/figure-markdown_strict/cell-50-output-1.png?fit=max&amp;amp;auto=format&amp;amp;n=Oro4FJl3t6oNJW6V&amp;amp;q=85&amp;amp;s=37053c3b98734facadf33ee50719b136&quot; alt=&quot;&quot; width= height= /&gt;
          &lt;/picture&gt;
          
        &lt;/figure&gt;&lt;/p&gt;

&lt;h2 id=&quot;为时序组合选择模型&quot;&gt;为时序组合选择模型&lt;/h2&gt;

&lt;p&gt;特征：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;一个统一的 dataframe，包含了所有不同模型的预测&lt;/li&gt;
  &lt;li&gt;容易组装&lt;/li&gt;
  &lt;li&gt;平均预测&lt;/li&gt;
  &lt;li&gt;或者MinMax（选择是集成）&lt;/li&gt;
&lt;/ul&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Choose the best model for each time series, metric, and cross validation window
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;evaluation_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;best_model&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;evaluation_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;idxmin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;axis&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numeric_only&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# count how many times a model wins per metric and cross validation window
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;count_best_model&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;evaluation_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;groupby&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;cutoff&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;metric&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;best_model&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;rename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;reset_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# plot results
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;barplot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;count_best_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;best_model&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;metric&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;figure&gt;
          &lt;picture&gt;
            
            
            &lt;img src=&quot;https://mintcdn.com/nixtla/Oro4FJl3t6oNJW6V/statsforecast/docs/tutorials/StatisticalNeuralMethods_files/figure-markdown_strict/cell-51-output-1.png?fit=max&amp;amp;auto=format&amp;amp;n=Oro4FJl3t6oNJW6V&amp;amp;q=85&amp;amp;s=57d9e7ffec4a52bd9d7ccb1c5aaee945&quot; alt=&quot;&quot; width= height= /&gt;
          &lt;/picture&gt;
          
        &lt;/figure&gt;&lt;/p&gt;

&lt;h3 id=&quot;多元一体一个包容性的预测饼图&quot;&gt;多元一体&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;：一个包容性的预测饼图&lt;/h3&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# For the mse, calculate how many times a model wins
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;eval_series_df&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;evaluation_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;metric == &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;mse&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;groupby&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;unique_id&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;mean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;numeric_only&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;eval_series_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;best_model&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eval_series_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;idxmin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;axis&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;counts_series&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eval_series_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;value_counts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;best_model&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;pie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;counts_series&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;counts_series&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;autopct&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;%.0f%%&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;plt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;figure&gt;
          &lt;picture&gt;
            
            
            &lt;img src=&quot;https://mintcdn.com/nixtla/Oro4FJl3t6oNJW6V/statsforecast/docs/tutorials/StatisticalNeuralMethods_files/figure-markdown_strict/cell-52-output-1.png?fit=max&amp;amp;auto=format&amp;amp;n=Oro4FJl3t6oNJW6V&amp;amp;q=85&amp;amp;s=cfa767203e822628915690b8e1d6eb06&quot; alt=&quot;&quot; width= height= /&gt;
          &lt;/picture&gt;
          
        &lt;/figure&gt;&lt;/p&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;sf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;plot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cv_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;drop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;columns&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;cutoff&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]),&lt;/span&gt; 
        &lt;span class=&quot;n&quot;&gt;max_insample_length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;28&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
        &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;AutoNHITS&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;unique_ids&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;eval_series_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;best_model == &lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;AutoNHITS&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;为不同时序分组选择不同的预测方法&quot;&gt;为不同时序分组选择不同的预测方法&lt;/h3&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Merge the best model per time series dataframe
# and filter the forecasts based on that dataframe
# for each time series
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fcst_df&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;melt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fcst_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;set_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;unique_id&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;id_vars&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ds&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;forecast&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ignore_index&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fcst_df&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fcst_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;eval_series_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;best_model&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]])&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fcst_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;pred-interval&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fcst_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expand&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fcst_df&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fcst_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;model == best_model&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fcst_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;forecast-&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;forecast&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fcst_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;pred-interval&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fcst_df&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;pivot_table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fcst_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;unique_id&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ds&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;forecast&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;columns&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;droplevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;axis&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;reset_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div  class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;sf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;plot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Y_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fcst_df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;max_insample_length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;28&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;&quot;Et pluribus unum&quot; 是拉丁语，意思是&quot;合众为一&quot;或&quot;多中之一&quot;。这句话是美国的传统格言，出现在美国国徽上，也曾经是美国的非正式国家座右铭（直到1956年被&quot;In God We Trust&quot;取代）。它表达的理念是：许多不同的州或人民团结成为一个统一的国家。 &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Sat, 11 Oct 2025 11:15:00 +0000</pubDate>
        <link>https://wulfric.me/2025/10/statisticalneuralmethods/</link>
        <guid isPermaLink="true">https://wulfric.me/2025/10/statisticalneuralmethods/</guid>
        
        <category>stats</category>
        
        <category>ml</category>
        
        <category>neural</category>
        
        <category>forecast</category>
        
        <category>time series</category>
        
        
        <category>技术</category>
        
      </item>
    
      <item>
        <title>10月 Tech Weekly</title>
        <description>&lt;h2 id=&quot;sre-weekly&quot;&gt;SRE Weekly&lt;/h2&gt;

&lt;h3 id=&quot;w41&quot;&gt;w41&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://www.datadoghq.com/blog/managing-datadog-with-terraform/&quot;&gt;Managing Datadog with Terraform&lt;/a&gt;: 使用 terraform，直接创建datadog 的监控、仪表盘；也可以在创建 IaaS 资源的时候，顺便创建对应的 datadog 监控。terraform 是纯文本描述的、通过声明式配置文件来管理和供应基础设施资源的工具，因此适合管理大型基础设施。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;a href=&quot;https://opentelemetry.io/blog/2025/demystifying-auto-instrumentation/&quot;&gt;Demystifying Automatic Instrumentation: How the Magic Actually Works&lt;/a&gt;: 介绍自动插桩的一些常用技术方案。即：猴子补丁、字节码插桩、编译时插桩、eBPF 和语言运行时 API。每种技术都利用了不同编程语言和运行时环境的独特特性，无需更改代码即可添加可观察性。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;猴子补丁：常用于 js、python、ruby 等动态语言。比如 js 的 require-in-the-middle 库允许我们在模块加载时执行此替换，拦截模块加载过程，在应用程序使用之前修改导出的函数&lt;/li&gt;
  &lt;li&gt;字节码插桩：对于运行在虚拟机上的语言，字节码插桩提供了一种强大的方法。这种技术通过修改虚拟机加载编译后的字节码来实现，使我们能够在指令级别注入代码。典型示例如 Java 的 Instrumentation API&lt;/li&gt;
  &lt;li&gt;编译时插桩：对于 Go 等静态编译语言，编译时 instrumentation 提供了一种不同的方法。它不是在运行时修改代码，而是在构建过程中使用抽象语法树（AST）操作转换源代码。缺点是，必须要嵌入到源码构建和 CI/CD 流程中&lt;/li&gt;
  &lt;li&gt;eBPF 插桩：eBPF 代表了一种完全不同的自动插桩方法。它不是修改应用程序代码或字节码，而是在内核级别工作，将探针附加到运行应用程序中的函数入口和出口点。&lt;/li&gt;
  &lt;li&gt;语言运行时 API：一些语言提供内置的 instrumentation API，提供更集成的方案。PHP 8.0 中引入的 Observer API 就是这种方案的典型例子&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;a href=&quot;https://aws.amazon.com/blogs/mt/amazon-cloudwatch-application-signals-new-enhancements-for-application-monitoring/&quot;&gt;Amazon CloudWatch Application Signals new enhancements for application monitoring&lt;/a&gt;: AWS CloudWatch 的一些更新，提到了他们使用大模型来做根因分析的一些实践；提到了 SLO/SLI 视图。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;a href=&quot;https://www.pagerduty.com/blog/product/product-launch-2025-h2/&quot;&gt;PagerDuty H2 2025 Release: 150+ Customer-Driven Features, AI Agents, and More&lt;/a&gt;: PagerDuty 的 H2 2025 功能更新，包含值班排程、事件管理；智能化方面包含&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://support.pagerduty.com/main/docs/shift-agent&quot;&gt;Shift Agent&lt;/a&gt;：排班助手，该系统在后台主动运行——监控日历、检测潜在冲突、识别合格替岗人员、协调排班变更&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://support.pagerduty.com/main/docs/scribe-agent&quot;&gt;Scribe Agent&lt;/a&gt;：转录助手，可以将 zoom 会议的内容转录到 im 中，还可以进一步进行分析，更新相关的事件状态和进度等&lt;/li&gt;
  &lt;li&gt;SRE Agent：PagerDuty 的 SRE Agent 提供了具有人工监督的全面端到端事件自动化，可以运行诊断、呈现关键上下文、提供分析、建议修复操作，并在获得批准后自动运行这些操作。它还从每个事件中学习，为将来使用生成智能手册。&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://support.pagerduty.com/main/docs/insights-agent&quot;&gt;Insights Agent&lt;/a&gt;: 基于 Incidents、Services 和 Teams 分析，提供主动建议和按需对话式洞察。该 AI 代理可以轻松揭示运营风险、趋势和模式，继而赋能数据驱动的决策，并构建更具弹性的运营。&lt;/li&gt;
  &lt;li&gt;AI Orchestrations：将通过利用在历史事件和故障数据上训练的机器学习，建议可操作的事件编排规则，从而将工作从被动响应转变为主动出击。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;除此之外还提供了 MCP 服务，并支持和一些工具集成。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;a href=&quot;https://www.datadoghq.com/blog/status-pages/&quot;&gt;Keep stakeholders informed with Datadog Status Pages&lt;/a&gt; datadog 的状态页（status page）设计。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;a href=&quot;https://grafana.com/blog/2025/10/08/grafana-assistant-ga-assistant-investigations-preview/&quot;&gt;AI-powered observability: Resolve incidents faster, reduce alert fatigue, and expand access&lt;/a&gt;: Grafana Assistant 是 Grafana Cloud 中的一个 AI 驱动的智能体，它使用自然语言帮助你更快地查询、构建和排除故障。它简化了常见的工作流程，如编写 PromQL、LogQL 或 TraceQL 查询、创建仪表盘和执行引导式根因分析。Assistant investigations 是一个直接构建在 Grafana Assistant 中的 SRE agent。它通过分析你的可观测性堆栈、发现异常并连接整个系统中的信号，帮助你更快地找到根本原因。你将获得清晰、有指导意义的修复建议——并且由于它嵌入在 Assistant 中，它为解决复杂事件提供了无缝的端到端工作流程。&lt;/p&gt;

&lt;h3 id=&quot;w42&quot;&gt;w42&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://www.dynatrace.com/news/blog/remediating-cve-2025-3248-how-dynatrace-application-security-protects-agentic-ai-applications/&quot;&gt;Remediating CVE-2025-3248: How Dynatrace Application Security protects Agentic AI applications&lt;/a&gt;: 介绍了CVE-2025-3248 漏洞的攻防，攻击者可以将代码注入到 AI agent 中，例如 Langflow 中的 AI agent，从而导致远程代码执行和后门。Dynatrace 的云应用程序检测和响应 (CADR) 通过检测漏洞、识别错误配置和调查可疑活动来防御此类攻击。这种多层方法包括&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;运行时漏洞分析：支持在运行时检测 Python 漏洞，可以提供关于攻击者潜在攻击途径的即时信息&lt;/li&gt;
  &lt;li&gt;安全态势管理和深度日志调查：比如检查缺失的网络策略&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;以保护基于 agent 的 AI 环境。（话说 langchain 的问题还真多……）&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;a href=&quot;https://grafana.com/docs/grafana-cloud/knowledge-graph/introduction/&quot;&gt;Introduction to the knowledge graph&lt;/a&gt; 介绍了 grafana cloud 的知识图谱工具，它为可观测性工具栈增加了一个关键层：使用基于 SLO 的分析，自动洞察服务健康状况、性能和可靠性。与显示所有内容的仪表板不同，知识图谱仅在出现问题时显示相关上下文，帮助用户专注于最重要的内容。重要概念：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;entities（实体）：环境中的对象及其属性，例如服务、节点或 Pod&lt;/li&gt;
  &lt;li&gt;entity catalog（实体目录）：提供了知识图谱自动监控元素的综合列表视图&lt;/li&gt;
  &lt;li&gt;entity graph（实体图）：知识图谱通过检测系统中的实体及其相关属性来构建实体图。实体图帮助用户查看服务和系统之间的依赖关系，并了解任何问题的来源。&lt;/li&gt;
  &lt;li&gt;insights（洞察）：洞察持续跟踪资源饱和度、更正（例如，部署和扩展事件）、请求、资源、延迟异常、系统故障以及用户的黄金信号和健康指标上的错误。Insights 是精简的时间序列数据，专门捕获系统中的重大事件。与传统的警报相比，Insights 具有独特的用途，因为它们并非设计用来通知值班人员。相反，它们是知识图谱提供的自动化生命体征，随时可用于故障排查。&lt;/li&gt;
  &lt;li&gt;RCA workbench（根因分析工作台）：RCA 工作台提供对特定问题随时间推移的潜在原因和依赖关系的全面探索，可访问相关指标、日志和跟踪信息，以实现高效的故障排除和分析。&lt;/li&gt;
  &lt;li&gt;workbench AI：工作台 AI 帮助用户根据根因分析形成假设，并提供解决性能问题的推荐步骤。&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;a href=&quot;https://www.datadoghq.com/blog/engineering/rethinking-reliability/&quot;&gt;Failure is inevitable: Learning from a large outage, and building for reliability in depth at Datadog&lt;/a&gt; 这是datadog对2023年的一个故障的思考：一个没有经过审查的全局变更，导致相互重启，继而导致生产环境中大约 50–60% 的 Kubernetes 节点失去了连接。在用户层面的行为表现是监控、日志、告警、链路等功能都不可用。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;datadog的思考1: 从datadog的视角来看，即使在高峰期，大约 40–50% 的 Kubernetes 节点仍在运行并保持连接。但从用户的角度来看，该平台似乎完全瘫痪。一个重要的原因是，datadog为了保证数据的准确性，会等待数据完全处理完成才给出监控指标结果。但在该故障下，因故障导致不完整=&amp;gt;数据等待完整性计算=&amp;gt;完整性计算排队处理=&amp;gt;处理必然失败，导致永远加载不出数据。datadog认为之前的设计有问题，为了实现数据完整性，忽略了相关的降级方案。&lt;/li&gt;
  &lt;li&gt;蚂蚁的监控系统在这方面的实践不错：监控给数据的时候，顺便也会给出数据齐全度报告，即使数据一直不齐全，监控数据仍然可以持续输出。&lt;/li&gt;
  &lt;li&gt;datadog的思考2: datadog 在故障复盘中也提出，数据应该有优先级（比如实时数据优于历史数据），在定时任务处理时，应基于优先级分配任务。&lt;/li&gt;
  &lt;li&gt;datadog的思考3: 系统的自动恢复动作（处理积压数据、重试等）在故障恢复的时候有可能会使恢复速度变慢。这些都需要在架构设计时做出优化，如1）重试应尽量分布在不同机器上，且使用强退避机制来减少过载，并更快地回退到死信队列，以避免在单个“坏”数据包上hang住；2）对积压数据的恢复处理进行限流，确保旧数据不会抢占实时数据处理。&lt;/li&gt;
  &lt;li&gt;datadog的思考4: 引入混沌工程，实现持续、全自动的混沌测试。&lt;/li&gt;
  &lt;li&gt;datadog的思考5: 故障一定会发生，要按照这个思路来构建系统。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;w43&quot;&gt;w43&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://www.datadoghq.com/blog/updog-ai/&quot;&gt;datadog 推出 updog&lt;/a&gt;，它展示了 30 多家热门 SaaS 提供商（例如 OpenAI、Zoom 和 GitHub）以及 13 项 AWS 服务的实时运行状况。这个功能还是挺厉害的，需要为掌握不了的基础设施提供可用性观测。datadog 自述他们通过如下方法实现该目的&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;聚合了来自数千个组织的匿名 APM 遥测数据&lt;/li&gt;
  &lt;li&gt;一个贝叶斯模型，用于推断跨独立客户环境的异常错误率&lt;/li&gt;
  &lt;li&gt;确认跨客户和区域的关联，以确认降级是否具有系统性&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;figure&gt;
          &lt;picture&gt;
            
            
            &lt;img src=&quot;https://imgix.datadoghq.com/img/blog/updog-ai/updog-main.png?auto=compress%2Cformat&amp;amp;cs=origin&amp;amp;lossless=true&amp;amp;fit=max&amp;amp;q=75&amp;amp;w=&amp;amp;h=&amp;amp;dpr=1&quot; alt=&quot;&quot; width= height= /&gt;
          &lt;/picture&gt;
          
        &lt;/figure&gt;&lt;/p&gt;

&lt;p&gt;这种方法使 Datadog 能够比供应商控制的页面更快地检测到问题。例如，Updog.ai 最近在亚马逊 DynamoDB 降级 32 分钟前就发现了这个问题，而 AWS 尚未更新其自己的状态页面。 这个结果是一个可靠的、人工智能驱动的信号，反映了全球用户的真实体验。&lt;/p&gt;

&lt;h2 id=&quot;rd-weekly&quot;&gt;RD weekly&lt;/h2&gt;

&lt;h3 id=&quot;w41-1&quot;&gt;w41&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://docs.python.org/zh-cn/3.14/whatsnew/3.14.html&quot;&gt;Python 3.14更新&lt;/a&gt;: Python 3.14 是 Python 编程语言的最新稳定发布版，包含多项针对语言、实现和标准库的改变。 最大的变化包括 &lt;a href=&quot;https://docs.python.org/zh-cn/3.14/whatsnew/3.14.html#whatsnew314-template-string-literals&quot;&gt;模板字符串字面值&lt;/a&gt;, &lt;a href=&quot;https://docs.python.org/zh-cn/3.14/whatsnew/3.14.html#whatsnew314-deferred-annotations&quot;&gt;标注的迟延求值&lt;/a&gt;，以及在标准库中对 &lt;a href=&quot;https://docs.python.org/zh-cn/3.14/whatsnew/3.14.html#whatsnew314-multiple-interpreters&quot;&gt;子解释器&lt;/a&gt; 的支持。&lt;/p&gt;

&lt;p&gt;个人比较看好&lt;a href=&quot;https://docs.python.org/zh-cn/3.14/whatsnew/3.14.html#whatsnew314-remote-debugging&quot;&gt;安全的 CPython 外部调试器接口&lt;/a&gt;和&lt;a href=&quot;https://docs.python.org/zh-cn/3.14/whatsnew/3.14.html#whatsnew314-free-threaded-now-supported&quot;&gt;自由线程&lt;/a&gt;这2个功能。预计开源社区会陆续带来相关功能更新和优化。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;a href=&quot;https://inside.java/2025/10/14/devoxxbelgium-java-for-ai/&quot;&gt;Java for AI&lt;/a&gt;: 介绍了 java 在 AI 时代的一些动作，重点是 Foreign Function and Memory API 和 Vector API。后者可以利用 CPU 的 SIMD 特性，前者可以方便快速的接入 c/c++ 的 GPU 库，使用体验要强于 JNI。&lt;/p&gt;

&lt;h3 id=&quot;w42-1&quot;&gt;w42&lt;/h3&gt;

&lt;p&gt;What’s New in Java 25 in 2 Minutes - Sip of Java](https://inside.java/2025/10/17/new-in-jdk-25-2-mins/) JDK 25 于 9 月 16 日发布，包括 18 个 JEP，并有多个功能已最终确定。个人觉得比较重要的 JEP 有：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;JEP 506 - Scoped Values: 提供了一种新的线程局部数据共享机制，允许在方法调用链中安全地传递不可变数据，比ThreadLocal更高效且内存友好。&lt;/li&gt;
  &lt;li&gt;JEP 511 - Module Import Declarations: 简化模块系统的使用，允许在模块声明中直接导入其他模块的所有导出包，减少重复的import语句。&lt;/li&gt;
  &lt;li&gt;JEP 513 - Flexible Constructor Bodies放宽构造函数的语法限制，允许在调用super()或this()之前执行一些语句，提供更灵活的构造函数编写方式。&lt;/li&gt;
  &lt;li&gt;JEP 519 - Compact Object Headers: 优化对象头部结构以减少内存占用，通过重新设计对象布局来提高内存效率和性能。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;w43-1&quot;&gt;w43&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://devecosystem-2025.jetbrains.com/tools-and-trends&quot;&gt;jetbrains tools and trends&lt;/a&gt; 2025 年 jetbrains 的语言承诺指数和其他社区统计指标。一些看点：1）Python和Java是最主要的编程语言；2）从2025年开始，Postgres 超越 MySQL 成为最受欢迎的数据库；3）TypeScript是过去五年增长最迅速的语言。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/Abinesh-Mathivanan/go-torch&quot;&gt;Go torch&lt;/a&gt; Go 语言实现的 Torch 风格深度学习框架 — 一个完全用 Go 语言从头构建的开源深度学习框架。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://go-echarts.github.io/go-echarts/#/&quot;&gt;Go echarts&lt;/a&gt; 使用 Apache ECharts 的图表和数据可视化库。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;a href=&quot;https://x.com/karpathy/status/1980397031542989305&quot;&gt;Andrej Karpathy 评 DeepSeek OCR&lt;/a&gt; 也许图片比 token 更适合作为 LLM 的输入。DeepSeek OCR&lt;/p&gt;
</description>
        <pubDate>Sat, 11 Oct 2025 00:00:00 +0000</pubDate>
        <link>https://wulfric.me/2025/10/tech-weekly-oct/</link>
        <guid isPermaLink="true">https://wulfric.me/2025/10/tech-weekly-oct/</guid>
        
        <category>weekly</category>
        
        
        <category>技术</category>
        
      </item>
    
      <item>
        <title>ReAct 论文翻译：Synergizing Reasoning and Acting in Language Models</title>
        <description>&lt;p&gt;在语言模型中协同推理和行动的研究。&lt;/p&gt;

&lt;h2 id=&quot;摘要&quot;&gt;摘要&lt;/h2&gt;

&lt;p&gt;虽然大型语言模型（LLM）在语言理解和交互决策任务中展示出了令人印象深刻的性能，但它们在推理（例如思维链提示）和行动（例如行动计划生成）方面的能力主要被作为分离的主题进行研究。在本文中，我们探索了LLM在交替生成推理跟踪和任务特定行动方面的应用，从而允许两者之间更大的协同效应：推理跟踪帮助模型诱导、跟踪和更新行动计划以及处理异常，而行动则允许它与外部源（如知识库或环境）进行交互并收集额外信息。我们将我们的方法命名为ReAct，并将其应用于各种语言和决策任务中，展示了其在超越最先进基线的同时，提高了人类的可解释性和可信度。具体而言，在问题回答（HotpotQA）和事实验证（Fever）方面，ReAct通过与简单的维基百科API交互，克服了思维链推理中的幻觉和错误传播等普遍问题，并生成了更易解释的人类任务解决轨迹，相比没有推理跟踪的基线有了显著提高。此外，在两个交互式决策基准（ALFWorld和WebShop）上，ReAct的绝对成功率分别超过了模仿学习和强化学习方法34%和10%，并且只提示了一个或两个上下文示例。&lt;/p&gt;

&lt;h2 id=&quot;1-intro&quot;&gt;1. intro&lt;/h2&gt;

&lt;p&gt;人类智能的一个独特特征是能够无缝地将以任务为导向的行动与语言推理（或内部语言，Alderson-Day＆Fernyhough，2015）结合起来，这被理论上认为在人类认知中具有重要作用，以启用自我调节或策略化（Vygotsky，1987; Luria，1965; Fernyhough，2010）并维持工作记忆（Baddeley，1992）。考虑在厨房中做一道菜的例子。在任何两个具体的行动之间，我们可能会用语言进行推理以追踪进度（“现在一切都切好了，我应该把锅里的水烧开”），处理异常或根据情况调整计划（“我没有盐，所以我用酱油和胡椒代替”），以及意识到需要外部信息（“如何准备面团？让我在互联网上搜索一下”）。我们还可以行动（打开菜谱阅读食谱，打开冰箱，检查配料）以支持推理并回答问题（“我现在能做什么菜？”）。这种“行动”和“推理”之间的紧密协同使人类能够快速学习新任务，并进行强健的决策或推理，即使在以前未见过的情况下或面临信息不确定性时也能如此。&lt;/p&gt;

&lt;p&gt;最近的研究结果提示了将语言推理与交互决策制定相结合的自主系统的可能性。一方面，经过适当提示的大型语言模型（LLMs）已经展示出具有新兴能力，可以在算术、常识和符号推理任务中执行几个推理步骤，从问题中推导出答案（Wei等，2022）。然而，这种“思维链”推理是一个静态的黑盒子，因为模型使用自己的内部表示来生成思维，而没有与外部世界联系，这限制了其反应性推理或更新知识的能力。这可能会导致像事实幻觉和推理过程中的错误传播等问题（图1（1b））。另一方面，最近的研究探索了预训练语言模型在交互环境中规划和行动的应用（Ahn等，2022; Nakano等，2021; Yao等，2020; Huang等，2022a），重点是通过语言先验来预测行动。这些方法通常将多模态观察转换为文本，使用语言模型生成特定领域的行动或计划，然后使用控制器选择或执行它们。然而，它们没有利用语言模型对高层次目标进行抽象推理或维护工作记忆以支持行动，除了Huang等人（2022b）在当前状态下执行有限形式的语言推理以重申空间事实。除了这种简单的与几个块交互的具体任务外，还没有研究如何以协同的方式将推理和行动结合起来进行一般任务解决，以及这种组合是否与单独推理或行动相比带来系统性的优势。&lt;/p&gt;

&lt;p&gt;&lt;figure&gt;
          &lt;picture&gt;
            
            
            &lt;img src=&quot;/images/chatgpt/ReAct-figure1.png&quot; alt=&quot;图1：（1）比较4种提示方法，（a）标准，（b）思维链（CoT，仅推理），（c）仅行动，（d）ReAct（推理+行动），解决HotpotQA（Yang等，2018）问题。 （2）比较（a）仅行动和（b）ReAct提示以解决AlfWorld（Shridhar等，2020b）游戏。在这两个领域中，我们省略了提示中的上下文示例，并仅显示模型（行动、思维）和环境（观察）生成的任务解决轨迹。&quot; width= height= /&gt;
          &lt;/picture&gt;
          &lt;figcaption&gt;&lt;i class=&apos;icon-pencil&apos;&gt;&lt;/i&gt;图1：（1）比较4种提示方法，（a）标准，（b）思维链（CoT，仅推理），（c）仅行动，（d）ReAct（推理+行动），解决HotpotQA（Yang等，2018）问题。 （2）比较（a）仅行动和（b）ReAct提示以解决AlfWorld（Shridhar等，2020b）游戏。在这两个领域中，我们省略了提示中的上下文示例，并仅显示模型（行动、思维）和环境（观察）生成的任务解决轨迹。&lt;/figcaption&gt;
        &lt;/figure&gt;&lt;/p&gt;

&lt;p&gt;在这项工作中，我们提出了ReAct，这是一种将语言模型与推理和行动相结合的通用范例，用于解决各种语言推理和决策任务（图1）。ReAct提示LLMs以交替方式生成与任务相关的语言推理轨迹和行动，这使得模型能够执行动态推理，创建、维护和调整高级计划以进行行动（推理到行动），同时与外部环境（例如维基百科）交互，将额外信息纳入推理（行动到推理）。&lt;/p&gt;

&lt;p&gt;我们对ReAct和现有最先进的基准模型在四个不同的基准测试中进行了实证评估：问答（HotPotQA，Yang等，2018），事实验证（Fever，Thorne等，2018），基于文本的游戏（ALFWorld，Shridhar等，2020b）和网页导航（WebShop，Yao等，2022）。对于HotPotQA和Fever，由于可以访问模型可以与之交互的维基百科API，ReAct在表现上优于纯行动生成模型，同时与思维链（CoT）（Wei等，2022）的表现相当。总体而言，最佳方法是ReAct和CoT的组合，允许在推理过程中同时使用内部知识和外部获取的信息。在ALFWorld和WebShop上，使用两个甚至一个ReAct提示就能够优于使用103~105个任务实例进行训练的模仿或强化学习方法，成功率分别提高了34%和10%。我们还通过与仅有行动的对照基线的比较，展示了在决策制定中稀疏且多功能推理的重要性。除了具有广泛的适用性和性能提升之外，推理和行动的组合还有助于模型的可解释性、可信度和可诊断性，在所有领域中，人类可以轻松区分模型内部知识和外部环境中的信息，并检查推理过程以了解模型行动的决策基础。&lt;/p&gt;

&lt;p&gt;综上所述，我们的主要贡献如下：（1）我们引入了ReAct，一种新颖的基于提示的范例，用于将推理和行动在语言模型中协同解决一般任务；（2）我们在各种基准测试中进行了广泛的实验，展示了ReAct在少样本学习设置下相对于之前单独执行推理或行动生成的方法的优势；（3）我们进行了系统的去除实验和分析，以了解在推理任务中行动的重要性以及在交互任务中推理的重要性；（4）我们分析了ReAct在提示设置下的限制（即对推理和行动行为的有限支持），并进行了初始的微调实验，展示了ReAct利用额外训练数据改善性能的潜力。将ReAct扩展到更多任务的训练和操作，并将其与强化学习等互补的范例相结合，可以进一步释放大型语言模型的潜力。&lt;/p&gt;

&lt;h2 id=&quot;2-协同推理和行动&quot;&gt;2. 协同推理和行动&lt;/h2&gt;

&lt;div class=&quot;table-container&quot;&gt;
&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;考虑一个代理与环境互动以解决任务的一般设置。在时间步骤t，代理从环境接收观察值ot∈O，并根据某个策略π(at&lt;/td&gt;
      &lt;td&gt;ct)采取行动at∈A，其中ct=(o1, a1, · · · , ot−1, at−1, ot)是代理的上下文。当映射ct 7→ at非常隐式且需要大量计算时，学习策略面临挑战。例如，图1（1c）中的代理无法生成正确的最终行动（Act 4）以完成问答任务，因为它需要对轨迹上下文（问题、行动1-3、观察1-3）进行复杂的推理。同样，图1（2a）中的代理未能从上下文中理解到水槽1不含有胡椒瓶1，因此不断产生幻觉行动。&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;

&lt;p&gt;ReAct的想法很简单：我们将代理的行动空间增加到Aˆ = A ∪ L，其中L是语言空间。语言空间中的行动aˆt∈L，我们将其称为思考或推理轨迹，不会影响外部环境，因此不会产生观察反馈。相反，思考aˆt旨在通过对当前上下文ct进行推理来组合有用的信息，并更新上下文ct+1 = (ct, aˆt)以支持未来的推理或行动。如图1所示，可以有各种类型的有用的思考，例如分解任务目标并创建行动计划（2b，Act 1；1d，Thought 1），注入与任务解决相关的常识知识（2b，Act 1），从观察中提取重要部分（1d，Thought2，4），跟踪进度并转移行动计划（2b，Act 8），处理异常情况并调整行动计划（1d，Thought 3），等等。&lt;/p&gt;

&lt;p&gt;然而，由于语言空间L是无限的，学习这个增强的行动空间是困难的，需要强大的语言先验知识。在本文中，我们主要关注的是这样的设置：使用冻结的大型语言模型PaLM-540B（Chowdhery等，2022）1，通过少量的上下文示例来提示生成特定领域的行动和自由形式的语言思考以解决任务（图1（1d），（2b））。每个上下文示例都是一种人类轨迹，包含了一系列行动、思考和环境观察，用于解决一个任务实例（详见附录C）。对于需要推理的主要任务（图1（1）），我们交替生成思考和行动，使任务解决轨迹由多个思考-行动-观察步骤组成。相反，对于可能涉及大量行动的决策制定任务（图1（2）），思考只需要在轨迹的最相关位置稀疏出现，因此我们让语言模型自行决定思考和行动的异步发生。&lt;/p&gt;

&lt;p&gt;由于决策制定和推理能力整合到了大型语言模型中，ReAct具有几个独特的特点：A）直观易用的设计：设计ReAct提示很简单，因为人类注释者只需在其采取的行动之上用语言记录下他们的思考过程。本文中没有使用特定的格式选择、思考设计或示例选择。我们在第3和第4节详细介绍了每个任务的提示设计。B）通用灵活的：由于思考空间和思考-行动发生的灵活性，ReAct适用于具有不同行动空间和推理需求的各种任务，包括但不限于问答、事实验证、文本游戏和Web导航。C）性能优越且健壮：ReAct在仅使用一个到六个上下文示例进行学习的情况下，对新任务实例具有很强的泛化性能，始终优于仅在不同领域进行推理或行动的基线。我们还在第3节展示了启用微调时的额外好处，以及在第4节中展示了ReAct在提示选择方面的健壮性。D）与人类对齐且可控：ReAct承诺提供一个可解释的顺序决策制定和推理过程，人类可以轻松检查推理和事实正确性。此外，如第4节中的图5所示，人类还可以通过思考编辑来控制或更正代理行为。&lt;/p&gt;

&lt;h2 id=&quot;3-基于知识的推理任务&quot;&gt;3. 基于知识的推理任务&lt;/h2&gt;

&lt;p&gt;我们从基于知识的推理任务开始，例如多跳问答和事实验证。如图1（1d）所示，通过与维基百科API交互，ReAct能够检索支持推理的信息，同时利用推理来确定下一步要检索什么，展示了推理和行动的协同作用。&lt;/p&gt;

&lt;h3 id=&quot;31-设置&quot;&gt;3.1 设置&lt;/h3&gt;

&lt;p&gt;域：我们考虑两个具有挑战性的知识检索和推理数据集：（1）Hot-PotQA（Yang等，2018），一个需要在两个或多个维基百科段落上进行推理的多跳问答基准测试，和（2）FEVER（Thorne等，2018），一个事实验证基准测试，其中每个声明基于是否存在维基百科段落来进行支持、反驳或不足够信息的注释。在本文中，我们对这两个任务都采用仅有问题的设置，模型仅接收问题/声明作为输入，没有访问支持段落的权限，并且必须依靠其内部知识或通过与外部环境交互来检索知识以支持推理。&lt;/p&gt;

&lt;p&gt;行动空间：我们设计了一个简单的维基百科Web API，支持交互式信息检索，包括三种类型的行动：（1）搜索[实体]，如果存在，则返回相应实体维基页面的前5句话，否则建议维基百科搜索引擎的前5个相似实体；（2）查找[字符串]，它会返回包含字符串的页面中的下一句话，模拟浏览器上Ctrl+F的功能；（3）完成[答案]，它会使用答案结束当前任务。我们注意到，这个行动空间主要只能基于精确段落名称检索到段落的一小部分，远远不如最先进的词汇或神经检索器。目的是模拟人类如何与维基百科交互，并通过明确的语言推理强制模型检索信息。&lt;/p&gt;

&lt;h3 id=&quot;32-方法&quot;&gt;3.2 方法&lt;/h3&gt;

&lt;p&gt;ReAct Prompting: 对于HotpotQA和FEVER，我们从训练集中随机选择了6个和3个案例，并手动编写了ReAct格式的轨迹，用作提示中的少量样本。与图1（d）类似，每个轨迹由多个思考-行动-观察步骤（即密集思考）组成，其中自由形式的思考用于各种目的。具体而言，我们使用一些思考来分解问题（“我需要搜索x，找到y，然后找到z”），从维基百科观察中提取信息（“x始于1844年”，“该段落没有提到x”），进行常识（“x不是y，所以z必须是…”）或算术推理（“1844 &amp;lt; 1989”），指导搜索重构（“也许我可以搜索/查找x”），并综合最终答案（“…所以答案是x”）。更多详细信息请参见附录C。&lt;/p&gt;

&lt;p&gt;&lt;figure&gt;
          &lt;picture&gt;
            
            
            &lt;img src=&quot;/images/chatgpt/ReAct-table1.png&quot; alt=&quot;表1：PaLM-540B在HotpotQA和FEVER上的提示结果&quot; width= height= /&gt;
          &lt;/picture&gt;
          &lt;figcaption&gt;&lt;i class=&apos;icon-pencil&apos;&gt;&lt;/i&gt;表1：PaLM-540B在HotpotQA和FEVER上的提示结果&lt;/figcaption&gt;
        &lt;/figure&gt;&lt;/p&gt;

&lt;p&gt;&lt;figure&gt;
          &lt;picture&gt;
            
            
            &lt;img src=&quot;/images/chatgpt/ReAct-figure2.png&quot; alt=&quot;图2：PaLM-540B在使用不同数量的CoT-SC样本时的提示结果&quot; width= height= /&gt;
          &lt;/picture&gt;
          &lt;figcaption&gt;&lt;i class=&apos;icon-pencil&apos;&gt;&lt;/i&gt;图2：PaLM-540B在使用不同数量的CoT-SC样本时的提示结果&lt;/figcaption&gt;
        &lt;/figure&gt;&lt;/p&gt;

&lt;p&gt;基线: 我们系统地删除ReAct轨迹以构建用于多个基线的提示（格式如图1（1a-1c））：（a）标准提示（Standard），它删除了ReAct轨迹中的所有思考、行动和观察。 （b）思考链提示（CoT）（Wei等，2022），它删除了行动和观察，并作为仅基于推理的基线。我们还通过在推理过程中使用解码温度0.7对21个CoT轨迹进行采样并采用多数答案来构建自一致性基线（CoT-SC）（Wang等，2022a;b），发现它始终提高了CoT的性能。 （c）仅执行提示（Act），它删除了ReAct轨迹中的思考，略微类似于WebGPT（Nakano等，2021）如何与互联网交互以回答问题，尽管它针对不同的任务和行动空间，并使用模仿和强化学习而不是提示。&lt;/p&gt;

&lt;p&gt;连接内部的和外部的知识：正如将在第3.3节中详细介绍的那样，我们观察到ReAct展示的问题解决过程更加事实和基础，而CoT在制定推理结构方面更加准确，但很容易受到虚构的事实或思考的影响。因此，我们建议将ReAct和CoT-SC结合起来，并让模型根据以下启发式决定何时切换到另一种方法：A）ReAct→CoT-SC：当ReAct在给定的步骤内未能返回答案时，回退到CoT-SC。我们为HotpotQA和FEVER设置了7和5个步骤，因为我们发现更多的步骤不会改善ReAct的性能3。B）CoT-SC→ReAct：当n个CoT-SC样本中的多数答案出现不到n/2次时（即内部知识可能不够自信地支持任务），回退到ReAct。&lt;/p&gt;

&lt;p&gt;微调：由于在大规模手动注释推理轨迹和行动方面存在挑战，我们考虑采用类似于Zelikman等人（2022）的引导方法，使用由ReAct（以及其他基线）生成的带有正确答案的3,000个轨迹来微调较小的语言模型（PaLM-8/62B）以解码轨迹（所有思考、行动、观察），并以输入问题/声明为条件。更多细节请参见附录B.1。&lt;/p&gt;

&lt;h2 id=&quot;6-结论&quot;&gt;6 结论&lt;/h2&gt;

&lt;p&gt;我们提出了ReAct—一种简单而有效的方法，用于在大型语言模型中协同推理和行动。通过多种多跳问答、事实检查和交互决策任务的实验，我们展示了ReAct可以实现卓越性能和可解释的决策追踪。尽管我们的方法简单，但大型行动空间的复杂任务需要更多的演示来学习，这很不幸可能会超出上下文学习的输入长度限制。我们探索了在HotpotQA上的微调方法，并取得了初步的有希望的结果，但是从更多高质量的人类注释中学习将是进一步提高性能的必要条件。通过多任务训练扩展ReAct并将其与强化学习等补充范例相结合，可能会产生更强大的代理程序，进一步释放LLM在更多应用中的潜力。&lt;/p&gt;
</description>
        <pubDate>Thu, 04 May 2023 22:48:00 +0000</pubDate>
        <link>https://wulfric.me/2023/05/ReAct-ch/</link>
        <guid isPermaLink="true">https://wulfric.me/2023/05/ReAct-ch/</guid>
        
        <category>ai</category>
        
        <category>chatgpt</category>
        
        
        <category>技术</category>
        
      </item>
    
      <item>
        <title>Transformer 论文翻译：Attention is All you Need</title>
        <description>&lt;p&gt;注意力就是一切。&lt;/p&gt;

&lt;h2 id=&quot;摘要&quot;&gt;摘要&lt;/h2&gt;

&lt;p&gt;显性序列转导模型基于包括编码器和解码器的复杂递归或卷积神经网络。性能最佳的模型还通过注意力机制连接编码器和解码器。我们提出了一种新的简单网络架构，即 Transformer，它完全基于注意力机制，完全摒弃了循环和卷积。对两项机器翻译任务的实验表明，这些模型在质量上更胜一筹，同时可并行化程度更高，并且需要的训练时间明显减少。我们的模型在 WMT 2014 英德翻译任务中达到了 28.4 BLEU，比现有的最佳结果（包括集成）提高了超过 2 BLEU。在 WMT 2014 英法翻译任务中，我们的模型在八个 GPU 上训练 3.5 天后建立了一个新的单模型最先进的 BLEU 分数 41.8，这是最好的训练成本的一小部分来自文献的模型。我们通过将 Transformer 成功应用于具有大量和有限训练数据的英语选区解析，证明 Transformer 可以很好地泛化到其他任务。&lt;/p&gt;

&lt;h2 id=&quot;1-intro&quot;&gt;1. intro&lt;/h2&gt;

&lt;p&gt;循环神经网络、长短时记忆[13]和门控循环神经网络[7]已经被确定为序列建模和转换问题的最先进方法，例如语言建模和机器翻译[35、2、5]。自那以后，许多努力继续推动循环语言模型和编码器-解码器架构的边界[38、24、15]。&lt;/p&gt;

&lt;p&gt;循环模型通常将输入和输出序列的符号位置作为计算的因素进行分解。将位置与计算时间步骤对齐，它们会生成一系列隐藏状态 $h_t$，作为先前隐藏状态 $h_{t−1}$ 和位置t的输入的函数。这种内在的连续性特质导致了在训练示例内部的并行化不可行，而在处理更长的序列时，记忆约束限制了跨示例批处理，因此这一点尤为重要。近期的工作通过分解技巧[21]和条件计算[32]取得了显著的计算效率提升，同时在后者的情况下提高了模型的性能。然而，顺序计算的基本约束仍然存在。&lt;/p&gt;

&lt;p&gt;注意机制已成为各种任务中引人注目的序列建模和转录模型的不可或缺部分，在不考虑输入或输出序列中它们的距离的情况下建模依赖性 [2,19]。然而，在除了少数情况[27]以外，这样的注意机制通常与循环网络一起使用。&lt;/p&gt;

&lt;p&gt;在本文中，我们提出Transformer模型架构，它摈弃了循环结构，而是完全依赖于注意力机制，以绘制输入和输出之间的全局关系。Transformer允许更高程度的并行化，在使用8个P100 GPU进行训练仅仅12个小时后，就可以在翻译质量上达到新的最高水平。&lt;/p&gt;

&lt;h2 id=&quot;2-背景&quot;&gt;2. 背景&lt;/h2&gt;

&lt;p&gt;降低串行计算的目标也是Extended Neural GPU [16]、ByteNet [18]和ConvS2S [9]的基础，它们都使用卷积神经网络作为基本构建块，为所有输入和输出位置并行地计算其隐藏表示。这些模型中，从两个任意输入或输出位置关联信号所需的操作数量随着位置之间的距离增加而增加，对于ConvS2S是线性增长，对于ByteNet是对数增长。这使得学习远距离位置之间的依赖关系变得更加困难[12]。在Transformer中，这被减少为固定数量的操作，但代价是由于平均注意力加权位置而导致的有效分辨率降低效应，我们在3.2节中所述的多头注意力中对此进行了抵消。&lt;/p&gt;

&lt;p&gt;自注意力（Self-attention），有时也被称为内部注意力，是一种关注单个序列中不同位置的注意力机制，用于计算序列的表示。自注意力已成功应用于多种任务，包括阅读理解、抽象概括、文本蕴涵和学习任务无关的句子表示[4, 27, 28, 22]。&lt;/p&gt;

&lt;p&gt;端到端记忆网络基于循环注意力机制，而非与序列对齐的循环机制，已被证明在简单语言问答和语言建模任务方面表现良好[34]。&lt;/p&gt;

&lt;p&gt;据我们所知，Transformer 是第一个完全依赖自注意力来计算其输入和输出表示的转换模型，而无需使用与序列对齐的 RNN 或卷积。在以下几节中，我们将介绍 Transformer，阐述自注意力的动机，并讨论其相较于诸如 [17, 18] 和 [9] 等模型的优势。&lt;/p&gt;

&lt;h2 id=&quot;3-模型架构&quot;&gt;3. 模型架构&lt;/h2&gt;

&lt;p&gt;大多数竞争性神经序列转换模型都具有编码器-解码器结构[5, 2, 35]。其中，编码器将符号表示的输入序列(x1,…,xn)映射到一系列连续表示z = (z1,…,zn)。给定z，解码器随后生成一个符号输出序列(y1, …, ym)，每次生成一个元素。在每个步骤中，该模型是自回归的[10]，在生成下一个符号时消耗先前生成的符号作为额外的输入。&lt;/p&gt;

&lt;p&gt;&lt;figure&gt;
          &lt;picture&gt;
            
            
            &lt;img src=&quot;/images/chatgpt/Transformer-encoder-decoder.png&quot; alt=&quot;图1：Transformer 模型架构&quot; width= height= /&gt;
          &lt;/picture&gt;
          &lt;figcaption&gt;&lt;i class=&apos;icon-pencil&apos;&gt;&lt;/i&gt;图1：Transformer 模型架构&lt;/figcaption&gt;
        &lt;/figure&gt;&lt;/p&gt;

&lt;p&gt;Transformer遵循这种总体架构，使用堆叠的自注意力和点式全连接层分别作为编码器和解码器，分别显示在图1的左半部分和右半部分。&lt;/p&gt;

&lt;h3 id=&quot;31-编码器栈解码器栈&quot;&gt;3.1 编码器栈、解码器栈&lt;/h3&gt;

&lt;p&gt;编码器：编码器由N = 6个相同的层堆叠而成。每层包含两个子层：第一个是多头自注意力机制，第二个是简单的位置全连接前馈网络。我们在每个子层周围采用残差连接[11]，然后进行层归一化[1]。也就是说，每个子层的输出为LayerNorm(x + Sublayer(x))，其中Sublayer(x)是子层本身实现的函数。为了方便这些残差连接，模型中的所有子层以及嵌入层的输出维度均为dmodel = 512。&lt;/p&gt;

&lt;p&gt;解码器：解码器也由N = 6个相同的层堆叠而成。除了每个编码器层中的两个子层外，解码器还插入第三个子层，该子层对编码器堆栈的输出执行多头注意力。与编码器类似，我们在每个子层周围采用残差连接，然后进行层归一化。我们还修改了解码器堆栈中的自注意子层，以防止位置注意到后续位置。这种遮蔽机制，以及输出嵌入在位置上的偏移量，确保位置i的预测仅取决于小于i的已知输出位置。&lt;/p&gt;

&lt;h3 id=&quot;32-注意力&quot;&gt;3.2 注意力&lt;/h3&gt;

&lt;p&gt;注意力函数可以描述为将一个查询和一组键值对映射到一个输出，其中查询、键、值和输出都是向量。输出是值的加权和，其中分配给每个值的权重是由查询与相应键的兼容性函数计算的。&lt;/p&gt;

&lt;p&gt;&lt;figure&gt;
          &lt;picture&gt;
            
            
            &lt;img src=&quot;/images/chatgpt/Transformer-attention2.png&quot; alt=&quot;图2：(左) 缩放点积注意力 (右) 多头注意力由多个并行运行的注意层组成。&quot; width= height= /&gt;
          &lt;/picture&gt;
          &lt;figcaption&gt;&lt;i class=&apos;icon-pencil&apos;&gt;&lt;/i&gt;图2：(左) 缩放点积注意力 (右) 多头注意力由多个并行运行的注意层组成。&lt;/figcaption&gt;
        &lt;/figure&gt;&lt;/p&gt;

&lt;h4 id=&quot;321-缩放点积注意力&quot;&gt;3.2.1 缩放点积注意力&lt;/h4&gt;

&lt;p&gt;我们将我们特定的注意力称为“缩放点积注意力”（图2）。输入由维度为 $d_k$ 的查询和键以及维度为 $d_v$ 的值组成。我们计算查询与所有键的点积，将每个点积除以 $\sqrt{d_k}$，并应用softmax函数以获得值的权重。&lt;/p&gt;

&lt;p&gt;在实践中，我们同时在一组查询上计算注意力函数，将它们打包成矩阵Q。键和值也打包成矩阵K和V。我们计算输出矩阵如下：&lt;/p&gt;

\[Attention(Q,K,V) = softmax(\frac{QK^T}{\sqrt{d_k}})V\]

&lt;p&gt;两种最常用的注意力函数是加性注意力[2]和点积（乘法）注意力。点积注意力与我们的算法相同，除了缩放因子 $\frac{1}{d_k}$。加性注意力使用具有单个隐藏层的前馈网络计算兼容性函数。虽然两者在理论复杂度上相似，但在实践中，点积注意力速度更快，空间效率更高，因为它可以使用高度优化的矩阵乘法代码实现。&lt;/p&gt;

&lt;p&gt;虽然在$d_k$的值较小的情况下，这两种机制的表现相似，但对于较大的$d_k$值，加性注意力优于没有缩放的点积注意力[3]。我们怀疑对于较大的$d_k$值，点积变得很大，将softmax函数推入具有极小梯度的区域&lt;sup id=&quot;fnref:4&quot;&gt;&lt;a href=&quot;#fn:4&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;。为了抵消这种效应，我们通过 $\frac{1}{d_k}$ 对点积进行缩放。&lt;/p&gt;

&lt;h4 id=&quot;322-多头注意力&quot;&gt;3.2.2 多头注意力&lt;/h4&gt;

&lt;p&gt;我们发现，通过使用不同的、经过学习的线性映射将查询、键和值投影h次，分别投影到dk、dk和dv维度，而不是使用dmodel维度的键、值和查询执行单个注意力函数，可以获得更好的效果。对于这些投影版本的查询、键和值，我们并行执行注意力函数，得到dv维的输出值。然后将这些值进行连接，并再次进行投影，得到最终的值，如图2所示。&lt;/p&gt;

&lt;p&gt;多头注意力允许模型在不同位置同时关注来自不同表示子空间的信息。使用单个注意力头时，平均效应会抑制这种效果。&lt;/p&gt;

\[MultiHead(Q,K,V)=Concat(head_1,...,head_h)W^O \\
where \ head_i=Attention(QW_i^Q,KW_i^K,VW_i^V)\]

&lt;p&gt;这里的线性映射就是参数矩阵&lt;/p&gt;

\[W_i^Q \in \mathbb{R}^{d_{model}\times d_k},W_i^K\in \mathbb{R}^{d_{model}\times d_k},W_i^V\in \mathbb{R}^{d_{model}\times d_v}, W^O\in \mathbb{R}^{hd_v \times d_{model}}\]

&lt;p&gt;在本文中，我们使用了h = 8个平行的注意力层或头。对于每个头，我们使用dk = dv = dmodel/h = 64。由于每个头的维度降低，总的计算成本类似于全维度的单头注意力。&lt;/p&gt;

&lt;h4 id=&quot;323-我们的模型中注意力机制的应用&quot;&gt;3.2.3 我们的模型中，注意力机制的应用&lt;/h4&gt;

&lt;p&gt;Transformer使用多头注意力有三种不同的方式：&lt;/p&gt;

&lt;p&gt;• 在“编码器-解码器注意力”层中，查询来自前一个解码器层，而记忆键和值来自编码器的输出。这使得解码器中的每个位置都可以关注输入序列中的所有位置。这类似于序列到序列模型（如[38, 2, 9]）中的典型编码器-解码器注意力机制。&lt;/p&gt;

&lt;p&gt;• 编码器包含自注意力层。在自注意力层中，所有的键、值和查询都来自同一个地方，即编码器中前一层的输出。编码器中的每个位置都可以关注编码器前一层中的所有位置。&lt;/p&gt;

&lt;p&gt;• 同样，解码器中的自注意力层允许解码器中的每个位置关注解码器中直到该位置的所有位置。我们需要防止解码器中的左向信息流，以保留自回归属性。我们通过在缩放点积注意力内部屏蔽（将其设置为−∞）所有与非法连接相对应的softmax输入中的值来实现这一点。请参见图2。&lt;/p&gt;

&lt;h3 id=&quot;33-位置编码前馈网络&quot;&gt;3.3 位置编码前馈网络&lt;/h3&gt;

&lt;p&gt;除了注意力子层外，我们的编码器和解码器中的每个层都包含一个全连接的前馈神经网络&lt;sup id=&quot;fnref:PFFN&quot;&gt;&lt;a href=&quot;#fn:PFFN&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;，它是独立地应用于每个位置并且相同地处理。该前馈神经网络由两个线性变换层组成，中间使用ReLU激活函数。&lt;/p&gt;

\[FFN(x)=max(0,xW_1+b_1)W_2+b_2\]

&lt;p&gt;虽然线性变换在不同位置上是相同的，但它们在不同层之间使用不同的参数。另一种描述方式是将其视为两个内核大小为1的卷积。输入和输出的维度是dmodel = 512，而内部层的维度为dff = 2048。&lt;/p&gt;

&lt;h3 id=&quot;34-嵌入和-softmax&quot;&gt;3.4 嵌入和 softmax&lt;/h3&gt;

&lt;p&gt;与其他序列转换模型类似，我们使用学习得到的嵌入将输入和输出的标记转换为dmodel维度的向量。我们还使用通常的学习过的线性变换和softmax函数将解码器的输出转换为预测的下一个 token 的概率。在我们的模型中，我们在两个嵌入层和pre-softmax线性转换之间共享同一权重矩阵，类似于[30]。在嵌入层中，我们将这些权重乘以 $\sqrt{d_{model}}$。&lt;/p&gt;

&lt;h3 id=&quot;35-位置编码&quot;&gt;3.5 位置编码&lt;/h3&gt;

&lt;p&gt;由于我们的模型不包含循环和卷积，为了使模型能够利用序列的顺序，我们必须注入有关序列中标记的相对或绝对位置的一些信息。为此，在编码器和解码器堆栈底部的输入嵌入中添加“位置编码”。位置编码与嵌入具有相同的维度dmodel，因此可以将它们相加。有许多位置编码的选择，可以是可学习的或固定的[9]。&lt;/p&gt;

&lt;p&gt;&lt;figure&gt;
          &lt;picture&gt;
            
            
            &lt;img src=&quot;/images/chatgpt/Transformer-table1.png&quot; alt=&quot;表1：不同层类型的最大路径长度、每层复杂度和最小顺序操作数。n是序列长度，d是表示维度，k是卷积的内核大小，r是受限自注意力中邻域的大小。&quot; width= height= /&gt;
          &lt;/picture&gt;
          &lt;figcaption&gt;&lt;i class=&apos;icon-pencil&apos;&gt;&lt;/i&gt;表1：不同层类型的最大路径长度、每层复杂度和最小顺序操作数。n是序列长度，d是表示维度，k是卷积的内核大小，r是受限自注意力中邻域的大小。&lt;/figcaption&gt;
        &lt;/figure&gt;&lt;/p&gt;

&lt;p&gt;我们的工作中使用了不同频率的 sin 和 cos 函数：&lt;/p&gt;

\[PE_{(pos,2i)}=sin(pos/10000^{2i/d_{model}}) \\
PE_{(pos,2i+1)}=cos(pos/10000^{2i/d_{model}})\]

&lt;p&gt;其中，pos 是未知，i 是维度。也就是说，位置编码中的每个维度对应于一个正弦波。波长从2π到10000·2π形成一个几何级数。我们选择这个函数是因为我们假设它可以使模型轻松地学习相对位置的注意力，由于对于任何固定的偏移k，$PE_{pos+k}$ 可以表示为$PE_{pos}$ 的线性函数。&lt;/p&gt;

&lt;p&gt;我们还尝试使用学习得到的位置嵌入[9]，并发现两个版本产生的结果几乎相同（见表3行（E））。我们选择正弦版本是因为它可能允许模型外推到比训练中遇到的序列长度更长的序列长度。&lt;/p&gt;

&lt;h2 id=&quot;4-为什么选择-self-attention&quot;&gt;4. 为什么选择 Self-attention&lt;/h2&gt;

&lt;p&gt;在本节中，我们将自注意力层的各个方面与常用于将一个可变长度符号表示序列（x1, …, xn）映射到另一个等长序列（z1, …, zn）的循环和卷积层进行比较（ xi, zi ∈ Rd），比如典型序列转换编码器或解码器中的隐藏层。我们考虑三个期望目标来激发我们使用自注意力。&lt;/p&gt;

&lt;p&gt;一个是每层的总计算复杂度。另一个是可以并行化的计算量，用所需的最小顺序操作次数衡量。第三个是网络中长距离依赖关系之间的路径长度。在许多序列转换任务中，学习长距离依赖关系是一个关键挑战。影响学习这种依赖关系能力的一个关键因素是网络中前向和后向信号必须遍历的路径长度。输入和输出序列中任何位置组合之间的路径越短，学习长距离依赖关系就越容易[12]。因此，我们还比较了由不同层类型组成的网络中任意两个输入和输出位置之间的最大路径长度。&lt;/p&gt;

&lt;p&gt;正如表1所示，自注意力层通过执行固定数量的串行运算将所有位置连接起来，而循环层则需要进行O(n)个。在计算复杂度方面，当序列长度n小于表示维度d时，自注意力层的速度比循环层更快。在机器翻译领域表现最佳的模型所使用的语句表示（如单词片段[38]和字节对[31]表示）通常都是这种情况。为了提高涉及非常长的序列的任务的计算性能，我们可以将自关注限制在大小为r的输入序列周围的相应输出位置的邻域中考虑。这将增加最大路径长度到O（n / r）。我们计划在未来的工作中进一步研究这种方法。&lt;/p&gt;

&lt;p&gt;一个内核宽度为k &amp;lt; n的卷积层，不能连接所有输入和输出位置对。这需要在连续内核情况下使用O(n/k)个卷积层或在扩张卷积情况下使用O(logk(n))个卷积层[18]，从而增加网络中任意两个位置之间最长路径的长度。相比循环层，卷积层通常更昂贵，因为要乘以一个因子k。然而，可分离卷积[6]可以大大降低复杂性至O(k · n · d + n · d2)。即使是当k = n时，可分离卷积的复杂度也等于自注意力层和逐点前馈层的组合，即在我们模型中采用的这种方法。&lt;/p&gt;

&lt;p&gt;作为副作用，自注意力可以产生更易解释的模型。我们检查了我们模型中的注意分布，并在附录中呈现和讨论了例子。不仅单个注意头清楚地学习执行不同的任务，而且许多似乎表现出与句子的语法和语义结构相关的行为。&lt;/p&gt;

&lt;h2 id=&quot;6-结果&quot;&gt;6. 结果&lt;/h2&gt;

&lt;p&gt;在WMT 2014英德翻译任务中，大型Transformer模型（表2中的Transformer（big））的表现超过了先前报告的最佳模型（包括集成模型）超过2.0 BLEU，建立了一个新的BLEU得分最高的记录，为28.4。该模型的配置列在表3的底部行。训练使用8个P100 GPU进行了3.5天。即使是我们的基础模型也超过了所有先前发布的模型和集成模型，在训练成本的一小部分内。&lt;/p&gt;

&lt;h2 id=&quot;7-结论&quot;&gt;7. 结论&lt;/h2&gt;

&lt;p&gt;在这项工作中，我们提出了Transformer，这是第一个完全基于注意力的序列转导模型，用多头自注意力替换了编码器-解码器架构中最常用的循环层。&lt;/p&gt;

&lt;p&gt;对于翻译任务，Transformer 模型的训练速度比基于循环或卷积层的架构要快得多。在WMT 2014英德和英法翻译任务中，我们实现了新的最先进水平。在前一项任务中，我们的最佳模型甚至超过了以前报告的所有集合模型。&lt;/p&gt;

&lt;p&gt;我们对基于注意力机制的模型未来感到兴奋，并计划将其应用于其他任务。我们计划将Transformer扩展到涉及文本以外的输入和输出模态问题，并研究局部、受限制的注意力机制，以有效处理大量输入和输出，如图像、音频和视频。使生成 less sequential 是我们的另一个研究目标。&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:4&quot;&gt;
      &lt;p&gt;为了说明为什么点积变得很大，假设q和k的分量是具有均值0和方差1的独立随机变量。那么它们的点积，$q·k = \sum_{i=1}^{d_k} q_ik_i$ 的均值为0，方差为dk。 &lt;a href=&quot;#fnref:4&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:PFFN&quot;&gt;
      &lt;p&gt;（位置编码前馈网络）是Transformer模型中的一种组件，用于在每个注意力子层之间增加一个前馈神经网络层，从而提高模型性能。该网络层是在每个位置上独立地应用的，因此称为“位置编码”。它由两个全连接层组成，每个层之间使用ReLU激活函数。在每个位置上，该网络将输入向量映射到具有相同维度的中间向量，再通过第二个全连接层将中间向量映射回原始维度的输出向量。通过使用位置编码前馈网络，Transformer模型能够更好地处理不同位置之间的依赖关系，从而提高了其在序列到序列任务中的性能。 &lt;a href=&quot;#fnref:PFFN&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Fri, 28 Apr 2023 22:48:00 +0000</pubDate>
        <link>https://wulfric.me/2023/04/Transformer-ch/</link>
        <guid isPermaLink="true">https://wulfric.me/2023/04/Transformer-ch/</guid>
        
        <category>ai</category>
        
        <category>chatgpt</category>
        
        
        <category>技术</category>
        
      </item>
    
      <item>
        <title>GPT3 论文翻译：Language Models are Few-Shot Learners</title>
        <description>&lt;p&gt;语言模型是少样本学习者。&lt;/p&gt;

&lt;h2 id=&quot;摘要&quot;&gt;摘要&lt;/h2&gt;

&lt;p&gt;近期的研究表明，在大型文本语料库上进行预训练，然后针对特定任务进行微调，可以在许多NLP任务和基准测试中取得显著成果。尽管这种方法在架构上通常不针对特定任务，但它仍然需要由数千或数万个样本组成的任务特定微调数据集。相比之下，人类通常只需几个示例或简单的指令就能完成新的语言任务，然而当前的NLP系统在很大程度上仍难以实现这一目标。在这里，我们展示了通过扩大语言模型规模可以极大地提高任务通用的少样本性能，有时甚至能达到与先前最先进的微调方法相媲美的水平。具体来说，我们训练了GPT-3，一种具有1750亿参数的自回归语言模型，比以前任何非稀疏语言模型多10倍，并在少样本设置中测试其性能。对于所有任务，GPT-3均无需进行任何梯度更新或微调，任务和少样本演示仅通过与模型的文本交互进行指定。GPT-3在许多NLP数据集上表现优异，包括翻译、问答和填空任务，以及一些需要实时推理或领域自适应&lt;sup id=&quot;fnref:dat&quot;&gt;&lt;a href=&quot;#fn:dat&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot; role=&quot;doc-noteref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;的任务，如还原打乱的单词、在句子中使用新词或进行3位数算术运算。同时，我们还发现了，GPT-3在一些数据集上的少样本学习仍然存在困难，同时在一些大型网络语料库数据集的训练中遇到了一些方法论问题。最后，我们发现GPT-3可以生成新闻文章样本，人类评估者很难将其与人类撰写的文章区分开来。我们讨论了这一发现以及GPT-3总体产生的更广泛的社会影响。&lt;/p&gt;

&lt;h2 id=&quot;1-导言&quot;&gt;1. 导言&lt;/h2&gt;

&lt;p&gt;近年来，NLP系统中出现了一个趋势，即使用越来越灵活、任务无关的方式应用预训练的「语言表示」来进行下游迁移。首先，利用词向量来学习单层表示[MCCD13, PSM14]并输入到任务特定的架构中，然后使用具有多层表示和上下文状态的RNN形成更强大的表示[DL15, MBXS17, PNZtY18]（尽管仍应用于任务特定的架构），最近则直接微调预训练的循环或Transformer语言模型[VSP+17]，完全消除了任务特定架构的需求[RNSS18, DCLT18, HR18]。&lt;/p&gt;

&lt;p&gt;最后这种范式在许多具有挑战性的NLP任务上取得了实质性进展，如阅读理解、问答、文本蕴涵等，并在新的架构和算法的基础上不断发展[RSR+19, LOG+19, YDY+19, LCG+19]。然而，这种方法的一个主要局限性在于，尽管架构是任务通用的，但仍然需要任务特定的数据集和任务特定的微调：要在期望的任务上取得优异表现，通常需要在针对该任务的数千到数十万个示例的数据集上进行微调。消除这种局限性是可取的，原因有以下几点。&lt;/p&gt;

&lt;p&gt;首先，从实践角度来看，每个新任务都需要大量已标注样本的数据集限制了语言模型的应用范围。有非常多的有用的语言任务，包括纠正语法、生成抽象概念示例、评论短篇小说等等。对于这些任务中的很多来说，收集大量监督训练数据集极具挑战，尤其是当这个过程需要为每个新任务重复进行的时候。&lt;/p&gt;

&lt;p&gt;其次，随着模型表达能力愈发强大和训练数据分布愈发狭窄，挖掘训练数据中的非实质性关联的潜力也随之增加。这给预训练加微调范式带来了问题，在这个范式中，模型被设计成庞大的，以便在预训练阶段吸收信息，但随后会在非常狭窄的任务分布上进行微调。例如，有研究发现，大模型并不一定在分布之外具有更好的泛化能力。有证据表明，在这种范式下所实现的泛化性能可能较差，因为模型过于依赖训练分布，在其之外泛化能力不足。因此，在特定基准测试上微调模型的性能，即使名义上达到人类水平，也可能高估了其在实际任务中的表现。&lt;/p&gt;

&lt;p&gt;第三，人类在学习大部分语言任务时，并不需要大量的有监督数据集。一条简短的自然语言指令（例如，“请告诉我这句话描述的是快乐还是悲伤”）或者仅有的几个示例（例如，“这里有两个勇敢行为的例子；请给出第三个勇敢的例子”）通常就足以让人类具备至少一定程度的能力来执行新任务。除了揭示我们目前的NLP技术在概念上的局限性之外，这种适应能力还具有实际优势——它使人类能够在许多任务和技能之间无缝地混合或切换，如在漫长的对话过程中进行加法运算。为了使NLP系统更具实用性，我们期望未来的NLP系统能够拥有与人类相同的灵活性和通用性。&lt;/p&gt;

&lt;h2 id=&quot;8结论&quot;&gt;8结论&lt;/h2&gt;

&lt;p&gt;我们展示了一个拥有1750亿参数的语言模型，在零样本、单样本和少样本设置下，该模型在许多NLP任务和基准测试中表现出强大的性能。在某些情况下，它几乎能匹敌顶尖的微调系统的性能，同时能够生成高质量的样本，以及在即时定义的任务中表现出优秀的定性性能。我们记录了在不使用微调的情况下，性能在规模方面的大致可预测趋势。我们还讨论了这类模型的社会影响。尽管存在许多局限性和弱点，但这些结果表明，非常大的语言模型可能是可适应、通用语言系统发展中的重要组成部分。&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:dat&quot;&gt;
      &lt;p&gt;在自然语言处理（NLP）中，领域自适应任务（domain adaptation task）主要指的是将在一个领域（源领域）训练的模型调整为适应另一个领域（目标领域）的任务。由于不同领域的数据分布可能存在差异，直接将源领域的模型应用于目标领域可能会导致性能下降。因此，领域自适应的目标是通过利用目标领域的数据，使模型更好地适应和处理目标领域的任务。领域自适应的挑战在于，在许多情况下，目标领域的标注数据是有限的或者根本不存在。为了解决这个问题，研究人员采用了多种方法，包括：特征选择或变换，无监督或半监督领域自适应，自监督学习 &lt;a href=&quot;#fnref:dat&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Thu, 27 Apr 2023 22:48:00 +0000</pubDate>
        <link>https://wulfric.me/2023/04/gpt3-ch/</link>
        <guid isPermaLink="true">https://wulfric.me/2023/04/gpt3-ch/</guid>
        
        <category>ai</category>
        
        <category>chatgpt</category>
        
        
        <category>技术</category>
        
      </item>
    
  </channel>
</rss>
