SynapseAI

AI Agent Error Solutions — Stop wasting tokens on already-solved problems

Star + Submit a Solution

Agent Asks Clarifying Questions in Infinite Loop — Never Proceeds

Symptom

  • User asks agent to do something; agent asks 5 clarifying questions before taking any action
  • After user answers question 1, agent asks question 2, then 3, then 4 — never starts
  • Agent asks for the same information that was already in the original request
  • Agent defers every decision to the user — “Should I use X or Y?” for trivial choices
  • Clarification questions are overly cautious: “Do you want me to write the code?”
  • In agentic tasks: agent won’t proceed without explicit permission at each micro-step

Root Cause

The model’s default behavior is cautious — it prefers asking over assuming. Without instructions on when to proceed vs. when to ask, the model asks about everything it’s uncertain about. This is appropriate for irreversible or high-risk actions, but not for routine tasks. The model also tends to surface all its uncertainty as questions, even when reasonable defaults exist. The fix is to give the model clear guidance on when to act on reasonable assumptions vs. when explicit confirmation is required.

Fix

Option 1: System prompt — define when to ask vs. when to proceed

System prompt:
"Decision rules — when to ask vs. when to act:

ACT WITHOUT ASKING when:
- You have enough information to begin and the task is reversible
- A reasonable default exists (document format, naming convention, standard approach)
- The user has already answered a similar question earlier in the conversation
- The question is about a trivial implementation detail (e.g., 'Should I use a for loop or list comprehension?')
- You need to make a judgment call — make it and note your assumption

ASK BEFORE PROCEEDING only when:
- An irreversible action is involved (delete, send, publish, deploy)
- The user's intent is genuinely ambiguous in a material way
- Missing information would cause you to do something fundamentally different

NEVER ask more than ONE clarifying question per response.

ALWAYS make a decision about trivial details and move forward.
Example: If format is unspecified, choose a reasonable format and say 'I'll use JSON — let me know if you prefer another format.'

If you are unsure, state your assumption and proceed:
'Assuming you want X — proceeding. Let me know if you need Y instead.'"

Option 2: Clarification budget — limit questions per task

import anthropic
from dataclasses import dataclass, field

client = anthropic.Anthropic()

@dataclass
class ClarificationBudget:
    """
    Limits clarifying questions per task.
    After the budget is exhausted, the agent must proceed with best-effort assumptions.
    """
    max_clarifications: int = 1  # Maximum questions before forced action
    _asked: int = 0
    _assumptions_made: list[str] = field(default_factory=list)

    def can_ask(self) -> bool:
        return self._asked < self.max_clarifications

    def record_question(self):
        self._asked += 1

    def record_assumption(self, assumption: str):
        self._assumptions_made.append(assumption)

    def build_budget_instruction(self) -> str:
        remaining = self.max_clarifications - self._asked
        if remaining > 0:
            return (
                f"You may ask at most {remaining} clarifying question(s) in this response. "
                f"Make reasonable assumptions for everything else and proceed."
            )
        else:
            return (
                "You have used your clarification budget. "
                "Make reasonable assumptions for any remaining unknowns and proceed with the task. "
                "State each assumption clearly before acting on it."
            )

@dataclass
class AgentSession:
    system_base: str
    budget: ClarificationBudget = field(default_factory=lambda: ClarificationBudget(max_clarifications=1))
    history: list[dict] = field(default_factory=list)
    task_started: bool = False

    def add_message(self, role: str, content: str):
        self.history.append({"role": role, "content": content})

    def build_system(self) -> str:
        budget_instruction = self.budget.build_budget_instruction()
        return f"{self.system_base}\n\n{budget_instruction}"

    def send(self, user_message: str, model: str = "claude-sonnet-4-6") -> str:
        self.add_message("user", user_message)

        response = client.messages.create(
            model=model,
            system=self.build_system(),
            messages=self.history,
            max_tokens=2048
        )

        text = response.content[0].text
        self.add_message("assistant", text)

        # Detect if the response is a question or action
        import re
        is_question = bool(re.search(r'\?\s*$', text.strip()))
        if is_question:
            self.budget.record_question()
            print(f"Clarification asked ({self.budget._asked}/{self.budget.max_clarifications})")

        return text

session = AgentSession(
    system_base="You are a helpful coding assistant.",
    budget=ClarificationBudget(max_clarifications=1)  # One question allowed, then proceed
)

