{
  "source_file": "0930",
  "source_platform": "Hashnode",
  "author": "Juan Carlos Olamendy",
  "title_listed": "Building an AI Agent From Scratch Using the Anthropic API: A Complete Guide",
  "keywords": "",
  "domain": "大模型算法",
  "content_type": "深度技术解析",
  "is_paid_flag": "否",
  "url": "https://juancolamendy.hashnode.dev/building-an-ai-agent-from-scratch-using-the-anthropic-api-a-complete-guide",
  "publish_date": "2025-10-15",
  "corpus_stage": "",
  "notes": "",
  "id": "813e0ec4c824",
  "collected_at": "2026-10-02T14:10:48",
  "title": "Building an AI Agent From Scratch Using the Anthropic API: A Complete Guide",
  "body_markdown": "Building an AI Agent From Scratch Using the Anthropic API: A Complete Guide\nPublished\nOctober 15, 2025\n•\n12\nmin read\n•\nView as Markdown\nJ\nJuan Carlos Olamendy\nSenior AI Engineer | Building & Telling Stories about AI/ML Systems | Software Engineer\nThe Power Is In Your Hands\nMost developers spend weeks wrestling with bloated agent frameworks, burning through $2,000+ in API costs while debugging black-box abstractions they don't understand.\nHere's the truth: you don't need complex frameworks to build a production-ready AI agent.\nIn this guide, you'll build a fully functional agent from scratch in under 200 lines of Python—complete with tool integration, memory management, and multi-step reasoning. No expensive platforms. No mysterious abstractions. Just clean, extensible code you control entirely.\nBy the end, you'll have an autonomous agent that breaks down complex problems, uses external tools, and maintains conversation context—all while understanding exactly how every component works. You'll learn the core patterns for tool design, the agentic loop that enables autonomous behavior, and battle-tested strategies for memory management and error handling.\nLet's build something real.\nSetting Up Your Development Environment\nLet's get your workspace ready for agent development. You'll need Python 3.8 or higher and a few essential libraries. First, install the required packages and set up your API credentials.\nimport\nos\nimport\njson\nfrom\ntyping\nimport\nList, Dict, Any, Optional\nfrom\ndotenv\nimport\nload_dotenv\nimport\nanthropic\n\nload_dotenv()\nCreate a\n.env\nfile in your project root with your Anthropic API key:\nANTHROPIC_API_KEY=your_api_key_here\nYou can obtain your API key from the Anthropic Console at\nconsole.anthropic.com\n. Keep this key secure and never commit it to version control.\nBuilding a Modular Tool System\nTools are the hands and eyes of your agent—they allow it to interact with the world beyond text generation.\nLet's build a flexible tool system that can be easily extended with new capabilities.\nWe'll create a weather information tool that demonstrates real-world utility.\nDesigning the Tool Interface\nEvery tool needs a consistent interface so the agent can discover and use it effectively. We'll define a base structure that all tools must implement.\nclass\nBaseTool\n:\n\"\"\"Base class for all agent tools\"\"\"\ndef\nget_name\n(\nself\n) -> str:\n\"\"\"Returns the tool name\"\"\"\nraise\nNotImplementedError\ndef\nget_description\n(\nself\n) -> str:\n\"\"\"Returns what the tool does\"\"\"\nraise\nNotImplementedError\ndef\nget_parameters\n(\nself\n) -> Dict[str, Any]:\n\"\"\"Returns the parameter schema for the tool\"\"\"\nraise\nNotImplementedError\ndef\nexecute\n(\nself, **kwargs\n) -> Dict[str, Any]:\n\"\"\"Executes the tool with given parameters\"\"\"\nraise\nNotImplementedError\nThis base class establishes a contract that every tool must fulfill.\nThe agent will call\nget_name()\n,\nget_description()\n, and\nget_parameters()\nto understand what the tool does.\nWhen the agent decides to use a tool, it calls\nexecute()\nwith the appropriate parameters.\nImplementing a Weather Information Tool\nNow let's build a concrete tool implementation that provides weather information. This tool simulates weather data retrieval—in production, you'd connect to a real weather API.\nclass\nWeatherTool\n(\nBaseTool\n):\n\"\"\"A tool for retrieving weather information\"\"\"\ndef\nget_name\n(\nself\n) -> str:\nreturn\n\"get_weather\"\ndef\nget_description\n(\nself\n) -> str:\nreturn\n\"Retrieves current weather information for a specified city. Use this when users ask about weather conditions, temperature, or forecasts.\"\ndef\nget_parameters\n(\nself\n) -> Dict[str, Any]:\nreturn\n{\n\"type\"\n:\n\"object\"\n,\n\"properties\"\n: {\n\"city\"\n: {\n\"type\"\n:\n\"string\"\n,\n\"description\"\n:\n\"The city name (e.g., 'New York', 'London', 'Tokyo')\"\n},\n\"unit\"\n: {\n\"type\"\n:\n\"string\"\n,\n\"enum\"\n: [\n\"celsius\"\n,\n\"fahrenheit\"\n],\n\"description\"\n:\n\"Temperature unit preference\"\n}\n            },\n\"required\"\n: [\n\"city\"\n]\n        }\ndef\nexecute\n(\nself, city: str, unit: str =\n\"celsius\"\n) -> Dict[str, Any]:\n\"\"\"\n        Simulates weather data retrieval.\n        In production, this would call a real weather API.\n        \"\"\"\n# Simulated weather data\nweather_database = {\n\"new york\"\n: {\n\"temp\"\n:\n22\n,\n\"condition\"\n:\n\"Partly cloudy\"\n,\n\"humidity\"\n:\n65\n},\n\"london\"\n: {\n\"temp\"\n:\n15\n,\n\"condition\"\n:\n\"Rainy\"\n,\n\"humidity\"\n:\n80\n},\n\"tokyo\"\n: {\n\"temp\"\n:\n28\n,\n\"condition\"\n:\n\"Sunny\"\n,\n\"humidity\"\n:\n55\n},\n\"paris\"\n: {\n\"temp\"\n:\n18\n,\n\"condition\"\n:\n\"Overcast\"\n,\n\"humidity\"\n:\n70\n},\n        }\n\n        city_key = city.lower()\nif\ncity_key\nnot\nin\nweather_database:\nreturn\n{\n\"error\"\n:\nf\"Weather data not available for\n{city}\n\"\n,\n\"available_cities\"\n: list(weather_database.keys())\n            }\n\n        data = weather_database[city_key]\n        temp = data[\n\"temp\"\n]\n# Convert temperature if needed\nif\nunit ==\n\"fahrenheit\"\n:\n            temp = (temp *\n9\n/\n5\n) +\n32\nreturn\n{\n\"city\"\n: city,\n\"temperature\"\n: round(temp,\n1\n),\n\"unit\"\n: unit,\n\"condition\"\n: data[\n\"condition\"\n],\n\"humidity\"\n: data[\n\"humidity\"\n]\n        }\nThis tool demonstrates proper error handling and parameter validation. Notice how we provide detailed descriptions for each parameter—this helps Claude understand how to use the tool correctly.\nCreating the Tool Registry\nWe need a system to manage multiple tools and convert them into the format Claude expects. Let's build a tool registry that handles this transformation.\ndef\ncreate_tool_schema\n(\ntool: BaseTool\n) -> Dict[str, Any]:\n\"\"\"\n    Converts a tool into Claude's expected schema format.\n\n    Args:\n        tool: The tool instance to convert\n\n    Returns:\n        A dictionary matching Claude's tool schema specification\n    \"\"\"\nreturn\n{\n\"name\"\n: tool.get_name(),\n\"description\"\n: tool.get_description(),\n\"input_schema\"\n: tool.get_parameters()\n    }\ndef\nregister_tools\n(\ntools: List[BaseTool]\n) -> tuple[List[Dict], Dict[str, BaseTool]]:\n\"\"\"\n    Registers multiple tools and creates lookup structures.\n\n    Args:\n        tools: List of tool instances to register\n\n    Returns:\n        A tuple of (tool_schemas, tool_map) for agent use\n    \"\"\"\ntool_schemas = [create_tool_schema(tool)\nfor\ntool\nin\ntools]\n    tool_map = {tool.get_name(): tool\nfor\ntool\nin\ntools}\nreturn\ntool_schemas, tool_map\nThese utility functions separate the concerns of tool definition and tool registration. The\ncreate_tool_schema()\nfunction transforms our Python classes into the JSON schema format that Claude understands. The\nregister_tools()\nfunction creates both the schema list for API calls and a lookup dictionary for executing tools.\nBuilding the Agent Core\nNow we'll construct the agent's brain—the component that manages conversation flow and orchestrates tool usage.\nThe agent needs to maintain conversation history, handle API interactions, and manage the request-response cycle.\nInitializing the Agent\nLet's create the agent class with proper initialization and configuration.\nclass\nAgent\n:\n\"\"\"\n    An AI agent powered by Claude that can use tools to solve problems.\n    \"\"\"\ndef\n__init__\n(\nself,\n        tools: Optional[List[BaseTool]] = None,\n        model: str =\n\"claude-sonnet-4-20250514\"\n,\n        system_prompt: Optional[str] = None\n):\n\"\"\"\n        Initialize the agent with tools and configuration.\n\n        Args:\n            tools: List of tool instances the agent can use\n            model: Claude model identifier\n            system_prompt: Custom system instructions for the agent\n        \"\"\"\nself.client = anthropic.Anthropic(api_key=os.getenv(\n\"ANTHROPIC_API_KEY\"\n))\n        self.model = model\n        self.conversation_history: List[Dict[str, Any]] = []\n# Set default system prompt if none provided\nself.system_prompt = system_prompt\nor\nself._get_default_system_prompt()\n# Register tools\nif\ntools:\n            self.tool_schemas, self.tool_map = register_tools(tools)\nelse\n:\n            self.tool_schemas, self.tool_map = [], {}\ndef\n_get_default_system_prompt\n(\nself\n) -> str:\n\"\"\"Returns the default system prompt for the agent\"\"\"\nreturn\n\"\"\"You are a helpful AI assistant that solves problems systematically.\n\nWhen faced with complex questions, break them down into smaller steps.\nUse available tools when you need to retrieve information or perform actions.\nThink through problems logically and explain your reasoning.\nAlways verify your answers before presenting them to the user.\"\"\"\nThe initialization method sets up all the essential components. We store the API client, model configuration, conversation history, and tool registry. The system prompt defines the agent's behavior and personality—customize this to match your use case.\nImplementing the Chat Interface\nThe chat method is where the magic happens—it sends messages to Claude and receives responses.\ndef\nsend_message\n(\nself, user_message: str\n) -> anthropic.types.Message:\n\"\"\"\n    Sends a user message to Claude and returns the response.\n\n    Args:\n        user_message: The user's input message\n\n    Returns:\n        Claude's response message object\n    \"\"\"\n# Add user message to conversation history\nself.conversation_history.append({\n\"role\"\n:\n\"user\"\n,\n\"content\"\n: user_message\n    })\n# Prepare API call parameters\napi_params = {\n\"model\"\n: self.model,\n\"max_tokens\"\n:\n4096\n,\n\"system\"\n: self.system_prompt,\n\"messages\"\n: self.conversation_history\n    }\n# Add tools if available\nif\nself.tool_schemas:\n        api_params[\n\"tools\"\n] = self.tool_schemas\n# Call Claude API\nresponse = self.client.messages.create(**api_params)\n# Add assistant's response to conversation history\nself.conversation_history.append({\n\"role\"\n:\n\"assistant\"\n,\n\"content\"\n: response.content\n    })\nreturn\nresponse\nThis method handles the complete request-response cycle. We append the user's message to history, prepare the API parameters, make the call, and store Claude's response. Notice how we conditionally include tools only when they're available—this keeps the API call clean for tool-less agents.\nExtracting Text Responses\nClaude's responses can contain multiple content blocks, including text and tool use blocks. We need a helper function to extract just the text.\ndef\nget_text_response\n(\nresponse: anthropic.types.Message\n) -> str:\n\"\"\"\n    Extracts text content from Claude's response.\n\n    Args:\n        response: The message object from Claude\n\n    Returns:\n        Concatenated text from all text content blocks\n    \"\"\"\ntext_parts = []\nfor\nblock\nin\nresponse.content:\nif\nblock.type ==\n\"text\"\n:\n            text_parts.append(block.text)\nreturn\n\"\\n\"\n.join(text_parts)\nThis function iterates through all content blocks and collects text. When Claude uses tools, it might include both thinking (text) and tool calls in the same response.\nImplementing the Agentic Loop\nThe agentic loop is where your agent becomes truly autonomous. This loop allows the agent to use tools, evaluate results, and continue reasoning until it reaches a final answer.\nUnlike a simple chatbot that responds once, an agent can perform multiple actions in sequence to solve complex problems.\nDetecting Tool Usage\nFirst, we need a function to identify when Claude wants to use tools.\ndef\nextract_tool_uses\n(\nresponse: anthropic.types.Message\n) -> List[Dict[str, Any]]:\n\"\"\"\n    Extracts all tool use blocks from Claude's response.\n\n    Args:\n        response: The message object from Claude\n\n    Returns:\n        List of dictionaries containing tool use information\n    \"\"\"\ntool_uses = []\nfor\nblock\nin\nresponse.content:\nif\nblock.type ==\n\"tool_use\"\n:\n            tool_uses.append({\n\"id\"\n: block.id,\n\"name\"\n: block.name,\n\"input\"\n: block.input\n            })\nreturn\ntool_uses\nWhen Claude decides it needs a tool, it includes a\ntool_use\nblock in its response. This function finds all such blocks and extracts the relevant information. Each tool use has an ID (for tracking), a name (which tool to use), and input (the parameters).\nExecuting Tools Safely\nTool execution is where your agent interacts with the outside world—this requires careful error handling.\ndef\nexecute_tool_safely\n(\ntool: BaseTool,\n    tool_name: str,\n    tool_input: Dict[str, Any]\n) -> Dict[str, Any]:\n\"\"\"\n    Executes a tool with error handling and logging.\n\n    Args:\n        tool: The tool instance to execute\n        tool_name: Name of the tool (for logging)\n        tool_input: Parameters to pass to the tool\n\n    Returns:\n        The tool's execution result or error information\n    \"\"\"\ntry\n:\n        print(\nf\"  → Executing\n{tool_name}\nwith input:\n{tool_input}\n\"\n)\n        result = tool.execute(**tool_input)\n        print(\nf\"  ✓ Tool execution successful\"\n)\nreturn\nresult\nexcept\nException\nas\ne:\n        error_msg =\nf\"Tool execution failed:\n{str(e)}\n\"\nprint(\nf\"  ✗\n{error_msg}\n\"\n)\nreturn\n{\n\"error\"\n: error_msg}\nThis function wraps tool execution in try-except blocks. If something goes wrong (invalid parameters, network errors, etc.), we capture the error and return it as a result. The agent can then see what went wrong and potentially retry or adjust its approach.\nProcessing Tool Results\nAfter executing tools, we need to format the results for Claude.\ndef\ncreate_tool_result_content\n(\ntool_uses: List[Dict[str, Any]],\n    tool_map: Dict[str, BaseTool]\n) -> List[Dict[str, Any]]:\n\"\"\"\n    Executes tools and formats results for Claude.\n\n    Args:\n        tool_uses: List of tool use requests from Claude\n        tool_map: Mapping of tool names to tool instances\n\n    Returns:\n        List of tool result objects formatted for the API\n    \"\"\"\ntool_results = []\nfor\ntool_use\nin\ntool_uses:\n        tool_name = tool_use[\n\"name\"\n]\n        tool_id = tool_use[\n\"id\"\n]\n        tool_input = tool_use[\n\"input\"\n]\n# Execute the tool\ntool = tool_map[tool_name]\n        result = execute_tool_safely(tool, tool_name, tool_input)\n# Format result for Claude\ntool_results.append({\n\"type\"\n:\n\"tool_result\"\n,\n\"tool_use_id\"\n: tool_id,\n\"content\"\n: json.dumps(result)\n        })\nreturn\ntool_results\nClaude expects tool results in a specific format. Each result must include the original tool use ID (so Claude knows which call it corresponds to) and the content as a JSON string.\nBuilding the Main Agent Loop\nNow we combine everything into a loop that handles multi-turn tool usage.\ndef\nrun_agent_loop\n(\nagent: Agent,\n    initial_message: str,\n    max_iterations: int =\n10\n,\n    verbose: bool = True\n) -> str:\n\"\"\"\n    Runs the agent in a loop, handling tool use until completion.\n\n    Args:\n        agent: The agent instance to run\n        initial_message: The user's initial query\n        max_iterations: Maximum iterations to prevent infinite loops\n        verbose: Whether to print detailed execution logs\n\n    Returns:\n        The final text response from the agent\n    \"\"\"\niteration =\n0\ncurrent_message = initial_message\nif\nverbose:\n        print(\nf\"\\n\n{\n'='\n*\n80\n}\n\"\n)\n        print(\nf\"AGENT EXECUTION START\"\n)\n        print(\nf\"\n{\n'='\n*\n80\n}\n\"\n)\n        print(\nf\"User Query:\n{initial_message}\n\\n\"\n)\nwhile\niteration < max_iterations:\n        iteration +=\n1\nif\nverbose:\n            print(\nf\"\\n--- Iteration\n{iteration}\n---\"\n)\n# Send message to Claude\nresponse = agent.send_message(current_message)\n# Check if Claude wants to use tools\ntool_uses = extract_tool_uses(response)\nif\nnot\ntool_uses:\n# No tools needed - we have the final answer\nfinal_response = get_text_response(response)\nif\nverbose:\n                print(\nf\"\\n\n{\n'='\n*\n80\n}\n\"\n)\n                print(\nf\"AGENT EXECUTION COMPLETE\"\n)\n                print(\nf\"\n{\n'='\n*\n80\n}\n\"\n)\n                print(\nf\"Final Response:\n{final_response}\n\\n\"\n)\nreturn\nfinal_response\n# Execute tools and prepare results\nif\nverbose:\n            print(\nf\"Claude wants to use\n{len(tool_uses)}\ntool(s):\"\n)\n\n        tool_results = create_tool_result_content(tool_uses, agent.tool_map)\n# Send tool results back to Claude\ncurrent_message = tool_results\nreturn\n\"Maximum iterations reached without completion.\"\nThis function orchestrates the entire agent execution. It loops until Claude provides a final answer (no tool use) or hits the iteration limit. Each iteration sends a message, checks for tool use, executes any requested tools, and feeds the results back.\nComplete Implementation Example\nLet's bring everything together into a working example that demonstrates the full capabilities of our agent system.\ndef\ncreate_weather_agent\n() -> Agent:\n\"\"\"\n    Factory function to create a weather-enabled agent.\n\n    Returns:\n        Configured agent instance with weather tool\n    \"\"\"\nweather_tool = WeatherTool()\n\n    custom_system_prompt =\n\"\"\"You are a helpful weather assistant.\n\nWhen users ask about weather, use the get_weather tool to retrieve accurate information.\nPresent weather data in a clear, conversational manner.\nIf users don't specify a temperature unit, use celsius by default.\nAlways mention the weather condition along with the temperature.\"\"\"\nreturn\nAgent(\n        tools=[weather_tool],\n        system_prompt=custom_system_prompt\n    )\ndef\nmain\n():\n\"\"\"Main execution function demonstrating agent capabilities\"\"\"\n# Create agent with weather tool\nagent = create_weather_agent()\n# Test Case 1: Simple weather query\nprint(\n\"\\n\"\n+\n\"=\"\n*\n80\n)\n    print(\n\"TEST 1: Simple Weather Query\"\n)\n    print(\n\"=\"\n*\n80\n)\n    response = run_agent_loop(\n        agent,\n\"What's the weather like in Tokyo?\"\n,\n        verbose=\nTrue\n)\n    print(response)\n# Test Case 2: Multi-city comparison\nprint(\n\"\\n\"\n+\n\"=\"\n*\n80\n)\n    print(\n\"TEST 2: Multi-City Comparison\"\n)\n    print(\n\"=\"\n*\n80\n)\n    response = run_agent_loop(\n        agent,\n\"Compare the weather in London and Paris. Which city is warmer?\"\n,\n        verbose=\nTrue\n)\n    print(response)\n# Test Case 3: Temperature unit conversion\nprint(\n\"\\n\"\n+\n\"=\"\n*\n80\n)\n    print(\n\"TEST 3: Temperature Unit Preference\"\n)\n    print(\n\"=\"\n*\n80\n)\n    response = run_agent_loop(\n        agent,\n\"What's the temperature in New York in Fahrenheit?\"\n,\n        verbose=\nTrue\n)\n    print(response)\nif\n__name__ ==\n\"__main__\"\n:\n    main()\nThis complete example shows how to create specialized agents, run them through various scenarios, and monitor their performance.\nConclusion\nYou've just built a complete AI agent system from scratch using the Anthropic API.\nThis agent can reason through complex problems, use tools to gather information, maintain conversation context, and provide verified answers.\nThe modular architecture we've created allows you to easily add new tools, customize behavior, and scale to production environments.\nThe key takeaways from this guide include understanding that agents are more than chatbots—they're autonomous systems that can take actions.\nTool design is crucial—well-designed tools with clear schemas enable Claude to use them effectively.\nThe agentic loop enables multi-step reasoning and complex problem-solving. Memory management keeps conversations efficient and cost-effective.\nFrom here, you can extend your agent with additional tools like database queries, API integrations, file operations, or web searches.\nYou can implement streaming responses for better UX, add conversation summarization for long-running sessions, or build multi-agent systems where specialized agents collaborate.\nThe future of AI isn't just in better models—it's in better agent architectures that leverage those models effectively.\nYou now have the foundation to build production-ready AI agents that can solve real-world problems autonomously.\nStart experimenting with your own tools and use cases. The possibilities are limitless.\nPS:\nIf you like this article, share it with others ♻️\nWould help a lot ❤️\nAnd feel free to follow me for more content like this.\n#\nartificial-intelligence\n#\nagentic-ai\n#\nllm",
  "metadata": {
    "title": "Building an AI Agent From Scratch Using the Anthropic API: A Complete Guide",
    "description": "The Power Is In Your Hands\nMost developers spend weeks wrestling with bloated agent frameworks, burning through $2,000+ in API costs while debugging black-box abstractions they don't understand.\nHere's the truth: you don't need complex frameworks to ...",
    "keywords": null,
    "author": null,
    "og:title": "Building an AI Agent From Scratch Using the Anthropic API: A Complete Guide",
    "og:description": "The Power Is In Your Hands\nMost developers spend weeks wrestling with bloated agent frameworks, burning through $2,000+ in API costs while debugging black-box abstractions they don't understand.\nHere's the truth: you don't need complex frameworks to ...",
    "og:url": "https://juancolamendy.hashnode.dev/building-an-ai-agent-from-scratch-using-the-anthropic-api-a-complete-guide",
    "og:site_name": "Juan C Olamendy",
    "og:image": "https://cdn.hashnode.com/res/hashnode/image/upload/v1760537735530/01bbee9a-bd5b-4460-963f-b3b35b1002df.png",
    "og:image:width": "1200",
    "og:image:height": "630",
    "og:type": "article",
    "twitter:card": "summary_large_image",
    "twitter:creator": "https://twitter.com/juancolamendy",
    "twitter:title": "Building an AI Agent From Scratch Using the Anthropic API: A Complete Guide",
    "twitter:description": "The Power Is In Your Hands\nMost developers spend weeks wrestling with bloated agent frameworks, burning through $2,000+ in API costs while debugging black-box abstractions they don't understand.\nHere's the truth: you don't need complex frameworks to ...",
    "twitter:image": "https://cdn.hashnode.com/res/hashnode/image/upload/v1760537735530/01bbee9a-bd5b-4460-963f-b3b35b1002df.png",
    "article:published_time": "2025-10-15T15:03:43.615Z",
    "article:author": "https://juancolamendy.hashnode.dev/@juancolamendy"
  },
  "paywall": {
    "listed_paid": false,
    "detected": false,
    "marker": "",
    "public_only": false,
    "policy": "付费/会员内容只保留页面公开可见部分，不绕过付费墙"
  },
  "crawl": {
    "success": true,
    "status_code": 200,
    "error": ""
  },
  "code_blocks": [
    {
      "file": "attachments/813e0ec4c824/code/body-000.txt",
      "chars": 132,
      "language": "text"
    },
    {
      "file": "attachments/813e0ec4c824/code/body-001.txt",
      "chars": 36,
      "language": "text"
    },
    {
      "file": "attachments/813e0ec4c824/code/body-002.txt",
      "chars": 552,
      "language": "text"
    },
    {
      "file": "attachments/813e0ec4c824/code/body-003.txt",
      "chars": 2108,
      "language": "text"
    },
    {
      "file": "attachments/813e0ec4c824/code/body-004.txt",
      "chars": 861,
      "language": "text"
    },
    {
      "file": "attachments/813e0ec4c824/code/body-005.txt",
      "chars": 1511,
      "language": "text"
    },
    {
      "file": "attachments/813e0ec4c824/code/body-006.txt",
      "chars": 982,
      "language": "text"
    },
    {
      "file": "attachments/813e0ec4c824/code/body-007.txt",
      "chars": 425,
      "language": "text"
    },
    {
      "file": "attachments/813e0ec4c824/code/body-008.txt",
      "chars": 555,
      "language": "text"
    },
    {
      "file": "attachments/813e0ec4c824/code/body-009.txt",
      "chars": 757,
      "language": "text"
    },
    {
      "file": "attachments/813e0ec4c824/code/body-010.txt",
      "chars": 902,
      "language": "text"
    },
    {
      "file": "attachments/813e0ec4c824/code/body-011.txt",
      "chars": 1780,
      "language": "text"
    },
    {
      "file": "attachments/813e0ec4c824/code/body-012.txt",
      "chars": 1699,
      "language": "text"
    }
  ],
  "images": [
    {
      "status": "ok",
      "url": "https://cdn.hashnode.com/res/hashnode/image/upload/v1760537735530/01bbee9a-bd5b-4460-963f-b3b35b1002df.png",
      "bytes": 2384461,
      "reused": true,
      "local_path": "attachments/813e0ec4c824/images/000.png",
      "src": "https://cdn.hashnode.com/res/hashnode/image/upload/v1760537735530/01bbee9a-bd5b-4460-963f-b3b35b1002df.png",
      "alt": "Building an AI Agent From Scratch Using the Anthropic API: A Complete Guide",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://cdn.hashnode.com/res/hashnode/image/upload/v1760540402845/7fcef87d-7953-47be-8f80-704cd81fd7e4.png",
      "bytes": 44395,
      "reused": true,
      "local_path": "attachments/813e0ec4c824/images/002.png",
      "src": "https://cdn.hashnode.com/res/hashnode/image/upload/v1760540402845/7fcef87d-7953-47be-8f80-704cd81fd7e4.png",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://cdn.hashnode.com/res/hashnode/image/upload/v1760540460201/c04e7859-2a18-4e86-8242-aa4183a467b5.png",
      "bytes": 78712,
      "reused": true,
      "local_path": "attachments/813e0ec4c824/images/003.png",
      "src": "https://cdn.hashnode.com/res/hashnode/image/upload/v1760540460201/c04e7859-2a18-4e86-8242-aa4183a467b5.png",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://cdn.hashnode.com/res/hashnode/image/upload/v1760540492513/130f03da-d873-4984-ac00-7451172d41bd.png",
      "bytes": 42986,
      "reused": true,
      "local_path": "attachments/813e0ec4c824/images/004.png",
      "src": "https://cdn.hashnode.com/res/hashnode/image/upload/v1760540492513/130f03da-d873-4984-ac00-7451172d41bd.png",
      "alt": "",
      "downloaded": true
    }
  ],
  "audio": [],
  "videos": [],
  "links": [
    {
      "href": "https://juancolamendy.hashnode.dev/building-an-ai-agent-from-scratch-using-the-anthropic-api-a-complete-guide.md",
      "label": "View as Markdown",
      "type": "a"
    },
    {
      "href": "https://hashnode.com/@juancolamendy",
      "label": "J",
      "type": "a"
    },
    {
      "href": "https://twitter.com/juancolamendy",
      "label": "",
      "type": "a"
    },
    {
      "href": "https://www.linkedin.com/in/juancolamendy/",
      "label": "",
      "type": "a"
    },
    {
      "href": "http://console.anthropic.com/",
      "label": "console.anthropic.com",
      "type": "a"
    },
    {
      "href": "https://juancolamendy.hashnode.dev/tag/artificial-intelligence",
      "label": "# artificial-intelligence",
      "type": "a"
    },
    {
      "href": "https://juancolamendy.hashnode.dev/tag/agentic-ai",
      "label": "# agentic-ai",
      "type": "a"
    },
    {
      "href": "https://juancolamendy.hashnode.dev/tag/llm",
      "label": "# llm",
      "type": "a"
    }
  ],
  "retry_at": "2026-10-03T08:05:50",
  "repair_version": 2,
  "repaired_at": "2026-10-14T10:50:34Z",
  "source_url": "https://juancolamendy.hashnode.dev/building-an-ai-agent-from-scratch-using-the-anthropic-api-a-complete-guide",
  "papers": [],
  "actual_video_files": [],
  "body_chars": 19439,
  "expected_body_images": 4,
  "discovery_status": "ok",
  "delivery_status": "ok",
  "missing_reason": "",
  "raw_html": "attachments/813e0ec4c824/pages/source.html",
  "local_html": "attachments/813e0ec4c824/pages/article.html",
  "body_base_path": "attachments/813e0ec4c824/pages/",
  "body_markdown_path": "attachments/813e0ec4c824/pages/article.md"
}