NOOA多智能体系统入门:子Agent隔离、状态共享与Python协调模式完全指南
【免费下载链接】labs-OO-AgentsNVIDIA Object Oriented Agents: the Pythonic way to build AI Agents.项目地址: https://gitcode.com/gh_mirrors/la/labs-OO-Agents
NOOA(NVIDIA Object Oriented Agents)是 NVIDIA 推出的 Pythonic AI Agent 框架,其多智能体系统的核心理念是:子 Agent 只是一个普通的 Python 对象。本文将带你快速掌握 NOOA 多智能体系统的三大核心模式——子 Agent 隔离、状态共享与 Python 协调,无需任何工作流引擎或 DAG 配置,用你熟悉的 Python 就能搭建可靠的多 Agent 应用。🚀
为什么需要多智能体系统?
很多 Agent 框架把提示词、工具、回调、工作流拆成一个个彼此独立的抽象。NOOA 换了一种思路:一个 Agent 就是一个 Python 类,字段是状态、方法是能力、docstring 是提示词、类型注解是契约。
当你发现某段工作需要独立的模型交互历史、上下文、工具状态或可复用的角色时,就应该引入一个子 Agent。判断标准很简单:
- 确定性操作(查价格、算折扣)→ 普通 Python 方法
- 共享同一角色、历史、工具→ 同一对象上的另一个 agentic 方法
- 需要隔离或可复用角色→ 拆出子 Agent
⚠️ 官方提醒:把每个函数都拆成 Agent 并不是可扩展性策略——Agent 越多,提示词、历史、模型调用和故障边界就越多。
子Agent隔离:每个Worker都有独立"工作台"
NOOA 多智能体系统的第一原则是隔离。一个正确隔离的子 Agent 应该拥有自己独立的:
- 事件历史与上下文块(per-instance event history and context blocks)
- 生成锁与 REPL 会话:每个 agentic 方法调用都有独立的 CodeAct REPL 会话,REPL 变量只在单次调用内跨代码单元保留
- 有状态工具与连接:在该实例上构造,而不是挂在类上
- 可见字段与角色指令
这里有一个新手最常踩的坑:
❌子 Agent 不会自动继承父 Agent 的历史和上下文!数据必须通过方法参数或共享应用对象显式传递。
方法签名本身就定义了交接契约——写作者不会悄悄依赖研究员的提示词历史,数据流向在代码里一目了然。这种隔离随 Python 对象的生命周期存在;默认存储是内存的,进程重启不会自动恢复旧历史,需要持久化时要显式使用存储与快照/恢复流程(详见 prompts-and-context.md)。
状态共享:显式传递,拒绝隐式全局
既然默认是隔离的,那么"共享"就应该被写得清清楚楚。官方文档 multi-agent-systems.md 给出的原则是:
- 交接靠类型化参数:
writer.write(question, evidence)—— 签名即文档,数据流可追踪 - 可复用状态放共享应用对象:比如共享的数据库客户端、配置对象,作为构造参数注入
- 不要传隐式全局状态:这是 NOOA 列举的常见错误之一
一个典型的隔离+显式交接示例(来自 multi-agent-systems.md):
class Researcher(Agent): async def research(self, question: str) -> list[str]: """Find evidence relevant to the question.""" ... class Writer(Agent): async def write(self, question: str, evidence: list[str]) -> str: """Write an answer supported only by the supplied evidence.""" ...注意evidence参数:写作者只依赖显式传入的证据,而不是研究员的整个对话记忆。
还有一个容易忽略的边界:独立的 Agent 和工具实例,并不能隔离它们共同指向的外部资源。并发的写操作仍然需要各自独立的工作树、沙箱、数据库命名空间,或围绕共享资源的确定性协调。
Python协调模式:串行的、并行的、LLM驱动的
NOOA 最 Pythonic 的地方在于:协调完全由普通 Python 完成。编排器不需要继承Agent——它不含 agentic 方法,就是正常的 Python 类:
class AnswerPipeline: def __init__(self, llm): self.researcher = Researcher(llm=llm) self.writer = Writer(llm=llm) async def run(self, question: str) -> str: evidence = await self.researcher.research(question) return await self.writer.write(question, evidence)这就是 orchestration.md 所说的"把图翻译成 Python":边变成普通调用,条件变成if,扇出变成asyncio.gather。
模式一:顺序交接(Sequential)
两个await调用串起来即可。顺序由你写死的代码保证,模型无需"记得"步骤。
模式二:并行扇出(Parallel)
⚡ 关键规则:每个并发任务使用独立的 Agent 实例。内置的 Predict 和 CodeAct 策略会在同一实例上串行化生成调用,并发调用同一实例会被内部锁排队;独立实例才能真正并行。
模式三:LLM 驱动生成(Model-directed)
CodeAct 方法本质是让模型写 Python,而"生成子 Agent"本身也是普通 Python——所以只要模型知道子 Agent 类存在(例如暴露为类属性出现在doc(self)中),它就能在运行时自主决定何时创建、以什么顺序调用子 Agent。
这三种模式的完整可交互教程见 04_composing_subagents.ipynb,用了一个"周末行程规划"的玩具场景演示父子 Agent 协作。
模型继承:子Agent自动"认亲"
在父 Agent 的活跃调用内创建的子 Agent,如果自己的类没有声明llm=,会自动继承父 Agent 的模型。
但在应用层编排器或构造函数中创建 Agent 时(此时没有活跃的父调用上下文),应显式传入llm=:
self.researcher = Researcher(llm=accurate_llm) # 高精度模型 self.writer = Writer(llm=fast_llm) # 低延迟模型显式构造更易于理解,也让"每个角色用什么模型"清晰可见——这是按角色做模型选择的推荐姿势。
与Supervisor/图节点框架的对比
你完全可以在 NOOA 中实现 Supervisor、路由器、辩论模式、Worker 池等经典模式:Supervisor 通常就是一个 Python 编排器或一个专注的路由方法,Workers 就是普通 Agent 实例。
区别在于:状态归属和交接都摆在 Python 代码里,而不是藏在框架管理的图状态中。这对调试、测试和重构非常友好。
新手常见误区清单 🧭
来自 multi-agent-systems.md 的官方避坑指南:
- 误以为子 Agent 会继承上下文块或对话历史
- 多个并行任务共享一个有状态工具或同一个 Agent 实例
- 传递隐式全局状态而不是类型化参数
- 用 LLM Supervisor 做本可以用确定性 Python 表达的路由
- 在没有明确隔离或专业化收益时创建过多角色
从哪里开始
- 概念文档:multi-agent-systems.md、orchestration.md
- 交互教程:04_composing_subagents.ipynb、tour.md
- 策略实现源码:src/nooa/strategies/
- 框架核心包:src/nooa/
一句话总结 NOOA 多智能体系统的哲学:模型负责判断,Python 负责流程。对象组合成系统,代码即编排。🐍
【免费下载链接】labs-OO-AgentsNVIDIA Object Oriented Agents: the Pythonic way to build AI Agents.项目地址: https://gitcode.com/gh_mirrors/la/labs-OO-Agents
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考