fix(runtime,hands): 搜索功能修复 — glm空参数回退+schema简化
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled

根因: glm-5.1 不理解 oneOf+const 复杂 schema,发送 tool_calls 时
arguments 为空 {}。同时缺少从对话上下文提取用户意图的回退机制。

修复:
1. researcher input_schema 从 oneOf+const 改为扁平化属性 — glm 正确传参
2. loop_runner 增加 empty-input 回退 — 从最近用户消息注入 _fallback_query
3. researcher infer_action 增加 _fallback_query 分支处理
4. 调试日志降级 INFO→DEBUG (openai tool_calls delta, researcher input)
This commit is contained in:
iven
2026-04-22 16:06:47 +08:00
parent 3cb9709caf
commit 5816f56039
3 changed files with 64 additions and 35 deletions

View File

@@ -208,7 +208,7 @@ impl LlmDriver for OpenAiDriver {
tracing::debug!("[OpenAI:stream] SSE #{}: {}", sse_event_count, &data[..data.len().min(300)]);
}
if data == "[DONE]" {
tracing::debug!("[OpenAI:stream] Received [DONE], total SSE events: {}, raw bytes: {}", sse_event_count, raw_bytes_total);
tracing::debug!("[OpenAI:stream] Received [DONE], total SSE events: {}, raw bytes: {}, tool_calls: {:?}", sse_event_count, raw_bytes_total, accumulated_tool_calls);
// Emit ToolUseEnd for all accumulated tool calls (skip invalid ones with empty name)
for (id, (name, args)) in &accumulated_tool_calls {
@@ -264,7 +264,7 @@ impl LlmDriver for OpenAiDriver {
// Handle tool calls
if let Some(tool_calls) = &delta.tool_calls {
tracing::trace!("[OpenAI] Received tool_calls delta: {:?}", tool_calls);
tracing::debug!("[OpenAI] Received tool_calls delta: {:?}", tool_calls);
for tc in tool_calls {
// Tool call start - has id and name
if let Some(id) = &tc.id {

View File

@@ -380,6 +380,26 @@ impl AgentLoop {
if abort_result.is_some() {
break;
}
// GLM and other models sometimes send tool calls with empty arguments `{}`
// Inject the last user message as a fallback query so the tool can infer intent.
let input = if input.as_object().map_or(false, |obj| obj.is_empty()) {
if let Some(last_user_msg) = messages.iter().rev().find_map(|m| {
if let Message::User { content } = m {
Some(content.clone())
} else {
None
}
}) {
tracing::info!("[AgentLoop] Tool '{}' received empty input, injecting user message as fallback query", name);
serde_json::json!({ "_fallback_query": last_user_msg })
} else {
input
}
} else {
input
};
// Check tool call safety — via middleware chain
{
let mw_ctx_ref = middleware::MiddlewareContext {