Welcome back to the Harness Engineering series — a 10-part journey from raw language model to production-ready agentic system. Made by builders. For builders.
In Part 5, we looked at the Context — the payload the model sees on every call. Now we look at what happens after the model, having seen that Context, decides to do something.
The model calls a tool. The tool has to execute somewhere. That somewhere is the Environment.
It's easy to under-appreciate. The Environment feels like plumbing — the filesystem, the shell, the network, the machine underneath. But it's where every side effect the model requests actually lands, and how you design it is what separates "an AI agent doing things on your behalf" from scary to routine.
What's ahead:
By the end of this article, you'll know what the Environment actually is, why every tool with side effects depends on it, and the three properties (bounded, reproducible, inspectable) that separate a production-ready environment from a demo one.
Let's get started.
📚 Want to go deeper than the articles? #
While you follow along with this series, I've put together two hands-on resources that go further than any single article can: — A self-paced course where I walk you through building a production-grade agentic harness from the ground up, in code.[Build a Harness from Scratch — Udemy Course]— A live, cohort-based workshop for builders who want direct feedback, Q&A, and to work through the material with peers.[Harness Engineering for AI Agents — Live Maven Workshop]Both are optional — the series stands on its own. But if you want the full studio-quality version, that's where it lives.
The Environment is the runtime that tools operate inside. Concretely, it includes:
If a tool has any kind of side effect, the Environment is where that side effect materializes. When your read_file
tool opens a file, it's opening a file in an environment. When your bash
tool runs ls
, it's running that in some shell, on some filesystem. When your fetch_url
tool fires an HTTP request, it's doing so from some network stack, subject to some rules about what it can reach.
None of this is exotic. If you've ever set up a CI pipeline, you've made environment decisions — what OS the runner uses, what dependencies are pre-installed, what secrets are exposed, what artifacts persist between steps. The Environment for an agent is the same kind of concern, applied to the same kind of question: inside what world does this thing run?
Because tools can't exist in a vacuum.
A read_file
tool is meaningless without a filesystem to read from. A bash
tool is meaningless without a shell to run in. A fetch_url
tool is meaningless without a network to reach out through. Every tool with side effects needs a target for those side effects, and the Environment is that target.
Put another way: the Tools (Part 4) are the model's reach, but the Environment is what they reach into. You can't separate the two. A well-designed tool set embedded in a poorly-designed environment produces an agent that either can't act (because the environment blocks it) or acts too freely (because the environment doesn't).
Three properties separate a production-ready Environment from a demo one.
The Environment defines the scope within which the agent can operate. Good environments are bounded — the agent can do the things it needs to do, and nothing else.
That means:
rm -rf
the host machineBoundedness isn't about not trusting the model. It's about containing blast radius. Even a perfectly-behaved model, working on a legitimate task, will occasionally make mistakes — hallucinate a filename, misread an argument, run a command with unintended flags. In an unbounded environment, one of those mistakes can wreck real things. In a bounded environment, the same mistake gets caught by the walls.
Every run starts from a clean, identical state. The Environment is recreated cleanly per task, not reused across tasks.
This one takes a moment to appreciate. In a reproducible environment, the agent doesn't inherit anything from previous work — no leftover files, no mutated database rows, no dirty processes, no stale caches. Every task begins from the same known baseline.
Why does this matter? Two reasons:
Reproducibility is what makes agentic behavior testable. Without it, every run is bespoke, and the whole system stops being an engineering artifact and starts being an unpredictable pet.
Engineers need to be able to see what the agent did to the Environment after a run.
Inspectability is the Environment's contribution to observability (which we look at in Part 8). It's not enough to know what the model said; you need to see what actually happened in the world as a result. Those two things do not always agree.
The model can claim it "successfully updated the config" while the config file, on inspection, hasn't been touched. Or it can claim to have "cleaned up temporary files" while /tmp
is stuffed with leftovers. An inspectable environment gives you ground truth to check the model's story against.
Codex is a clean example of what a well-designed Environment enables.
When you hand Codex a task, it runs inside a sandboxed container that gets a fresh clone of your repository. Inside that container, the agent can do essentially anything — read files, edit them, run tests, spin up services, execute arbitrary code, hit the network. And critically, it does all of that without ever touching your real machine.
When the task is done, the container is destroyed. The agent's changes come back to you as a pull request you can review before anything real changes.
That environment is why engineers trust Codex to run code they didn't write.
Notice the trade the sandbox enables. Inside the sandbox, the agent has a lot of freedom — it can delete files, run wild bash
, hit external services. The freedom is what makes it useful. But the blast radius is bounded to a disposable container. Whatever damage the agent could theoretically do stays inside walls that get torn down at the end of the task.
Without the sandbox, the same agent doing the same work would be touching your real filesystem, your real credentials, your real repositories. The tools would be identical. The model would be identical. But the risk profile would be completely different.
The sandbox is what turns "let an AI run arbitrary code" from a reckless idea into a routine engineering practice. That's the Environment doing the load-bearing work.
The Environment is the runtime that everything else takes place inside. The Loop drives the cycle. The Tools define what the model can request. The Context defines what the model sees when it decides. And the Environment defines where the doing happens and what the doing is allowed to touch.
Together, those four components already give you an agent that can act on the world within bounds. But there's still something missing — something you feel the absence of the moment a session ends or the context window fills up. The agent forgets. Everything it learned, discovered, or decided during a task disappears.
That's what Part 7 is about: the Memory Layer. How to give the harness persistence — within a task, and across sessions.
Remember that this article is part of a longer 10-part series that walks you through every component of an agentic harness.
Here's the roadmap:
See you in the next one.
Happy coding :)