NOOA Makes an AI Agent a Plain Python Object, and the Interesting Part Is Three Dots
NVIDIA's contribution to the new Open Secure AI Alliance is not a model and not a scanner. It's an agent framework, which is an argument about where agent bugs actually live.
A method body of ... normally means "I'll write this later." In NVIDIA's new agent framework it means the method is finished.
class SupportAgent(Agent):
"""You are a support agent."""
order_db: OrderDB
# Ordinary method. Just Python.
def is_refund_eligible(self, order: Order) -> bool:
return order.delivered and order.days_since_delivery <= 30
# Agentic method: the runtime hands this to an LLM.
async def triage(self, message: str, order: Order) -> Ticket:
"""Create a typed support ticket."""
...
Both of those run. is_refund_eligible is deterministic Python that never touches a model. triage gets completed at runtime by an LLM loop, where the signature is the contract, the docstring is the prompt, and the return annotation is what the output gets validated against. Same class, same file, four spaces of indentation apart, and one of them bills you.
That is the entire design of NVIDIA Labs Object-Oriented Agents, or NOOA, published to arXiv on July 22 by a fifteen-author team out of NVIDIA Labs and released on GitHub under Apache 2.0. An agent is a Python object. Fields are state. Methods are actions. Docstrings are prompts. Type annotations are contracts.
Why a framework showed up as a security contribution
NOOA landed properly today, and not in the way you'd expect. NVIDIA announced the Open Secure AI Alliance this morning with more than thirty inaugural partners including Microsoft, IBM, Cloudflare, Hugging Face, LangChain, Palantir, Siemens, and the Linux Foundation. Other members brought the things you'd guess: HPE brought SPIFFE/SPIRE for workload identity, Hugging Face handed Safetensors to the PyTorch Foundation, Microsoft brought its MDASH multi-model scanning harness.
NVIDIA brought a way of writing Python classes.
The reasoning in NVIDIA's post is the part worth sitting with. An agent, they argue, is a whole system made of models, harnesses, and guardrails, and safety lives across that system rather than in the weights. So the harness itself becomes security surface. If you can't unit test an agent's behavior, you can't audit it either, and every governance conversation about agents collapses into vibes and screenshots.
I think that's correct, and I think it's an uncomfortable thing for the current generation of agent frameworks to hear. The dominant pattern right now spreads one agent across a prompt template, a tool schema in JSON, callback code, and a workflow graph defined in YAML or a builder DSL. Four artifacts, four mental models, and no stack trace that crosses all of them. When the agent does something stupid at 3am, you read logs and guess.
The mechanism, and why the interface cuts both ways
The specific claim NOOA makes is that agent-specific machinery (context, events, state rendering, long-term memory, validated model loops) can be exposed through plain Python APIs instead of framework-specific ones, so developers and models work against the same surface.
That last part matters more than it sounds. When the model wants to act, it doesn't emit a JSON tool call. It writes Python in a Jupyter-style REPL with access to self, to imports, and to your helper functions. Your methods and type annotations already describe the callable interface, so there's much less separate tool-schema plumbing to maintain. The model is coding against your object, and coding against objects is a thing models are extremely well trained on because the internet is full of it.
The paper identifies six model-facing ideas it says NOOA is first to combine on one surface: typed input and output, pass-by-reference over live objects, code as action, programmable loop engineering, explicit object state, and model-callable harness APIs for context and events. The authors are careful to note the community is already converging on several of these separately, which is a more honest framing than most framework papers manage. Results are reported on SWE-bench Verified, Terminal-Bench 2.0, and ARC-AGI-3, and NVIDIA describes the framework as reaching state-of-the-art across software engineering, cybersecurity, and reasoning benchmarks. That's a vendor claim about a vendor framework, and nobody outside NVIDIA has reproduced it yet.
Here's my problem with the design, and it's the same feature from the other side. In a NOOA class, you cannot tell by reading which methods are expensive, slow, or nondeterministic. is_refund_eligible and triage look like siblings. One is a comparison operator. The other is a network call that can hallucinate, retry, and fail differently on Tuesday. The ... is the only tell, and it is three characters wide.
Framework abstractions are annoying partly because they're loud. A @tool decorator or a graph node is ugly, and its ugliness is a warning label. NOOA trades the warning label for readability, and that's a real trade rather than a free win. If you adopt this, the first convention I'd write into your AGENTS.md is a naming rule that makes generation methods obvious at a glance, because the language will not do it for you.
Put this into practice
The lowest-friction version of this is about ten minutes and does not require you to port anything.
-
Install the core package into a throwaway project. NOOA installs from GitHub with
uv, not from PyPI.uv init noaa-test && cd noaa-test, thenuv add "nooa @ git+https://github.com/NVIDIA-NeMo/labs-OO-Agents.git@main". Pinning to@mainis what the README tells you to do, which tells you something about release maturity. -
Point it at a model you already pay for, or at nothing. The client goes through LiteLLM, so
get_llm_client("claude-haiku-4-5")works after you exportANTHROPIC_API_KEY, andget_llm_client("ollama_chat/qwen3:1.7b", api_base="http://localhost:11434")works with no key at all. Start local if you want to poke at it for free. -
Write one class with one
...method and run it. The quickstart example is a feedback analyzer with a single generation method. Run it, then rename the method fromanalyze_feedbacktoanalyze_feedback_brieflyand run it again. The output changes, because your method name is part of the prompt. This is the single fastest way to feel what's actually happening, and it's also the moment you understand the maintenance risk: renaming a method is now a prompt change, and your linter has no opinion about that. -
Turn on the trace viewer before you build anything real.
uv run nooa start-devputs a viewer on localhost:5001. Every model call, code execution, and method invocation is traced by default with parent-child spans preserved, and if the viewer isn't running, tracing silently switches off. That default is convenient and slightly dangerous, since "I forgot to start the viewer" and "nothing was recorded" look identical. -
Only then decide about porting. Install the optional subpackages (
nooa-cli,nooa-memory,eval_pipeline) if you get that far. Rewriting a working workflow-graph agent into objects is a genuine rewrite, not an afternoon refactor, and I would not spend that budget on a framework this young without a specific problem it solves for you.
Honest limitations
The repo has 14 stars, zero forks, zero watchers, and 58 commits as of this afternoon. No releases published. The CLI is marked beta in the README. That is a fair description of a research release from a chip company's lab, and it is not a fair description of something you should put in a production path this quarter.
The benchmark claims are self-reported. SWE-bench Verified, Terminal-Bench 2.0, and ARC-AGI-3 numbers come from the paper's own runs, and "state-of-the-art results across software engineering, cybersecurity and reasoning benchmarks" is NVIDIA's language in its own launch post. Treat it as a hypothesis with a repo attached.
The readability problem I described above is structural, not a bug they can patch. So is the debugging story underneath it. You get Python stack traces, which is a real improvement over reading a graph execution log, but a stack trace through a generation method tells you where the model was called, not why it chose what it chose. The framework makes the plumbing inspectable. It does not make the model inspectable.
And there's a governance question the Alliance framing raises without answering. Auditability is a property of a system someone actually audits. Shipping a framework that supports tracing, testing, and governance is upstream of anyone doing those things. Nobody has been fired yet for not writing unit tests for an agent.
What I'd actually watch
The number I care about over the next month is not stars. It's whether any of the other Alliance members ship harness code that adopts even two of NOOA's six model-facing ideas. LangChain is an inaugural partner and has the most to lose from "your abstractions are the problem" becoming the consensus read, which makes it the interesting one to watch.
There's also a question I can't answer from the paper, and if you try this, I'd like to hear what you find. When the model writes Python against your live objects instead of calling a tool schema, does it get better at your domain because the interface is familiar, or does it get more dangerous because self is right there? The framework's whole bet is the first one. Both seem plausible to me from the outside, and the only way to know is to hand it an object with a destructive method on it and watch.
Sources: NVIDIA-NeMo/labs-OO-Agents on GitHub, arXiv:2607.20709, NVIDIA: Industry Leaders Unite in Open Secure AI Alliance.