日期:2026-10-20。用途:区分调用方式、当前能力边界与发行包缺陷;不修改引擎代码。
后续复测: 已按官方 README 与启动脚本安装 Launcher 0.1.25,在独立实例复跑原请求及边界用例。四项原问题在已测范围不再复现:16 组执行结果正确,1 组非法重复聚合在校验阶段拒绝。环境、预期/实际结果、日志与范围见 0.1.25 复测报告。下文保留 0.1.24 的原始诊断,不将其当作新版现状。
按用户要求已提交引擎 GitHub,均标记为 bug,并回读核对标题、正文及状态:
本地发布记录与回读结果见 GitHub 提交清单及核对记录。
结论:不是整套 LAG 不可用,也不只是提示词问题。LAG 原调用有重复聚合和表达式分层问题;timeWindow 的命名空间路由、月度环比计划,以及部分计算字段校验/分层确有引擎问题。
沿用教学服务 http://127.0.0.1:18067、Namespace chinook、命名数据源 chinook-sqlite、模型 ChinookInvoiceTimeQueryModel。匿名开发模式,CLI/Launcher 为 0.1.24。没有重启、改配置、改模型、迁移数据、切换数据源或修改产品源码。
Launcher 的内部版本为 9.3.0-SNAPSHOT。不能把本地 main 的源码直接当作发行包实现;本次提取实际运行 JAR 内的 model-engine,使用 javap 核对关键方法。版本、哈希与参考源码 HEAD 见 implementation-identity.json。
通过公开 CLI 的 validate、explain、execute 拆成 13 个小样例。每个执行请求先校验;计划探查只 explain,不把改变分析粒度的候选当作修复。正常返回也与只读原件逐行、逐月核对,不能把 success: true 当作正确答案。
完整请求与响应:diagnostic-manifest.json。核对结论:verification.json。这里 verification 的成功指排查断言符合观察,包括复现错误,不表示 13 个候选全部可用。
本接口先按月生成内层汇总,再在外层应用窗口函数。因此应引用已由内层聚合的指标:
{
"name": "previousAmount",
"expression": "lag(invoiceAmount, 1)",
"windowOrderBy": [{"field": "invoiceDate$yearMonth", "dir": "asc"}]
}
实际 SQL 的结构为:
WITH stage1 AS (
-- 按年月分组,SUM(Total) AS invoiceAmount
)
SELECT ..., LAG(invoiceAmount, 1) OVER (ORDER BY yearMonth)
FROM stage1
SQL 为省略引用符的结构示意,完整 SQL 以 explain 响应为准。
LAG 的含义是“有序结果中的上一行”,不是无条件的“上一自然月”。缺月或日期范围截掉上期时,需要另外处理;本次没有声称引擎会自动补齐日历。
原请求写 lag(sum(invoiceAmount), 1)。在本接口两阶段实现中,它变成对 stage1 再做 LAG(SUM(invoiceAmount), 1) OVER (...),外层没有相应分组,SQLite 将 12 行再次聚合成 1 行。
这是调用方式问题:不能把单层 SQL 的嵌套聚合习惯直接搬到这个分组后窗口阶段。引擎目前校验、执行均返回成功,也没有提示结果粒度改变,存在校验与诊断不足。
原差额、比例直接写成 sum(...) - lag(...),同时设置 windowOrderBy。本实现给整段表达式追加 OVER,得到类似:
(invoiceAmount - LAG(invoiceAmount, 1)) OVER (...)
合法 SQL 应让 OVER 跟在 LAG 调用后;当前这种整段拼接会语法错误。即使去掉多余的 SUM,复合表达式仍失败,证明不是只有重复聚合这一个原因。单字段的窗口配置应保持单个窗口表达式,后续运算需要受支持的下一阶段。
进一步尝试在同一层 calculatedFields 中先定义 previousAmount,再让差额、比例引用它,虽然返回成功,十一月差额却是 37.62,而正确值是 −5.00,比例字段未返回。explain 显示依赖窗口结果的表达式被展开回内层 stage1,引用了同层投影别名,未正确安排“汇总 → 窗口 → 差额”的层次。SQLite 的双引号兼容行为使某些无效列引用没有直接报错,造成成功但错误的结果。
这部分属于引擎的依赖规划/校验缺陷;若暂不支持该组合,也应在校验阶段明确拒绝。对普通选择别名 totalAmount 的同层引用,另一个样例已被 POST_AGGREGATE_CALCULATED_FIELD_UNSUPPORTED 拒绝,不能据此推断所有类似依赖都已被拦截。
原年月分组和 timeWindow 参数符合当前公开写法,普通月度聚合正常,比较查询却报 no such table: Invoice。本次没有给它改连默认库或向默认库复制业务表。
实际发行包中的调用链为:
chinook,并用它编译 timeWindow 的 Compose 计划,因而 explain 能生成含正确 Invoice 字段的 SQL。PlanExecution.executePlan 编译完成后调用 executeComposeSql → executeSql,执行接口仅传 SQL、参数、模型名。resolveRawSqlDataSource 获取 namespace 时读取 ComposeRuntimeHolder.currentBundle();这个入口没有像 ScriptRuntime 那样安装 ComposeRuntimeBundle,传入的 ComposeQueryContext 没有被执行阶段使用。chinook 的绑定,回退主 DataSource。该默认 SQLite 实际没有 Invoice 表,于是报错。这属于执行上下文传递缺陷,非用户连接错误、缺少 Invoice 表或应当把表复制到默认库。关键发行包方法见 release-critical-methods.txt,默认库只读结构检查见 primary-sqlite-schema-check.json。
另外,explain 的最终同比 SQL 没有日期 WHERE 条件或日期绑定参数,尽管请求的 timeWindow.value 为 2012 年。范围参数没有落实到当前观察到的计划,修好路由后仍需核对当前期、比较期的范围与输出,不能只让 SQL 执行成功。
请求按 year、month 分组,grain 为 month。TimeWindowExpander.expandComparative 的 mom 分支却选择原始 invoiceDate$id 作为关联键,后续派生计划要求它出现在源输出中,因而编译失败。
为定位而补上 $id 并按它分组,explain 虽可编译,但计划变成日级汇总,而且把上期的月份序号加一,同时要求两边原始日期完全相等。这不是正确的月度环比;月份加一也没有处理十二月到次年一月。该候选仅 explain,没有把它执行成一个“修好”的月度例子。
这是引擎的比较粒度、时间键位移与关联设计问题,不应靠调用方多加一个日期字段来掩盖。
实际发行包的 validateQueryInternal 调用 buildSqlOnly;timeWindow 拦截器设置 skipQuery 后,该方法返回空 SQL,没有继续执行最终 timeWindow Compose 计划编译,因此环比的源输出字段错误直到 explain/execute 才出现。
普通计算字段校验能解析表达式、生成 SQL,但没有证明窗口分层正确,也未拦截上述同层依赖错误。当前应区分:参数合法、计划可编译、SQL 可执行、结果符合粒度与业务定义,四者不能互相代替。
timeWindow.value 的期间约束。本次只定位和验证,产品仓库工作树保持干净;未提交、发布或重启。原始失败与新正反例均保留,第十一章原五用例验收没有被覆盖。
复跑入口:diagnose-chapter-11-windows.py、verify-chapter-11-window-diagnostics.py。前者经公开 CLI 采集请求、SQL 解释和响应,后者只读原件核对已成功的简单 LAG,并确认错误候选仍被识别为错误。