Skip to content

[Bug]: ToolInterruptEntry 在 0.1.15→0.1.16 丢失 2 参构造器——@AllArgsConstructor 随新增字段改写签名,下游源码与二进制双重不兼容 #316

Description

说明:本描述已于 2026-09-17 修订。初版把 0.1.15 → 0.1.16 称作「patch 版本」并据此建议调整版本号方案,这个判断是错的——本仓库只有 0.1.x 一条线,第三位就是正常的发布递进位,真正的补丁走 .postN(0.1.13.post1、0.1.14.post1/post2)。相关段落已删除。本 issue 现在只针对一处零成本可修的兼容性细节。

问题

com.openjiuwen.core.singleagent.interrupt.ToolInterruptEntry 在 0.1.15 → 0.1.16 之间丢失了 2 参构造器。

该类新增第三个字段 settledDecisions(javadoc 标注 @since 0.1.16,来源应为 #​163 中断重放缺少已判定事实)。类上有 Lombok @AllArgsConstructor,字段数变化直接改写全参构造器签名,原 2 参版本随之消失:

# 0.1.15(javap 反编译)
public ToolInterruptEntry();
public ToolInterruptEntry(ToolCall, InterruptRequest);

# 0.1.16
public ToolInterruptEntry();
public ToolInterruptEntry(ToolCall, InterruptRequest, java.util.List<RailSettledDecision>);

settledDecisions 本身的设计没有问题,#​163 的需求也是合理的。本 issue 只针对它附带的这处兼容性副作用。

为什么这个破坏很难被察觉

上游自身从未调用过这个构造器。 在 930 分支与 v0.1.15 标签上全仓搜索 new ToolInterruptEntry( 均无命中,内部一律走 builder。也就是说字段新增时,仓内没有任何调用点会编译失败,CI 也不会给出信号——破坏只在下游显现。

加上 settledDecisions 带 @Builder.Default(默认空列表),语义上也不存在「必须强制调用方显式传入」的设计动机。综合这两点,我们判断这次签名变更是 @AllArgsConstructor 的副作用,而非有意收紧 API。如果实际上是有意为之,烦请在此说明,我们在下游按新签名适配即可,本 issue 可以直接关闭。

下游影响

凡以 2 参形式构造该对象的代码,升级到 0.1.16 后编译失败:

no suitable constructor found for ToolInterruptEntry(
    com.openjiuwen.core.foundation.llm.schema.ToolCall,
    com.openjiuwen.core.singleagent.interrupt.InterruptRequest)

agent-solution 仓有 4 处这样的调用(均在 edp-agent-engine 的测试代码中),已改为 builder 形式适配完毕,故对我们不构成阻塞。提出本 issue 是因为:由于是构造器签名变更而非类删除,类清单比对无法发现它,只在编译期暴露;对已编译的下游产物还会表现为运行期 NoSuchMethodError。后续其他使用方可能重复踩到。

补充一个范围数据供参考:我们对 0.1.15 与 0.1.16 两个正式发布 jar 的 com.openjiuwen.core.* 全部 963 个公开类做了构造器签名逐一比对(两种实现独立跑出同一结果),构造器丢失的类只有 ToolInterruptEntry 一个,同区间 core jar 的类移除数为 0。所以这是个例,不是普遍做法。

复现

// 依赖 agent-core-java 0.1.15
ToolCall call = ToolCall.builder().id("x").name("y").build();
InterruptRequest req = InterruptRequest.builder().build();
new ToolInterruptEntry(call, req);      // 0.1.15 编译通过
// 依赖改为 0.1.16 → 编译失败

建议

补一个 2 参构造器委托,与 builder 省略该字段时语义等价:

public ToolInterruptEntry(ToolCall toolCall, InterruptRequest request) {
    this(toolCall, request, new ArrayList<>());
}

两行,不改变任何既有行为,0.1.15 的调用方无需改动即可升级。

如果希望从根上避免同类问题再次发生,可以考虑给这类可演进的 DTO 停用 @AllArgsConstructor(或改为 @AllArgsConstructor(access = AccessLevel.PRIVATE)),只对外暴露 @Builder + @NoArgsConstructor——builder 天然对新增字段免疫,而 @AllArgsConstructor 会把「加字段」这个通常向后兼容的动作变成签名变更。这一条属于可选的长期改进,不影响本 issue 的处理。

环境

  • agent-core-java 0.1.15 → 0.1.16(930 分支,a2549228b)
  • 下游:agent-solution / common 分支,JDK 21,Maven 3.8.7

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions