LangChain is the most popular framework for building AI agents. It is also overkill for 90% of use cases. The abstraction layer adds complexity, hides what is actually happening, and creates a dependency on a fast-moving open-source project with frequent breaking changes. Here is how to build the same thing from scratch.

What an Agent Actually Is

An AI agent is an LLM in a loop. That is it. You send messages to the model, check if it wants to call a tool, run the tool, append the result, and send everything back. Repeat until the model produces a final answer.

The core loop is roughly 150 lines of Python using just the OpenAI SDK. No frameworks, no abstractions, no magic.

Step 1: Define Your Tools

Tools are Python functions with a schema. You describe what the tool does, what parameters it takes, and the model decides when to call it.

For each tool, you need: a function name, a description the model can understand, and a JSON schema describing the parameters. The model never executes code — it generates text describing what it wants to do, and your code parses and runs it.

Step 2: The Agent Loop

The loop is simple:

  1. Send the conversation history (including system prompt) to the LLM
  2. Check if the response contains a tool call
  3. If yes, execute the tool, append the result as a new message, and go back to step 1
  4. If no, return the response as the final answer

This is not a simplification. This is literally how LangChain, CrewAI, and every other agent framework works under the hood. The difference is you can see exactly what is happening.

Step 3: Memory

Memory is just the messages list persisted between turns. On each new user message, append it to the list and run the loop. On each tool call result, append it too. The model sees the full conversation history and responds accordingly.

For long conversations, implement a sliding window — keep the system prompt and the last N messages. This prevents the context window from filling up with stale history.

Step 4: Bounding

The most important safety feature: limits. An unbounded agent can loop forever, calling tools repeatedly without making progress. Two safeguards:

  • max_steps limit — hard cap on how many tool calls the agent can make per request. 10 is usually reasonable.
  • Repeat detection — if the model calls the same tool with the same parameters twice in a row, break the loop and return what you have.

What About Local Models?

This pattern works with any provider. OpenAI, Anthropic (using tool_use), and local models via Ollama all support the same tool-calling interface. Point your agent at localhost:11434 instead of api.openai.com and everything else stays the same.

Local models are less reliable at tool calling than GPT-4 or Claude, but for simple tool sets (2-5 tools), they work surprisingly well with clear descriptions.

When You Actually Need a Framework

Frameworks solve real problems at scale:

  • When you have many concurrent agents that need shared observability
  • When you need pre-built memory backends like vector stores or session databases
  • When your team has 2+ developers who need shared conventions and documentation
  • When you need production features like rate limiting, retry logic, and fallback chains

For a solo developer building a personal tool, none of those apply. Build it once from scratch, understand exactly what is happening, and then decide if a framework would save you meaningful time.

The 150-Line Reality

Here is what your agent needs: a system prompt, a list of tool definitions, the conversation history, and the loop. That is under 150 lines of Python with the OpenAI SDK. It will handle tool calls, memory, and bounded execution. It will be debuggable, transparent, and yours.

LangChain saves you those 150 lines. But it adds thousands of lines of dependencies, an abstraction layer you need to learn, and a version upgrade cycle you need to track. For most personal projects, the tradeoff is not worth it.