NVIDIA-labs OO Agents: Native Python Object-Oriented Agents Review
0. Introduction
한 줄 요약: NVIDIA Object-Oriented Agents, NOOA는 agent를 별도의 prompt graph나 tool schema 집합이 아니라 하나의 Python object로 표현하고, method, field, docstring, type annotation, live object reference를 그대로 LLM loop의 action, state, prompt, contract로 사용하는 model-agnostic agent framework다.
이 논문을 지금 볼 가치가 있는 이유는 다음과 같음.
- Agent framework가 늘어날수록 prompt, JSON schema, callback, graph, memory adapter가 서로 다른 추상화로 흩어지는 문제가 커지고 있다.
- NOOA는 새로운 agent DSL을 추가하는 대신, LLM이 이미 잘 아는 Python의 class, method, typing, exception, async, object state를 model-facing interface로 재사용한다.
- CodeAct와 pass-by-reference를 결합해 tool output 전체를 매 turn prompt에 다시 직렬화하지 않고, live Python object를 execution environment에 유지한다.
- Type annotation을 단순한 documentation이 아니라 입력, 출력, 종료 조건을 검증하는 executable contract로 사용한다.
- SWE-bench Verified, Terminal-Bench 2.0, CyberGym, ARC-AGI-3를 통해 framework abstraction이 실제 agent 성능, token 사용량, 종료 신뢰성에 어떻게 연결되는지 보여준다.
최근 agent system은 보통 다음 요소를 따로 관리한다.
- System prompt와 role prompt
- Tool name, description, JSON schema
- Tool dispatcher와 callback
- Workflow graph와 state machine
- Context construction과 transcript compaction
- Long-term memory와 retrieval
- Output parser와 validator
- Trace, event, retry, termination logic
각 요소가 필요하다는 점은 분명하다. 문제는 같은 capability가 developer와 model에게 서로 다른 언어로 보인다는 점이다. Developer는 Python function과 object를 작성하지만, model은 다시 문자열 prompt와 JSON tool schema를 읽는다. Runtime value는 JSON으로 직렬화되고, 큰 object는 prompt에 반복 삽입되거나 file path로 우회된다. Agent의 durable state는 framework-specific graph state, session state, memory record로 분산된다.
NOOA가 던지는 질문은 단순하다.
Agent가 이미 Python을 잘 안다면, agent runtime 자체를 Python object model로 보여주는 편이 더 자연스럽지 않은가?
논문은 agent를 class로 정의한다. Method는 model이 실행할 capability이고, field는 명시적인 state이며, docstring은 prompt가 되고, type annotation은 contract가 된다. 일반 method body는 deterministic Python으로 실행된다. 반면 body가 ...인 method는 runtime에서 LLM-driven loop로 완성된다.
이 선택의 의미는 syntax 단순화보다 크다. Prompt engineering, tool engineering, state engineering, loop engineering을 같은 source file 안에서 다룰 수 있기 때문이다. Agent behavior를 일반 software처럼 test, trace, refactor, version, profile할 수 있고, model도 동일한 object surface를 직접 읽고 조작한다.
1. Problem Setting
1-1. Problem definition
이 논문이 겨냥하는 문제는 agent framework의 abstraction fragmentation이다.
Agent application의 실제 실행 흐름은 대략 다음과 같다.
- Application이 task input을 전달한다.
- Harness가 prompt와 model-visible context를 구성한다.
- Model이 action을 선택한다.
- Runtime이 tool이나 code를 실행한다.
- Observation과 state가 갱신된다.
- Model이 반복 여부 또는 종료를 결정한다.
- Output이 validation을 통과하면 caller에게 반환된다.
기존 framework에서는 이 흐름이 여러 layer로 나뉘는 경우가 많다.
| Concern | 흔한 표현 방식 | 생기는 문제 |
|---|---|---|
| Capability | Tool schema, decorator, registry | 실제 Python function과 model schema가 중복될 수 있다 |
| Prompt | Template, config, callback | Source code와 behavior instruction이 떨어진다 |
| State | Graph state, session store, hidden runtime | Model이 현재 state를 직접 이해하기 어렵다 |
| Control flow | Workflow graph, router, callback | Dynamic loop와 exception recovery를 별도 DSL로 배워야 한다 |
| Large value | JSON, transcript, file path | 직렬화 비용과 context duplication이 커진다 |
| Termination | Final text, stop flag, no-tool response | 완료 선언과 실제 검증이 분리된다 |
| Memory | 외부 vector store와 adapter | Agent의 object state와 memory semantics가 분리된다 |
이 구조는 developer productivity만의 문제가 아니다. Model-facing interface에도 직접 영향을 준다.
예를 들어 tool result가 큰 table object라고 하자. JSON tool calling에서는 다음 turn에 필요한 row만 보더라도 전체 result가 transcript에 들어갈 수 있다. 이를 file로 바꾸면 token은 줄지만, model은 path 기반으로 다시 읽어야 한다. Host process가 이미 가지고 있는 object를 model-written code가 직접 참조할 수 있다면 불필요한 serialization을 줄일 수 있다.
또 다른 예는 종료다. Model이 작업을 완료했다고 말하는 것과, 실제 test command를 실행하고 evidence를 포함한 typed result를 반환하는 것은 다르다. 자연어 convention만으로 종료를 관리하면 premature termination이 생기기 쉽다.
따라서 NOOA의 problem setting은 다음 세 질문으로 정리할 수 있다.
- Agent capability와 state를 ordinary Python abstraction으로 표현할 수 있는가.
- Developer와 model이 같은 object interface를 공유할 수 있는가.
- 이 interface가 단순한 API 미학을 넘어 reliability와 efficiency를 개선하는가.
1-2. Why previous approaches are insufficient
1) Agent-specific DSL의 학습 비용
Workflow graph와 tool schema는 agent behavior를 명시적으로 만드는 데 유용하다. 하지만 framework마다 node, edge, state, handoff, middleware, checkpoint 개념이 다르다. Developer는 일반 programming knowledge 외에 framework-specific execution model을 학습해야 한다.
Model도 비슷한 부담을 가진다. LLM은 Python, shell, standard library, popular package에 대한 풍부한 training exposure를 가지고 있다. 반면 특정 framework의 최신 DSL과 hidden callback semantics는 상대적으로 낯설다.
NOOA는 이 차이를 줄이기 위해 가능한 한 Python 자체를 interface로 사용한다.
2) Tool call이 너무 작은 action unit이 되는 문제
JSON tool call은 한 action을 안전하게 제한하는 데 좋다. 하지만 반복, 조건문, 병렬 실행, intermediate variable, exception handling이 필요한 workflow에서는 model이 여러 tool call을 왕복해야 한다.
예를 들어 100개 record를 검사하고 실패한 item만 수정하려면 다음과 같은 control flow가 필요하다.
- 각 item을 순회한다.
- validator를 호출한다.
- error type별로 repair method를 바꾼다.
- 결과를 object state에 누적한다.
- 일부 task는 async로 병렬화한다.
Code-as-action에서는 이 구조를 model이 Python code로 직접 작성할 수 있다. Harness가 모든 control flow를 tool schema로 미리 정의할 필요가 없다.
3) Transcript 중심 state의 한계
많은 agent는 model이 지금까지 본 대화를 사실상 state로 사용한다. 그러나 transcript는 state database가 아니다.
- 같은 object가 여러 번 문자열로 복사된다.
- Current value와 old observation이 섞인다.
- 특정 state를 수정하려면 새로운 message를 추가해야 한다.
- Context compaction이 일어나면 reference continuity가 약해질 수 있다.
NOOA는 explicit object field와 persistent Python namespace를 사용한다. Model은 object state를 읽거나 수정하고, tool result를 variable로 유지할 수 있다.
4) 직렬화된 pass-by-value
기존 tool interface에서 argument와 result는 대개 JSON-compatible value로 변환된다. 작은 scalar에는 문제가 없지만, dataframe, image, database handle, repository object, simulator state처럼 큰 object에는 비효율적이다.
NOOA의 pass-by-reference는 model prompt에 bounded preview만 보여주고, 실제 object는 execution namespace에 live reference로 둔다. Model은 필요할 때 method와 indexing을 사용해 일부만 검사한다.
다만 이 장점은 중요한 deployment trade-off를 만든다. In-process execution은 live reference를 유지하지만, remote sandbox boundary를 넘으면 object가 serialized copy로 바뀔 수 있다. 따라서 performance와 isolation을 동시에 설계해야 한다.
5) 자연어 종료 조건
일반적인 agent loop는 model이 final answer를 내거나 tool call을 멈추면 종료된다. 이 방식은 simple QA에는 충분하지만 software task에서는 위험하다.
- Test를 돌리지 않고 완료했다고 선언할 수 있다.
- Output file이 없어도 final text를 낼 수 있다.
- Evidence 없이 success를 주장할 수 있다.
- Return schema를 만족하지 않아도 loop가 끝날 수 있다.
NOOA는 return type을 validation contract로 사용한다. Invalid return은 exception이나 validation feedback으로 loop에 다시 들어가고, valid typed object가 만들어져야 caller에게 반환된다.
2. Core Idea
2-1. Main contribution
NOOA의 핵심 기여는 세 층으로 볼 수 있다.
1) Agent-as-a-Python-object programming model
Agent 전체를 하나의 Python class로 표현한다.
from dataclasses import dataclass, field
@dataclass
class ResearchAgent:
notes: list[str] = field(default_factory=list)
def normalize_query(self, query: str) -> str:
return " ".join(query.strip().split())
async def investigate(self, query: str) -> "ResearchReport":
"""Investigate the query, verify evidence, and return a report."""
...
이 예시에서 역할은 다음처럼 나뉜다.
ResearchAgentclass: agent의 state boundarynotes: durable object statenormalize_query: deterministic capabilityinvestigate: agentic method- Docstring: method-level instruction
- Input and return annotation: typed contract
...: LLM-driven loop가 들어갈 위치
Developer가 agent를 호출할 때는 일반 method call처럼 사용한다.
report = await agent.investigate(query)
Application 입장에서는 unstructured chat loop가 아니라 typed async method call이다.
2) 여섯 가지 model-facing capability의 결합
논문은 NOOA가 하나의 surface에서 결합하는 capability를 여섯 가지로 정리한다.
| Capability | 역할 |
|---|---|
| Typed input and output | Method signature와 return type으로 call contract를 만든다 |
| Pass-by-reference | Live Python object를 serialization 없이 execution namespace에 둔다 |
| Code as action | Model이 ordinary Python code로 control flow와 tool use를 작성한다 |
| Programmable loop engineering | Context, event, retry, validation, strategy를 method별로 조정한다 |
| Explicit object state | Agent state를 field와 object mutation으로 표현한다 |
| Model-callable harness APIs | Model이 context와 event history를 Python API로 직접 다룬다 |
개별 capability는 다른 framework에도 부분적으로 존재한다. 논문의 주장은 이 여섯 요소를 하나의 coherent object model 안에 묶는 데 있다.
3) Interface 자체에 대한 empirical evaluation
Framework paper는 API 예시만 보여주고 끝나는 경우가 많다. NOOA는 model이 이 interface를 실제로 이해하는지 capability test를 만들고, 동일한 benchmark-agnostic agent를 여러 benchmark에 적용한다.
- 88개 capability test
- 각 test를 5회 반복
- 10개 model
- 총 4,400 record
- SWE-bench Verified 500 task
- Terminal-Bench 2.0 89 task
- CyberGym L1
- ARC-AGI-3 25-game fleet
이 평가의 목적은 Python object가 예쁘다가 아니라 다음을 확인하는 것이다.
- Model이 typed method를 올바르게 호출하는가.
- Large object의 bounded preview와 live reference를 활용하는가.
- Error를 보고 code를 수정하는가.
- Explicit state와 memory를 유지하는가.
- Validated return을 통해 신뢰성 있게 종료하는가.
2-2. Design intuition
NOOA의 설계 직관은 다섯 가지 principle로 정리된다.
P1. Python에 이미 있는 abstraction을 다시 만들지 않는다
Class, method, field, typing, exception, async, import, loop, condition을 agent DSL로 재발명하지 않는다. Python의 mature abstraction을 그대로 사용한다.
P2. Agentic loop를 typed method call로 본다
Application은 agent loop의 transcript를 직접 관리하지 않는다. Method input을 넘기고 validated output을 받는다. Loop는 method 내부 implementation detail이 된다.
P3. Deterministic work는 LLM loop 밖으로 뺀다
Parsing, arithmetic, exact rule, state transition, file validation처럼 deterministic하게 처리할 수 있는 작업은 ordinary method에 둔다. Semantic judgment, planning, synthesis처럼 open-ended한 부분만 ... method로 둔다.
이 경계가 source code에서 바로 보인다는 점이 중요하다.
P4. Model이 이미 가진 Python prior를 사용한다
Model은 새로운 tool DSL을 배우는 대신 Python code를 작성한다. Loop, condition, helper function, library import, async orchestration을 이미 아는 방식으로 표현한다.
P5. Harness를 hidden runtime으로 두지 않는다
Context, event, state rendering, memory를 model-callable API로 노출한다. Agent가 자기 context를 관찰하고 관리할 수 있게 만든다.
이 중 가장 중요한 것은 P2와 P3의 조합이다. Agent를 method call로 감싸는 것만으로는 충분하지 않다. Exact operation을 deterministic code로 밀어내고, LLM에는 semantic uncertainty가 남은 구간만 맡겨야 typed boundary가 실제 reliability로 이어진다.
3. Architecture / Method
3-1. Overview
| Item | Description |
|---|---|
| Goal | Agent runtime을 ordinary Python object model로 표현한다 |
| Agent unit | Python class instance |
| Capability | Method |
| Durable state | Object field |
| Prompt surface | Class and method docstring, rendered context |
| Contract | Type annotation and runtime validation |
| Agentic marker | Method body ... |
| Action modality | Predict 또는 CodeAct strategy |
| Execution state | Persistent Python namespace and live object references |
| Context | Static, append-only event history, dynamic tail |
| Long-term memory | Typed record, retrieval, graph relation, reflection |
| Termination | Return type validation을 통과한 object |
| Isolation | Agent process 바깥의 sandbox, container, VM 권장 |
3-2. Module breakdown
1) Agent class와 method dispatch
NOOA에서 ordinary method와 agentic method는 같은 class 안에 존재한다.
class RepoAgent:
repository: "Repository"
completed_tasks: list[str]
def run_tests(self, command: str) -> "TestResult":
return self.repository.shell(command)
def solve_issue(self, issue: "Issue") -> "PatchResult":
"""Inspect the issue, modify the repository, and verify the patch."""
...
run_tests는 deterministic Python이다. solve_issue는 LLM loop다. Model은 agentic method 안에서 self.run_tests(...), repository object method, imported library를 사용할 수 있다.
이 구조는 tool registration을 class definition에 흡수한다. Public method가 capability surface가 되고, object field가 state와 dependency를 가진다.
2) Method별 strategy
Agentic method는 하나의 고정 loop만 사용하지 않는다. Method마다 strategy를 선택할 수 있다.
- Predict: 한 번의 model call로 typed output을 예측하는 single-shot method
- CodeAct: Model이 Python code를 작성하고, 실행 결과를 보고 반복하는 iterative method
Simple classification이나 extraction에는 Predict가 적합하다. Repository 수정, shell interaction, simulator 탐색처럼 multi-step action이 필요한 작업에는 CodeAct가 적합하다.
중요한 점은 strategy 선택이 agent 전체가 아니라 method boundary에 있다는 것이다. 하나의 class 안에서 cheap single-shot method와 long-horizon CodeAct method를 함께 둘 수 있다.
3) CodeAct loop
CodeAct loop를 단순화하면 다음과 같다.
- Method argument, object state, docstring, event history로 context를 만든다.
- LLM이 Python code cell을 생성한다.
- Runtime이 code를 persistent namespace에서 실행한다.
- Stdout, exception, expression value, state mutation을 event로 기록한다.
- Model이 다음 code를 생성하거나 return value를 만든다.
- Return type validator가 결과를 검사한다.
- Validation에 실패하면 error를 event로 추가하고 loop를 계속한다.
- Validation에 성공하면 caller에게 typed value를 반환한다.
Pseudo code로 표현하면 다음과 같다.
while True:
context = render_context(method, args, state, events)
code = llm.generate(context)
result = execute_python(code, namespace)
events.append(result)
if result.has_return_value:
validated = validate(result.value, return_type)
if validated.ok:
return validated.value
events.append(validated.error)
이 loop에서 중요한 것은 observation이 text message만이 아니라 execution event라는 점이다. Model-written code, stdout, exception, return validation, object mutation이 structured history로 남는다.
4) Context의 세 구역
논문은 context를 세 영역으로 나눈다.
| Region | 내용 | 갱신 방식 |
|---|---|---|
| Static context | Method docstring, signatures, stable instructions | 거의 고정 |
| Event history | Model code, execution result, exception, validation event | Append-only |
| Dynamic context | Current object state, latest memory, budget, task status | 매 turn 재렌더링 |
이 layout은 prefix cache reuse를 고려한다. 변하지 않는 static context와 append-only history를 앞에 두고, 자주 바뀌는 dynamic context를 뒤에 둔다. 매 turn 전체 prompt prefix가 흔들리지 않게 하려는 설계다.
Context management를 별도 summarizer에만 맡기지 않고, 어떤 region이 stable하고 어떤 region이 dynamic한지를 programming model에 반영한다.
5) Pass-by-reference와 bounded preview
Method argument가 큰 object일 때 NOOA는 전체 내용을 prompt에 직렬화하지 않는다. Model에는 다음과 같은 bounded preview를 보여줄 수 있다.
- Python type
- Object name or handle
- True length or shape
- Head and tail sample
- 사용 가능한 method
실제 object는 Python namespace에 그대로 존재한다.
# Model sees a bounded preview in context.
# The full dataframe remains available as a live object.
subset = records[records["status"] == "failed"]
summary = subset.groupby("error_type").size()
이 방식의 장점은 세 가지다.
- Large value를 매 turn prompt에 반복하지 않는다.
- Model이 필요한 부분만 programmatically inspect할 수 있다.
- Object identity와 mutation이 여러 step에 걸쳐 유지된다.
하지만 live host object를 model-written code에 노출한다는 것은 security boundary가 더 중요해진다는 뜻이기도 하다.
6) Python execution environment
NOOA는 Jupyter-like persistent execution을 사용한다.
- Variable이 step 사이에 유지된다.
- Imported module을 재사용할 수 있다.
- Helper function을 정의할 수 있다.
- Loop와 conditional을 쓸 수 있다.
- Async call과 subagent dispatch를 code 안에서 조합할 수 있다.
- Object와 external system의 side effect가 유지된다.
Method-local variable은 method scope에 묶이고, object field와 external resource mutation은 지속된다. 이 distinction은 ordinary Python의 local state와 object state를 agent loop에 그대로 가져온 것이다.
7) Typed return validation
Agentic method의 return annotation은 종료 protocol이다.
예를 들어 다음 output을 생각해볼 수 있다.
@dataclass
class PatchResult:
patch_path: str
verification_command: str
verification_output: str
evidence: list[str]
Model이 단순히 완료했다고 쓰는 것으로는 method가 끝나지 않는다. PatchResult를 만들고, required field와 type validation을 통과해야 한다.
Validation error는 loop 내부 feedback이 된다.
- Missing field
- Wrong type
- Empty evidence
- Invalid path
- Failed custom validator
이 구조는 termination을 prompt convention에서 executable contract로 바꾼다.
8) Event와 state API
Agent는 자기 event history와 context를 Python API로 다룰 수 있다. 이를 통해 model이 다음 작업을 할 수 있다.
- 특정 error event를 검색한다.
- 오래된 observation을 정리한다.
- Current state를 다시 render한다.
- 중요한 event를 memory로 저장한다.
- Subagent result를 object state에 합친다.
Harness가 hidden orchestration layer가 아니라 agent가 사용할 수 있는 capability가 된다.
9) Long-term memory
NOOA의 memory는 단순한 vector search 결과 문자열이 아니다. Agent가 직접 memory record를 작성하고 관리하는 typed subsystem이다.
논문이 설명하는 memory design에는 다음 요소가 포함된다.
- 하나의 SQLite 기반 persistence
- Embedding retrieval과 keyword retrieval
- Relevance, recency, importance를 결합한 ranking
- Memory type별 record
- Memory 간 graph relation
- Reflection pipeline
- Recall된 record에서 live reference 복원
- Access event와 trace linkage
Memory type은 application에 따라 info, skill, episode, todo, reflection처럼 나눌 수 있다. Agent는 무엇을 저장할지, importance를 어떻게 줄지, 언제 deliberate recall을 할지 결정한다.
핵심은 memory가 transcript archive가 아니라 agent-curated object state라는 점이다. Memory가 읽힐 때도 단순 text chunk만 주는 것이 아니라, 연결된 artifact나 live object를 다시 참조할 수 있게 설계한다.
10) Deployment isolation
NOOA의 pass-by-reference는 in-process execution에서 가장 강하다. Model-written code가 agent process 안에서 실행되면 live object를 그대로 사용할 수 있다.
하지만 이 구조에서 validator는 return contract를 보호할 뿐 host security를 보장하지 않는다. Model-generated code는 file system, network, process, credential, imported library에 접근할 수 있다. 따라서 실제 deployment에서는 agent process 전체를 다음 boundary 안에 둬야 한다.
- Container
- VM
- Kernel-enforced sandbox
- Restricted user and filesystem permission
- Network policy
- CPU, memory, wall-clock limit
- Secret isolation
- Audit logging
논문도 preferred deployment로 agent process 바깥의 sandboxing을 강조한다. In-process cell guard 하나만으로 production isolation을 대신해서는 안 된다.
4. Training / Data / Recipe
4-1. Data
이 논문은 foundation model을 새로 학습하는 paper가 아니다. 핵심 artifact는 framework와 evaluation suite다. 따라서 여기서 data는 agent interface를 검증하기 위한 task와 benchmark를 의미한다.
Capability suite
- 88개 test
- 10개 model
- Test당 5회 run
- Model당 440 record
- 전체 4,400 record
Test는 single tool call보다 agentic behavior에 가까운 capability를 포함한다.
- Typed method call
- Object method discovery
- Mutable state update
- Live object reference 사용
- REPL iteration
- Exception recovery
- Batch bookkeeping
- Intermediate answer refinement
- Helper function decomposition
- Validated return
End-to-end benchmark
| Benchmark | Scope | Evaluation unit |
|---|---|---|
| SWE-bench Verified | Real repository issue 해결 | 500 task |
| Terminal-Bench 2.0 | Multi-step terminal interaction | 89 task |
| CyberGym L1 | Vulnerability discovery and PoC validation | Security task |
| ARC-AGI-3 | Unknown interactive grid environment 탐색 | 25 public games per fleet |
SWE-bench와 Terminal-Bench에는 같은 253-line benchmark-agnostic agent를 사용했다고 보고한다. Benchmark마다 거대한 custom harness를 새로 작성하지 않고, deterministic adapter와 typed output contract를 바꾸는 방향이다.
4-2. Training strategy
NOOA 자체에는 model training이 없다. 대신 중요한 것은 agent loop recipe다.
1) Capability를 method로 분리한다
먼저 task를 semantic uncertainty와 deterministic operation으로 나눈다.
- LLM이 판단할 부분은 agentic method로 둔다.
- Parsing, validation, scoring, state transition은 ordinary method로 둔다.
2) Method마다 strategy를 선택한다
- Single-shot extraction: Predict
- Multi-step repository work: CodeAct
- Expensive task: Smaller helper method + larger coordinator method
- Parallelizable task: Async method와 subagent
3) Input과 output type을 먼저 설계한다
Prompt를 길게 쓰기 전에 caller가 무엇을 넘기고, 어떤 evidence를 포함한 output을 받아야 하는지 type으로 정의한다.
4) Object state를 최소한으로 노출한다
Model이 실제로 알아야 할 durable state만 field로 둔다. Secret, credential, privileged handle은 별도 restricted object나 process boundary로 분리한다.
5) Large object에는 bounded preview를 쓴다
전체 serialization 대신 type, shape, sample, available method를 보여주고, 상세 inspection은 code로 수행하게 한다.
6) Return validator를 종료 조건으로 사용한다
Final answer text를 믿지 않는다. Evidence, verification command, artifact path, status를 typed result에 포함시킨다.
7) Agent process를 격리한다
Pass-by-reference 때문에 in-process execution을 쓰더라도, 그 process 자체를 sandbox 안에 둔다.
4-3. Engineering notes
1) Class가 너무 커지지 않게 한다
모든 capability를 하나의 giant agent class에 넣으면 method discovery와 state rendering이 오히려 복잡해질 수 있다. Domain별 component object와 helper class로 나누는 것이 좋다.
2) Public method surface를 관리한다
Model이 호출 가능한 method와 internal helper를 구분해야 한다. Python에 존재한다고 모두 model-visible tool이 되어서는 안 된다.
3) Preview와 live object의 불일치를 막는다
Context에 표시한 bounded preview가 오래된 snapshot이면 model이 현재 object state를 오해할 수 있다. Dynamic context tail에서 shape, version, updated_at 같은 metadata를 갱신하는 편이 안전하다.
4) Side effect를 명시한다
Method docstring과 type만으로는 destructive operation의 위험이 충분히 드러나지 않을 수 있다. File deletion, database write, deployment처럼 irreversible action은 approval gate와 deterministic policy를 추가해야 한다.
5) Exception을 observation으로 설계한다
Exception text가 너무 길거나 secret을 포함하면 context와 보안에 문제가 생긴다. Structured error type, redacted message, retry hint를 주는 편이 낫다.
6) Trace와 artifact를 함께 남긴다
Code event만 저장하면 실제 side effect를 재현하기 어렵다. Command, stdout, exit code, changed files, state diff, return object, validator result를 같이 기록해야 한다.
7) Prefix-cache friendly context를 유지한다
Stable instruction과 append-only event를 앞에 두고, 자주 바뀌는 state를 뒤에 두는 layout은 serving cost에 영향을 줄 수 있다. Dynamic block가 prompt 앞쪽에 들어가면 cache reuse가 깨질 수 있다.
5. Evaluation
5-1. Capability tests
10개 model에서 전체 4,400 record 중 4,309개가 통과해 97.9% pass rate를 기록한다.
| Group | Passed | Pass rate |
|---|---|---|
| Small or efficient models | 1,689 / 1,760 | 96.0% |
| Large or frontier models | 2,620 / 2,640 | 99.2% |
| Overall | 4,309 / 4,400 | 97.9% |
전체 수치만 보면 interface가 거의 해결된 것처럼 보인다. 하지만 stress subset을 보면 차이가 커진다.
- Stress subset 전체: 254 / 300, 84.7%
- Large or frontier: 169 / 180, 93.9%
- Small or efficient: 85 / 120, 70.8%
Stress test는 다음과 같은 behavior를 포함한다.
- Large batch에서 item별 bookkeeping 유지
- Exception 이후 recovery
- REPL에서 여러 step refinement
- Intermediate answer 수정
- Repeated transformation을 helper로 분해
즉 method call syntax 자체는 작은 model도 잘 이해한다. 남은 병목은 long-horizon state tracking과 recovery다. Framework interface가 간단하더라도 model capability 차이는 사라지지 않는다.
5-2. SWE-bench Verified
SWE-bench Verified 결과는 다음과 같다.
| Harness | GPT-5.5 off | GPT-5.5 high | GPT-5.5 xhigh | Opus 4.6 off | Opus 4.6 high |
|---|---|---|---|---|---|
| NOOA | 67.2 | 78.8 | 82.2 | 76.8 | 79.8 |
| OpenCode | 59.2 | 75.0 | 78.6 | 76.0 | 75.2 |
| PI | 60.8 | 73.6 | 78.2 | 75.6 | 75.8 |
이 표에서 중요한 것은 최고 점수만이 아니다.
GPT-5.5 xhigh 기준으로 논문은 다음 interaction cost를 보고한다.
- NOOA: 약 28 call, 1.1M token, 82.2%
- OpenCode: 비슷한 call 수, 약 1.3M token, 78.6%
- PI: 66 call, 약 2.2M token, 78.2%
NOOA의 설명은 live object와 bounded preview가 transcript duplication을 줄이고, validated termination이 premature stop을 줄였다는 것이다.
다만 이 결과를 순수한 framework primitive ablation으로 보면 안 된다. 각 harness의 prompt, tool design, context policy, termination policy, implementation maturity가 함께 다르다. 결과는 object orientation 하나가 4 point를 만든다는 인과 증명보다, coherent harness design 전체의 성능으로 읽는 편이 안전하다.
5-3. Terminal-Bench 2.0
| Harness | GPT-5.5 off | GPT-5.5 high | GPT-5.5 xhigh | Opus 4.6 off | Opus 4.6 high |
|---|---|---|---|---|---|
| NOOA | 46.1 | 73.0 | 73.0 | 64.0 | 65.2 |
| OpenCode | 34.8 | 60.7 | 52.8 | 49.4 | 43.8 |
| PI | 37.1 | 68.5 | 75.3 | 65.2 | 58.4 |
NOOA가 모든 setting에서 최고인 것은 아니다. GPT-5.5 xhigh에서는 PI가 75.3으로 NOOA의 73.0보다 높다. Opus off에서도 PI가 65.2로 NOOA 64.0보다 높다.
그러나 low reasoning setting에서는 NOOA의 차이가 크다. 논문은 interface가 model 자체의 planning과 verification discipline이 약할 때 더 큰 보정 효과를 줄 수 있다고 해석한다.
Validated termination 분석
Terminal-Bench 실패 trace에서 OpenCode는 model이 tool call 없이 응답하면 종료한다. 논문은 OpenCode의 실패한 GPT-5.5 trial 중 77%가 10 step 안에 종료된다고 보고한다.
NOOA는 model이 evidence와 verification command를 포함한 typed return을 만들어야 종료한다. 이 차이는 stop policy가 단순 runtime detail이 아니라 task success에 직접 연결된다는 점을 보여준다.
5-4. CyberGym L1
CyberGym은 codebase에서 security bug를 찾고, 이를 재현하는 proof-of-concept를 작성하는 benchmark다.
NOOA agent는 다음 구조를 사용한다.
- CodeAct agent
- Shell과 todo manager
- Submission interface를 deterministic method로 분리
- Summary와 vulnerability description의 정합성을 검사하는 lightweight judge
- Non-deterministic crash를 걸러내는 repeated submission
보고된 solve rate는 다음과 같다.
| System | Network | Solve rate | Open source |
|---|---|---|---|
| Microsoft MDASHv2 | Unknown | 95.6 | No |
| Crystalline | Blocked | 89.6 | No |
| NOOA with GPT-5.5 | Blocked | 86.8 | Yes |
| OpenAI Daybreak | Unknown | 85.6 | No |
| Codex plus skill | Open | 83.5 | Yes |
| Codex | Blocked | 64.9 | Yes |
NOOA는 표에 포함된 open-source system 중 가장 높은 결과를 보고한다. 하지만 network condition과 model, submission logic이 완전히 같지 않으므로 leaderboard row 간 직접 비교에는 주의가 필요하다.
5-5. ARC-AGI-3
ARC-AGI-3는 unknown grid game 안에서 action을 수행하며 rule, objective, control을 발견하는 interactive reasoning benchmark다.
기존 DreamTeam system은 다음 규모였다.
- 6개 specialized agent
- 1,821 line의 role prompt
- 4,690 line의 harness-side retrodiction engine
NOOA version은 이를 다음으로 줄인다.
- 1개 agent
- 50-line world-model skill
- CodeAct REPL을 simulator로 사용
- Context block을 shared state로 사용
- Memory를 carry-forward ledger로 사용
25개 game, 2-hour cap에서 보고된 fleet result는 다음과 같다.
| Setting | Model | RHAE |
|---|---|---|
| World model plus memory | GPT-5.5 | 50.2 |
| Hypothesis baseline plus memory | GPT-5.5 | 41.7 |
| World model plus markdown memory | GPT-5.5 | 38.4 |
| World model plus memory | GPT-5.6-sol | 85.1 |
GPT-5.5에서 memory subsystem은 같은 world-model skill의 markdown-file ablation보다 11.8 point 높다. 이는 memory interface가 단순 편의 기능이 아니라 interactive task에서 measurable effect를 가질 수 있음을 보여준다.
다만 GPT-5.6-sol raw baseline과 harness result는 evaluation budget이 다르다고 논문도 명시한다. 85.1 대 13.3을 순수한 harness effect로 단정하기보다 indicative comparison으로 봐야 한다.
5-6. 무엇을 진짜 봐야 하는가
1) Interface fluency와 agent reliability는 다르다
97.9% capability pass rate는 model이 API syntax를 이해한다는 강한 신호다. 하지만 stress subset이 84.7%라는 점은 long-horizon reliability가 별도 문제임을 보여준다.
2) Termination은 architecture다
Agent가 언제 멈추는지는 작은 heuristic이 아니다. Typed evidence와 verification을 요구하면 success claim의 기준이 바뀐다.
3) Token efficiency는 pass-by-reference에서 온다
Tool output을 transcript에 반복해서 넣지 않고 live object로 유지하면 context cost를 줄일 수 있다. 특히 large table, repository state, simulator object에서 의미가 크다.
4) Framework benchmark는 controlled ablation이 어렵다
NOOA, OpenCode, PI는 loop, prompt, tool, compaction, stop rule이 모두 다르다. Benchmark 점수는 end-to-end harness quality를 보여주지만, 개별 design principle의 causal contribution을 분리하지는 못한다.
5) Simple source code가 simple runtime을 뜻하지는 않는다
Developer-facing class는 간단하지만, 뒤에는 context renderer, persistent execution, event store, validator, memory, tracing, sandbox가 필요하다. Abstraction이 complexity를 제거한다기보다 적절한 곳에 숨기고 일관된 surface로 제공한다.
6. Limitations
- In-process code execution의 security risk가 크다.
- Model-written Python이 live host object를 직접 다룬다.
- Return validator는 output contract를 검증할 뿐 file, network, process access를 막지 않는다.
- Production에서는 agent process 전체를 container, VM, kernel sandbox, network policy 안에 둬야 한다.
- Pass-by-reference와 strong isolation 사이에 trade-off가 있다.
- In-process execution은 true live reference를 유지한다.
- Remote sandbox boundary를 넘으면 object를 serialize해야 해 핵심 장점 일부가 줄어든다.
- Shared-memory proxy나 capability-safe object handle이 후속 engineering 과제가 된다.
- End-to-end benchmark는 component-level ablation이 아니다.
- NOOA와 비교 harness는 prompt, context policy, stop rule, code execution 방식이 다르다.
- 어떤 primitive가 몇 point를 기여했는지 분리하기 어렵다.
- Capability suite가 NOOA interface에 유리하게 설계되었을 가능성이 있다.
- Suite는 framework가 중요하다고 정의한 capability를 측정한다.
- 다른 framework의 장점인 hosted tool ecosystem, managed safety, visual workflow, enterprise integration은 같은 비중으로 평가되지 않는다.
- Framework comparison rubric은 저자 평가다.
- 14개 framework를 여섯 capability axis로 비교하지만, version snapshot과 documentation interpretation에 의존한다.
- 빠르게 변하는 agent SDK에서는 발행 이후 점수가 달라질 수 있다.
- Small model의 stress reliability가 충분하지 않다.
- 전체 pass rate는 높지만 stress subset에서 small or efficient model은 70.8%다.
- Batch bookkeeping, recovery, refinement처럼 실제 long-horizon task에 가까운 조건에서 gap이 커진다.
- Large object를 live reference로 둔다고 reasoning 문제가 사라지지는 않는다.
- Model이 어떤 row와 field를 검사해야 하는지 여전히 판단해야 한다.
- Bounded preview가 잘못 설계되면 중요한 signal을 놓치거나 불필요한 scan을 반복할 수 있다.
- Long-term memory result의 범위가 제한적이다.
- ARC-AGI-3의 25 public game에서 controlled ablation을 제공한다.
- 다른 domain, longer horizon, multi-user memory, privacy constraint에서도 같은 효과가 나는지는 추가 검증이 필요하다.
- Model과 가격 정보가 빠르게 변한다.
- 논문은 2026년 7월 시점의 model version, reasoning mode, pricing을 사용한다.
- 재현 시 API behavior, token accounting, benchmark policy를 다시 확인해야 한다.
- Python 중심 설계는 모든 runtime에 맞지 않을 수 있다.
- Browser sandbox, mobile runtime, strict serverless, polyglot system에서는 Python live object가 자연스러운 integration unit이 아닐 수 있다.
- Cross-process와 cross-language boundary에서는 typed RPC와 serialization layer가 다시 필요하다.
7. My Take
7-1. Why this matters for my work
이 논문의 가장 실용적인 메시지는 agent를 prompt plus tools가 아니라 typed stateful software component로 보라는 것이다.
Document AI나 RAG agent를 예로 들면 model이 다루는 object는 문자열만이 아니다.
- OCR page object
- Bounding box와 token inventory
- Retrieved document handle
- Table dataframe
- Schema와 extraction result
- Evidence span
- Validation report
- Service client
이 값을 매 turn JSON으로 직렬화하면 context가 커지고 identity가 끊긴다. Live object와 bounded preview를 사용하면 agent는 필요한 field만 code로 조회할 수 있다.
또한 extraction agent의 종료 조건을 typed contract로 만들 수 있다.
@dataclass
class GroundedAnswer:
answer: str
evidence_ids: list[int]
quoted_spans: list[str]
verification_status: str
이 구조에서는 answer text만 맞는 것으로 끝나지 않는다. Evidence ID, literal span, verification status가 있어야 반환된다. Evidence-grounded RAG, document extraction, benchmark evaluator에 잘 맞는 pattern이다.
Agent framework 선택 기준도 달라진다
Framework를 고를 때 connector 수와 graph UI만 볼 것이 아니라 다음을 확인해야 한다.
- Model이 state를 어떤 abstraction으로 보는가.
- Large result가 transcript에 어떻게 들어가는가.
- Termination이 어떻게 검증되는가.
- Deterministic logic과 model judgment의 경계가 어디인가.
- Sandbox가 object reference와 어떻게 결합되는가.
- Trace가 replay 가능한가.
NOOA는 이 질문을 선명하게 만든다는 점에서 의미가 있다.
7-2. Reuse potential
1) Typed agent method
Service layer에서 agent를 chat endpoint로만 노출하지 않고 typed async method로 감싼다.
result = await grounding_agent.align(document, key_values)
Caller는 transcript가 아니라 schema-validated result를 받는다.
2) Evidence-carrying termination
Final return에 answer뿐 아니라 evidence, check command, artifact path, confidence, refusal reason을 포함한다. Completion claim을 executable condition으로 바꾼다.
3) Object-backed context
Large OCR result나 retrieval corpus를 prompt에 전부 넣지 않고 object reference로 유지한다. Context에는 inventory, shape, head, tail, searchable field만 보여준다.
4) Deterministic verifier 분리
JSON parsing, ID validity, bbox range, source existence, citation overlap은 deterministic method로 둔다. LLM은 ambiguous alignment와 semantic judgment에 집중한다.
5) Method-scoped model routing
간단한 classification method는 small model, long-horizon investigation method는 frontier model을 사용할 수 있다. Agent class는 하나지만 method별 cost profile을 다르게 설계한다.
6) Context region 분리
Static instruction, append-only trace, dynamic state를 분리해 prefix cache와 observability를 개선한다.
7) Memory를 typed record로 관리
Memory를 free-form summary file로만 두지 않고 skill, episode, failure, todo, policy 같은 type으로 구분한다. Retrieval 결과에 source object reference와 access trace를 붙인다.
8) Sandbox-first deployment
Agent code가 live object를 다루기 전에 privilege boundary를 먼저 정한다.
- Read-only mount
- Temporary writable workspace
- Egress deny by default
- Secret broker
- Resource limit
- Per-task identity
- Full event audit
Pass-by-reference는 convenience feature가 아니라 security architecture와 함께 설계해야 한다.
7-3. Follow-up papers
- CodeAct: Executable Code Actions Elicit Better LLM Agents
- OpenHands: An Open Platform for AI Software Developers as Generalist Agents
- MemGPT: Towards LLMs as Operating Systems
- Letta: Stateful Agents with Memory
- Recursive Language Models
- GEPA: Reflective Prompt Evolution Can Outperform Reinforcement Learning
- DSPy: Compiling Declarative Language Model Calls into Self-Improving Pipelines
- The AI Scientist-v2 and long-horizon research agent systems
8. Summary
- NOOA는 agent를 Python class로 표현하고 method, field, docstring, type annotation을 action, state, prompt, contract로 재사용한다.
- Body가
...인 method는 LLM-driven Predict 또는 CodeAct loop로 실행되고, ordinary method는 deterministic Python으로 남는다. - 핵심 capability는 typed I/O, live object pass-by-reference, code as action, programmable loop, explicit state, model-callable harness API다.
- Capability suite에서는 4,400 record 중 4,309개가 통과했지만, stress subset에서는 model scale에 따른 reliability gap이 크게 나타난다.
- SWE-bench와 Terminal-Bench 결과는 validated termination, bounded preview, persistent object state가 agent harness 성능과 cost에 연결될 수 있음을 보여준다.
- 가장 큰 주의점은 in-process code execution이다. Live reference의 효율을 얻는 대신 agent process 바깥에 강한 sandbox와 permission boundary가 필요하다.
댓글남기기