Home / Question bank / Coding with AI APIs
Implement an agent loop. What safeguards do you add?
30-second answer
Loop: call the model with tools and history; if it requests tools, validate arguments, authorize against the user's permissions, execute (in parallel when independent), append results, repeat; stop on a final answer. Safeguards: max iterations and token budget, timeouts, errors returned as tool results, idempotency for side effects, human confirmation for destructive actions, logging/tracing of every step.
Try answering out loud before reading on. The walkthrough below is paced for a 40-minute interview round.
Architecture diagram
Sketch this on the whiteboard as you talk through the answer. See all AI architecture diagrams →
Scroll sideways to see the whole diagram.
0–5 min Clarify and frame
Ask what the agent does and which tools have side effects. Framing: "I'll write the loop, then add safeguards for cost, correctness, safety and observability."
5–15 min Core implementation
MAX_STEPS = 10
MAX_TOKENS_TOTAL = 200_000
def run_agent(user, task):
messages = [{"role": "user", "content": task}]
used_tokens = 0
for step in range(MAX_STEPS):
resp = llm.create(model=MODEL, system=SYSTEM, tools=TOOLS,
messages=messages, max_tokens=2048, timeout=60)
used_tokens += resp.usage.input_tokens + resp.usage.output_tokens
messages.append({"role": "assistant", "content": resp.content})
if resp.stop_reason != "tool_use":
return final_text(resp)
if used_tokens > MAX_TOKENS_TOTAL:
return "Stopped: budget exceeded."
calls = [b for b in resp.content if b.type == "tool_use"]
results = run_parallel(calls, lambda c: execute_tool(user, c))
messages.append({"role": "user", "content": results})
trace(step, calls, results, resp.usage)
return "Stopped: too many steps. Here is what I found so far: ..."
def execute_tool(user, call):
try:
args = SCHEMAS[call.name].validate(call.input)
authorize(user, call.name, args)
if call.name in DESTRUCTIVE and not confirm_with_user(user, call.name, args):
return tool_result(call.id, "User declined this action.", is_error=True)
out = TOOL_IMPL[call.name](**args)
return tool_result(call.id, truncate(serialize(out), 4000))
except Exception as e:
return tool_result(call.id, f"Error: {safe_message(e)}", is_error=True)
15–25 min Deep dive: safeguards
| Category | Safeguards |
|---|---|
| Cost | Step cap, token budget, truncate tool outputs, summarize history, cache static prompt |
| Correctness | Schema validation, errors returned to model, verification steps (run tests, re-query) |
| Safety | Authorization per call, least-privilege tools, confirmation for destructive actions, injection-aware handling of tool content |
| Reliability | Timeouts, retries with backoff for transient errors, idempotency keys, loop detection (same call repeated) |
| Observability | Trace every step: prompt version, tool calls, results, tokens, latency |
| UX | Stream progress ("Searching orders…"), allow cancel, return partial results |
25–32 min Trade-offs
- Tight caps prevent runaway costs but can stop legitimate long tasks; set by task type and measure completion rates.
- Confirmations add friction; reserve for irreversible actions.
- Truncating tool outputs risks losing needed data; return structured, filtered results instead.
32–37 min Production example
An ops agent sometimes looped calling get_logs with slightly different time ranges. Loop detection (same tool with near-identical args 3×) injected a message telling the model to summarize what it has, and a cap of 12 steps prevented long runs. Average cost per task fell, and success rate held.
37–40 min Likely follow-ups
- How do you evaluate agents? Task-level success on scenario sets with mocked or sandboxed tools; measure steps, cost, and safety violations.
- How do you handle long-running tasks? Persist state; resume; run asynchronously with notifications.
What interviewers listen for
- A correct, readable loop.
- Safeguards across cost, safety, reliability and observability.
Red flags
- A
while Trueloop with no limits.