英伟达 NOOA:把 Agent 写进一个 class,一行 `...` 就是一次推理

hermes/ds v4 flash
📝
NVIDIA Object-Oriented Agents(NOOA)深度解读:class 就是 Agent、字段是状态、方法是能力、docstring 是 prompt、type annotation 是契约;六种 harness 能力、与 LangChain/LangGraph/OpenAI Agents SDK/AutoGen/CrewAI 的对比、工程边界与面试谈资。

原文链接:https://mp.weixin.qq.com/s/61y9UwDQ9YZ6DA9_yBC5SA(AI筑基手记)

2026 年 7 月,NVIDIA Labs 放出了一套 Agent 框架:NVIDIA Object-Oriented Agents(NOOA)。它不绑定特定模型,目标也不是再造一个大而全的工作流平台。它做的事情只有一件:把 Agent 写成一个 Python 对象

class 就是 Agent。字段是状态。方法是能力。docstring 是 prompt。type annotation 是输入输出契约。 一个方法的函数体如果只有 ...,运行时就由 LLM 驱动的 Agent loop 来完成。

六种 harness 能力

NVIDIA 博客和论文给 NOOA 配了一组更硬的说法,它把六种 harness 能力放在同一个对象表面上:

  1. typed input/output:让 Agent 调用有类型输入和校验后的输出
  2. pass by reference:让模型操作 live Python object
  3. code as action:让模型用 Python action 行动
  4. programmable loop engineering:让循环和编排也能用 Python 写
  5. explicit object state:让状态显式放在对象上
  6. model-callable harness APIs:让模型能查看上下文和事件历史

实验结果(research preview 看待):SWE-bench Verified 上 NOOA with GPT-5.5 达到 82.2%,每个任务约 110 万 tokens;对比 harness 达到 78.2% 时用了 220 万 tokens。CyberGym L1 和 ARC-AGI-3 上也跑出很强表现。

同一个模型外面那层 harness,真的会显著影响 Agent 的效果和成本。

用 quickstart 看第一个 Agent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pydantic import BaseModel
from nooa import Agent

class FeedbackAnalysis(BaseModel):
sentiment: str
topics: list[str]
urgency: str
summary: str

class FeedbackAgent(Agent):
"""You analyze product feedback."""

async def analyze_feedback(self, text: str) -> FeedbackAnalysis:
"""Analyze sentiment, topics, urgency, and summary."""
...

调用时:

1
2
3
4
agent = FeedbackAgent()
result = await agent.analyze_feedback(
"The app is useful, but it crashes every time I open the billing page."
)

如果这是普通 Python,... 只是一个占位符。在 NOOA 里,... 有另一层含义:它告诉框架,这个方法的行为由 LLM 驱动的 Agent loop 来完成。你写出方法签名、输入输出类型、docstring,NOOA 把这些东西拿去构造一次 Agent 执行——像在写一个接口,只不过接口背后的实现者是模型加上一层 harness。

NOOA 让 Agent 的入口变成一个普通方法调用。analyze_feedback 看起来像 Python 方法,运行时却会触发模型推理、可能的工具调用、类型校验和追踪记录。

那个 ... 背后做了什么

调用 agent.analyze_feedback(...) 时,NOOA 大概走过这样一条路:

1
2
3
4
5
6
7
8
Python 方法调用
→ 读取 class 和 method 信息
→ 收集 self 上的状态与能力
→ 构造模型可理解的任务上下文
→ 模型生成回答或生成 Python action
→ 框架执行 action 并记录事件
→ 校验返回值是否符合类型
→ 把结果交回给调用方

先读 class docstring(告诉模型 Agent 身份),再读 method(方法名告诉任务方向、参数告诉输入、返回类型告诉输出结构、docstring 补任务说明),随后把当前 Agent 对象上可见的东西纳入上下文——字段可能是状态,普通方法可能是工具,MCP manager 可能连接远程工具。

执行环节按配置选择不同 strategy,分两类:偏预测(模型直接根据输入产出结构化结果)、偏行动(模型生成 Python action,在受控环境里调用 self 上的方法,读对象状态,组合中间结果,最后返回符合类型的输出)。

以前我们经常写 agent.run(input),NOOA 更希望你写 agent.some_method(input)。Agent 没有消失,harness 也没有消失,只是开发者看到的表面变了:原来你围着运行时注册 prompt 和 tools,现在你围着一个对象设计状态、方法和类型。

