agentscope-ai/agentscope-java

[Bug]:AnthropicChatModel 将 ThinkingBlock 作为普通 text 传回 API,导致 400 错误

Open

#2,400 opened on Jul 27, 2026

 (2 comments) (0 reactions) (1 assignee)Java (693 forks)user submission
bughelp wanted

Repository metrics

Stars
 (3,253 stars)
PR merge metrics
 (Avg merge 2d 8h) (58 merged PRs in 30d)

Description

先撤销刚才的源码修改:

已撤销。以下是你可以提 issue 的完整描述:


Issue:

环境信息

  • AgentScope v2.0.0
  • 模型:deepseek-v4-pro(通过 Anthropic 协议接入的第三方模型)
  • 场景:多轮对话,ReActAgent

问题现象

多轮对话时,Anthropic API 报 400 错误:

The `content[].thinking` in the thinking mode must be passed back to the API.

根因

AnthropicMessageConverter.java 第131-137行,将 ThinkingBlock 转换为普通 TextBlockParam 传回 API:

} else if (block instanceof ThinkingBlock thinkingBlock) {
    contentBlocks.add(
            ContentBlockParam.ofText(
                    TextBlockParam.builder()
                            .text(thinkingBlock.getThinking())
                            .build()));
}

问题在于:

  1. 模型上一轮回复产生了 thinking 块,AgentScope 存入对话历史
  2. 下一轮请求时,thinking 块被转成普通 text 类型传回 API
  3. 但 Anthropic API 要求 thinking 内容必须用** thinking content type** 原样传回(包含 thinkingsignature 等字段),不能用 text 类型
  4. 如果对话经过 CompactionMiddleware 压缩,thinking 块可能被截断或丢失,传回不完整内容,API 直接报 400

触发条件

  1. 使用 Anthropic 协议的模型(Claude / 第三方如 deepseek-v4-pro)
  2. 开启了 thinking 模式(enableThinking: truethinkingBudget 设了值)
  3. 多轮对话(第二轮起),或对话历史经过 compaction 压缩后

建议修复

方案A(推荐):ThinkingBlock 应该用 Anthropic 的 thinking content type 传回,包含 thinkingsignature 字段,而不是转成 text

} else if (block instanceof ThinkingBlock thinkingBlock) {
    // 用 Anthropic 原生的 thinking content type 传回
    contentBlocks.add(ContentBlockParam.ofThinking(
            ThinkingBlockParam.builder()
                    .type("thinking")
                    .thinking(thinkingBlock.getThinking())
                    .signature(thinkingBlock.getSignature()) // 需要在 ThinkingBlock 中保存 signature
                    .build()));
}

方案B(兜底):如果无法保证 thinking 块的完整性(如 compaction 后),应该跳过 ThinkingBlock,不传回 API:

} else if (block instanceof ThinkingBlock) {
    // Skip: thinking 块不完整时传回会导致 400
    continue;
}

补充说明

  • CompactionMiddleware 压缩后也会丢失 thinking 块的 signature,即使转成正确的 thinking type 也会失败
  • 对于第三方模型(非 Claude 原生),thinking 的 signature 机制可能不适用
  • 建议增加一个配置项:stripThinkingBlocks=true,在发送前移除所有 thinking 块

Contributor guide