OpenProgram · NOOAEnglish

直接调用、Agent 类与自动 DAG 上下文

维护者设计 · 装饰器移除实施中

设计范围

Agent 定义可复用模型配置。Context 定义父继承、命名内容、动态求值和历史可见性。

框架自动管理执行上下文。应用调用 agent() 或普通 Agent 方法,不需要手动构造 Context。

所有入口复用 Runtime、AgentSpec、工具策略、执行控制和 Session DAG。Agent 配置不默认拥有会话。

建议的用法

直接调用

from openprogram import agent

answer = agent("Analyze this paper",
               model="configured-model")

使用当前执行的会话;没有外层执行时创建独立的一次执行。

复用一个 Agent

from openprogram import Agent

bot = Agent(model="configured-model")
first = bot("Investigate this topic")
second = bot(first)

无需继承类。实例复用配置;历史是否连续由执行作用域决定。同一实例的独立顶层调用默认互不共享历史。

定义自己的类

class Researcher(Agent):
    model = "configured-model"

    def research(self, question):
        text = self.prepare(question)
        return self(text)

    def prepare(self, text):
        return text.strip()

result = Researcher().research("A topic")

方法正常编写,无逐方法装饰器;research 和 prepare 均可记录。self(...) 使用相同的 Agent 执行内核。

小写 agent() 提供便捷调用入口,大写 Agent 是可配置、可调用的类。二者进入同一内部执行函数;不创建进程级共享历史的默认 Bot。构造实例只解析配置,不发模型请求、不分配持久会话。首次执行时才解析会话和创建调用。

同步入口用于普通同步代码;异步代码使用 await bot.arun(...) 或 await agent_async(...)。已有事件循环内使用异步入口。

上下文的生命周期

触发条件规则
进入程序或顶层调用框架建立执行作用域并绑定当前 Context;存在外层会话时沿用其明确分支。
进入函数或方法派生当前作用域,保留父子关系与可见性规则;不复制完整历史。
每次模型请求之前解析 Context 的 DAG 来源、实例内容、动态内容和调用覆盖;筛选、去重、预算处理后生成模型输入。
模型返回将模型交互更新到当前执行状态;下一次构建可按规则使用。
函数返回、异常或取消恢复父作用域;已产生的模型交互按 expose / render_range 可见,不把全部子上下文无条件合并。
离开顶层执行解除任务局部绑定并关闭本次拥有的资源。复用 Agent 本身不延续历史;会话续接由宿主或显式会话选择决定。

调用关系决定上下文的继承与可见性;predecessor 决定会话顺序。Context 是运行时的构建对象,不要求复制或持久化每个时刻的完整内容。会话持久化与 Trace 导出沿用现有机制,不能决定 Agent 的上下文所有权。

每个 Agent 执行分别管理自己的 DAG。聊天只记录用户消息、模型请求和直接工具调用;普通辅助函数不产生持久化节点。调用另一个 Agent 时创建独立 DAG,父图只保存调用结果和 child_session_id 引用。权限、取消与文件修改备份仍由原执行负责,不依赖图所有权。

行为测试的宿主在应用 OS 沙箱前初始化标准 SessionStore。测试进程通过 Context 绑定已有 SessionNodeWriter,Agent 调用复用它。候选测试不能创建子进程、修改源码快照、访问宿主秘密或使用网络。取消结束规范执行;结果未知的外部操作记录仍可检查。确切的 assistant 投影在节点或存储暂不可用时通过持久 DAG outbox 重试。显示投影失败不能使已执行的取消变为拒绝。取消带恢复错误的暂停执行时,显示状态改为 cancelled,清除错误元数据,并保留已有内容。资源释放 observer 失败仍向控制调用方报告。普通 Job 完成由持有结果的执行方完成投影。取消通知不能在该执行方提供结果与错误详情前写入其他 Job 终态。

普通函数采集

def research(question):
    data = prepare(question)
    return analyze(data)

def prepare(question):
    return question.strip()

def analyze(data):
    return agent(data)

受管 Program 加载器保留必要的 Runtime 注入与入口适配,内部辅助函数使用轻量作用域,不逐次持久化。函数签名、返回值、异常与 import 保持正常 Python 语义。

Agent 子类的实例、静态和类方法遵循 Agent 所有权边界。普通内部方法不写入 DAG,也不自动注册为工具。

方法配置函数与受管 Agent 方法使用同一作用域处理,不重复采集。入口登记与工具暴露保持显式。

任意宿主文件、依赖包、lambda、动态生成代码与不可用源码不自动采集。其 agent() 调用仍可执行,普通 Python 调用关系不逐次持久化。

统一 Context 模型

Context 具有父作用域、命名内容、动态提供函数和历史可见性规则。每次模型请求都使用同一模型。

组成行为
父作用域子上下文继承当前执行上下文。作用域退出时恢复父绑定。
内容命名值提供指令和业务数据。子上下文可覆盖名称,不修改父上下文。
求值Python 提供函数收到当前 Context,在框架构建模型请求时运行。
可见性现有 Session DAG 按分支、调用作用域、expose 和 render_range 选择允许读取的交互。

实例绑定是命名内容的特例。调用派生 Context 时加入 Agent 配置中的内容。Agent 实例不默认拥有执行会话。

父 Context + Agent 配置 + 调用内容
    → 派生当前 Context
    → 求值命名内容并选择可见 DAG 交互
    → 应用现有预算与压缩
    → Runtime.exec 模型请求

历史选择与内容替换分别处理。文本相同不表示交互相同。框架在实际请求中记录选中的 DAG 身份。

Context 不增加第二个历史数据库或 provider 运行时。工具授权与历史可见性分别处理。

显式事件生命周期

Context(store=writer, head_id=None) 为重复调用绑定现有 SessionNodeWriter。Context 会话通过已有 Session DAG 续接模型交互。

head_id 选择已有分支末端。未指定末端时续接当前会话末端。调用者拥有显式提供的 store。

NOOA 式实例事件生命周期属于显式 Context 绑定。Agent 配置仍不默认拥有历史。

参数与权限

配置归属与优先级
model / effort / instructions / tools显式调用覆盖 → 实例配置 → 类默认 → 当前会话默认 → 产品默认。缺省与空列表等显式清空须区分。
expose / render_range默认沿用现有行为;method_options 或显式函数元数据提供可见性设置。
tool visibility / approval独立于 DAG 可见性;必须经过现有工具注册、权限检查和审批。更内层的配置不能扩大外层权限。
AgentSpec / session持久 Bot 配置复用 AgentSpec;执行作用域引用 Session DAG;实例可配置 Context 内容与来源。仅创建 Python 对象不自动创建侧栏 Bot 或调度任务。

构建上下文默认只需要模型交互与必要调用关系;普通函数的完整参数、返回值和局部变量不属于默认采集要求。显式传入的模型内容仍需有界序列化;self 使用身份或类型引用,不序列化整个对象或客户端。方法名必须包含模块及类限定名,避免两个类的同名方法冲突。普通 Python 副作用仍遵循宿主执行和沙箱规则;记录调用不等于对任意 Python 操作逐一审批。

并发与失败

当前 Context、调用身份和有效配置使用任务局部绑定,进入时保存、退出时恢复。继承时不复制完整历史,也不共享可被原地修改的分支配置。嵌套调用可重入;共享 Agent 配置不意味着共享执行上下文。若显式使用可变实例内容或共享会话,需要明确的同步与快照规则。

异常、超时、取消均恢复父作用域,保留已完成模型交互的状态。独立调用复用现有权限请求上下文;缺少审批时沿用非交互规则。恢复仍通过现有安全点和显式步骤完成,不恢复任意 Python 栈。

代码范围

Context 位于 openprogram/context/model.py,Agent 位于 agentic_programming/agent_class.py,共用调用作用域位于 agentic_programming/call_scope.py。

Runtime 历史选择保留在 agentic_programming/runtime/history.py。受管源码加载保留在 programs/_source_loader.py 和 _registry.py。

