Getting Started with ONTO in Python: A 10-Minute Tutorial
Install ONTO, run your first agent, extract typed facts, plan an action, and execute it under consent scopes. No API key required.
This is a 10-minute walk through your first ONTO agent in Python. By the end, you’ll have an agent that extracts typed facts from natural language, plans actions, gets your approval, and executes them under consent scopes — the whole Extract → Plan → Approve → Execute loop.
Step 1 — Install
pip install onto-core
That’s the whole install. ONTO is a single package with sensible defaults.
Step 2 — Get a free Ollama API key
ONTO defaults to Ollama Cloud, which has a free open-weight tier. Get a key:
# Visit https://ollama.com/settings/keys — sign up, generate a key.
export OLLAMA_API_KEY=...
If you’d rather use Claude or GPT, set ANTHROPIC_API_KEY or OPENAI_API_KEY instead. The code below works either way.
Step 3 — Your first run
Create quickstart.py:
from onto import Onto, Models, BuiltinTools
# Initialize with the open-weight default.
onto = Onto(
model=Models.GPT_OSS_20B,
builtin_tools=BuiltinTools.STANDARD,
)
result = onto.run_sync(
"Hello. I'm Alice. UTC, mornings.",
user_id="alice",
consent_scopes=["memory:write"],
extract=True, # ← extract typed facts from this message
)
print("Output:", result.final_output)
print("\nExtracted assertions:")
for ea in result.extracted_assertions:
print(f" {ea.level:8} {ea.subject}.{ea.predicate} = {ea.object!r}")
Run it:
python quickstart.py
You should see:
Output: Hi Alice! Nice to meet you. ...
Extracted assertions:
profile alice.timezone = "UTC"
profile alice.prefers = "mornings"
Two typed assertions. The LLM identified them, the runtime routed them to the profile level, and they’re now in your local memory store. Next time you run, the agent will already know.
Step 4 — Memory persists
Run a second call without re-introducing yourself:
result = onto.run_sync(
"What do you know about me?",
user_id="alice",
consent_scopes=["memory:read"],
)
print(result.final_output)
The agent loads alice’s profile assertions at the start of the run and uses them in its response. The first answer might be something like “You’re Alice, in UTC, and you prefer mornings.”
Memory survives. This is what a conversation buffer can’t do.
Step 5 — Plan Mode
Now let’s plan something with side effects:
plan = onto.run_sync(
"Plan a 3-day trip to Lyon for next weekend.",
user_id="alice",
mode="plan_only", # ← no tools fire
consent_scopes=["tasks:write", "tasks:read"],
)
print("Plan tasks:")
for task in plan.tasks:
print(f" - [{task.tool}] {task.rationale}")
You’ll see something like:
Plan tasks:
- [search_hotels] Find hotels in Lyon for the dates.
- [book_hotel] Book the chosen hotel.
- [search_trains] Find trains from your origin to Lyon.
- [book_train] Book the chosen train.
None of these have actually fired. The agent’s reasoning is captured as typed Task objects you can inspect, approve, or modify.
Step 6 — Execute approved tasks
Now you decide which to run:
for task in plan.tasks:
if input(f"Approve [{task.tool}]? (y/n): ").lower() == "y":
result = onto.execute_task(
task,
consent_scopes=[
"tools:web.fetch",
"tools:http_request",
# In a real deployment, also: "booking:hotel", "booking:train"
],
)
print(f" → {result.summary}")
This is the structural guarantee: the Plan run carried tasks:write (it can propose) but not execute scopes (so side-effect tools refuse). Only the Execute run, with explicit consent, actually fires the tools.
Step 7 — Check the cost
cost = onto.cost(user_id="alice")
print(f"Tokens: {cost.tokens.input + cost.tokens.output}")
print(f"Cost: ${cost.dollars:.4f}")
print(f"Foreground: ${cost.foreground.dollars:.4f}")
print(f"Background: ${cost.background.dollars:.4f}")
ONTO tracks per-user cost split between foreground (your turns) and background (any summarization or memory consolidation that ran). On the free Ollama Cloud tier, the dollar cost is zero — but the token counts are real and useful for capacity planning.
What just happened
In ~25 lines of code you’ve used five primitives:
extract=True— typed-fact extraction at the right level (profile vs session vs domain vs tool).- UPO — durable user memory that persisted across calls.
mode="plan_only"— Plan Mode, the runtime invariant that proposed actions without firing them.consent_scopes=[...]— Policy Guard, the per-call consent enforcement.onto.cost(...)— the cost meter, per-user attribution.
This is the whole agent runtime in miniature. Everything else — multi-tenant scoping, MCP tools, subagents, background workers, eval framework — composes on top.
Where to go next
- Industries — see how the primitives map to specific verticals (healthcare, customer support, personal AI, research).
- Features — deeper coverage of each primitive.
- Comparison — how ONTO stacks up against Claude, OpenAI, Google, LangChain.
- Canonical docs — full SDK reference, deployment guides, model providers.
Install ONTO and try the rest of the examples. If something doesn’t work, file an issue — we’d rather fix the rough edges than ignore them.
Frequently asked questions
Do I need a paid model API key?
No. ONTO defaults to Ollama Cloud's free open-weight tier. Set OLLAMA_API_KEY (free at ollama.com/settings/keys) and you're running. To use Claude or GPT, set ANTHROPIC_API_KEY or OPENAI_API_KEY instead.
Will this work on a laptop without a GPU?
Yes — Ollama Cloud runs the model on their hardware, you just hit the API. For fully local (no network), you need an Ollama install with sufficient RAM/VRAM for your chosen model.
Is there a TypeScript or Rust version?
Both. `npm install @onto/core` for TypeScript, `cargo add onto-runtime onto-storage onto-llm` for Rust. Same APIs and semantics across all three.
The agent SDK where humans drive the state.
Plan Mode, typed memory, per-call consent scopes, and open-weight defaults. Open source under MIT or Apache-2.0.