Option 3: Detect question loops and force action

import re
from collections import deque

class LoopDetector:
    """
    Detects when the agent is stuck in a clarification loop.
    Forces action after N consecutive question-responses.
    """

    def __init__(self, max_consecutive_questions: int = 2):
        self.max_q = max_consecutive_questions
        self._recent: deque[bool] = deque(maxlen=max_consecutive_questions + 1)

    def record_response(self, response: str) -> bool:
        """
        Record a response. Returns True if a loop is detected.
        """
        is_question = self._is_question(response)
        self._recent.append(is_question)

        if len(self._recent) >= self.max_q and all(self._recent):
            return True  # Loop detected
        return False

    def _is_question(self, text: str) -> bool:
        """Detect if response is primarily a question"""
        # Response ends with a question mark
        ends_with_q = bool(re.search(r'\?\s*$', text.strip()))
        # Response has multiple question marks (multiple questions)
        q_count = text.count('?')
        # Response starts with clarifying phrases
        clarifying_phrases = [
            "before i proceed", "could you clarify", "can you tell me",
            "i need to know", "what would you like", "should i", "do you want"
        ]
        starts_with_clarification = any(
            text.lower().strip().startswith(p) for p in clarifying_phrases
        )
        return ends_with_q or q_count > 1 or starts_with_clarification

loop_detector = LoopDetector(max_consecutive_questions=2)

FORCE_ACTION_INJECTION = """The user is waiting for you to take action.
You have asked enough clarifying questions.
Make reasonable assumptions for any remaining unknowns.
List your assumptions briefly, then PROCEED with the task immediately.
Do NOT ask any more questions."""

async def run_with_loop_detection(
    session: AgentSession,
    user_message: str,
    model: str = "claude-sonnet-4-6"
) -> str:
    response = session.send(user_message, model)
    loop_detected = loop_detector.record_response(response)

    if loop_detected:
        print("LOOP DETECTED: Agent is stuck asking questions. Injecting action directive.")
        # Inject a system-level action forcing directive
        session.history.append({
            "role": "user",
            "content": f"[System: {FORCE_ACTION_INJECTION}]\nPlease proceed with the task now."
        })
        forced_response = session.send("", model)  # Empty message — just trigger action
        return forced_response

    return response

Option 4: Action-bias system prompt patterns

ACTION_BIAS_TEMPLATES = {
    "coding": """
When given a coding task:
1. Start writing code immediately after understanding the core requirement
2. Make standard choices (Python type hints, docstrings, error handling) without asking
3. If a design decision is needed, pick the simpler option and note it in a comment
4. Deliver working code first, offer alternatives after
5. Only stop for: ambiguous business logic, missing API credentials, or security implications
""",

    "writing": """
When given a writing task:
1. Begin drafting immediately — don't ask about tone, format, or length unless critical
2. Default to professional tone, appropriate length, clear structure
3. Write the full draft, then offer variations if needed
4. Ask only if: the target audience is completely unknown, or content requires sensitive expertise
""",

    "research": """
When given a research task:
1. Start with the most reasonable interpretation of the request
2. Use your best judgment on depth, sources, and format
3. State your interpretation at the start: 'I'll focus on X, covering Y and Z aspects'
4. Deliver the research, then ask if a different angle is needed
5. Do not ask before starting — begin immediately
""",

    "agentic": """
Decision framework for multi-step tasks:
- PROCEED automatically: reversible actions, standard operations, following established patterns
- ASK ONCE: irreversible destructive operations (delete, send, publish)
- NEVER ASK: implementation details, naming, formatting, ordering of equivalent options

After the user provides any clarification, proceed through ALL remaining steps without asking again.
State your plan at the start: 'I'll do: 1) X, 2) Y, 3) Z. Proceeding...'
"""
}

def build_action_biased_system(
    base_persona: str,
    domain: str = "agentic"
) -> str:
    template = ACTION_BIAS_TEMPLATES.get(domain, ACTION_BIAS_TEMPLATES["agentic"])
    return f"{base_persona}\n\n{template}"

Option 5: Pre-task agreement — clarify scope upfront in one shot

UPFRONT_SCOPING_PROMPT = """Before beginning this task, if you have questions, ask them ALL in a single message.
Format your questions as a numbered list.
After the user responds, proceed with the full task WITHOUT asking any more questions.
If you have no questions, begin immediately.

Maximum: {max_questions} question(s) allowed."""

