{"slug": "how-to-build-an-ai-agent-from-scratch-in-python-free-no-framework", "title": "How to build an AI agent from scratch in Python (free, no framework)", "summary": "A tutorial published by NestDaddy shows how to build a working AI agent in about 60 lines of plain Python with no framework, using the free local model qwen2.5:7b run through Ollama and NestDaddy's news and search API, whose free plan provides 1,000 calls a month without a card. The guide demonstrates the agent answering \"what happened in AI today?\" with real headlines and links, and identifies fabricated facts as the most common bug in beginner agents, which it fixes by giving the model tool descriptions and a loop rather than letting it run code itself. Full code is posted in the nestdaddy-examples GitHub repository, with a 6-minute video version on YouTube.", "body_md": "# How to build an AI agent from scratch in Python (free, no framework)\n\nAn AI agent is a model, some tools and a loop. Build one in about 60 lines of Python with a free local model, watch it read today's news, and fix the hallucination every beginner agent has.\n\nEvery agent framework, from LangChain to CrewAI, is built on one small idea. Once you have written it yourself, the frameworks stop being magic. This guide builds a working agent in about 60 lines of plain Python, with a free model that runs on your own computer, and no framework at all.\n\nThe agent will answer \"what happened in AI today?\" with real headlines and links, and you will see the most common bug in AI agents (making up facts) and the one rule that fixes it.\n\n*Prefer video? The same tutorial is on [YouTube](https://youtu.be/4FqSnSnudaM) (6 minutes). Full code: [nestdaddy-examples/python/ai-agent-from-scratch](https://github.com/nestdaddy/nestdaddy-examples/tree/main/python/ai-agent-from-scratch).*\n\n```\nPress Run to call the API.\n```\n\n[Get your free API key](https://nestdaddy.com/mcp)\n\n## What an AI agent really is\n\nA language model on its own only knows what it learned in training. It cannot look anything up, it does not know today's date, and it cannot act. An agent gives it three things:\n\n1. **Tools.** Normal functions in your code: search the news, read a web page, query a database.\n2. **A manual.** For each tool, a short description and its inputs as a JSON schema. This is what the model reads to decide when to use a tool.\n3. **A loop.** The model either answers, or asks for a tool. Your code runs the tool, gives the result back, and the model decides again, until it can answer.\n\nThe model never runs code itself. It writes a request (\"call `news_search` with these arguments\") and your code decides what to do with it. That is all \"function calling\" means.\n\n## Setup: two free things\n\n**A model.** [Ollama](https://ollama.com) runs open models on your own computer. Pull one that supports tools; `qwen2.5:7b` works well on a normal laptop with a GPU:\n\n```\nollama pull qwen2.5:7b\n```\n\nIf your computer is slow, any OpenAI-compatible API works too (Gemini, OpenAI, Groq). You change one line.\n\n**Fresh information.** The agent needs a source of today's news. This guide uses NestDaddy's news and search API, which returns clean JSON from its own index. The [free plan](https://nestdaddy.com/apis) gives you 1,000 calls a month with no card, which is plenty for learning:\n\n```\nexport NESTDADDY_API_KEY=\"your-free-key\"\npip install openai requests\n```\n\nWe use the `openai` library only because almost every model, Ollama included, speaks its format.\n\n## Step 1: the model alone\n\n``` python\nimport os\nfrom openai import OpenAI\n\nllm = OpenAI(base_url=os.getenv(\"LLM_BASE_URL\", \"http://localhost:11434/v1\"),\n             api_key=os.getenv(\"LLM_API_KEY\", \"ollama\"))\n\nreply = llm.chat.completions.create(\n    model=os.getenv(\"LLM_MODEL\", \"qwen2.5:7b\"),\n    messages=[{\"role\": \"user\", \"content\": \"What happened in AI today?\"}],\n)\nprint(reply.choices[0].message.content)\n```\n\nThe answer we got started with \"As of my last update in October 2023...\". The model is not broken. It simply has no way to look.\n\n## Step 2: give it a tool\n\nA tool is a plain function. This one calls the news API and returns a short list. Keep it small: everything you return is text the model has to read, and less noise means better answers.\n\n```\nNEWS_API = \"https://nestdaddy.com/api/v1\"\nHEADERS = {\"X-API-Key\": os.getenv(\"NESTDADDY_API_KEY\", \"\")}\n\ndef news_search(query, days=3):\n    r = requests.get(f\"{NEWS_API}/news/search\", headers=HEADERS, timeout=30,\n                     params={\"q\": query, \"days\": days, \"limit\": 5})\n    r.raise_for_status()\n    return [{\"title\": a[\"title\"], \"source\": a.get(\"source_domain\"),\n             \"date\": a.get(\"published_timestamp\"), \"url\": a[\"url\"]}\n            for a in r.json().get(\"results\", [])]\n```\n\nThen the manual. The description matters more than you think: \"use it for anything that happened recently\" is the sentence that makes the model reach for this tool at the right moment.\n\n```\nTOOLS = {\"news_search\": news_search}\n\nTOOL_SPECS = [\n    {\"type\": \"function\", \"function\": {\n        \"name\": \"news_search\",\n        \"description\": \"Search the latest news. Use it for anything that happened recently.\",\n        \"parameters\": {\"type\": \"object\", \"properties\": {\n            \"query\": {\"type\": \"string\", \"description\": \"Short key words, like a search box\"},\n            \"days\": {\"type\": \"integer\", \"description\": \"How many days back to search, 1 to 30\"}},\n            \"required\": [\"query\"]}}},\n]\n```\n\n## Step 3: the agent loop\n\nThis is the heart of every agent:\n\n``` python\ndef run_agent(question, max_steps=6):\n    messages = [\n        {\"role\": \"system\", \"content\": f\"You are a helpful assistant. Today is {date.today()}. \"\n                                      \"Use your tools for anything recent. Cite the URLs you used.\"},\n        {\"role\": \"user\", \"content\": question},\n    ]\n    for _ in range(max_steps):\n        reply = llm.chat.completions.create(model=MODEL, messages=messages, tools=TOOL_SPECS).choices[0].message\n        if not reply.tool_calls:          # no tool needed: this is the final answer\n            return reply.content\n        messages.append(reply)\n        for call in reply.tool_calls:     # the model asked for one or more tools\n            args = json.loads(call.function.arguments or \"{}\")\n            print(f\"  -> {call.function.name}({args})\")\n            try:\n                result = TOOLS[call.function.name](**args)\n            except Exception as e:        # give errors back to the model instead of crashing\n                result = f\"error: {e}\"\n            messages.append({\"role\": \"tool\", \"tool_call_id\": call.id,\n                             \"content\": json.dumps(result, ensure_ascii=False)[:6000]})\n    return \"Stopped: too many steps.\"\n```\n\nFour details worth noticing:\n\n- The system message tells the model **today's date** . It does not know it otherwise.\n- Errors go back to the model as text, so it can try something else instead of crashing your program.\n- Each tool result is linked to the request by its `tool_call_id` .\n- `max_steps` stops a confused model from looping forever.\n\nRun it and the agent searches the news (`-> news_search({'query': 'AI', 'days': 1})`), then answers with that day's real headlines and their links. Same model as step 1. Now it can look.\n\n## Step 4: read the full article, and fix a hallucination\n\nHeadlines are a start, but a detailed question needs the whole article. So add a second tool:\n\n``` python\ndef read_page(url):\n    r = requests.get(f\"{NEWS_API}/extract\", headers=HEADERS, timeout=30, params={\"url\": url})\n    r.raise_for_status()\n    return r.json().get(\"text\", \"\")[:4000]\n```\n\nAdd it to `TOOLS` and give it a manual entry (\"Read the full text of a web page, for example a news article\").\n\nThen we asked: *\"What did Anthropic recommend this week for AI safety evaluations? Give me the key points.\"* The first time, the agent searched, saw one headline, and wrote **ten confident key points**: transparency, ethical guidelines, public engagement. None of them were in the article. It never read it. This is the most common bug in AI agents: the model fills the gap between a headline and a detailed answer with things that sound right.\n\nThe fix was one rule in the system message:\n\n```\n\"Use your tools for anything recent. Search results are only headlines: \"\n\"before you give details, read the best article with read_page. \"\n\"Only state facts you found with your tools, and cite the URLs you used.\"\n```\n\nSame question again: the agent searched, chose the article, called `read_page`, and only then answered. The points it gave (preregistration of analysis methods, validating automated graders against human experts) are really in the article. We never told it which page to read or when. We only gave it the rule.\n\n## What to build next\n\nYou now know what is inside every agent framework: a model, tools, a manual and a loop. From here you can:\n\n- add memory, so it remembers earlier questions;\n- add more tools: web search, research papers, your own database;\n- plug the same tools into Claude, Cursor or VS Code with [MCP](https://nestdaddy.com/mcp) , without writing the loop yourself.\n\nThe full code (three files, one per step) is on [GitHub](https://github.com/nestdaddy/nestdaddy-examples/tree/main/python/ai-agent-from-scratch), MIT licensed. If you build something with it, a free NestDaddy key gives your agent 1,000 news and search calls a month: [get one here](https://nestdaddy.com/apis).", "url": "https://wpnews.pro/news/how-to-build-an-ai-agent-from-scratch-in-python-free-no-framework", "canonical_source": "https://nestdaddy.com/blog/how-to-build-an-ai-agent-from-scratch", "published_at": "2026-11-14 00:00:00+00:00", "updated_at": "2026-11-14 11:20:37.996665+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "large-language-models", "developer-tools"], "entities": ["NestDaddy", "Ollama", "qwen2.5:7b", "LangChain", "CrewAI", "GitHub", "YouTube", "OpenAI"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/how-to-build-an-ai-agent-from-scratch-in-python-free-no-framework", "markdown": "https://wpnews.pro/news/how-to-build-an-ai-agent-from-scratch-in-python-free-no-framework.md", "text": "https://wpnews.pro/news/how-to-build-an-ai-agent-from-scratch-in-python-free-no-framework.txt", "jsonld": "https://wpnews.pro/news/how-to-build-an-ai-agent-from-scratch-in-python-free-no-framework.jsonld"}}