Skip to content
Practice2026-08-1713 min readRev. 2026-08-17

Build a Working Agent with sovereign-agent

Clone it, install it editable, run the offline example, then read the 67-symbol API you'd actually build on. A working agent in about fifteen minutes, no API key required.

Key takeaways

  • The packaged wheel excludes examples, chapters, tests, and lessons on purpose, so a plain pip install cannot run the README's own scripts.
  • sovereign-agent doctor checks Python version, config, API key, disk space, and mount allowlist before you ever call run_task, and it catches the most common failure before it happens.
  • The research-assistant example runs fully offline by default and still proves its report is grounded, because its closing dataflow audit checks the output against what the tools actually returned.
  • 67 public symbols are covered by a semver contract as of v0.2.0. Everything else lives under sovereign_agent._internal and can change on any release, including a patch.

Rod Rivera

Author

Build a Working Agent with sovereign-agent

Rod's note — read with a pencil; the margins are for you.

What this piece is, and what it deliberately is not

Clone it, install it editable, run one example, no API key required. Fifteen minutes from empty terminal to a working agent that produced a real report and audited its own output for fabricated citations. That's the whole arc below, in order: clone, install, doctor, run.

This piece assumes you either already know why sovereign-agent is built the way it is, or don't care yet — you want a working agent on your machine, fast. If you want the why (sessions as directories instead of a database, why the executor parses JSON defensively, the real six-hour debugging session that forced several of those decisions), this site already has six lessons for that: 27 through 32 in the Tools, Memory, and Multi-Agent Systems course. That story is already told there, and told better than a second pass here would tell it.

sovereign-agent's own chapters/README.md names the distinction between those two things better than I can improve on: two dominant patterns exist for teaching a library alongside its code. The Raschka pattern, after Sebastian Raschka's Build a Large Language Model From Scratch, puts the tutorial and the production code in the same tree, so you learn by rebuilding pieces of the real thing. That's what chapters/ in the repo does, and it's also the shape of the six course lessons on this site: rebuild the reasoning, understand the bug each decision removes.

The Howard pattern, after Jeremy Howard's fastai library plus its separate course, treats the library as a sealed box you bring in and use. You don't rebuild the planner. You import it, call one function, and watch an agent complete a task. That's this article.

Install: why a bare pip install will not work for this tutorial

Run pip install sovereign-agent and you get the library. You do not get examples/, chapters/, tests/, or lessons/. That's not an oversight. pyproject.toml excludes them from the wheel explicitly:

toml
[tool.setuptools.packages.find]
where = ["."]
include = ["sovereign_agent*"]
exclude = ["tests*", "chapters*", "lessons*", "examples*"]

Reasonable choice for a production dependency. Ships you the library, not four other directories of tutorial scaffolding you didn't ask for. But it means the README's own quickstart, and this tutorial's plan to run a real example, needs the source tree, not just the package. So the install step here is a clone plus an editable install, not a bare pip install.

Clone the repo
bash
git clone https://github.com/zeroemployeeorg/sovereign-agent
cd sovereign-agent

You need Python 3.12 or newer. pyproject.toml declares requires-python = ">=3.12", which pip enforces at install time, and sovereign-agent doctor (below) checks your interpreter version first and reports it if it's too old, though the check itself only warns rather than blocking anything by itself.

Install editable, from the clone
bash
pip install -e .

Editable install means the sovereign_agent package resolves to the files in this clone, and examples/ sits right next to it in the same tree, both importable, both on disk, no wheel exclusion in the way. If you want the optional extras (evidently, otel, voice, docker), install pip install -e ".[all]" instead. The core dependencies either way are openai>=1.40, typer>=0.12, croniter>=2.0, and python-dateutil>=2.8.2.

Confirm it's wired up
bash
sovereign-agent doctor

This is the pip-installed CLI's own preflight command, lighter than the contributor-only make doctor, but it checks the things that actually break a first run. Read what it checks before you run anything that calls an LLM.

Run the offline example
bash
python -m examples.research_assistant.run

No API key needed. This is the part that proves the install worked.

Run doctor before you run anything that calls an LLM

Here's the order that actually matters, and it's the opposite of what most quickstarts teach. Most READMEs tell you to run the example first and let the error teach you what's missing. sovereign-agent ships a command whose entire job is to tell you what's missing before you hit the error.

sovereign-agent doctor checks, in order: Python version, whether Config.from_env() loads without raising, whether your LLM API key environment variable is actually set, free disk space in your sessions directory (it warns under 1GB free), the mount allowlist, and Config.validate()'s own self-check. Pass --skip-llm and it stops there. Without that flag, and with a key present, it does one more thing. It sends the model a real round-trip message, literally "Reply with just the word OK.", and prints what comes back.

If your API key isn't set, doctor's own output says so plainly: "LLM API key not set. Export {cfg.llm_api_key_env} and retry." That's the whole failure, caught before you've written a line of your own code.

The error you'll hit if you skip doctor and go straight to the README's quickstart

The README's own first example is run_task("What's the weather in Edinburgh?"). It's a good demo of the surface area, and it will fail for anyone who hasn't set an API key, because run_task calls out to a real LLM by default. Here's the actual check, from sovereign_agent/_internal/llm_client.py:

python
key = api_key or os.environ.get(api_key_env)
if not key:
    raise ExternalError(
        code="SA_EXT_AUTH_EXPIRED",
        message=(
            f"LLM API key not available. Set env var {api_key_env!r} "
            "or pass api_key= explicitly."
        ),
        retriable=False,
    )
The exact error you'll see

LLM API key not available. Set env var 'NEBIUS_KEY' or pass api_key= explicitly.

