{"slug": "execution-trees-not-more-logs-a-better-debugging-model-for-ai-agents", "title": "Execution Trees, Not More Logs: A Better Debugging Model for AI Agents", "summary": "A developer introduced execution trees as a debugging model for AI agents in the open-source toolkit AgentInspect, arguing that flat logs fail to capture causality in complex agent runs. The tool records nested step relationships, making failures and fallbacks explicit, and is available as a TypeScript library with a local inspection CLI.", "body_md": "A flat log can tell you that five things happened. It often cannot tell you which operation caused the next one, which failure triggered a fallback, or whether three tool calls were children of one planning step or unrelated work.\n\nThat distinction matters for AI agents because the path is part of the behavior.\n\nI maintain [AgentInspect](https://github.com/rajudandigam/agent-inspect), an open-source TypeScript toolkit for inspecting agent executions locally. This article explains why I chose execution trees as the primary debugging model, using synthetic fixtures verified against [ agent-inspect@6.17.4](https://github.com/rajudandigam/agent-inspect/tree/agent-inspect%406.17.4).\n\nConsider a support agent that performs these operations:\n\n```\n09:00:00.000 plan started\n09:00:00.020 inventory request started\n09:00:00.060 inventory request failed: 503\n09:00:00.061 inventory request started\n09:00:00.120 inventory request succeeded\n09:00:00.150 answer completed\n```\n\nThis is enough to reconstruct a simple story, but the reconstruction is happening in your head. Add nested agents, parallel tools, reused operation names, and interleaved application logs, and timestamps stop being a reliable picture of causality.\n\nAn execution tree makes the relationship explicit:\n\n```\nsupport-agent\n├── plan\n├── fetch-inventory (failed: 503)\n├── fetch-inventory (success)\n└── draft-answer\n```\n\nThe tree does not replace raw event data. It is a projection of that data for the question developers usually ask first: *What path did this run take?*\n\nAgentInspect provides wrappers for a run and for named steps. Here is a deliberately small example:\n\n``` js\nimport { inspectRun, step } from \"agent-inspect\";\n\nawait inspectRun(\n  \"travel-planner\",\n  async () => {\n    const plan = await step(\"plan\", async () => ({\n      destinations: [\"SFO\", \"SEA\"],\n    }));\n\n    const [flights, hotels] = await Promise.all([\n      step.tool(\"search-flights\", async () => [\n        { id: \"F-101\", price: 220 },\n      ]),\n      step.tool(\"search-hotels\", async () => [\n        { id: \"H-202\", nightly: 180 },\n      ]),\n    ]);\n\n    return step.llm(\"rank-options\", async () => ({\n      plan,\n      flights,\n      hotels,\n    }));\n  },\n  { traceDir: \"./.agent-inspect\" },\n);\n```\n\nThis is manual instrumentation. It does not claim that a wrapper can automatically discover every framework-internal operation. The purpose is to record the boundaries you care about: the run, its planning step, the two sibling tool calls, and the final model-facing step.\n\nThen inspect the run locally:\n\n```\nnpx agent-inspect view travel-planner \\\n  --dir .agent-inspect \\\n  --summary\n```\n\nA three-level synthetic fixture renders like this:\n\n```\nExecution Tree:\n✔ outer (120ms)\n  ✔ middle (80ms)\n    ✔ inner (50ms)\n```\n\nThose two spaces are not decoration. They tell us that `inner`\n\nbelongs to `middle`\n\n, which belongs to `outer`\n\n. If `inner`\n\nfails, we know which higher-level operation owned it. With flat logs, matching IDs or surrounding timestamps would be required to infer the same structure.\n\nNesting is especially useful when one agent delegates to another, a tool performs several sub-operations, or a retrieval step owns both a query rewrite and a vector search.\n\nNow consider an error-recovery fixture:\n\n```\nExecution Tree:\n✖ tool:primary-search (100ms)\n    Error: primary search unavailable\n✔ tool:fallback-search (200ms)\n✔ handle-recovered-result (50ms)\n```\n\nThe final run may still be successful. If we looked only at the answer, the failed primary search could disappear from the debugging story. The tree preserves both facts:\n\nThat distinction can change the engineering decision. A successful answer produced by a fallback may be acceptable, but a sudden rise in fallback use could still indicate a degraded dependency or an expensive routing change.\n\nRetries deserve their own visible shape:\n\n```\nExecution Tree:\n✖ tool:fetch-inventory (40ms)\n    Error: synthetic 503 from upstream\n✖ tool:fetch-inventory (45ms)\n    Error: synthetic 503 from upstream\n✔ tool:fetch-inventory (60ms)\n✔ handle-recovered-result (30ms)\n```\n\nA final success status would hide the cost of reaching success. The repeated tool name makes the retry sequence visible. It also gives a deterministic check something concrete to evaluate: for example, whether `fetch-inventory`\n\nexceeded an allowed call count.\n\nThe tree alone does not tell us whether the retry policy was correct. It gives us evidence that the policy was exercised.\n\nA parallel fixture renders as sibling operations:\n\n```\nExecution Tree:\n✔ tool:search-hotels (300ms)\n✔ tool:search-flights (200ms)\n✔ tool:search-cars (100ms)\n```\n\nThe durations are not meant to be added. These steps are siblings, and may overlap. That protects us from a common timeline mistake: assuming each timestamped operation waited for the previous one.\n\nThe tree does not prove that concurrency was optimally implemented, but it accurately preserves the structural relationship needed to investigate it.\n\nIt is tempting to turn a readable tree into the only stored artifact. I avoided that because a human-readable view necessarily compresses information.\n\nThe underlying trace may include identifiers, timestamps, status, inputs or outputs (subject to capture policy), observations, and metadata. Different questions need different projections:\n\n``` php\nstructured trace\n├── tree      -> what path happened?\n├── check     -> did an invariant hold?\n├── diff      -> what changed between runs?\n├── report    -> what should a reviewer read?\n└── bundle    -> what evidence can be shared?\n```\n\nAn execution tree is the fastest entry point, not a substitute for checks or analysis.\n\nSuppose the retry tree reveals that an inventory tool can run three times. If the intended policy permits at most two calls, encode that expectation rather than relying on future visual inspection.\n\nAt the CLI level, a trajectory check can require tools and fail on recorded observations:\n\n```\nnpx agent-inspect check travel-planner \\\n  --dir .agent-inspect \\\n  --preset trajectory \\\n  --required-tool search-flights \\\n  --fail-on-observation failed\n```\n\nFor richer rules, AgentInspect exposes an experimental `TraceContract`\n\nAPI that can express tool requirements, forbidden tools, maximum calls, ordering, run status, duration, model allowlists, and token ceilings. Because that API is beta in the referenced release, pin the version and test the exact semantics before using it as a CI gate.\n\nThe important workflow is broader than one API:\n\nA clean tree does not prove that an answer is correct. A required retrieval step may return irrelevant documents. A model call may produce unsupported claims. A tool can succeed technically while returning stale data.\n\nExecution trees are strongest for structural questions:\n\nUse semantic evaluators, domain tests, and human review for content quality. The most reliable agent debugging workflow combines these layers rather than asking one visualization to answer every question.\n\nThe final response is what the user sees, but the execution path is what the engineer can improve. A tree turns that path from an inferred narrative into a concrete artifact.\n\nThat is the design principle behind AgentInspect’s local view: preserve causal structure, expose unsuccessful work even when recovery succeeds, and make suspicious patterns easy to convert into repeatable checks.\n\nYou can explore the exact release used here on [GitHub](https://github.com/rajudandigam/agent-inspect/tree/agent-inspect%406.17.4). If you try it, start with a synthetic failure-and-fallback fixture. A perfect happy path is the least interesting test of a debugger.", "url": "https://wpnews.pro/news/execution-trees-not-more-logs-a-better-debugging-model-for-ai-agents", "canonical_source": "https://dev.to/raju_dandigam/execution-trees-not-more-logs-a-better-debugging-model-for-ai-agents-3d4g", "published_at": "2026-09-02 04:46:27+00:00", "updated_at": "2026-09-02 04:52:27.086517+00:00", "lang": "en", "topics": ["developer-tools", "ai-agents", "ai-tools"], "entities": ["AgentInspect", "rajudandigam"], "alternates": {"html": "https://wpnews.pro/news/execution-trees-not-more-logs-a-better-debugging-model-for-ai-agents", "markdown": "https://wpnews.pro/news/execution-trees-not-more-logs-a-better-debugging-model-for-ai-agents.md", "text": "https://wpnews.pro/news/execution-trees-not-more-logs-a-better-debugging-model-for-ai-agents.txt", "jsonld": "https://wpnews.pro/news/execution-trees-not-more-logs-a-better-debugging-model-for-ai-agents.jsonld"}}