diff --git a/src/agent/dispatcher.rs b/src/agent/dispatcher.rs index fcead248..78d23c31 100644 --- a/src/agent/dispatcher.rs +++ b/src/agent/dispatcher.rs @@ -33,12 +33,16 @@ impl Agent { /// Returns `AgenticLoopResult::Response` on completion, or /// `AgenticLoopResult::NeedApproval` if a tool requires user approval. /// + /// When `resume_after_tool` is true the loop already knows a tool was + /// executed earlier in this turn (e.g. an approved tool), so it won't + /// force the LLM to use tools if it responds with text. pub(super) async fn run_agentic_loop( &self, message: &IncomingMessage, session: Arc>, thread_id: Uuid, initial_messages: Vec, + resume_after_tool: bool, ) -> Result { // Load workspace system prompt (identity files: AGENTS.md, SOUL.md, etc.) let system_prompt = if let Some(ws) = self.workspace() { @@ -110,6 +114,8 @@ impl Agent { const MAX_TOOL_ITERATIONS: usize = 10; let mut iteration = 0; + let mut tools_executed = resume_after_tool; + loop { iteration += 1; if iteration > MAX_TOOL_ITERATIONS { @@ -193,12 +199,30 @@ impl Agent { match output.result { RespondResult::Text(text) => { + // If no tools have been executed yet, prompt the LLM to use tools + // This handles the case where the model explains what it will do + // instead of actually calling tools + if !tools_executed && iteration < 3 { + tracing::debug!( + "No tools executed yet (iteration {}), prompting for tool use", + iteration + ); + context_messages.push(ChatMessage::assistant(&text)); + context_messages.push(ChatMessage::user( + "Please proceed and use the available tools to complete this task.", + )); + continue; + } + + // Tools have been executed or we've tried multiple times, return response return Ok(AgenticLoopResult::Response(text)); } RespondResult::ToolCalls { tool_calls, content, } => { + tools_executed = true; + // Add the assistant message with tool_calls to context. // OpenAI protocol requires this before tool-result messages. context_messages.push(ChatMessage::assistant_with_tool_calls( diff --git a/src/agent/thread_ops.rs b/src/agent/thread_ops.rs index c1d6442f..66f3723c 100644 --- a/src/agent/thread_ops.rs +++ b/src/agent/thread_ops.rs @@ -278,7 +278,7 @@ impl Agent { // Run the agentic tool execution loop let result = self - .run_agentic_loop(message, session.clone(), thread_id, turn_messages) + .run_agentic_loop(message, session.clone(), thread_id, turn_messages, false) .await; // Re-acquire lock and check if interrupted @@ -1057,7 +1057,7 @@ impl Agent { // Continue the agentic loop (a tool was already executed this turn) let result = self - .run_agentic_loop(message, session.clone(), thread_id, context_messages) + .run_agentic_loop(message, session.clone(), thread_id, context_messages, true) .await; // Handle the result