mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-22 16:43:35 +00:00
fix: 修复 Claude API 400 错误:tool_result/tool_use 不匹配问题
错误信息:
messages.14.content.0: unexpected tool_use_id found in tool_result blocks: toolu_01Ekn6YJMk7yt7hNcn4PZxtM.
Each tool_result block must have a corresponding tool_use block in the previous message.
根本原因:
文件: src/services/claudeRelayService.js 中的 _enforceCacheControlLimit() 方法
原实现问题:
1. 当 cache_control 块超过 4 个时,直接删除整个内容块(splice)
2. 这会删除 tool_use 块,导致后续的 tool_result 找不到对应的 tool_use_id
3. 也会删除用户的文本消息,导致上下文丢失
重要背景(官方文档确认)
根据 Claude API 官方文档:
- 最多可定义 4 个 cache_control 断点
- 如果超过限制,API 不会报错,只是静默地忽略多余的断点
- "20 个块回溯窗口" 是缓存命中检查的范围,与断点数量限制无关
因此,这个函数的原始设计(删除内容块)是不必要且有害的。
修复方案:
保留函数但修改行为:只删除 cache_control 属性,保留内容本身
修改位置;
文件: src/services/claudeRelayService.js
修改内容:
将 removeFromMessages() 和 removeFromSystem() 函数从"删除整个内容块"改为"只删除 cache_control 属性":
// 修改前:直接删除整个内容块
message.content.splice(contentIndex, 1)
// 修改后:只删除 cache_control 属性,保留内容
delete contentItem.cache_control
效果对比;
| 场景 | 修复前 | 修复后 |
|------------|----------------|----------------|
| 用户文本消息 | ❌ 整个消息被删除 | ✅ 保留消息,只移除缓存标记 |
| tool_use 块 | ❌ 被删除导致 400 错误 | ✅ 保留完整内容 |
| system 提示词 | ❌ 整个提示词被删除 | ✅ 保留提示词内容 |
| 缓存功能 | ⚠️ 强制限制 | ✅ 降级(不缓存但内容完整) |
This commit is contained in:
@@ -789,7 +789,8 @@ class ClaudeRelayService {
|
||||
return total
|
||||
}
|
||||
|
||||
const removeFromMessages = () => {
|
||||
// 只移除 cache_control 属性,保留内容本身,避免丢失用户消息
|
||||
const removeCacheControlFromMessages = () => {
|
||||
if (!Array.isArray(body.messages)) {
|
||||
return false
|
||||
}
|
||||
@@ -803,12 +804,8 @@ class ClaudeRelayService {
|
||||
for (let contentIndex = 0; contentIndex < message.content.length; contentIndex += 1) {
|
||||
const contentItem = message.content[contentIndex]
|
||||
if (contentItem && contentItem.cache_control) {
|
||||
message.content.splice(contentIndex, 1)
|
||||
|
||||
if (message.content.length === 0) {
|
||||
body.messages.splice(messageIndex, 1)
|
||||
}
|
||||
|
||||
// 只删除 cache_control 属性,保留内容
|
||||
delete contentItem.cache_control
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -817,7 +814,8 @@ class ClaudeRelayService {
|
||||
return false
|
||||
}
|
||||
|
||||
const removeFromSystem = () => {
|
||||
// 只移除 cache_control 属性,保留 system 内容
|
||||
const removeCacheControlFromSystem = () => {
|
||||
if (!Array.isArray(body.system)) {
|
||||
return false
|
||||
}
|
||||
@@ -825,12 +823,8 @@ class ClaudeRelayService {
|
||||
for (let index = 0; index < body.system.length; index += 1) {
|
||||
const systemItem = body.system[index]
|
||||
if (systemItem && systemItem.cache_control) {
|
||||
body.system.splice(index, 1)
|
||||
|
||||
if (body.system.length === 0) {
|
||||
delete body.system
|
||||
}
|
||||
|
||||
// 只删除 cache_control 属性,保留内容
|
||||
delete systemItem.cache_control
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -841,12 +835,13 @@ class ClaudeRelayService {
|
||||
let total = countCacheControlBlocks()
|
||||
|
||||
while (total > MAX_CACHE_CONTROL_BLOCKS) {
|
||||
if (removeFromMessages()) {
|
||||
// 优先从 messages 中移除 cache_control,再从 system 中移除
|
||||
if (removeCacheControlFromMessages()) {
|
||||
total -= 1
|
||||
continue
|
||||
}
|
||||
|
||||
if (removeFromSystem()) {
|
||||
if (removeCacheControlFromSystem()) {
|
||||
total -= 1
|
||||
continue
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user