{"slug": "why-do-we-import-100mb-of-frameworks-to-run-a-50-line-llm-reasoning-loop", "title": "Why do we import 100MB of frameworks to run a 50-line LLM reasoning loop?", "summary": "A developer argues that building an AI agent from scratch with vanilla Python and the OpenAI SDK is simpler and more transparent than using bloated frameworks like AutoGen, LangChain, or CrewAI. The post demonstrates a minimal agent with a reasoning loop, tool calling, and state management in under 50 lines of code, emphasizing that complex orchestration frameworks are unnecessary for basic agentic design.", "body_md": "Stop Importing Bloated Frameworks: Build a Python AI Agent from Scratch\n\nYou want to build an AI agent.\n\nSo you head to the docs of a popular orchestration framework, copy the boilerplate, import 20 modules, and spin up an agent. It works—until it doesn't.\n\nSuddenly, you're looking at a 50-line stack trace originating from a library wrapper. You don't know where the query failed, what the exact prompt was, or why the tool call failed to parse.\n\nHere is the truth: **You don't need AutoGen, LangChain, or CrewAI to build a working AI agent.**\n\nYou just need vanilla Python and a basic understanding of the three core pillars of agentic design.\n\n**The Three Pillars of an AI Agent**\n\nAny basic agent can be broken down into three simple components:\n\n`role`\n\nand `content`\n\n) passed to and from the LLM.`while`\n\nloop that calls the LLM, checks if it wants to use a tool, runs the tool if requested, appends the result to the State, and repeats until the LLM returns a final answer.Let’s build one.\n\nThis example uses the official `openai`\n\nSDK, but the same logic applies to Anthropic, Gemini, or local models running via Ollama.\n\n``` python\npython\nimport os\nimport json\nfrom openai import OpenAI\n\n# Initialize client\nclient = OpenAI(api_key=os.environ.get(\"OPENAI_API_KEY\"))\n\n# 1. Define the tools our agent can use\ndef get_weather(location):\n    if \"tokyo\" in location.lower():\n        return \"Tokyo is sunny and 25°C.\"\n    return \"Cool and rainy, 15°C.\"\n\n# Map the function name to the actual function object\ntools_map = {\n    \"get_weather\": get_weather\n}\n\n# Define the JSON schema so the LLM knows how to call it\ntool_definition = {\n    \"type\": \"function\",\n    \"function\": {\n        \"name\": \"get_weather\",\n        \"description\": \"Get the current weather for a location\",\n        \"parameters\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"location\": {\"type\": \"string\"}\n            },\n            \"required\": [\"location\"]\n        }\n    }\n}\n\n# 2. The Agent reasoning loop\ndef run_agent(user_prompt):\n    # Initialize the State (Memory)\n    messages = [\n        {\"role\": \"system\", \"content\": \"You are a helpful assistant. Call tools when necessary.\"},\n        {\"role\": \"user\", \"content\": user_prompt}\n    ]\n\n    # Run the loop (max 5 turns to prevent infinite runs)\n    for _ in range(5):\n        response = client.chat.completions.create(\n            model=\"gpt-4o-mini\",\n            messages=messages,\n            tools=[tool_definition]\n        )\n\n        message = response.choices[0].message\n        messages.append(message)\n\n        # Check if the model wants to call a tool\n        if message.tool_calls:\n            for tool_call in message.tool_calls:\n                name = tool_call.function.name\n                args = json.loads(tool_call.function.arguments)\n\n                print(f\"[*] Calling tool: {name} with args: {args}\")\n                tool_output = tools_map[name](**args)\n\n                # Append tool response back to state\n                messages.append({\n                    \"role\": \"tool\",\n                    \"tool_call_id\": tool_call.id,\n                    \"name\": name,\n                    \"content\": tool_output\n                })\n        else:\n            # If no tool was called, this is the final answer\n            return message.content\n\n# Run it\nif __name__ == \"__main__\":\n    result = run_agent(\"What is the weather like in Tokyo right now?\")\n    print(f\"\\n[Agent Response]: {result}\")\n```\n\n", "url": "https://wpnews.pro/news/why-do-we-import-100mb-of-frameworks-to-run-a-50-line-llm-reasoning-loop", "canonical_source": "https://dev.to/venu_varma/why-do-we-import-100mb-of-frameworks-to-run-a-50-line-llm-reasoning-loop-3afh", "published_at": "2026-06-25 15:31:08+00:00", "updated_at": "2026-06-25 15:43:47.645536+00:00", "lang": "en", "topics": ["ai-agents", "large-language-models", "developer-tools"], "entities": ["OpenAI", "AutoGen", "LangChain", "CrewAI", "Anthropic", "Gemini", "Ollama", "GPT-4o-mini"], "alternates": {"html": "https://wpnews.pro/news/why-do-we-import-100mb-of-frameworks-to-run-a-50-line-llm-reasoning-loop", "markdown": "https://wpnews.pro/news/why-do-we-import-100mb-of-frameworks-to-run-a-50-line-llm-reasoning-loop.md", "text": "https://wpnews.pro/news/why-do-we-import-100mb-of-frameworks-to-run-a-50-line-llm-reasoning-loop.txt", "jsonld": "https://wpnews.pro/news/why-do-we-import-100mb-of-frameworks-to-run-a-50-line-llm-reasoning-loop.jsonld"}}