Every few months, a new AI agent framework arrives promising to solve the complexity problem. LangChain added abstractions on top of abstractions. CrewAI introduced role-playing orchestration. AutoGen brought multi-agent conversation. Each layer was well-intentioned; most became a new source of debugging friction.
NVIDIA’s NOOA (Native Object-Oriented Agents), released July 30, 2026 as a research preview, takes a different approach: instead of building a framework on top of Python, it makes the Python class the framework.
The Core Insight: Your Agent Is Already a Python Class
NOOA’s central claim is that every component of an AI agent maps naturally to Python class constructs:
| Agent concept | Python construct |
|---|---|
| What the agent can do | Class methods |
| Agent memory and state | Instance fields |
| Instructions to the model | Docstrings |
| Input/output contracts | Type annotations |
This is not just an aesthetic choice. It has real engineering consequences.
from nooa import Agent
class CodeReviewer(Agent):
"""
You are an expert Python code reviewer.
Focus on: correctness, performance, security, readability.
Always explain WHY something is a problem, not just that it is.
Be direct and specific. Skip praise unless exceptional.
"""
recent_files: list[str] = []
severity_threshold: str = "medium"
def review_file(self, path: str, content: str) -> str:
"""Review a single Python file. Return structured feedback."""
...
def check_security(self, code: str) -> list[str]:
"""Scan for common security vulnerabilities. Return list of issues found."""
...
def summarize_session(self) -> str:
"""Summarize all files reviewed this session with overall recommendations."""
...
The docstring becomes the system prompt. The method docstrings become tool descriptions. The type annotations get runtime validation. The instance fields are agent state that persists across calls. No separate tool registration, no prompt templates in dictionaries, no schema declarations — the class declaration is the agent specification.
Install and run:
pip install nooa # v0.0.8, Python 3.12-3.13
import asyncio
from nooa import Agent
class ResearchAgent(Agent):
"""
You are a technical research agent. When asked about a topic,
search for current information, analyze it critically, and
summarize the key findings with your own assessment.
"""
sources_consulted: list[str] = []
def search_web(self, query: str) -> str:
"""Search the web for current information on a topic."""
...
def fetch_url(self, url: str) -> str:
"""Fetch and extract the main content from a URL."""
...
async def main():
agent = ResearchAgent(model="claude-opus-5")
result = await agent.run("What are the key differences between NOOA and LangGraph?")
print(result)
asyncio.run(main())
Models plug in through LiteLLM, so Claude, GPT-5.6, Gemini, local Ollama, and vLLM endpoints all work with the same code.
Benchmark Numbers: Strong, With Caveats
NVIDIA reports 82.2% on SWE-bench Verified, 86.8% on CyberGym L1, and 85.1% on ARC-AGI-3. These are good numbers, but they come with the usual benchmark caveats: NOOA is a harness, not a model — performance varies heavily based on which LLM you put underneath and how you write your class docstrings.
The more honest reading of the benchmarks: NOOA’s structured approach to tool definition and prompt construction (via docstrings and type annotations) reduces prompt inconsistency enough to improve reliability on structured tasks. The agent isn’t smarter; the instructions are cleaner.
What This Changes for Production Agent Development
I’ve been through three generations of agent frameworks at work — from hand-rolled tool-calling loops to LangChain v0.x to LangGraph. The debugging experience has gotten incrementally better, but a core problem persists: when something goes wrong, you’re debugging framework abstractions as much as your own logic.
NOOA’s approach has a concrete advantage here: the agent specification lives in plain Python, readable by any IDE, searchable by any code analysis tool, debuggable with standard Python debuggers. When a tool call fails, the traceback points to your code, not a framework callback.
The tradeoff is that you lose higher-level orchestration features. NOOA doesn’t give you built-in multi-agent coordination, memory stores, or RAG pipeline integration. If your agent workflow requires those, you’re combining NOOA with other tools.
When NOOA fits well:
Single-purpose agents with clear state. A code reviewer, a data extractor, a document processor — tasks with a defined scope where the agent state is small and the tools are well-specified. The class model shines here.
Teams that want readable agent code. Onboarding engineers to an agent codebase built with NOOA is straightforward: the class is the specification. No separate prompt files, no tool registry documentation to maintain.
Polyglot model environments. If you’re hedging between Claude, GPT-5.6, and open-source models, LiteLLM underneath means you swap model strings without touching agent logic.
When NOOA doesn’t fit:
Complex multi-agent workflows. NOOA doesn’t ship with orchestration primitives. If you need agent A to spawn agents B and C and coordinate their outputs, you’re writing that yourself or using it alongside an orchestration framework.
Long-horizon memory. Instance fields are in-memory state; they don’t persist across process restarts. Production systems that need durable agent memory need to wire that separately.
The Bigger Pattern
NOOA is the latest example of a trend I’ve tracked across the past 18 months: the AI tooling ecosystem is moving from framework-centric to primitive-centric. Early frameworks did everything, poorly. Current frameworks are narrowing scope and doing their specific piece well.
NOOA owns the “agent as a unit of code” problem. You still need to compose it with your orchestration layer, your memory system, your observability stack. But the composition seams are cleaner because NOOA’s surface area is smaller and its contracts are expressed in native Python.
The Apache 2.0 license and research preview status mean the API will change. I wouldn’t bet a production critical path on it today — but I would run a significant proof-of-concept. The conceptual model is sound, and if the NVIDIA Labs team keeps iterating, this approach could meaningfully simplify how teams reason about agent code.
Getting Started
pip install nooa
from nooa import Agent
class SummaryAgent(Agent):
"""
Summarize technical documents clearly and concisely.
Target audience: engineering managers who need executive summaries.
Format: 3 bullet points maximum, each under 25 words.
"""
def summarize(self, text: str) -> str:
"""Summarize the provided technical document."""
...
import asyncio
async def main():
agent = SummaryAgent(model="gpt-4o") # or claude-opus-5, gemini-2.0-flash
summary = await agent.run("Summarize this document", text=long_doc)
print(summary)
asyncio.run(main())
The GitHub repository is at NVIDIA-NeMo/labs-OO-Agents. The arxiv paper (2607.20709) covers the harness engineering concepts in detail if you want to understand the performance claims.
Thuận Lương is a Technical Lead with 15+ years in .NET, cloud architecture, and AI systems. He writes about real-world lessons from building production systems.