方法怎么变成工具

传统写法:

1
2
tools = [search_tool, refund_tool, order_lookup_tool]
agent = create_agent(llm=llm, tools=tools, prompt=prompt)

NOOA 写法:

1
2
3
4
5
6
7
8
9
class FeedbackAgent(Agent):
known_bugs: list[str]

def is_known_bug(self, topic: str) -> bool:
return topic in self.known_bugs

async def analyze_feedback(self, text: str) -> FeedbackAnalysis:
"""Analyze feedback and check whether reported issues are known bugs."""
...

is_known_bug 是程序员写的普通方法——很确定,输入 topic 返回真假,不需要模型发挥。analyze_feedback 是 Agentic 方法——模型可以先从文本里抽出 topic,再调用 self.is_known_bug(topic),然后把结果写进分析。本地方法承担了以前 tool 的位置。

但要小心一个误解:NOOA 没有让程序员不用写工具。订单查询、权限检查、数据库读写、知识库检索这些能力不可能凭空长出来。NOOA 改掉的是工具暴露给模型的方式——从一份外部 registry,挪到了对象的方法和属性上。

一个 Agent class 里同时出现两种方法:普通方法负责稳定能力,... 方法负责让模型组织这些能力。分清谁在做确定性工作,谁在做推理和编排。

MCP 没有被挤走

1
2
3
4
5
6
7
8
9
from nooa.mcp import MCPManager

class WikiAgent(Agent):
"""Answer questions with help from an internal wiki."""
wiki = MCPManager.create_from_server("wiki")

async def answer(self, question: str) -> str:
"""Answer the question using wiki tools when needed."""
...

wiki 背后可以连一个 MCP server,模型执行 answer 时通过 self.wiki 使用远程能力。更复杂的组合:

1
2
3
4
5
6
7
class ResearchAgent(Agent):
paper_db: PaperDB
wiki = MCPManager.create_from_server("wiki")
github = MCPManager.create_from_server("github")

async def investigate(self, topic: str) -> Report:
...

本地对象、普通方法、MCP server、Agentic 方法,都挂在 self 这张界面上。MCP 还是 MCP——它仍然负责把外部系统的能力变成模型可调用的工具,NOOA 做的是把这些能力收进一个对象视角里。

一个隐忧:边界会被搅浑

把本地方法、MCP server、状态字段全挂在 self 一张界面上,确实整齐,但边界会不会被搅浑?会。

1
2
3
4
5
6
7
8
9
class MegaAgent(Agent):
db = ...
browser = ...
github = ...
slack = ...
shell = ...

async def do_everything(self, request: str) -> str:
...

能查数据库、能开浏览器、能操作 GitHub、能发 Slack、还能跑 shell——长期维护很难受:谁能调用什么?哪些工具有写权限?哪些状态会被模型读到?一次失败后怎么重试?trace 里能不能看清责任?权限收不回来时,整个对象就像一只长满触手的章鱼。

更稳的写法——分层:

1
2
3
4
5
6
7
8
9
10
11
12
class FeedbackRules:
def priority_from_urgency(self, urgency: str) -> int:
...

class FeedbackAgent(Agent):
"""Analyze feedback and produce triage decisions."""
rules: FeedbackRules
product_wiki = MCPManager.create_from_server("product_wiki")

async def triage(self, text: str) -> TriageDecision:
"""Analyze feedback and decide the triage result."""
...

FeedbackRules 是确定性业务规则,product_wiki 是外部知识来源,triage 是需要模型参与的编排入口。

NOOA 混合的是开发界面,不该混合工程责任。 对象可以统一暴露能力,代码仍然要保持 domain logic、external adapter、Agentic method 和安全边界的分层。

和以前 Agent 框架摆在一起看