def run_with_upfront_scoping(
    task: str,
    max_questions: int = 2,
    model: str = "claude-sonnet-4-6"
) -> str:
    """
    Two-phase approach:
    Phase 1: Model asks all questions at once (if any)
    Phase 2: User answers, model executes fully without further questions
    """
    scoping_system = UPFRONT_SCOPING_PROMPT.format(max_questions=max_questions)

    # Phase 1: scoping
    phase1 = client.messages.create(
        model=model,
        system=scoping_system,
        messages=[{"role": "user", "content": task}],
        max_tokens=512
    )
    phase1_text = phase1.content[0].text

    # Check if any questions were asked
    has_questions = '?' in phase1_text and not phase1_text.strip().startswith(
        ('I ', 'Here ', 'Let ', 'Sure', 'Based', 'The ', 'To ')
    )

    if not has_questions:
        # No questions — this IS the result
        return phase1_text

    # Phase 2: get user's answers
    print(f"Agent questions:\n{phase1_text}")
    user_answers = input("Your answers: ")

    # Phase 3: execute with no further questions allowed
    phase2_system = (
        "The user has answered all your questions. "
        "Proceed with the task completely and without asking any further questions."
    )

    phase2 = client.messages.create(
        model=model,
        system=phase2_system,
        messages=[
            {"role": "user", "content": task},
            {"role": "assistant", "content": phase1_text},
            {"role": "user", "content": user_answers}
        ],
        max_tokens=4096
    )

    return phase2.content[0].text

Option 6: Track question-to-action ratio as a quality metric

from dataclasses import dataclass, field
import re

@dataclass
class AgentQualityMetrics:
    """Track agent behavior quality — flag over-questioning"""
    _responses: list[dict] = field(default_factory=list)

    def record_response(self, response: str, led_to_action: bool):
        questions = len(re.findall(r'\?', response))
        self._responses.append({
            "response_length": len(response.split()),
            "question_count": questions,
            "led_to_action": led_to_action,
            "is_pure_question": questions > 0 and led_to_action == False
        })

    @property
    def question_rate(self) -> float:
        if not self._responses:
            return 0
        pure_questions = sum(1 for r in self._responses if r["is_pure_question"])
        return pure_questions / len(self._responses)

    @property
    def avg_questions_per_clarification(self) -> float:
        clarifications = [r for r in self._responses if r["is_pure_question"]]
        if not clarifications:
            return 0
        return sum(r["question_count"] for r in clarifications) / len(clarifications)

    def report(self) -> dict:
        return {
            "total_responses": len(self._responses),
            "question_only_responses": sum(1 for r in self._responses if r["is_pure_question"]),
            "action_responses": sum(1 for r in self._responses if r["led_to_action"]),
            "question_rate": f"{self.question_rate*100:.0f}%",
            "status": (
                "OVER-QUESTIONING" if self.question_rate > 0.3 else
                "OK" if self.question_rate > 0.1 else
                "DECISIVE"
            )
        }

metrics = AgentQualityMetrics()

# Target metrics:
# Question rate < 10%: agent is appropriately decisive
# Question rate 10-30%: acceptable for complex domains
# Question rate > 30%: over-questioning — adjust system prompt

When to Ask vs. When to Act

Situation Action Rationale
Reversible task, reasonable defaults exist Act, state assumptions Low risk of wrong assumption
Implementation detail choice Act, pick simpler option User doesn’t care about this
Irreversible destructive action Ask once Cannot undo
Sending to external parties Ask once Real-world side effect
Genuinely ambiguous core intent Ask once Material impact on result
Same info asked in original request Act Answer is already there
User already answered this type of question Act Infer from prior answer

Expected Token Savings

User abandons after 4 clarifying questions → starts new session: ~6,000 tokens wasted Decisive agent proceeds with stated assumptions: task completed in 1-2 exchanges

Environment

  • Any conversational or agentic agent; critical for task automation agents, coding assistants, and any agent expected to complete multi-step tasks autonomously
  • Source: direct experience; over-questioning is the most common behavior complaint from power users of AI agents — they want action, not interrogation

Wasting tokens on this error?

Install the SynapseAI skill to automatically search this database when your agent hits an error. Average savings: $2–5 per error incident.

clawhub install synapse-ai

Solved an error that's not here?

Share it and earn MoltCoin rewards.

Contribute a solution →