Ideas ranked by how hard they would be to build on any other agent framework — with an honest note on the ones that don't really need NOOA at all.
Seven projects that could be built on NVIDIA-labs OO Agents, scored on a single question: how hard would this be to build on any other agent framework? Ideas that would work equally well on LangGraph or a hand-rolled tool loop are marked as such rather than padded out.
The framework has six genuinely distinguishing traits. Two matter most — agents being real Python classes (the axiom everything else derives from) and live objects passed by reference into the model's REPL rather than serialized to JSON. A third, always-on typed ATIF trajectories, is the most underrated. Code-as-action is impactful but not unique — it is published prior art, and projects resting on it alone score poorly.
Two orderings, do not confuse them. Rank answers "which project is worth the most?" Sequence answers "which do you open your editor on Saturday?" They disagree, deliberately — the highest-value project is not the best first move.
So: rank order 03 → 01, build order 01 → 03. Build 03 first and you are writing a regression harness for agents you have not yet learned to write; build 01 first and you arrive already knowing what your own trace looks like when it goes wrong — which is the thing the harness has to detect.
"Build something on framework X" is only interesting if the thing you build is hard to build on framework Y. So the projects below are scored against a specific question: does this exercise something NOOA does that LangGraph, CrewAI, or a hand-rolled tool loop does not?
Reading the source, six things qualify. Everything else NOOA offers — MCP clients, LiteLLM model routing, Langfuse/Phoenix/OTLP exporters, a memory package — is table stakes across the ecosystem and earns a project no points.
A metaclass turns fields into state, methods into capabilities, the class docstring into the system prompt, and type annotations into the I/O contract. The practical consequence is that agents inherit, compose, get type-checked, and get unit-tested like any other object.
src/nooa/metaclass.py, src/nooa/agent.py
... body means "an LLM implements this"Methods whose body is a literal ellipsis become generation methods; methods with real bodies stay deterministic Python. One class holds both, and the visual difference between "this is guessed" and "this is guaranteed" is one token wide.
src/nooa/ellipsis_detection.py, src/nooa/metaclass.py
The model acts by writing Python into a Jupyter-style REPL that has self, imports, and helpers in scope — not by emitting tool-call JSON. Loops, filters, and intermediate variables come for free, without a schema per operation.
src/nooa/strategies/codeact.py; README.md:64
The strongest differentiator. Conventional frameworks serialize every tool argument to JSON and hand the model a copy. NOOA puts the real object in the REPL namespace. The model calls methods on the same instance your process is holding, and mutations are simply real.
README.md:65 ("live-object arguments passed by reference")
doc(obj) renders any class, function, module, or instance as a prompt-ready contract, expanding referenced types inline and deduplicating them — and the model can call it mid-run to expand detail on demand. Annotated[str, hidden] keeps fields such as API keys out of that rendering entirely.
src/nooa/agentdoc/core.py:45, src/nooa/agentdoc/_visibility.py
Every LLM call, code execution, and method invocation is traced with parent-child spans, and runs export to ATIF v1.7 — a versioned Pydantic trajectory schema with normative rules on step ordering, joinability, and context-management boundaries. This is not a log file. It is a typed, diffable artifact.
src/nooa/tracing/, src/nooa/atif/schema.py
src/nooa/_visible.py is an explicit no-op kept only for backward compatibility — its own docstring says "everything is visible by default in nooa" — and hidden is a static Annotated[...] marker resolved per class, not a runtime toggle. The right mechanism for guarding a call is MethodPrecondition / MethodPostcondition in src/nooa/strategy_validation.py: preconditions fail fast before generation, and postconditions raising InvariantError are routed back to the model as correctable feedback rather than crashing the run. That is a better fit anyway — the model learns why it was refused.
An agent class holding your actual position data and broker connection as typed fields — portfolio: DataFrame, broker: Broker, prices: PriceFeed — where analysis happens by the model writing pandas directly against those objects in the REPL.
class PortfolioAnalyst(Agent, llm=llm):
"""You analyse a live portfolio and propose rebalances."""
portfolio: DataFrame
broker: Broker
api_key: Annotated[str, hidden] = "" # never rendered into a prompt
def max_position_pct(self) -> float: # real body: deterministic
return 0.15
async def propose_rebalance(self, thesis: str) -> RebalancePlan:
"""Propose a rebalance consistent with the stated thesis."""
... # ellipsis: LLM-driven
This is the cleanest demonstration of D (live objects) that exists. Consider what the same task costs elsewhere: to let a model compute a rolling correlation, you must anticipate the operation, define a compute_correlation tool with a JSON schema, serialize the frame in, deserialize a result out — and you must do that again for every unanticipated operation. Under C (code as action) the model writes three lines of pandas against the frame that already exists in memory. The set of analyses it can perform is no longer bounded by the set you predicted.
It also lands A and B hard, because a portfolio agent is exactly where the deterministic/generated boundary matters most: max_position_pct() has a real body and is therefore a guarantee, while propose_rebalance() has an ellipsis body and is therefore a guess. Encoding "which parts of my trading logic are allowed to be wrong" as a syntactic property of the class is a genuinely different way to reason about risk.
Add a MethodPrecondition asserting that no proposed order exceeds max_position_pct(), and a postcondition raising InvariantError on violation, and you get J: the model is told why its plan was rejected and retries against the constraint, instead of the run dying.
One agent class with several MCP servers attached as typed fields — mail, calendar, a notes or knowledge store — performing a real recurring job end to end rather than answering one question per server.
Partially, and it is worth being precise about which part. MCP support itself is not a NOOA differentiator; every serious framework has a client, and examples/quickstart/11_mcp.py shows a conventional one. The project earns its place on a narrower claim: under conventional tool-calling, combining three servers means three model turns — call mail, get JSON back, reason, call calendar, get JSON back, reason, call notes. Under C, the model writes one cell that fetches from all three, filters in Python, joins them on a key, and returns a result. The reasoning happens in code rather than across round-trips, which is fewer tokens, fewer opportunities to lose the thread, and one trace span instead of six.
That is a real and measurable benefit. It is also the only unique thing this project demonstrates.
A pytest plugin that captures real agent runs as ATIF trajectories, stores them as fixtures, and — when you change a prompt, swap a model, or switch strategies — replays and diffs behaviour, not just final output. Did it take more steps? Call a different method? Branch elsewhere? Burn 40% more tokens for the same answer?
This is the project I would bet on, because it is the one that is genuinely hard to build anywhere else, and the reason is structural rather than incidental.
Behavioural regression testing for agents needs three things at once. First, a complete and structured record of what happened — F gives you that by default, with parent-child spans preserved across orchestrators, generation methods, and helpers, and with src/nooa/atif/schema.py providing a versioned Pydantic schema whose normative rules (sequential step ids, joinability, context-management boundary semantics) are enforced by tests in the repo. Diffing two typed trajectories is tractable in a way that diffing two log files is not. Second, the agent must be re-instantiable in a test harness with dependencies substituted — which A gives you free, because the agent is a class and the test is just pytest. Third, deterministic replay of non-model state, which the event-sourced storage layer (src/nooa/storage/, snapshots) supports.
Most frameworks give you at most one of the three. Tracing is usually an add-on exporter producing spans shaped for a dashboard, not for assertion; agents are usually graph configurations rather than objects, so there is nothing clean to instantiate; and state is usually held in a runner you cannot snapshot.
The wider point is that prompt engineering is currently done by vibes, because nobody can see regressions. A tool that fails CI on "this prompt edit made the agent call the database twice as often" would be useful to everyone building on the framework.
packages/nooa-bench/src/nooa_bench/trace_analyzer.py already exists, as do runner.py and protocol.py, and examples/quickstart/14_atif_trajectory.py demonstrates export. So this is extending an existing foundation, not inventing from nothing. Read those three files before scoping — the honest version of this project might be "add trajectory diffing and a pytest fixture API to nooa-bench," which is smaller and more likely to be upstreamed.
An agent that, on completing a task it found difficult, writes the procedure down as a reusable capability and loads it next time. Facts go to long-term memory; procedures become code.
Because the framework ships explicit affordances for exactly this, which is unusual. src/nooa/tools/method_writing_lib.py defines a MethodWriting skill whose docstring instructs the model to define helper functions at the top of a REPL cell — and, more interestingly, to decorate standalone async functions with @strategy(PredictStrategy()) and an ellipsis body to create per-item LLM sub-calls fanned out through asyncio.gather. The agent is not merely writing helper code; it is authoring new generation methods at runtime and parallelising them. That is B and C composing in a way that has no clean analogue in a tool-calling framework, where a new capability means a new schema registered out-of-band.
library_writing_lib.py sits alongside it for persisting capabilities beyond a single cell, the skills/nooa-self-extending/ skill documents the pattern, and DynamicMethodAdditionError is a first-class error type in src/nooa/errors — a good sign that dynamic method addition is a supported path with defined failure modes rather than an accident.
Pair it with nooa-memory, whose module list (retrieval.py, reflection.py, forgetting.py, generative.py) is more opinionated than a bare vector store. Then instrument the thing that actually matters: skills written, skills reused, and tokens-per-task over weeks. That last chart is the entire project — without it you have an agent that accumulates clutter and no evidence it is learning.
A small team — say author, reviewer, and integrator — where one agent holds another as a typed field and invokes it as a method: await self.reviewer.critique(patch). No router, no message schema, no graph definition.
It is the sharpest available test of the framework's central claim. If agents really are ordinary Python objects (A), then multi-agent orchestration should reduce to ordinary composition, and the entire category of orchestration machinery that other frameworks sell — routers, state graphs, message protocols, handoff schemas — should evaporate into method calls. Nothing else on this list puts the thesis so directly at risk.
Two secondary features carry real weight here. D means the author agent can hand the reviewer a live object rather than a serialized summary, so the reviewer inspects the same artifact rather than a lossy description of it. And F's parent-child spans mean a nested agent call appears as a nested span, so the trace of a three-agent run reads as a call tree — which is what it actually is.
Composition may relocate complexity rather than remove it. Message buses exist partly to solve problems that method calls do not address: retry and backoff at a boundary, cycle detection, concurrency between peers, and observability of a handoff. If you find yourself writing a coordinator class that dispatches, tracks status, and retries, you have rebuilt an orchestrator with worse ergonomics — and that negative result is worth writing up, because it bounds the claim. Build the equivalent in one graph-based framework and compare debuggability honestly rather than charitably.
Take an open model at two or three sizes, run one benchmark, and ablate the harness: context blocks on/off, summarization on/off, skills on/off, CodeAct versus Predict versus Prefill. The question worth answering is whether harness capability substitutes for parameter count — and where that substitution breaks down.
The repo is built for it. src/nooa/strategies/ contains codeact.py, predict.py, pure_python.py, prefill.py, and composite.py behind a common GenerationStrategy base with set_default_strategy(), so swapping the reasoning loop is a config change rather than a rewrite — that is the ablation axis handed to you. src/nooa/context_blocks/ and the skill registry give two more axes. packages/nooa-bench supplies BenchAgent and a runner, and util/eval_pipeline the scoring. The repo's own framing — a paper and a blog post on harness capabilities and model performance — means there is a baseline to compare against rather than a vacuum.
Local models are first-class: Ollama and vLLM are both supported through LiteLLM with no API key, so a full ablation grid costs GPU time rather than inference spend. That matters when the design calls for dozens of runs per cell to get error bars.
CodeActLiteStrategy and ReflexionStrategy are experimental and deliberately gated behind a FutureWarning in src/nooa/__init__.py — they are re-exported lazily through nooa.experimental precisely so the warning cannot be bypassed. Ablations involving them sit on an unstable API and may not reproduce across versions.examples/arc_agi_3/ and examples/cybergym/ are already in the repo, each with configs, skills, and tests. Both currently start each episode cold. The experiment: give the agent memory that persists across episodes and measure whether it transfers.
Modestly, and mostly by combining nooa-memory with the event-sourced snapshot layer to carry state across runs. ARC is the more interesting target because its puzzles are deliberately designed to resist transfer — a positive result there would be a real finding, and a negative one is informative rather than embarrassing. trace_analyzer.py in nooa-bench gives you the measurement apparatus without building it.
| Project | A Classes |
B... |
C CodeAct |
D Live obj |
E doc() |
F ATIF |
Unique? |
|---|---|---|---|---|---|---|---|
| 01 Portfolio analyst | ● | ● | ● | ● | ◐ | ○ | High |
| 02 MCP aggregator | ◐ | ◐ | ● | ○ | ○ | ○ | Medium |
| 03 Trajectory CI | ● | ○ | ○ | ○ | ○ | ● | Highest |
| 04 Self-extending | ● | ● | ● | ◐ | ● | ◐ | High |
| 05 Composed teams | ● | ● | ◐ | ● | ○ | ● | Med-high |
| 06 Harness ablation | ◐ | ● | ● | ○ | ○ | ● | Medium |
| 07 Cross-episode memory | ◐ | ○ | ◐ | ○ | ○ | ◐ | Low |
● central to the project · ◐ used but incidental · ○ not exercised. "Unique?" answers: how hard would this be on another framework?
A ranking is only as good as the axis it sorts on, and the obvious axis — "showcases the most NOOA features" — is the wrong one. Counting features rewards breadth over depth, and a project that touches six features incidentally is worth less than one that makes two features load-bearing. So the ranking below is built in two steps: rank the features by how much they matter, then rank the projects by how centrally they use the features that matter most.
The critical distinction, and the one most write-ups of this framework miss, is that uniqueness and impact are different axes. Some of NOOA's best ideas are not original to it, and some of its most original ideas are not that consequential. Sorting by "importance" means weighing both.
Most important, because it is load-bearing for everything else. B is syntax on top of it, D is only possible because self is a real object in scope, and the testability that makes project 03 viable falls out of it for free. On its own it looks like an ergonomic preference; structurally it is the axiom the rest of the framework is derived from. Moderately unique — DSPy has module classes, but does not unify state, prompt, and I/O contract into one declaration.
Most unique. Frameworks that support code-as-action mostly still hand the model serialized data; putting the caller's actual in-memory instance in the REPL namespace, mutations and all, is rare. It also has the highest ratio of behaviour change to explanation length — one sentence describes it, and it removes an entire category of stale-copy bugs. Ranked below A only because it depends on A to exist.
The most underrated. Tracing is universal, so the instinct is to discount it — but a versioned Pydantic schema with enforced normative rules is a different artifact from a span exporter aimed at a dashboard. One you can assert against in a test; the other you can only look at. That difference is what makes the highest-ranked project possible at all.
Very high impact, but not unique — and this is where honest scoring bites. The CodeAct pattern is published prior art and ships in other libraries, notably smolagents. NOOA executes it well and wires it to self, but a project whose entire argument is "the model writes Python" is not an argument for this framework. Discounted accordingly, which is what pushes project 02 to the bottom.
Genuinely clarifying — encoding "this part is allowed to be wrong" as a one-token syntactic property is a good idea with real review and audit value. But it is sugar over A, and no project succeeds or fails on it.
doc() progressive disclosureReal engineering, supporting role. It solves context bloat, which matters at scale, but it is a token-efficiency mechanism rather than a capability. Notably it is central to only one project on this list — either a gap in the list, or confirmation that it is infrastructure rather than a headline.
Four criteria, weighted. Feature centrality (30%): does it make the high-ranked features load-bearing rather than incidental? Counterfactual difficulty (30%): how hard would this be on another framework — the real test of "showcase"? Compounding impact (25%): does finishing it make other work easier, or help anyone but you? Low cost and risk (15%): effort, and the odds of ending with nothing.
| # | Project | Centrality ×0.30 | Counterfactual ×0.30 |
Compounding ×0.25 | Low risk ×0.15 | Score |
|---|---|---|---|---|---|---|
| 03 | Trajectory CI | 4 | 5 | 5 | 2 | 4.25 |
| 01 | Portfolio analyst | 5 | 4 | 2 | 5 | 3.95 |
| 04 | Self-extending expert | 5 | 4 | 3 | 2 | 3.75 |
| 05 | Composed teams | 4 | 3 | 4 | 3 | 3.55 |
| 06 | Harness ablation | 3 | 2 | 4 | 2 | 2.80 |
| 07 | Cross-episode memory | 2 | 2 | 3 | 4 | 2.55 |
| 02 | MCP aggregator | 2 | 2 | 2 | 5 | 2.45 |
Scores are 1–5 per criterion. The weights are a judgment call, not a measurement — see "if you disagree with the weights" below.
Wins on the two heaviest criteria at once. Counterfactually it is the hardest thing on this list to build elsewhere, and for a structural reason rather than a convenience one: it needs typed trajectories, always-on nested tracing, and re-instantiable agents simultaneously, and most frameworks supply at most one. It also compounds — every other project becomes measurable instead of anecdotal once it exists, and it is plausibly upstreamable given how much of nooa-bench is already there.
Why it wins despite not touching D or C at all: this is the case for ranking by centrality rather than by feature count. It exercises only two of the six leverage points — but both are load-bearing, one is the framework's axiom and the other its most underrated asset, and the project would be impossible if either were absent. Compare project 02, which touches more surface area and demonstrates almost nothing.
The best pure showcase, and the only project scoring 5 on centrality: A, B, C, and D are all load-bearing, and D — the single most unique feature — is the entire point. It loses first place solely on compounding impact, because it is a personal tool that helps nobody else. Note the tension this exposes: if you rank by "showcases the most unique feature," this is first, not 03. The weights, not the evidence, decide between them.
Ties project 01 on centrality — an agent authoring new generation methods at runtime and fanning them out through asyncio.gather composes B, C, and E in a way tool-calling frameworks cannot express, since there a new capability means a schema registered out-of-band. It ranks third only on risk. This is the project most likely to yield a striking demo and no measurable improvement, and unlike the others that failure mode is invisible unless you instrument tokens-per-task from day one.
Scores well on compounding because the result is the deliverable — it bounds the framework's central claim either way, and a negative result ("composition relocates orchestration complexity rather than removing it") is as useful as a positive one. Held back on counterfactual difficulty by an awkward fact: composing objects is just Python, so the build is easy and the experiment is what carries the value. That makes it the most falsifiable project here and the one whose worth least depends on it succeeding.
The clearest case of the two axes diverging. Research value is high and the result would be genuinely interesting, but ablation is generic methodology — NOOA makes it cheap rather than possible, so counterfactual difficulty scores low. Two of the ablation arms also sit on experimental, FutureWarning-gated strategies. Pick this for the finding, not the demonstration.
Ranks low on every axis except risk, where existing scaffolding in examples/arc_agi_3/ gives it a genuine edge. Cross-episode memory is a well-trodden research direction and touches little of the framework. Reasonable as a warm-up for 06; weak as a destination.
Last, and the ranking earns its keep here by saying so. This is the project most likely to feel productive and prove least — MCP support is universal, the results arrive as data rather than live objects so D is untouched, and the sole distinctive benefit is that CodeAct collapses three round-trips into one cell. Genuinely useful software; not an argument for the framework. Its 5 on low-risk is the only thing keeping it off the floor.
The weights encode a specific goal — "demonstrate what makes NOOA different, and leave something useful behind." Under different goals the order changes, and it is worth seeing how much:
Only project 01 appears in the top three of every ordering, including the weighted one. That is a stronger endorsement than its second-place score suggests, and it is the reason the recommendation below hedges the way it does.
open() gives arbitrary file access, importlib loads modules straight from a path, and reflection reaches the rest.
Do 01 then 03, in that order — and the ranking is what argues for the sequence rather than against it.
01, the portfolio analyst, is the only project in the top three of every ordering above. It is a weekend of work, it makes the framework's most unique feature the entire point, and it answers the question you should settle before investing further: does passing live objects into the model's REPL actually change how you build, or is the object-oriented framing an aesthetic preference? That is cheap to find out and expensive to assume.
03, the trajectory regression harness, is the weighted winner and the real destination. It is the one place where NOOA's design gives you something structurally hard to build elsewhere — typed ATIF trajectories, always-on parent-child tracing, and plain-class agents are each common enough alone and rare in combination. It helps anyone else building on the framework, it is plausibly upstreamable into nooa-bench, and it converts every subsequent experiment from anecdote into measurement.
The sequencing matters more than it looks: build 03 first and you will be writing a regression harness for agents you have not yet learned to write. Build 01 first and you arrive at 03 already knowing what a trace of your own agent looks like when it goes wrong — which is precisely the thing the harness has to detect.