What a personal AI assistant that runs 24/7 needs
If you want to know how to build a personal AI assistant that runs 24/7, the model is the easy part. The trick is everything that keeps it awake: a loop that never exits, memory that survives a reboot, tools it can safely call, and a host that does not fall asleep the way your laptop does the second you close the lid. A weekend chatbot answers when you are watching it. A real assistant checks your calendar at 7am, drafts the reply you asked for at midnight, and files the receipt that hit your inbox while you slept.
Strip away the hype and a personal assistant that runs 24/7 needs five things:
A model good enough to plan and call tools reliably.
A loop that wakes on a timer or an event and keeps running.
Tools so it can do things, not just talk: read email, hit a calendar API, run a shell command, query a database.
Memory that outlives the process, so it remembers what it did yesterday.
A host that never sleeps, because none of the above matters if the machine is off.
The rest of this guide walks each step with real commands, real costs, and honest tradeoffs. We will build something small on purpose, then talk about where to actually run it.
Step 1: pick a model and framework
Two decisions here: which model, and how much framework to wrap around it.
For the model, you want strong tool-calling and instruction-following, because an assistant that mis-calls a tool at 2am with nobody watching is worse than useless. Anthropic's Claude models are a strong default for this; the API docs at anthropic.com cover tool use, streaming, and prompt caching, and Claude's tool-use reliability is the main reason I reach for it in unattended agents. OpenAI's GPT-4o class models work well too. If privacy or cost pushes you local, a quantized Llama 3.1 8B on Ollama runs on a 16GB machine, with a real drop in tool-calling accuracy that you will feel.
A rough comparison of the tradeoff you are making:
| Option | Approx cost | Tool-call reliability | Runs unattended |
|---|---|---|---|
| Claude / GPT-4o via API | Pennies to a few dollars a day | High | Yes |
| Local Llama 3.1 8B (Ollama) | Hardware only, no per-call fee | Medium | Yes, if the box stays up |
| Small local 3B model | Hardware only | Low | Risky for actions |
On framework: resist the urge to adopt a heavy agent framework on day one. A plain Python loop with the vendor SDK is easier to debug and does not hide the control flow you need to reason about. Reach for a framework when you actually need its features. If you would rather host a ready-made autonomous agent than write the loop, OpenClaw is a real open-source gateway assistant (ghcr.io/openclaw/openclaw) that already ships the loop, tool layer, community skills, and a persistent workspace.
Install the basics for a from-scratch build:
python -m venv .venv && source .venv/bin/activate
pip install anthropic
export ANTHROPIC_API_KEY=sk-ant-...python -m venv .venv && source .venv/bin/activate
pip install anthropic
export ANTHROPIC_API_KEY=sk-ant-...Keep that key out of your source. Read it from the environment, and on a real host inject it from a secret store rather than a committed .env.
Step 2: give it tools and memory
An assistant with no tools is a search box. The value shows up when it can act: check a calendar, send an email, run a command, look something up. And it only feels like your assistant once it remembers context across runs.
Memory for a personal assistant does not need to be fancy. A single append-only JSON Lines file on disk gets you surprisingly far: every task and result on its own line, easy to read, easy to replay. Reach for SQLite when you want queries, and pgvector when you want semantic recall across weeks. Start with the file.
Here is a minimal but honest agent loop. It pulls pending tasks, asks the model what to do, executes a tool call, writes the result to memory, and sleeps. This is the shape every always-on assistant collapses to:
import os
import time
import json
from anthropic import Anthropic
client = Anthropic() # reads ANTHROPIC_API_KEY from the environment
MEMORY_PATH = os.environ.get("MEMORY_PATH", "./data/memory.jsonl")
def append_memory(record):
with open(MEMORY_PATH, "a") as f: # append-only, survives restart
f.write(json.dumps(record) + "\n")
def check_calendar(_):
return "No meetings before noon." # real impl hits a calendar API
def send_email(args):
# send via your provider; return a status you can log
return f"Sent to {args['to']}"
TOOLS = {"check_calendar": check_calendar, "send_email": send_email}
TOOL_SPECS = [
{"name": "check_calendar", "description": "Read today's calendar",
"input_schema": {"type": "object", "properties": {}}},
{"name": "send_email", "description": "Send an email",
"input_schema": {"type": "object",
"properties": {"to": {"type": "string"},
"body": {"type": "string"}},
"required": ["to", "body"]}},
]
def run_once(task):
resp = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
system="You are a personal assistant. Use a tool when it helps.",
tools=TOOL_SPECS,
messages=[{"role": "user", "content": task}],
)
for block in resp.content: # model -> tool call
if block.type == "tool_use":
result = TOOLS[block.name](block.input)
append_memory({"task": task, "tool": block.name,
"result": result, "ts": time.time()})
return result
text = resp.content[0].text
append_memory({"task": task, "result": text, "ts": time.time()})
return text
def pending_tasks():
return ["Summarize my morning and flag anything urgent."] # your queue
while True: # the loop that never exits
for task in pending_tasks():
try:
run_once(task)
except Exception as err:
append_memory({"task": task, "error": str(err), "ts": time.time()})
time.sleep(5)
time.sleep(30)import os
import time
import json
from anthropic import Anthropic
client = Anthropic() # reads ANTHROPIC_API_KEY from the environment
MEMORY_PATH = os.environ.get("MEMORY_PATH", "./data/memory.jsonl")
def append_memory(record):
with open(MEMORY_PATH, "a") as f: # append-only, survives restart
f.write(json.dumps(record) + "\n")
def check_calendar(_):
return "No meetings before noon." # real impl hits a calendar API
def send_email(args):
# send via your provider; return a status you can log
return f"Sent to {args['to']}"
TOOLS = {"check_calendar": check_calendar, "send_email": send_email}
TOOL_SPECS = [
{"name": "check_calendar", "description": "Read today's calendar",
"input_schema": {"type": "object", "properties": {}}},
{"name": "send_email", "description": "Send an email",
"input_schema": {"type": "object",
"properties": {"to": {"type": "string"},
"body": {"type": "string"}},
"required": ["to", "body"]}},
]
def run_once(task):
resp = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
system="You are a personal assistant. Use a tool when it helps.",
tools=TOOL_SPECS,
messages=[{"role": "user", "content": task}],
)
for block in resp.content: # model -> tool call
if block.type == "tool_use":
result = TOOLS[block.name](block.input)
append_memory({"task": task, "tool": block.name,
"result": result, "ts": time.time()})
return result
text = resp.content[0].text
append_memory({"task": task, "result": text, "ts": time.time()})
return text
def pending_tasks():
return ["Summarize my morning and flag anything urgent."] # your queue
while True: # the loop that never exits
for task in pending_tasks():
try:
run_once(task)
except Exception as err:
append_memory({"task": task, "error": str(err), "ts": time.time()})
time.sleep(5)
time.sleep(30)Two things make this production-shaped rather than a toy. First, the while True never exits on its own, so as long as the host is up, the assistant is up. Second, memory is written to a file whose location comes from the MEMORY_PATH environment variable, so you can point it at storage that survives restarts. Swap pending_tasks() for a real queue, a webhook, or a cron-fed list, and give the tools real implementations. For inspiration on what to actually have it do, browse 30 AI agent examples.
One safety note: an assistant that can send email or run shell commands unattended can also make unattended mistakes. Start in an ask-before-acting mode for anything irreversible, and only flip to fully autonomous once you trust its behavior.
Step 3: host it always-on
Your code is done. Now it has to run somewhere that does not sleep. This is the step people skip, and it is the one that decides whether your assistant is actually 24/7 or just "24/7 when my laptop happens to be open."
The three realistic homes, and what they cost you in money and babysitting:
| Where | Uptime | Cost | Effort |
|---|---|---|---|
| Laptop cron | Terrible: sleeps, reboots, Wi-Fi drops | $0 | Low to set up, constant annoyance |
| $5 VPS + systemd | Good, if you patch and monitor | ~$5 per month | Medium: you own the box forever |
| Managed runtime | High: restarts automatically, files are saved | Flat from $12/mo plus tokens | Low: no server to babysit |
Laptop cron is where everyone starts and where everyone gets burned. macOS sleeps aggressively, launchd and cron jobs miss their window, and the first time you travel with the lid closed your assistant goes dark for a week. Fine for testing, not for something you rely on.
A $5 VPS is the honest minimum for a real always-on assistant. A Hetzner CX22 (about $4.50 per month) or a $6 DigitalOcean droplet gives you a machine that stays on. Wrap the loop in systemd so it restarts on crash and starts on boot:
# /etc/systemd/system/assistant.service
[Unit]
Description=Personal AI assistant
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
WorkingDirectory=/opt/assistant
EnvironmentFile=/opt/assistant/.env
ExecStart=/opt/assistant/.venv/bin/python -m assistant.main
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target# /etc/systemd/system/assistant.service
[Unit]
Description=Personal AI assistant
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
WorkingDirectory=/opt/assistant
EnvironmentFile=/opt/assistant/.env
ExecStart=/opt/assistant/.venv/bin/python -m assistant.main
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.targetThen systemctl enable --now assistant and it survives reboots. The catch is everything else that comes with owning a server: OS patches, a disk that fills with logs, a rotated API key that silently breaks the agent, backups of that memory.jsonl you now depend on. It is not hard, but it never stops needing a little attention. The full server walkthrough lives in host an AI agent on a server, and if you want the deeper reliability patterns behind restarts and health checks, see build autonomous agents that run 24/7.
The third option is a managed runtime, and this is where the last mile stops being your problem. Sokko runs the agent on managed, always-on infrastructure that restarts it automatically if it crashes and keeps each agent's files saved across restarts and redeploys, so the memory.jsonl from the code above just survives. You get access controls, an audit log, and securely stored project secrets instead of a hand-rolled .env. You create an always-on instance from the dashboard in a couple of clicks, or with a single CLI command.
The point is not that Sokko is the only way. It is that the "keep it running 24/7" step is exactly where managed hosting earns its keep, because your laptop sleeps and a bare VPS needs babysitting.
What it costs to run 24/7
Real numbers, because "cheap" is not a plan. For a personal assistant doing modest work (a few dozen model calls a day, short prompts), your monthly bill splits into two buckets: model tokens and hosting.
Model tokens. At Claude Sonnet pricing, a typical assistant handling 50 tasks a day with a few thousand tokens each lands around $1 to $5 per month. Prompt caching cuts the input cost when your system prompt and tool specs repeat every call, which they do. A chatty assistant that summarizes long documents all day can run $20 or more, so set a daily ceiling and alert on it.
Hosting. A $4.50 Hetzner CX22 or a $6 DigitalOcean droplet covers a single always-on agent. Local models cost hardware instead: a 16GB machine you already own is free at the margin but ties the assistant to that box staying powered on.
Managed. Flat monthly pricing per agent, from $12 per month, plus your own model token costs. You trade a few dollars for not owning patching, restarts, backups, and secret rotation. Whether that trade is worth it depends on how much your time is worth and how many agents you end up running.
Put together, a genuinely 24/7 personal assistant on a small VPS costs on the order of $6 to $12 a month all-in for light use. That is the real floor. Anyone quoting $0 is running it on a laptop that will let them down.
The bottom line
To build a personal AI assistant that runs 24/7, pick a model with reliable tool use, wrap it in a plain loop you can debug, give it real tools and durable memory, and then, most importantly, host it somewhere that never sleeps. The code is a weekend. The uptime is the actual job.
Start on a $5 VPS with systemd and you will have a real always-on assistant for the price of a coffee, plus a standing chore of patching and monitoring. Move to a managed runtime when you would rather the restart, storage, and secret plumbing were somebody else's problem. Either way, the moment your assistant keeps working after you close your laptop is the moment it stops being a demo and starts being useful.