mirror of
https://github.com/outbackdingo/optimclaw.git
synced 2026-09-01 17:19:24 +00:00
fix(llm): nudge LLM when it expresses tool intent without calling tools (#653)
* fix(llm): nudge LLM when it expresses tool intent without calling tools
Non-Anthropic models (especially GLM-5 via NEAR AI) frequently output
text like "Let me search for X" without including tool_calls, creating
a frustrating loop where the user waits but nothing happens.
Add llm_signals_tool_intent() detection that matches intent phrases
("let me search", "I'll fetch") while excluding conversational phrases
("let me explain", "let me know") and content inside code blocks.
When detected, inject a nudge message telling the model to actually
call the tool. Cap at 2 consecutive nudges to avoid infinite loops.
Applied to all three agentic loops: dispatcher (interactive chat),
agent/worker (background jobs), and worker/runtime (sandbox containers).
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* fix(nudge): address PR #653 review comments
1. Update doc comment to match implementation (code blocks only, not quotes)
2. Use match_indices() instead of find() to check all prefix occurrences
3. Add !available_tools.is_empty() guard in dispatcher nudge check
4. Reset consecutive_tool_intent_nudges on non-intent text responses
5. Add regression test for shadowed prefix detection
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* fix(nudge): address second round of PR #653 review comments
1. Strip double-quoted strings in tool-intent detection to avoid false
positives on quoted prose like `"Let me search the database"`.
2. Only reset consecutive_tool_intent_nudges when text does NOT signal
intent — preserves the 2-nudge cap when intent is detected but cap
is already reached.
3. Fix assertion message in nudge_cap test to report correct call index.
4. Add regression test for quoted strings outside code blocks.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
---------
Co-authored-by: Claude Opus 4.6 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
3f22f4321d
commit
8fbb782090
@@ -222,6 +222,8 @@ Work independently to complete this job. Report when done."#,
|
||||
) -> Result<String, WorkerError> {
|
||||
let max_iterations = self.config.max_iterations;
|
||||
let mut last_output = String::new();
|
||||
const MAX_TOOL_INTENT_NUDGES: u32 = 2;
|
||||
let mut consecutive_tool_intent_nudges: u32 = 0;
|
||||
|
||||
// Load tool definitions
|
||||
reason_ctx.available_tools = self.tools.tool_definitions().await;
|
||||
@@ -280,11 +282,28 @@ Work independently to complete this job. Report when done."#,
|
||||
return Ok(last_output);
|
||||
}
|
||||
reason_ctx.messages.push(ChatMessage::assistant(&response));
|
||||
|
||||
// Nudge the LLM if it expressed tool intent without calling tools
|
||||
let signals_intent = !reason_ctx.available_tools.is_empty()
|
||||
&& crate::llm::llm_signals_tool_intent(&response);
|
||||
if signals_intent && consecutive_tool_intent_nudges < MAX_TOOL_INTENT_NUDGES
|
||||
{
|
||||
consecutive_tool_intent_nudges += 1;
|
||||
tracing::info!(
|
||||
"LLM expressed tool intent without calling a tool, nudging"
|
||||
);
|
||||
reason_ctx
|
||||
.messages
|
||||
.push(ChatMessage::user(crate::llm::TOOL_INTENT_NUDGE));
|
||||
} else if !signals_intent {
|
||||
consecutive_tool_intent_nudges = 0;
|
||||
}
|
||||
}
|
||||
RespondResult::ToolCalls {
|
||||
tool_calls,
|
||||
content,
|
||||
} => {
|
||||
consecutive_tool_intent_nudges = 0;
|
||||
if let Some(ref text) = content {
|
||||
self.post_event(
|
||||
"message",
|
||||
@@ -344,6 +363,7 @@ Work independently to complete this job. Report when done."#,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
consecutive_tool_intent_nudges = 0;
|
||||
// Execute selected tools
|
||||
for selection in &selections {
|
||||
self.post_event(
|
||||
|
||||
Reference in New Issue
Block a user