A coding agent is not a smarter chat. It is a model that can call tools, wrapped in a loop that keeps going until the task is done. Knowing how that loop works explains both why agents are useful and why they burn through context so fast.
How it works
An agent has three parts:
- The model does the reasoning: it reads what is in front of it and decides what to do next.
- Tools are actions the model can trigger itself. In Claude Code the built-in tools fall into five groups: file operations (read, edit, create, rename files), search (find files by pattern, search content with regex), execution (run shell commands, start servers, run tests, use git), web (search, fetch docs) and code intelligence (type errors, go to definition, find references, which needs a plugin).
- The loop feeds the result of every tool call back to the model, so each step is decided by what the previous step found.
The Claude Code docs describe the loop as three phases: gather context, take action and verify results. The phases blend into each other and repeat until the task is complete. A question about the codebase may only need the first phase; a bug fix cycles through all three several times. You can interrupt at any point and steer it.
The part of Claude Code around the model, which provides the tools and manages what the model sees, is called the agentic harness.
Example
Say a test fails in a payment service. With a chat assistant, you paste the stack trace, copy the suggested fix into your editor, run the tests yourself and paste the next error back. You are the loop.
With an agent, you write one line and the loop runs on its own:
- Run the test suite to see what fails.
- Read the error output.
- Search for the source files involved.
- Read them.
- Edit the code.
- Run the tests again, and go back to step 2 if they are still red.
The same works for a frontend bug, for example a submit button that is not disabled while a request is in flight: the agent finds the component, changes it, then runs the linter and the tests.
The loop is easy to picture in code. This is a simplified sketch, not how Claude Code is implemented:
type ToolCall = { name: string; input: unknown };
type Step = { done: true; answer: string } | { done: false; call: ToolCall };
async function agent(
task: string,
model: (history: string[]) => Promise<Step>,
runTool: (call: ToolCall) => Promise<string>,
): Promise<string> {
const history = [task];
while (true) {
const step = await model(history); // the model sees the whole history every time
if (step.done) return step.answer;
const result = await runTool(step.call); // read a file, run tests, edit code...
history.push(JSON.stringify(step.call), result); // the result feeds the next decision
}
}Why it matters
- You stop being the proxy. The agent sees the real repo and the real test output, so it does not have to guess whether its code runs.
- Give it a check it can run. The loop closes on its own only if there is a pass/fail signal: tests, a build, a linter. Without one, “looks done” is the only signal, and you are back to verifying by hand. See Verifying agent output for the checks that work best.
- Every step lands in the context. Each file read and each command output is added to what the model sees, which is why agent sessions fill the context window much faster than a chat.
Related
- Coding agent vs chat assistant: what changes when the loop runs in your repo instead of in a chat.
- Context window: where all the tool results go.
- One task per session: keeping long loops from degrading.
- Verifying agent output: giving the loop a check it can run.