Local AI

Local AI agents — build private AI agents that run on your hardware

Build AI agents entirely on your own machine. Function calling with local models, tool integration, and RAG on local documents — all offline, all private, no data leaves your hardware. When you need production scale, migrate the same agent to Plugsky without code changes.

Architecture

A local AI agent architecture has four layers:

┌─────────────────────────────────────────────────┐
│                  Your Application                 │
│  (CLI, web app, desktop, automation script)      │
├─────────────────────────────────────────────────┤
│              Agent Orchestrator                    │
│  - Sends messages to local inference engine       │
│  - Handles tool call → execute → feed back loop  │
│  - Manages conversation context / token budget    │
├─────────────────────────────────────────────────┤
│           Local Inference Engine                   │
│  Ollama │ vLLM │ LM Studio │ llama.cpp            │
│  Runs open-weight models on your GPU / CPU        │
├─────────────────────────────────────────────────┤
│              Tool Execution Layer                  │
│  Web search (local scraper) │ File system          │
│  Local DB │ Code interpreter │ Custom scripts     │
└─────────────────────────────────────────────────┘

Every component runs on your machine. No cloud dependency. The local inference engine exposes an OpenAI-compatible API, and your agent code uses the standard OpenAI SDK.

Function calling with local models

Many local models support OpenAI-compatible function calling (tool use). The model receives a tools array alongside the messages and returns structured tool_calls when it decides a tool should be invoked. Your code executes the tool and feeds the result back.

Models with reliable function calling:

  • Llama 3.2 / 3.3 — best overall function calling among open-weight models
  • Qwen 2.5 — strong tool-use performance, supports parallel calls
  • Mistral / Nemo — good function calling in the 7B-12B range
  • DeepSeek-V3-Lite — excellent for complex tool schemas
  • Command R+ — designed for agentic use cases

Integrating tools

Local agents can integrate any tool that runs on your machine. Unlike cloud agents, there are no rate limits, no IP whitelists, and no data egress concerns. Common local tools include:

Tool typeExamplesImplementation
File systemRead, write, search local filesPython os/glob/shutil
Web searchLocal scraping, self-hosted SearXNGhttpx + BeautifulSoup
DatabaseSQLite, PostgreSQL, DuckDBDB-API connectors
Code executionPython sandbox, SQL queriessubprocess, Docker containers
Document processingPDF, DOCX, CSV parsingPyMuPDF, python-docx, pandas
Local APIsHome Assistant, Jellyfin, Plexhttpx to local endpoints

RAG on local documents

Local RAG keeps your documents entirely on your hardware. The pipeline uses three local components:

1. Local embedding model

python
# Using Ollama for embeddings
from openai import OpenAI

client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")

def embed(texts):
    resp = client.embeddings.create(
        model="nomic-embed-text",
        input=texts,
    )
    return [e.embedding for e in resp.data]

2. Local vector database

python
# ChromaDB — runs in-process, no server needed
import chromadb

db = chromadb.PersistentClient(path="./agent-knowledge")
collection = db.get_or_create_collection("docs")

# Add documents
collection.add(
    ids=["doc1", "doc2"],
    embeddings=[embedding1, embedding2],
    metadatas=[{"source": "report.pdf"}, {"source": "email.pdf"}],
)

# Query at runtime
results = collection.query(
    query_embeddings=[query_embedding],
    n_results=5,
)

3. Local LLM (via Ollama/vLLM)

python
# Feed retrieved context into the agent
context = "\n".join(results["documents"][0])
agent_prompt = f"Context:\n{context}\n\nQuestion: {user_query}"

resp = client.chat.completions.create(
    model="llama3.2",
    messages=[
        {"role": "system", "content": agent_prompt},
        {"role": "user", "content": user_query},
    ],
    tools=tools,
)

The agent loop

The core loop for a local agent is identical to a cloud agent. The only difference is the base_url pointing to your local engine:

User input → LLM (with tools) → tool_calls?
  ├─ No  → return response
  └─ Yes → for each call:
             execute tool locally
             append result as "tool" message
             loop back to LLM

Local agents typically run with a step limit of 5-10 iterations to avoid runaway loops. Use tool_choice: "auto" for the model to decide when to call tools and when to respond directly.

Complete code example

python
from openai import OpenAI
import json

client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")

tools = [
    {
        "type": "function",
        "function": {
            "name": "search_files",
            "description": "Search local files by keyword",
            "parameters": {
                "type": "object",
                "properties": {
                    "query": {"type": "string"},
                    "path": {"type": "string"}
                },
                "required": ["query"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "read_file",
            "description": "Read the contents of a file",
            "parameters": {
                "type": "object",
                "properties": {
                    "path": {"type": "string"}
                },
                "required": ["path"]
            }
        }
    }
]

def local_agent(user_input, max_steps=5):
    messages = [{"role": "user", "content": user_input}]
    for step in range(max_steps):
        r = client.chat.completions.create(
            model="llama3.2",
            messages=messages,
            tools=tools,
        )
        msg = r.choices[0].message
        if not msg.tool_calls:
            return msg.content
        messages.append(msg)
        for tc in msg.tool_calls:
            fn = tc.function.name
            args = json.loads(tc.function.arguments)
            if fn == "search_files":
                result = search_vs_code(args["query"])
            elif fn == "read_file":
                result = open(args["path"]).read()
            messages.append({
                "role": "tool",
                "tool_call_id": tc.id,
                "content": json.dumps({"result": result})
            })
    return "Max steps reached"

# Example
output = local_agent("Find all Python files related to AI and summarize them")
print(output)

Local vs managed agent API

FactorLocal agent (DIY)Plugsky (managed agent API)
PrivacyComplete — data never leavesConfigurable — VPC/on-prem available
Model choice1-3 models (limited by VRAM)30+ models, swap instantly
Tool ecosystemAny local tool, script, or APIBuilt-in tools + any OpenAI-compatible tool
Function callingDepends on model qualityReliable across all models
UptimeMachine-dependent99.9% SLA
Team accessManual (LAN or VPN)Built-in API keys + RBAC
ScalingSingle GPU or machineAuto-scaling, multi-node
RAGManual setup (ChromaDB + embeddings)Managed — upload and query
Cost modelHardware + electricityFlat monthly ($7-$249)

Migration path to Plugsky

When your local agent needs team access, reliability, or access to more capable models, the migration takes one line change:

python
# Before (local)
client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")

# After (Plugsky)
client = OpenAI(base_url="https://api.plugsky.com/v1", api_key="sk-live-...")

# Everything else stays the same:
# tools, messages, tool_calls, streaming, agent loop — all identical

Plugsky's agent API adds managed capabilities on top of the same OpenAI-compatible interface: built-in RAG, sub-agent orchestration, audit logging, model routing, and team access controls. Your agent code never changes — only the endpoint and model name.

Build private agents, deploy with confidence

Prototype agents locally with complete privacy. Migrate to Plugsky when you need team access, uptime, and 30+ models — without rewriting a single line of agent code.

Start Free → Back to Local AI →