Try this first
Run Rover on a ten-turn task and try to answer this afterwards: which tool call changed
agent.py?
You cannot. The terminal has scrolled, the tool output was long, and everything looks the same.
What a useful log looks like
Not this:
DEBUG:anthropic:request POST /v1/messages
DEBUG:anthropic:response 200
That tells you the HTTP worked. You already knew that.
A useful agent log records decisions and effects — one line per event, scannable:
turn 3 think (2 blocks)
turn 3 tool read_file {"path": "agent.py"} -> 4.1 KB
turn 4 tool str_replace {"path": "agent.py", ...} -> ok, 1 replacement
turn 4 tool bash {"command": "python3 -c 'import agent'"} -> exit 1 ERROR
turn 5 tool read_file {"path": "agent.py"} -> 4.2 KB
turn 5 tool str_replace {"path": "agent.py", ...} -> ok, 1 replacement
turn 6 tool bash {"command": "python3 -c 'import agent'"} -> exit 0
turn 7 stop end_turn (verify: passed)
Read that. You can see the whole run: it edited, the import broke, it re-read the file, fixed
it, checked again, and stopped. Seven turns in eight lines.
The implementation
import json
def log(turn, kind, detail, result=""):
line = f"turn {turn:<3} {kind:<6} {detail}"
if result:
line += f" -> {result}"
print(line, flush=True)
with open("rover.log", "a") as f:
f.write(line + "\n")
Called at three points:
log(turn, "tool", f"{call.name} {json.dumps(call.arguments)[:80]}", summarise(output, failed))
log(turn, "stop", reply.stop, f"verify: {'passed' if ok else 'FAILED'}")
log(turn, "usage", f"in={reply.usage.get('in')} out={reply.usage.get('out')}")
Where summarise turns a tool result into a few words — ok, exit 1 ERROR, 4.1 KB,
no matches — never the whole output. The full output belongs in the transcript, not the log.
Log the arguments, truncated
read_file tells you nothing. read_file {"path": "agent.py"} tells you what happened.
Truncate hard — eighty characters — because a str_replace argument can be a whole function
and will drown the log it is supposed to make readable.
Log the usage line
One line per turn with input and output tokens. It costs nothing and it answers the question
you will eventually ask: where did the money go?
You will see input tokens climbing every turn even when nothing much happens. That is the
transcript growing, which is Module 6.
The two logs
Worth separating from the start:
| Log | Contents | For |
|---|---|---|
| Console | One line per event | Watching a run happen |
| File | The same lines, plus the full transcript as JSON | Working out what went wrong afterwards |
Dump the whole messages list to a JSON file at the end of a run. When something behaves
strangely three turns in, that file is the only artifact that can tell you why — and it is the
exact input the model saw.
Log decisions and effects, one line each. If you cannot reconstruct a run from your log,
you cannot debug your agent.
Try this before the next lesson
Add the log. Run the three-part task from Lesson 4.2.
Then read only the log — not the terminal output — and write down what happened. If you cannot
tell, add whatever line was missing. That is how you find out what your log needs.