Handling tool outcomes
Once an agent can invoke tools, it also needs to understand what happened as a result. Tools do not just do things; they succeed, fail, or produce partial outcomes. If we want agent behavior to remain predictable and stateful, we must make tool outcomes explicit and usable inside decision logic.
Defining success and failure for a tool
A tool needs a clear definition of what “worked” means. That definition belongs with the tool itself, not scattered throughout agent logic. A successful result usually means the tool completed its task and produced a usable outcome, while failure means it did not.
Making this distinction explicit allows the agent to reason about outcomes instead of guessing based on side effects.
def write_page(filename, html):
if not html:
return False
with open(filename, "w") as f:
f.write(html)
return True
Here, success is defined by returning True, and failure by returning False.
Returning structured results from tools
Boolean success flags are sometimes enough, but tools often need to return more information. A structured result makes success, failure, and context visible at the same time.
Using dictionaries keeps results simple and readable.
def write_page(filename, html):
if not html:
return {"ok": False, "reason": "empty content"}
with open(filename, "w") as f:
f.write(html)
return {"ok": True, "path": filename}
The agent can now inspect both the outcome and any accompanying details.
Detecting tool execution failures
From the agent’s perspective, failure is detected by examining the returned result, not by catching errors deep inside the tool. The tool reports what happened, and the agent reacts.
This keeps control flow explicit and avoids hidden behavior.
result = write_page("mars.html", page_html)
if not result["ok"]:
state["last_error"] = result["reason"]
The agent now knows the tool failed and why.
Updating agent state based on tool outcomes
Tool results should directly influence agent state. Success might advance progress, while failure might record an error, reduce confidence, or block future actions.
State updates should be simple and intentional.
if result["ok"]:
state["pages_written"].append(result["path"])
else:
state["errors"].append(result["reason"])
This keeps state aligned with real-world effects.
Using tool results to influence future decisions
Once tool outcomes are reflected in state, decision logic can use them naturally. The agent does not need special-case rules; it just reads state and responds.
A failed tool might trigger a retry, a fallback, or a different action entirely.
if state["errors"]:
next_action = "retry_write"
else:
next_action = "generate_next_page"
Tool handling becomes part of normal decision-making, not an exception path.
Conclusion
By defining success and failure clearly, returning structured results, and updating state deliberately, tools become reliable signals instead of opaque side effects. At this point, the agent can observe what its actions produced and use that information to decide what to do next, which is exactly what we want from a controlled, deterministic system.