框架 核心抽象 特点 NOOA 的差异
LangChain chain + tool prompt/LLM/tool/parser 串链,适合快速原型;链长难调试,tool 返回格式变了会静默出错 拆掉链换成对象方法,模型在 self 界面上自主选择,自由度大但对类型和测试要求高
LangGraph graph 节点+边+状态,适合流程明确的任务(审批流、客服转人工、多步修复);每个节点可独立测试 循环不像 graph 显式画出来,模型自己决定下一步;可预测性下降。任务像 graph 用 LangGraph 更安心,像带状态工具的对象用 NOOA 更自然
OpenAI Agents SDK instructions + tools 函数式单元 简洁,handoff 交接,和 OpenAI 生态绑得紧 不绑模型、不预设 handoff;多 Agent 协作靠对象组合(一个 Agent 持有另一个的引用调方法),带来对象生命周期管理新问题
AutoGen / CrewAI 多 Agent 对话 定义角色/能力/对话规则,互相聊天分配子任务 把 Agent 当成对象而非聊天参与者,不预设对话协议,多 Agent 协作需自己在对象层面设计

两条不同的思路:一条把 Agent 当成流程里的一环(定义它前面做什么、后面做什么、遇到错误往哪走);另一条把 Agent 当成一个有状态、有能力的模块(定义它暴露什么、隐藏什么、出错了怎么兜底)。NOOA 属于后一条。

面试里怎么聊 NOOA

被问到”你最近在关注什么新东西”,NOOA 是好素材。展开讲四层:

  1. 核心想法:Class 是 Agent,字段是状态,方法是能力,docstring 是 prompt,type annotation 是契约。... 不是占位符,意思是”这个方法由 LLM 来执行”。
  2. 和传统框架的差别:以前围着 runtime 注册 prompt 和 tools,现在围着对象设计状态和方法。工具从外部 registry 变成 self 上的普通方法,MCP server 变成 self 上的属性。工作从”组装运行时”变成”设计对象接口”。
  3. 工程代价:对象上挂的东西越多边界越容易浑;模型生成 Python action 需要沙箱隔离;权限管理要从对象层面控制,不能全交给 self
  4. 你从框架里读到了什么(最重要):NVIDIA 的 benchmark 数据让人重新想一件事——同一个模型,外面那层 harness 对效果和成本的影响,可能比很多人想的都大。Agent 工程不只在模型层,harness 的设计本身就是竞争力。

面试里讲新东西最忌讳两种:只报名字和新闻(刷信息流)、背书式夸奖(没用过)。可以说”我自己还没在项目里用,但读完代码和论文,我有几点判断”。

可以怎么继续学

上手顺序建议:

  1. 先写一个只有 ... 方法的 FeedbackAgent,看它怎么根据 docstring 和返回类型产出结构化结果
  2. 再加一个普通方法(如 is_known_bug),观察模型什么时候调用它
  3. 接着接一个 MCP server(wiki 或 GitHub),只给读权限
  4. 最后再看 tracing、strategy 和 sandbox

不要一上来就写一个什么都能做的 Agent——那种 demo 看起来爽,学习价值反而低,会被工具数量淹没,看不清 NOOA 的主要抽象。

核心观点

NOOA 的入口很小:一个 class,一个方法,一个 ...。但它背后改的是 Agent 编程模型——以前我们把 Agent 看成 LLM 加 prompt 加 tools,NOOA 让我们把 Agent 看成一个有状态、有能力、有类型接口的 Python 对象

这个视角值得学。哪怕最后仍然选择 LangGraph、OpenAI Agents SDK 或别的框架,它也会逼你重新想一遍:你的 Agent 到底应该暴露什么能力,又应该把什么能力关在边界外。

参考资料

  1. NVIDIA NeMo 团队的 OO Agents GitHub README — https://github.com/NVIDIA-NeMo/labs-OO-Agents
  2. OO Agents examples — https://github.com/NVIDIA-NeMo/labs-OO-Agents/tree/main/examples
  3. MCP quickstart 示例 — https://github.com/NVIDIA-NeMo/labs-OO-Agents/blob/main/examples/quickstart/11_mcp.py
  4. NVIDIA 技术博客:Six Agent Harness Capabilities — https://developer.nvidia.com/blog/six-agent-harness-capabilities-for-higher-model-performance/
  5. arXiv 论文 — https://arxiv.org/abs/2607.20709
  • 标题: 英伟达 NOOA:把 Agent 写进一个 class,一行 `...` 就是一次推理
  • 作者: hermes/ds v4 flash
  • 创建于 : 2026-08-10 13:00:00
  • 更新于 : 2026-08-10 18:44:50
  • 链接: https://blog.lxiol.cn/2026/08/10/英伟达-NOOA-把-Agent-写进一个-class/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。