实现状态以代码审查与受影响检查为依据,在文末集中说明。

与 NOOA 的关系

方面取舍
类与方法采用类声明、普通调用以及框架内部方法处理;实例绑定本身已存在。
Context 建模采用类与实例式内容管理、动态求值和作用域覆盖;增加已有 DAG 来源与组合能力。默认有效上下文属于执行作用域。
历史筛选NOOA 按 call_id 筛选当前调用事件,并支持嵌套配置继承。我们保留 DAG 分支和范围规则;不能声称对方完全不按调用调整上下文。
Trace 与模型上下文NOOA 区分 events 与 traces。我们保留单一 DAG,生成不同展示和模型输入视图。
生成策略Predict、CodeAct、typed return 和持久 cell 作为后续独立方案,复用现有 llm/agent 执行内核。
不采用逐函数手写上下文、进程全局共享历史、全解释器追踪、第二套 provider/事件数据库。

公开文档和示例注明受 NVIDIA-labs OO Agents 启发,并引用其仓库与论文。独立实现不声称上游 API 兼容或复现其基准结果;若复用源码,保留对应许可和署名。

统一建模与实现契约

Context 是具有父作用域、命名内容、动态提供函数和历史可见性规则的类。所有请求使用相同的构建过程。DAG 只提供可见交互及调用关系,不作为另一个策略。实例绑定是该模型的特例:将命名内容绑定到实例,再在调用时加入当前作用域。

公共入口为 Agent、Context、agent 和 agent_async。Agent.__call__ 提供同步调用,Agent.arun 提供异步调用。Agent 子类普通方法,包括单下划线帮助方法,自动建立作用域。已登记 Program 的普通函数由限定源码加载器处理。业务代码不需要逐方法配置,也不需要手动启动 Trace。

Context.current() 返回当前绑定。Context.derive() 派生子上下文。Context 支持命名内容、动态提供函数以及可选历史过滤。bind() 发布当前任务上下文及显式 writer,拒绝与已有 writer 冲突,退出时恢复绑定。动态提供函数收到当前 Context,构建请求时求值。使用 Python callable,不执行字符串表达式。显式修改实例内容保留在实例上,框架生成的模型交互属于执行会话。

CallScope 区分正式操作记录与轻量函数执行。另一个 Agent 使用独立会话写入器;同一 Agent 的内部方法不新增结构节点。

模型请求继续经过 Runtime.exec、现有 provider、工具权限、预算及压缩路径。无外层执行时创建本次拥有的执行环境,结束时释放。模型配置相同或 Agent 对象相同不意味着会话相同。异步分支各自派生上下文,并保留明确调用身份。

实现包含统一 Context、Runtime 构建、共享调用作用域、Agent 类与便捷入口、已登记源码采集、产品文档和已交付程序迁移。NOOA 的 Predict、CodeAct 与完整 API 兼容不属于上下文建模等价的要求。

按用户要求优先编码。只运行覆盖公共入口、分支隔离、迁移后的原有行为及权限的必要验证。验证在后台进行,独立编码继续。完成前检查结果,不用未结束的测试证明完成。英文接口说明与错误信息采用 ASD-STE100 的简短句和一致术语。

实现范围

实现复用 Session DAG、Runtime、AgentSpec、权限检查、预算和压缩。方法配置与新自动作用域使用共用的调用作用域。

自动源码采集仅作用于受管 Program 加载器选择的源码。普通宿主文件和依赖包不自动采集函数调用。

入口登记和工具暴露保持显式。记录私有帮助函数不会将其注册为模型工具。生成器创建不产生已完成函数记录。

公共 API 与实际迁移必须符合已实现的加载器和执行范围。独立代码审查和受影响检查决定下方实现状态。

验收标准

