By the end of this guide you will have a fully private AI agent stack running on your own hardware. No data leaves your machine. No API keys billed per token. No vendor lock-in. You will understand what agents actually are, choose the right framework for your use case, connect it to a local LLM via Ollama, give your agent tools to use, add persistent RAG memory with Qdrant, build a multi-agent pipeline, and lock the whole thing down with Tailscale and Vault.
What you need to start: A machine with at least 16 GB RAM (Mac mini M4 Pro, a PC with an RTX 4060 Ti or better, or a RunPod GPU instance). Ollama installed. Python 3.11+. Docker.
Get more guides like this in your inbox
No spam. Unsubscribe anytime.
Part 1 — What Is an AI Agent (Actually)?
An AI agent is a loop. It receives a goal, uses an LLM to decide what to do next, executes a tool or action, observes the result, and repeats until the goal is reached or it gives up. That is the entire architecture. Everything else — memory, multi-agent coordination, RAG, guardrails — is scaffolding around that loop.
Every agent has four components: an LLM that decides what to do next (e.g. Ollama running Qwen3.5-27B), tools that are actions the agent can take (web search, file read, code execution), memory for context across turns (Qdrant vector store), and an orchestrator that manages the loop (CrewAI, LangGraph, n8n).
The privacy case for local agents: every tool call, every intermediate reasoning step, every document you feed into RAG goes through the LLM. If that LLM is OpenAI or Anthropic, your data is in their systems. For anything involving customer data, internal documents, medical records, or legal files, local agents are the only responsible choice.
Part 2 — Install Ollama and Choose Your Model
Ollama is the runtime that serves local LLMs with an OpenAI-compatible API. Every agent framework in this guide connects to it the same way. Install it, pull a model that supports function calling (tool use), and verify it is running before proceeding.
# Install via Homebrew
brew install ollama
# Pull the best all-round agent model (27B, needs 20GB RAM)
ollama pull qwen3.5:27b
# Fastest option for Mac mini M4 Pro 24GB
ollama pull qwen3.5:9b
# Budget option — works on 16GB RAM
ollama pull llama3.2:latest
# Verify Ollama is running
ollama serve # starts on http://localhost:11434
curl http://localhost:11434/api/tagsPart 3 — Framework Comparison: Which One Should You Use?
There are four serious frameworks for private local agents in 2026. n8n is best for non-coders and workflow automation — it has a visual drag-and-drop editor, native Ollama integration, and 400+ tool integrations. CrewAI is best for role-based multi-agent pipelines in Python. AG2 (AutoGen, github.com/ag2ai/ag2) is best for research and dynamic multi-agent conversations. LangGraph is best for custom stateful workflows and production systems.
Decision guide: if you don't write code, use n8n. If you want role-based agents (researcher + writer + reviewer), use CrewAI. If you want maximum control over agent conversation flow, use LangGraph. If you need agents that can spawn sub-agents dynamically, use AG2.
Part 4 — n8n: Visual Agent Workflows (No Code Required)
n8n is the fastest way to build a working agent. Install it with Docker Compose, connect it to Ollama, and use the visual editor to wire up a Chat Trigger → AI Agent → Memory → Tools pipeline. The AI Agent node handles the LLM loop, tool dispatch, and memory automatically.
mkdir n8n-agent && cd n8n-agent
cat > compose.yaml << 'EOF'
services:
n8n:
image: docker.n8n.io/n8nio/n8n:stable
restart: always
ports:
- "5678:5678"
environment:
- N8N_RUNNERS_ENABLED=true
- N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
- GENERIC_TIMEZONE=America/New_York
extra_hosts:
- "host.docker.internal:host-gateway"
volumes:
- n8n_data:/home/node/.n8n
volumes:
n8n_data:
EOF
docker compose up -d
# Open http://localhost:5678Part 5 — CrewAI: Role-Based Multi-Agent Pipelines
CrewAI models agents as a crew of specialists. Each agent has a role, a goal, and a backstory. Install with pip install 'crewai[litellm]' and connect to Ollama using LLM(model='ollama/qwen3.5:9b', base_url='http://localhost:11434').
from crewai import Agent, Task, Crew, LLM
llm = LLM(model="ollama/qwen3.5:9b", base_url="http://localhost:11434")
researcher = Agent(
role="Privacy Technology Researcher",
goal="Find accurate, up-to-date information on the given topic",
backstory="Expert researcher specializing in privacy tech and open-source AI.",
llm=llm, verbose=True
)
writer = Agent(
role="Technical Writer",
goal="Write clear, accurate technical content based on research",
backstory="Writes detailed technical guides for privacy-conscious users.",
llm=llm, verbose=True
)
research_task = Task(
description="Research the current state of private AI agents in 2026.",
expected_output="A detailed research brief with key findings and version numbers.",
agent=researcher
)
write_task = Task(
description="Using the research brief, write a 500-word introduction.",
expected_output="A polished 500-word introduction with accurate technical details.",
agent=writer,
context=[research_task]
)
crew = Crew(agents=[researcher, writer], tasks=[research_task, write_task])
result = crew.kickoff()Part 6 — AG2 (AutoGen): Dynamic Multi-Agent Conversations
AG2 (v0.11.5, github.com/ag2ai/ag2) supports dynamic agent conversations where agents can debate, self-correct, and spawn sub-agents. Install with pip install ag2[ollama]. It directly supports Ollama without LiteLLM.
from autogen import ConversableAgent
config = {
"config_list": [{
"model": "qwen3.5:9b",
"api_type": "ollama",
"client_host": "http://localhost:11434",
"stream": False
}],
"temperature": 0.7
}
assistant = ConversableAgent(
name="Assistant",
system_message="You are a helpful AI assistant. Be concise and accurate.",
llm_config=config, human_input_mode="NEVER"
)
critic = ConversableAgent(
name="Critic",
system_message="Point out flaws and missing information in responses.",
llm_config=config, human_input_mode="NEVER"
)
result = assistant.initiate_chat(
critic,
message="Explain why local AI agents are better for privacy.",
max_turns=3
)Part 7 — LangGraph: Stateful Agent Workflows
LangGraph models agent workflows as directed graphs where every state change is explicit and auditable. Install with pip install langgraph langchain-ollama. Use create_react_agent for a quick start, or build a custom StateGraph for full control.
from langgraph.prebuilt import create_react_agent
from langchain_ollama import ChatOllama
from langchain_core.tools import tool
llm = ChatOllama(model="qwen3.5:9b", base_url="http://localhost:11434")
@tool
def get_file_contents(path: str) -> str:
"""Read the contents of a local file. Input: absolute file path."""
try:
return open(path).read()
except Exception as e:
return f"Error: {e}"
@tool
def list_directory(path: str) -> str:
"""List files in a directory. Input: absolute directory path."""
import os
try:
return "\n".join(os.listdir(path))
except Exception as e:
return f"Error: {e}"
agent = create_react_agent(llm, tools=[get_file_contents, list_directory])
result = agent.invoke({
"messages": [{"role": "user",
"content": "List files in /tmp and read the first .txt file."}]
})
print(result["messages"][-1].content)Part 8 — RAG Memory: Give Your Agent Long-Term Knowledge
RAG (Retrieval-Augmented Generation) lets your agent search a private knowledge base before answering. Start Qdrant with Docker, ingest your documents using a local embedding model (no API key needed), then expose search as an agent tool.
# Start Qdrant
docker run -d -p 6333:6333 -p 6334:6334 \
-v "$(pwd)/qdrant_storage:/qdrant/storage:z" \
qdrant/qdrant
# Dashboard: http://localhost:6333/dashboard
# Install Python dependencies
pip install qdrant-client sentence-transformersRAG — Ingest Documents and Query
Use nomic-ai/nomic-embed-text-v1.5 (137MB, runs locally) to embed your documents into Qdrant, then expose search as a CrewAI or LangGraph tool.
from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams, PointStruct
from sentence_transformers import SentenceTransformer
import uuid, glob
embedder = SentenceTransformer("nomic-ai/nomic-embed-text-v1.5", trust_remote_code=True)
client = QdrantClient(url="http://localhost:6333")
client.create_collection(
collection_name="private_docs",
vectors_config=VectorParams(size=768, distance=Distance.COSINE)
)
# Ingest all markdown files
points = []
for filepath in glob.glob("/docs/**/*.md", recursive=True):
content = open(filepath).read()
words = content.split()
chunks = [" ".join(words[i:i+500]) for i in range(0, len(words), 500)]
for chunk in chunks:
points.append(PointStruct(
id=str(uuid.uuid4()),
vector=embedder.encode(chunk).tolist(),
payload={"text": chunk, "source": filepath}
))
client.upsert(collection_name="private_docs", points=points)
# Search function (use as agent tool)
def rag_search(query: str) -> str:
results = client.query_points(
collection_name="private_docs",
query=embedder.encode(query).tolist(),
limit=5
).points
return "\n\n---\n\n".join([r.payload["text"] for r in results])Part 9 — Multi-Agent Orchestration Patterns
Three patterns cover most use cases. Sequential pipeline: each agent hands off to the next (CrewAI default, best for research → draft → review). Hierarchical: a manager agent delegates to specialists (use Process.hierarchical in CrewAI, set allow_delegation=True on the manager). Debate and consensus: two agents argue, a third judges (AG2's conversation model, best for fact-checking and avoiding hallucination).
Avoiding common failures: always set max_turns or max_iterations (agents get stuck in loops), pass summaries between agents not full transcripts (context overflow), limit tools per agent to 3-5 (more tools confuses smaller models), use Pydantic schemas to validate handoffs.
Part 10 — Securing Your Agent Stack
A local agent with internet access, file system access, and code execution is a significant attack surface. These controls are non-negotiable.
# 1. Isolate agents in Docker with restricted capabilities
docker run --rm \
--network=none \
--read-only \
--tmpfs /tmp \
--cap-drop=ALL \
--security-opt=no-new-privileges \
my-agent-image
# 2. Store secrets in HashiCorp Vault (never hardcode)
docker run -d --name vault -p 8200:8200 \
-e VAULT_DEV_ROOT_TOKEN_ID=myroot hashicorp/vault:latest
vault kv put secret/agent/tools serper_api_key=your_key_here
# 3. Use Tailscale for remote access (never expose ports directly)
curl -fsSL https://tailscale.com/install.sh | sh && sudo tailscale upPart 11 — Complete Stack Reference
The full private agent stack: Ollama 0.18+ (LLM runtime), Qwen3.5 27B Q4 (agent model), n8n 2.14+ (orchestrator), Qdrant 1.13+ (vector DB), nomic-embed-text-v1.5 (embeddings), HashiCorp Vault (secrets), Tailscale (remote access), Open WebUI 0.8+ (chat interface). Hardware: Mac mini M4 Pro 48GB (~$1,799) or PC with RTX 4090 (~$1,800) for the full stack. Mac mini M4 Pro 24GB (~$1,299) for a lighter setup.
# Full compose.yaml — complete private agent stack
services:
n8n:
image: docker.n8n.io/n8nio/n8n:stable
ports: ["5678:5678"]
environment:
- N8N_RUNNERS_ENABLED=true
- N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
extra_hosts: ["host.docker.internal:host-gateway"]
volumes: [n8n_data:/home/node/.n8n]
qdrant:
image: qdrant/qdrant:latest
ports: ["6333:6333", "6334:6334"]
volumes: [qdrant_storage:/qdrant/storage]
open-webui:
image: ghcr.io/open-webui/open-webui:main
ports: ["3000:8080"]
environment:
- OLLAMA_BASE_URL=http://host.docker.internal:11434
extra_hosts: ["host.docker.internal:host-gateway"]
volumes: [open_webui:/app/backend/data]
volumes:
n8n_data:
qdrant_storage:
open_webui: