修复: AI 对话/工具可靠性(sanitize 三元组 + G2 签名重复 + handshake 不杀 loop + 空 tool_call id 兜底)

治 5 个对话停止/工具失败根因:sanitize 三元组按 id 配对治 400;G2 探索熔断从结果空
改签名重复判定(治误停正常探索);handshake 删越权强杀活 loop(generating 归 guard 单源);
空 tool_call id 兜底 gen_<index>(治 SenseNova 工具结果路由错位)。
This commit is contained in:
lxy
2026-08-02 02:21:30 +08:00
parent c76e77bd3c
commit 57d6a2d066
8 changed files with 991 additions and 185 deletions
+18 -11
View File
@@ -16,7 +16,7 @@ use std::time::Duration;
use tracing::{debug, error, warn};
use crate::provider::{
CompletionRequest, CompletionResponse, LlmProvider, MessageRole,
tool_call_id_or_fallback, CompletionRequest, CompletionResponse, LlmProvider, MessageRole,
StreamResult, TokenUsage, ToolCall,
};
// ChatMessage 仅单测构造 CompletionRequest 用,避免非 test 构建的 unused import 警告。
@@ -549,6 +549,9 @@ impl LlmProvider for AnthropicCompatProvider {
// content 块中拼接 text,收集 tool_use
let mut text = String::new();
let mut tool_calls: Vec<ToolCall> = Vec::new();
// CR-空 id:按 tool_use 块在数组中的顺序计数(仅 tool_use 递增),用于 fallback index。
// 用独立计数器而非 for enumerate,避免 text/unknown 块占用 index 致 fallback 编号跳号。
let mut tool_use_idx: usize = 0;
for block in resp.content {
match block.block_type.as_str() {
"text" => {
@@ -557,16 +560,20 @@ impl LlmProvider for AnthropicCompatProvider {
}
}
"tool_use" => {
let id = match block.id {
Some(id) if !id.is_empty() => id,
_ => {
warn!(
name = ?block.name,
"Anthropic tool_use 块缺少 id,已跳过(空 id 会回传空 tool_use_id 触发 500"
);
continue;
}
};
// CR-空 id:原逻辑空 id 直接 continue 跳过整个块(丢工具调用)。
// 改为兜底:id 非空原样,空 → `gen_anthropic_{idx}` fallbackDRY 共用
// tool_call_id_or_fallback)。Anthropic 一般非空,此为兼容缺陷兜底。
// 不再 warn+continuecontinue 会丢工具调用致 LLM 拿不到结果)。
let raw_id = block.id.unwrap_or_default();
let id = tool_call_id_or_fallback(&raw_id, tool_use_idx, "gen_anthropic");
if raw_id.is_empty() {
warn!(
fallback_id = %id,
name = ?block.name,
"Anthropic tool_use 块 id 为空,已生成 fallback id(原 continue 跳过会丢工具调用)"
);
}
tool_use_idx += 1;
let name = block.name.unwrap_or_default();
let args = block
.input