场景可观察结果
无模型父函数research → prepare / analyze → agent 的所有范围正确;模型输入输出和失败归属可核对;无需完整保留普通函数数据。
Context 继承与求值父继承、实例内容、动态求值与可见性均通过公共入口验证;动态内容每次请求刷新,同名覆盖顺序固定,来源去重不丢失重复发生的交互。
上下文等价相同 DAG、分支和规则下,新旧入口得到相同历史;只在真实请求产生时记录读取来源。
会话生命周期构造无副作用;同实例跨任务不混入历史,不同 Agent 在同任务按规则共享;独立调用隔离,已有会话显式续接。
自动预算与压缩长历史触发已有压缩且引用可追踪;摘要费用计入预算;失败不静默遗失历史。
并发与资源兄弟任务分支隔离、共享可变实例状态的并发策略、内部重入、取消/异常后父范围恢复、资源仅由所有者关闭。
加载与身份只采集授权源码;递归/导入别名保持记录;跨类同名方法不冲突;方法配置无重复节点。
权限及展示方法被记录不自动成为工具;拒绝先于副作用;独立入口同样执行策略;默认 App 展示函数、模型、工具和最终状态。

验证只执行公共入口、分支隔离、原有行为与权限所需的最少受影响检查,以已完成结果作为证据。默认安装 App 使用 default profile、18100、~/.openprogram 和 /Applications/OpenProgram.app。

可恢复函数执行在初次调用与继续执行时使用同一个持久函数节点 ID。模型 tool-call 身份保存在节点元数据中。继续执行复用该节点,并保留原始模型调用身份。

移除装饰器 API

Agent 与 Context 承担完整函数执行约定。删除的公共装饰器没有兼容适配器。Agent method_options 保留输入元数据、Runtime 注入、登记与 schema、可用性、权限、可见性、重试、执行控制及显式步骤恢复。

受管源码声明 PROGRAM_ENTRIES 与 __agent_options__。内部模块引用 call_state.py,方法使用 _is_agent_method 标记。已有执行节点数据格式与会话历史保留。

第一方 workflow、生成程序、扫描器、分发器和文档使用普通 Agent 类,不引入替代装饰器。

GUI、research 与 wiki harness 的源码迁移保留 agentics 包路径。构建阶段使用 scripts/release/program-patches 内的源码补丁产生待安装包,stage-program-source.py 应用源码变更,product-runtime.json 校验 checksum。这属于源码构建步骤,不是运行时兼容适配器。

六个本机已发布 workflow 同样在隔离源码中迁移并本地集成。本机与打包变更不证明上游远端已经发布。

装饰器移除和源码迁移已实现,独立规格审查与质量审查均通过。针对性检查覆盖源码加载、schema、Runtime 注入、权限、取消、恢复及不同方法的身份。实际本地源码和已安装 App 的部署另行验收。

图所有权代码已通过独立规格和质量审查,安装版本行为单独验收。

实现状态

Agent 与 Context 公共入口及源码迁移已经存在,针对核心迁移的规格与质量审查已有结果。原框架全部行为的保留需要独立规格审查、质量审查及完整验证。任务证据分别记录这些结果与核心迁移检查。验收覆盖自动调用作用域、会话展示、Goal 生命周期、取消、恢复与随产品发布的 Programs。调用记录必须保留 DAG 关系,并保持会话内容、Session 所有权与执行权限。

项目状态
Session DAG、Runtime、AgentSpec、权限与压缩本设计复用现有实现。
公共 Agent 与 Context 入口、共用作用域及受管源码已实现。受影响公共入口检查与两项独立审查均通过。
默认安装 App 验收提交源码后单独核验安装状态。使用 scripts/refresh-local-app.sh 与默认安装 App;源码检查不能单独证明安装后的行为。
NOOA Predict、持久 cell 与 CodeAct 兼容不在本次实现范围内。不声称上游 API 兼容或基准等价。

参考资料

  1. ASD-STE100 skill
  2. NOOA ContextManager
  3. NOOA context construction
  4. NVIDIA-labs OO Agents: Native Python Object-Oriented Agents
  5. NOOA execution architecture
  6. NOOA context lifetime
  7. NOOA event selection
  8. NOOA event tagging
  9. NOOA tracing
  10. NOOA CodeAct reference
  11. NOOA source license
  12. OpenProgram execution contract
  13. OpenProgram Agent configuration contract