Why console.log Isn't Enough When Building AI Agents A developer argues that console.log is insufficient for debugging AI agents and proposes structured event tracing with traceId, spanId, and parentSpanId to reconstruct causal relationships. The approach uses JSON-formatted events to build a tree view of agent runs, making failures like cache fallbacks visible even when top-level operations succeed. An AI agent fails, so you add a few log statements: console.log 'starting agent' ; console.log 'tool result', result ; console.log 'final answer', answer ; That works until the agent calls tools in parallel, retries one of them, falls back to cached data, and makes several model calls for different purposes. The terminal still contains the events, but it no longer explains the run. The limitation is not console.log itself. The limitation is flat, uncorrelated events . Agent debugging needs identity, parent-child relationships, lifecycle, and safe metadata. Without those, more log lines often create more noise rather than more understanding. Consider this output: 10:00:01 search started 10:00:01 search started 10:00:02 model started 10:00:02 search completed 10:00:03 search timed out 10:00:03 cache fallback used 10:00:04 model completed Several important questions remain: Timestamps describe when events were written. They do not describe causality. Some request paths are short and sequential, and flat logs are perfectly adequate. Agents become harder to observe because their control flow is often dynamic: The useful representation is usually a tree: support agent ├─ classify question │ └─ model call ├─ retrieve context │ ├─ vector search │ └─ keyword search ├─ check account │ ├─ billing api timeout │ └─ cached account fallback └─ generate answer └─ model call The two model calls now have different roles. The fallback belongs to check account , and the searches are parallel children of retrieval. The same events are easier to reason about because their relationships are explicit. The hardest agent failures do not always throw exceptions. A workflow can complete successfully while using the wrong path. Imagine a quote agent that reports an item as available. Every top-level operation says success , but the inventory service timed out and the agent used a cache that was a day old. The response is syntactically valid and the HTTP status is 200. The behavior is still wrong for the current user. A trace can make the path visible: generate quote ok ├─ find product ok ├─ check inventory ok │ ├─ live inventory error: timeout │ └─ cached inventory ok: age hours=24 └─ compose quote ok: inventory source=cache Flat logs can capture all of these facts, but only if every line carries enough context to reconstruct the relationships. At that point, you are already building a tracing model. The first improvement is to give every run and step stable identity. type AgentEvent = { traceId: string; spanId: string; parentSpanId: string | null; event: 'started' | 'completed'; name: string; kind: 'run' | 'model' | 'tool' | 'retrieval' | 'fallback'; timestamp: string; status?: 'ok' | 'error' | 'cancelled'; durationMs?: number; metadata?: Record