AI Agent Development #3: Planning and Self-Correction

5 min read

With Part 2 we had the loop and the tools in place. Short tasks already run well. But give it something with many steps — like “clean up last month’s order data and draft a report” — and the agent can lose its way midway or repeat the same mistake. In this post we make the agent plan ahead, verify intermediate results, and fix its own failures.

Giving rules of behavior with a system prompt #

So far, we have passed only the goal as a user message. For multi-step work, keep the task-specific goal separate from the rules that must always hold, and put those rules in the system prompt.

agent_system_prompt.py
SYSTEM = """You are an operations assistant agent that works with order data.

Rules:
- Before starting a task, make a step-by-step plan first and list it line by line.
- After finishing each step, confirm the result in one sentence and move to the next step.
- For any operation that changes data, query and check the current value before changing it.
- If the same tool fails twice in a row with the same input, find another approach or stop and report the situation.
"""

response = client.messages.create(
    model="claude-opus-4-8",
    max_tokens=16000,
    system=SYSTEM,
    tools=tools,
    messages=messages,
)

Rules work better when written as concrete actions rather than abstract directives. “Query the current value before changing it” is followed far more reliably than “act carefully.”

Making it plan first #

The first rule above is the key one. The more complex the task, the better the results when the agent lists a plan in text first instead of reaching for tools right away. With the plan sitting in the conversation, Claude looks back at it at every step, so it does not lose the original goal even in long work.

A plan is not set once and done. When an intermediate result differs from expectations, the plan needs revising. That too can be written as a rule, like “if an intermediate result differs from the plan, revise the remaining plan, list it again, and then proceed.”

Building verification into the work #

The most dangerous thing in multi-step work is stacking the next step on top of a failure you never noticed. The way to prevent it is simple: make checking a part of the work itself.

  • If you changed data, query it again afterward to confirm it changed as intended.
  • If you created a file, read it back to confirm the contents are right.
  • If you fixed code, run the tests to confirm they pass.

The tools for verification mostly exist already. A query tool is a verification tool. What you need is one rule line in the system prompt: “After any change operation, always query the result again to confirm it.”

When verification fails, that result enters the conversation as a tool result, and Claude sees the failure and attempts a fix. This is the basic circuit of self-correction. Without a verification step, the circuit never forms.

Stopping repeated mistakes #

Self-correction has one side effect. Sometimes the agent retries endlessly, tweaking the same approach a little each time. max_steps from Part 1 is the last-resort safeguard, but before it kicks in, the loop can detect the repetition and cut it off.

repeat_guard.py
from collections import Counter

failure_count = Counter()

def execute_tool(block) -> dict:
    result = try_run_tool(block)
    if result.get("is_error"):
        key = (block.name, str(block.input))
        failure_count[key] += 1
        if failure_count[key] >= 3:
            result["content"] += (
                " This approach has failed 3 times. Do not repeat the same attempt;"
                " find another approach or summarize the situation so far and report it."
            )
    return result

We count failures of the same tool with the same input, and once the limit is reached, append an instruction to the error message. This is exactly the principle from Part 2. An error message is a document Claude reads and follows, so write into it what the loop has figured out about the situation.

Tuning thinking depth with adaptive thinking #

Planning and self-correction are ultimately also a matter of reasoning quality. Claude has a thinking feature where it reasons internally before responding, and on the latest models you enable it by setting the type to adaptive. Claude decides for itself when and how much to think, based on the difficulty of the task.

adaptive_thinking.py
response = client.messages.create(
    model="claude-opus-4-8",
    max_tokens=16000,
    thinking={"type": "adaptive"},
    output_config={"effort": "high"},
    system=SYSTEM,
    tools=tools,
    messages=messages,
)

effort is the parameter that sets the overall level of effort. The options are low, medium, high, and max, and for agent work high or above is recommended. Deeper thinking uses more tokens, but in agents the plans get better and the total number of steps drops, so the overall cost often goes down.

Note that on claude-opus-4-8, sampling parameters like temperature have been removed, and sending them returns a 400 error. You shape behavior with the system prompt and effort.

Common planning pitfalls #

  • Putting rules in the user message — if you send the rules of behavior along with the goal every time, they get buried as the conversation grows. Rules that must always hold belong in the system prompt.
  • Stacking steps without verification — when work that went wrong at step 3 is discovered at step 7, there is a lot to unwind. Verification right after a change is the cheapest.
  • No limit on retries — trusting self-correction and letting it run forever just burns tokens on the same failure. Count failures, and at the limit, instruct a change of direction.

Key takeaways #

In this post we covered the behavior design that helps an agent hold up through multi-step work.

  • Rules of behavior go in the system prompt, written as concrete actions. The rule that makes the agent list a plan first is especially effective.
  • Building verification steps into the work, like re-querying after a change, is what gets the self-correction circuit running.
  • Detect and cut off repeated failures in the loop, and match thinking depth to the task with adaptive thinking and effort.

But as the steps grow, another problem appears. The conversation gets longer and the context approaches its limit. We cover that in the next post, “AI Agent Development #4: Context Management for Long-Running Work.”

X