NEBIUS_KEY is the default provider environment variable, for Nebius, chosen per the repo's own .env.example because their free-tier credits cover every example in this repo. If you already ran sovereign-agent doctor first, you saw this coming: its API-key check is the same condition, caught before run_task ever runs.

The fix, once you have a key: export NEBIUS_KEY=sk-... (or pass api_key= directly to the client). But you don't need a key at all for the next section, which is why it's the better first run.

The example that actually works with zero setup: research_assistant

examples/research_assistant/run.py is the simplest complete example in the repo, and it's deliberately safe to run cold. By default it uses a FakeLLMClient, a scripted, deterministic stand-in that drives the same trajectory a real model would, so you can see the full loop without an API key, a network connection, or a bill:

bash
python -m examples.research_assistant.run

Run it with a real model instead, once you have a key set:

bash
NEBIUS_KEY=... python -m examples.research_assistant.run --real

Either way, the shape is the same: a planner produces a subgoal, an executor calls a tool (web_lookup, scripted in the offline run), the tool result gets written to a file in the session's workspace, and the run completes. What makes this example worth running rather than just reading about is what happens after completion. It prints a dataflow integrity audit.

Every run also leaves a directory behind you can inspect directly, no viewer or query language needed:

Files changed
sessions/sess_<12hex>/session.json
sessions/sess_<12hex>/SESSION.md
sessions/sess_<12hex>/workspace/report.md
sessions/sess_<12hex>/logs/tickets/
sessions/sess_<12hex>/logs/trace.jsonl

session.json holds the forward-only state machine. workspace/report.md is the file the audit checks against web_lookup's actual output. logs/tickets/ is the append-only record of every operation, and logs/trace.jsonl, its sibling under the same directory, is what you'd cat to see exactly which tool got called, in what order, with what result. None of this is specific to the research-assistant example. It's what every scenario in the repo produces, because it's how the session directory works underneath any of them.

Why "it ran successfully" is not the same claim as "it worked"

The repo's own README names the failure mode this audit exists to catch: a framework can have every ticket green, every manifest verified, and still have shipped an LLM's complete fabrication, because tickets and manifests prove the pipeline ran, not that the content is true. The full story behind that lesson, including the actual fabricated code review that forced the fix, is lesson 30 on this site. It is worth reading in full and this piece will not retell it.

What matters here is what the dataflow audit at the end of research_assistant.run actually checks: not "did a tool get called," but "does every claim in the final report trace back to something a tool actually returned." Concretely, it counts how many web_lookup calls happened, how many results came back, and then cross-checks every arXiv ID that shows up in the written report against the IDs web_lookup actually returned. If the model invents a citation that was never in a tool result, the audit flags it: a failing mark next to the fabricated ID, not a silent pass.

Quick check — You ran pip install sovereign-agent in a fresh virtualenv. Can you now run python -m examples.research_assistant.run?

The public API surface: what you'd actually import to build your own

Everything in sovereign_agent.__all__, 67 symbols as of v0.2.0, is covered by an explicit semver contract documented in docs/API.md, titled "The 67 public symbols (v0.2.0)," under what the doc itself calls "Contract version: 1." That distinction matters more than it sounds like it should: anything in __all__ is stable within the 0.2.x series, and anything under sovereign_agent._internal, including the llm_client.py module the pitfall above quotes from, can change on any release, including a patch. If you're writing code against this library, import from the public surface, not from _internal, or accept that your integration is pinned to an exact version.

You won't touch all 67 symbols on a first build. The ones that actually come up when you're wiring together your own agent:

  • run_task: the one-function entry point. It's what the README's quickstart calls; internally it wraps an Orchestrator.
  • register_tool: the decorator that adds a function to the tool registry, the same mechanism the README's get_weather example uses.
  • Config, loaded via Config.from_env(): the same call doctor makes first, and the one that fails loudly if your environment isn't set up right.
  • Session, create_session, load_session: the session/state layer. A session is a directory; these are how you create or reattach to one.
  • Planner / DefaultPlanner and Executor / DefaultExecutor: the two halves of the loop you can swap out or subclass if the defaults don't fit your task.
  • ToolRegistry, ToolResult, global_registry, make_builtin_registry: the tool layer underneath register_tool, useful once you want to build a registry scoped to one scenario instead of the global default.
  • Orchestrator and TaskResult: what run_task is a convenience wrapper around, and what you'd reach for directly if you need more control than one function call gives you.
What run_task wraps, in the quickstart flow

That's the shape underneath the README's four-line example. run_task is the door; Orchestrator is the room; Planner, Executor, and ToolRegistry are what's actually furnishing it. Once you outgrow the one-liner, this is the order you start reading source in.

Questions that come up once you've run it once

Where to go from here

If you got the offline example running and read the audit output, you've done the Howard-pattern version: used the library as a black box, seen a working loop, and know which 67 names are safe to build on. That's a starting point for a scenario of your own. Register a tool, call run_task, read the session directory it wrote.

If you want the Raschka-pattern version, rebuilding the planner, the queue, the ticket state machine yourself, with tests that only pass once your implementation matches the production module, that's chapters/ in the repo: five chapters, five to eight hours of focused work. And if you want the decisions behind why any of this is built this way, rather than just how to call it, that's the six lessons this piece deliberately didn't repeat.

Read the eight architectural decisions

Sessions as directories, forward-only state, SHA-256 manifests, atomic-rename IPC, and the rest, with the production code and the bugs each one removes.

Ready to put an agent to work?

Join the Prof Rod newsletter for one educational lesson a week, with worked examples attached. It is free to register for and separate from the Zero Employee community.