Code Is the Action Space: How to Build Agentic AI That Writes and Runs Code

For AI agents doing real technical work in life sciences, code is the only action space that matters. This second post in the Harness Engineering series covers the three things to get right: context to write correct code, a governed place to run it, and visibility into what happened.
In the first post in my series on how harness engineering is the key to automating real-world life sciences workflows, I argued that the harness, the software around the model, is where the leverage is right now. I wanted to go deeper in this article on a part I called the action space: what the agent can actually do.
People get fancy about this, giving the agent a whole catalog of tools it can call. I think that's mostly a distraction. The only tool that matters is the ability to write and run code. This may be controversial, but I think "code is all there is." It's the grammar of getting a computer to do something.
Code is all there is
Look at what the work looks like day-to-day in life sciences. Strip away the vocabulary and most of it is code. Cox regression, differential expression, joint variant calling, structural comparison, molecular dynamics, all of which come down to code. Biostatistics, health economics, enzymology, the whole edifice of computational biology is people writing scripts in various languages, running them against data, staring at the output, and adjusting. While the domain expertise is real and hard-won, the action space is code.
This shape isn't special to life sciences. Swap the vocabulary for actuarial work, or logistics, or trading, and you find the same thing: experts writing scripts against data and iterating. That's why I say "code is all there is." But giving agents a well-designed coding action space is not easy. From our experience, there are three key things to get right.
Give agents the context to write correct code
We want agents to write the correct code, not just code that runs. There are many ways to run an analysis, and only some of them are right for the data in front of you. Knowing which one requires domain expertise. Hand a model a bare Python prompt and ask for a survival analysis on messy claims data and you'll get something plausible--and totally wrong.
The way to address this is context, and it comes in three flavors. The first is context about the data itself: how the tables are keyed, what the columns mean, what the codes resolve to, what values actually show up in a field. Knowing this is what lets the agent write a query with the right WHERE clause in SQL and the right transformation, rather than a query that runs clean but returns the wrong cohort.
The second is context about the libraries and functions available to it. You don't need much for the common stuff. Scipy, pandas, the standard scientific Python, and R packages are all over the pre-training data and the model knows them well. What it doesn't know is your private libraries, the less-used packages, and whatever changed in the last six months. That documentation has to be available at inference time and kept current as the packages move.
The third is example code. Nobody writes a joint-calling pipeline from scratch, and neither should the agent. Two ways to do this: reference code bundled into skills, and governed access to curated repositories in Dockstore and GitHub, so the agent starts from a vetted pipeline. A lot of that code is technically in the pre-training set, but giving the agent the real context and documentation at inference time makes a big difference in the quality of what it writes. Leverage the community. There is no reason to start from scratch.
Governance matters here, though. The set of repositories the agent can read has to be curated, not everything willy-nilly. An agent that can pull code in from anywhere is an attack vector. Curation is also what makes the reference code worth having: a vetted repo is a strong signal about how the work is supposed to be done; an arbitrary one is simply noise. And there's a lot of noisy code on the internet, even more so with the rise of AI-generated slop code.
Give agents a governed place to run code
The same logic applies to where the code executes, and there are two requirements that pull against each other--the environment has to be bounded and capable.
Bounded means sandboxed, with no internet access. An agent with open internet access is liable to pull in a package, run it, and corrupt the environment. It is better to keep the runtime closed, so everything that goes into it has to be vetted on the way in.
That means somebody has to do the vetting. Do you let anybody upload whatever Docker image they feel like? No. There's a path: people who know what they're doing review the runtimes, and those are the ones that get exposed to the agent. Once a runtime is available, with its libraries and its associated skills, the agent writes code and executes against it, and you know exactly what environment it's executing against.
Bounded doesn't mean underpowered, though, and this is where a lot of setups fall apart. You need real compute to deploy that vetted environment on, with whatever capabilities the job needs. Sometimes that's a small CPU box with limited memory for a quick transform. Sometimes the job is memory-heavy, or needs a GPU for model inference. And a lot of the real work runs on HPC clusters, like joint calling across thousands of samples, which means wiring the agent into something like AWS HealthOmics so it can write WDL and Nextflow jobs and run them at scale.
Then there's the issue of money. An LLM can burn thousands of dollars if you hand it high-performance compute. So it needs budgets and human-in-the-loop confirmation before the expensive jobs are executed. You wouldn't give a junior employee the Platinum Card on day one, now would you? Don't give an agent one either. The governance that bounds what an agent can run also has to bound what it can spend.
Let agents see the output from running code
Lastly, the agent has to see what happened when the code ran. When a script fails, it needs to introspect the logs, stdout, stderr, and exit codes, so it can diagnose what went wrong instead of guessing. Then it fixes the issue and retries. That's learning from a mistake, and it works because the failure came back with the context on why it happened. I'll be honest, watching an agent debug itself three times in a row and land on the right answer is still pretty satisfying.
Visibility doesn't stop at stack traces and exit codes. When the agent generates a plot, it should be able to look at the plot it just made, and most modern models are multi-modal enough to do that. And a lot of the real problems are subtler than a crash. The most dangerous runs are the ones that produce the wrong answer, say because a join dropped rows. Nothing in the logs catches that. What works is interviewing the user up front about validation and sanity checks, and then having the agent run them and check its work.
People have, rightly, started calling this loop engineering. You're designing the cycle, not the prompt: what the agent observes after it acts, what checkpoints it trusts to tell it whether it's done, and when it should stop and ask a human instead of spinning.
The loop should get better over time, with longer-term memory so the agent remembers next month that this dataset always needs that particular fix. But the base loop is where the value starts: write, run, look at the output, correct. That's how a person debugs, and it's how an agent has to work too.
None of this is glamorous. But it's real work, and it's what building a vertical-specific harness actually looks like. At the highest level, if you're working with computers--whether you're a human or an agent--code is all there is.