Build a reliable think-act-check loop with explicit state transitions.
An agent loop is the engine of the system. Keep it simple: plan, execute, evaluate, update state, repeat. The goal is not a giant framework. The goal is deterministic control around a probabilistic model.
If state is vague, everything downstream becomes guesswork. Store only what the loop needs to decide the next action and to stop safely.
type StepRecord = {
decision: string;
tool: string;
input: unknown;
output: unknown;
error?: string;
};
type LoopState = {
objective: string;
step: number;
maxSteps: number;
history: StepRecord[];
done: boolean;
};
A practical loop has four predictable phases:
export async function runLoop(state: LoopState) {
while (!state.done && state.step < state.maxSteps) {
const decision = await decideNextAction(state);
const result = await executeTool(decision.tool, decision.input);
state.history.push({
decision: decision.reason,
tool: decision.tool,
input: decision.input,
output: result,
});
state.done = evaluateObjective(state, result);
state.step += 1;
}
return state;
}
Full access
Unlock all 5 lessons, templates, and resources for AI Agent Fundamentals. Free.
Never treat errors as edge cases. In production, they are frequent. Handle tool timeouts, invalid JSON, empty responses, and auth failures with explicit branches.
if (!result.ok) {
state.history.push({
decision: "retry_with_fallback",
tool: decision.tool,
input: decision.input,
output: null,
error: result.errorCode,
});
}
One loop, one objective. If you need three major outcomes, run three agent loops or orchestrate them from a parent controller. Overloading one loop creates unpredictable behavior and impossible debugging.
Implement the loop skeleton above with mocked tools. Log each step as JSON and run three scenarios: success on step 1, success on step 3, and failure after max steps. You should be able to explain exactly why each run stopped.