{ "cells": [ { "cell_type": "markdown", "id": "1ff5d825", "metadata": {}, "source": [ "# Explore: grounding in an unfamiliar codebase\n", "\n", "This notebook drops the agent into a repository it's never seen before and asks it to figure out the real architecture. The filesystem is the agent's only workspace, and only the files it chooses to read end up in its context window, so exploration with `ls`, `grep`, and `read` is how it builds up a mental model.\n", "\n", "The interesting part is a trap we've planted in the fixture. `ARCHITECTURE.md` describes a layout that the code no longer follows, so an agent that trusts the docs without checking the code will confidently give the wrong answer. Grounding, in this context, means verifying what you read against what's actually there rather than treating documentation as authoritative.\n", "\n", "What this teaches beyond the iterate notebook:\n", "\n", "- **Exploration before action.** A good agent reads enough of the tree to understand it, then answers, not the other way around.\n", "- **Adding resources mid-session.** The sidebar at the end shows how to push more files into a running session via `sessions.resources.add` rather than re-creating the session. Useful when exploration uncovers something the agent should look at next." ] }, { "cell_type": "code", "execution_count": null, "id": "b36209d4", "metadata": {}, "outputs": [], "source": [ "import io\n", "import os\n", "\n", "from anthropic import Anthropic\n", "from utilities import (\n", " make_unfamiliar_repo_zip,\n", " stream_until_end_turn,\n", " wait_for_idle_status,\n", ")\n", "\n", "MODEL = os.environ.get(\"COOKBOOK_MODEL\", \"claude-sonnet-4-6\")\n", "\n", "client = Anthropic()" ] }, { "cell_type": "markdown", "id": "98ab21e5", "metadata": {}, "source": [ "## 1. Generate the repo fixture\n", "\n", "The repo is small enough that we build it in memory with a helper rather than keeping a disk fixture alongside the notebook. The helper plants a `services/` microservices layout and a stale `ARCHITECTURE.md` that still describes the old monolithic layout." ] }, { "cell_type": "code", "execution_count": null, "id": "108bd220", "metadata": {}, "outputs": [], "source": [ "buf = make_unfamiliar_repo_zip()\n", "fixture_zip = client.beta.files.upload(file=(\"repo.zip\", buf, \"application/zip\"))\n", "print(f\"fixture: {fixture_zip.id}\")" ] }, { "cell_type": "markdown", "id": "1c2ba40f", "metadata": {}, "source": [ "## 2. Agent + environment + session" ] }, { "cell_type": "code", "execution_count": null, "id": "faf4808b", "metadata": {}, "outputs": [], "source": [ "agent = client.beta.agents.create(\n", " name=\"cookbook-explore\",\n", " model=MODEL,\n", " system=(\n", " \"You are onboarding to an unfamiliar codebase. Explore before \"\n", " \"answering, docs can be stale. Verify what you read against \"\n", " \"actual code structure. Write notes to /tmp/NOTES.md as you go.\"\n", " ),\n", " tools=[\n", " {\n", " \"type\": \"agent_toolset_20260401\",\n", " \"default_config\": {\n", " \"enabled\": True,\n", " \"permission_policy\": {\"type\": \"always_allow\"},\n", " },\n", " }\n", " ],\n", ")\n", "\n", "env = client.beta.environments.create(\n", " name=\"cookbook-explore-env\",\n", " config={\"type\": \"cloud\", \"networking\": {\"type\": \"limited\"}},\n", ")\n", "\n", "session = client.beta.sessions.create(\n", " environment_id=env.id,\n", " agent={\"type\": \"agent\", \"id\": agent.id, \"version\": agent.version},\n", " resources=[{\"type\": \"file\", \"file_id\": fixture_zip.id, \"mount_path\": \"repo.zip\"}],\n", " title=\"Onboard to repo\",\n", ")\n", "print(f\"session: {session.id}\")" ] }, { "cell_type": "markdown", "id": "2710d8a0", "metadata": {}, "source": [ "## 3. Explore and watch for the stale-doc trap\n", "\n", "A grounded answer mentions the real `services/` layout and flags `ARCHITECTURE.md` as out of date. An ungrounded answer parrots the monolith layout the stale doc describes." ] }, { "cell_type": "code", "execution_count": null, "id": "a8acee26", "metadata": {}, "outputs": [], "source": [ "client.beta.sessions.events.send(\n", " session_id=session.id,\n", " events=[\n", " {\n", " \"type\": \"user.message\",\n", " \"content\": [\n", " {\n", " \"type\": \"text\",\n", " \"text\": (\n", " \"Unzip /mnt/session/uploads/repo.zip to /tmp/repo/. \"\n", " \"Then: what is the actual architecture of this \"\n", " \"codebase? Be specific about directory structure. \"\n", " \"Check if the docs are accurate.\"\n", " ),\n", " }\n", " ],\n", " }\n", " ],\n", ")\n", "\n", "print(\"=== exploration ===\")\n", "stream_until_end_turn(client, session.id)" ] }, { "cell_type": "markdown", "id": "28aea9a1", "metadata": {}, "source": [ "## 4. Read back the agent's notes\n", "\n", "The agent was told to keep notes in `/tmp/NOTES.md` as it worked. Printing that file is a useful way to see how its understanding of the codebase developed during exploration." ] }, { "cell_type": "code", "execution_count": null, "id": "90943a95", "metadata": {}, "outputs": [], "source": [ "client.beta.sessions.events.send(\n", " session_id=session.id,\n", " events=[\n", " {\n", " \"type\": \"user.message\",\n", " \"content\": [{\"type\": \"text\", \"text\": \"cat /tmp/NOTES.md\"}],\n", " }\n", " ],\n", ")\n", "stream_until_end_turn(client, session.id)" ] }, { "cell_type": "markdown", "id": "31d28889", "metadata": {}, "source": [ "## Sidebar: adding more context to a running session\n", "\n", "The `resources=` argument on `sessions.create` is the most common way to mount files, but the API also exposes a `/v1/sessions//resources` sub-resource for managing mounts on an existing session. This is useful here: if exploration uncovers a question that needs additional context (a config file, a changelog, an external schema), you can drop it in without tearing down the session.\n", "\n", "The pattern is the same upload-then-attach loop you already know, just split across two calls instead of one:" ] }, { "cell_type": "code", "execution_count": null, "id": "eeec2815", "metadata": {}, "outputs": [], "source": [ "hints = b\"# DEPLOY HISTORY\\n2026-03-01: monolith -> microservices migration complete\\n\"\n", "hints_file = client.beta.files.upload(\n", " file=(\"DEPLOY_HISTORY.md\", io.BytesIO(hints), \"text/markdown\")\n", ")\n", "\n", "added = client.beta.sessions.resources.add(\n", " session_id=session.id,\n", " type=\"file\",\n", " file_id=hints_file.id,\n", " mount_path=\"DEPLOY_HISTORY.md\",\n", ")\n", "print(f\"added resource {added.id} to session {session.id}\")\n", "\n", "attached = client.beta.sessions.resources.list(session_id=session.id)\n", "print(f\"{len(attached.data)} resources attached now\")\n", "\n", "client.beta.sessions.events.send(\n", " session_id=session.id,\n", " events=[\n", " {\n", " \"type\": \"user.message\",\n", " \"content\": [\n", " {\n", " \"type\": \"text\",\n", " \"text\": (\n", " \"There's a DEPLOY_HISTORY.md in your workspace now. \"\n", " \"Read it and tell me whether it changes anything in \"\n", " \"your earlier answer.\"\n", " ),\n", " }\n", " ],\n", " }\n", " ],\n", ")\n", "print(\"\\n--- follow-up with deploy history ---\")\n", "stream_until_end_turn(client, session.id)\n", "\n", "# Detach the file now that the agent is done with it. `delete` here\n", "# is the resource-detach verb, not the cookbook-wide archive.\n", "client.beta.sessions.resources.delete(session_id=session.id, resource_id=added.id)\n", "print(\"detached follow-up resource\")" ] }, { "cell_type": "markdown", "id": "50363d78", "metadata": {}, "source": [ "## Cleanup" ] }, { "cell_type": "code", "execution_count": null, "id": "358e0a02", "metadata": {}, "outputs": [], "source": [ "wait_for_idle_status(client, session.id)\n", "client.beta.sessions.archive(session.id)\n", "client.beta.environments.archive(env.id)\n", "client.beta.agents.archive(agent.id)\n", "print(\"archived\")" ] } ], "metadata": { "jupytext": { "cell_metadata_filter": "-all", "main_language": "python", "notebook_metadata_filter": "-all" } }, "nbformat": 4, "nbformat_minor": 5 }